Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 2 of 2

Thread: Teensy 3.6 FlexTimer PWM DMA Issue

  1. #1
    Junior Member
    Join Date
    Nov 2021
    Posts
    2

    Teensy 3.6 FlexTimer PWM DMA Issue

    Hi all,

    I'm working on configuring DMA on the Teensy 3.6 platform to run 4 PWM signals from the contents of a buffer. I'm having issues getting DMA to write the correct bytes currently. Ideally, I want the minor loop to copy 16 bytes (4 32bit ints) to FTM0_C0V, FTM0_C1V, FTM0_C2V, and FTM0_C3V and then offset back to FTM0_C0V after all 16 bytes are written. Right now it seems to be copying to the correct memory positions but it's not writing the data I'd expect it to. Here's my source code:

    Code:
    #include <DMAChannel.h>
    
    //128 bytes, 4 channels, *2 for circular buffer
    #define PWM_BUF_SIZE 128*4*2
    DMAMEM __attribute__((aligned(32))) uint32_t pwm_dma_buffer[PWM_BUF_SIZE];
    DMAChannel pwm_dma(true);
    
    void pwm_isr(void)
    {
      //Serial.println("i");
      int16_t *src;
      uint32_t *dest;
      uint32_t saddr;
    
      saddr = (uint32_t)(pwm_dma.TCD->SADDR);
      pwm_dma.clearInterrupt();
      if (saddr < (uint32_t)pwm_dma_buffer + sizeof(pwm_dma_buffer) / 2) {
        // DMA is transmitting the first half of the buffer
        // so we must fill the second half
        dest = &pwm_dma_buffer[PWM_BUF_SIZE/2];
      } else {
        // DMA is transmitting the second half of the buffer
        // so we must fill the first half
        dest = pwm_dma_buffer;
      }
      
      for (int i=0; i < PWM_BUF_SIZE/8; i++) {
        uint16_t sample = 0;//0+0x8000;
        uint32_t msb = 255;//((sample >> 8) & 255);
        uint32_t lsb = 255;//sample & 255;
        *dest++ = 400;//msb;
        *dest++ = 400;//lsb;
        *dest++ = 400;//msb;
        *dest++ = 400;//msb;
        
    //    *dest++ = 10;//lsb;
    //    *dest++ = 200;//msb;
    //    *dest++ = 100;//lsb;
    //    *dest++ = 10;//msb;
      }
    }
    
    void setup() {
      // put your setup code here, to run once:
      SIM_SCGC6 |= SIM_SCGC6_FTM0;
      
      FTM0_SC = 0;
      FTM0_CNT = 0;
      FTM0_MOD = 543;
      FTM0_C0SC = 0x69;  // send DMA request on match
      FTM0_C1SC = 0x28;
      FTM0_C2SC = 0x28;
      FTM0_C3SC = 0x28;
      FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
      CORE_PIN22_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
      CORE_PIN23_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
      CORE_PIN9_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
      FTM0_C0V = 120;
      FTM0_C1V = 0;
      FTM0_C2V = 0;
      FTM0_C3V = 0;
    
      for (int i=0; i<(PWM_BUF_SIZE/4); i+=4) {
        pwm_dma_buffer[i] = 55;
        pwm_dma_buffer[i+1] = 60;
        pwm_dma_buffer[i+2] = 70;
        pwm_dma_buffer[i+3] = 80;
    }
      
      pwm_dma.TCD->SADDR = pwm_dma_buffer;
      pwm_dma.TCD->SOFF = 4;
      pwm_dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2)
        | DMA_TCD_ATTR_DSIZE(2);// | DMA_TCD_ATTR_DMOD(6);
    // write 16 bytes per minor loop (4 32bit values) and offset by -32 after minor loop to return to FTM0_C0V
      pwm_dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE 
        | DMA_TCD_NBYTES_MLOFFYES_MLOFF(-32)
        | DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
    // reset to start of buffer at end of major loop
      pwm_dma.TCD->SLAST = -sizeof(pwm_dma_buffer);
      pwm_dma.TCD->DADDR = &FTM0_C0V;
    // offset by 8 to skip the FTM0_CnSC fields
      pwm_dma.TCD->DOFF = 8;
      pwm_dma.TCD->CITER_ELINKNO = sizeof(pwm_dma_buffer) / 16;
      pwm_dma.TCD->DLASTSGA = 0;
      pwm_dma.TCD->BITER_ELINKNO = sizeof(pwm_dma_buffer) / 16;
      pwm_dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
      pwm_dma.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM0_CH0);
      pwm_dma.enable();
      pwm_dma.attachInterrupt(pwm_isr);
      Serial.begin(115200);
    
      
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      if (pwm_dma.error()) Serial.println("dma error :(");
      delay(100);
      Serial.print(FTM0_CNT);
      Serial.print(" ch0: ");
      Serial.print(FTM0_C0V);
      Serial.print(" ");
      Serial.print(FTM0_C0SC);
      Serial.print(" ch1: ");
      Serial.print(FTM0_C1V);
      Serial.print(" ");
      Serial.print(FTM0_C1SC);
      Serial.print(" ch2: ");
      Serial.print(FTM0_C2V);
      Serial.print(" ");
      Serial.print(FTM0_C2SC);
      Serial.print(" ch3: ");
      Serial.print(FTM0_C3V);
      Serial.print(" ");
      Serial.print(FTM0_C3SC);
      Serial.print(" ch4: ");
      Serial.print(FTM0_C4V);
      Serial.print(" ");
      Serial.print(FTM0_C4SC);
      Serial.print(" ch5: ");
      Serial.print(FTM0_C5V);
      Serial.print(" ");
      Serial.println(FTM0_C5SC);
      
    }
    And here's an excerpt from what it's printing currently:
    Code:
    8 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    460 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    439 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    413 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    381 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    361 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    329 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    295 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    269 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    236 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    214 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    188 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    164 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    134 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    103 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    74 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    532 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    494 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    473 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    450 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    423 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    395 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    363 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    335 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    306 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    268 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    242 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    229 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    191 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    156 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    129 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    104 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    75 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    542 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    516 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    489 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    460 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    430 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    387 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    377 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    334 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    312 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    276 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    265 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    234 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    193 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    174 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    145 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    117 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    92 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    5 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    465 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    421 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    400 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    373 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    345 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    323 ch0: 0 105 ch1: 465 168 ch2: 2069 168 ch3: 1997 168 ch4: 0 40 ch5: 0 40
    I would have excepted the channel values to all be 400, matching the values that get set in the ISR, but I'm having the hardest time trying to figure out why they aren't. Any guidance would be much appreciated.

  2. #2
    Junior Member
    Join Date
    Nov 2021
    Posts
    2
    Just managed to get this darn thing working... there were mostly just assorted one offs and messed up limits on the loops (DMA and otherwise). In case it may help someone, here's my code:
    Code:
    #include <DMAChannel.h>
    
    #define PWM_BUF_SIZE 256
    DMAMEM uint32_t __attribute__((aligned(32))) pwm_dma_buffer[PWM_BUF_SIZE];
    DMAChannel pwm_dma(true);
    
    void pwm_isr(void)
    {
      //Serial.println("i");
      int16_t *src;
      uint32_t *dest;
      uint32_t saddr;
    
      saddr = (uint32_t)(pwm_dma.TCD->SADDR);
      pwm_dma.clearInterrupt();
      if (saddr < (uint32_t)pwm_dma_buffer + sizeof(pwm_dma_buffer) / 2) {
        // DMA is transmitting the first half of the buffer
        // so we must fill the second half
        dest = &pwm_dma_buffer[PWM_BUF_SIZE/2];
      } else {
        // DMA is transmitting the second half of the buffer
        // so we must fill the first half
        dest = pwm_dma_buffer;
      }
      
      for (int i=0; i <= PWM_BUF_SIZE/2; i+=4) {
    // can copy in values from other memory here to modify duty cycle on FTM0_C0V,FTM0_C1V,FTM0_C2V
    // FTM0_C3V is also written to by dma but not configured for output since I only need 3 signals
        *dest++ = 50
        *dest++ = 100;
        *dest++ = 200;
        *dest++ = 300;
      }
    }
    
    void setup() {
      // put your setup code here, to run once:
      SIM_SCGC6 |= SIM_SCGC6_FTM0;
      
      FTM0_SC = 0;
      FTM0_CNT = 0;
      FTM0_MOD = 543;
      FTM0_C0SC = 0x69;  // send DMA request on match
      FTM0_C1SC = 0x28;
      FTM0_C2SC = 0x28;
      FTM0_C3SC = 0x28;
      FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
      CORE_PIN22_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
      CORE_PIN23_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
      CORE_PIN9_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
      FTM0_C0V = 120;
      FTM0_C1V = 0;
      FTM0_C2V = 0;
      FTM0_C3V = 0;
    
      for (int i=0; i<(PWM_BUF_SIZE); i++) {
        pwm_dma_buffer[i] = 55; // zero must not be used
      }
    
      pwm_dma.disable();
      pwm_dma.TCD->SADDR = pwm_dma_buffer;
      pwm_dma.TCD->SOFF = 4;
      pwm_dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2)
        | DMA_TCD_ATTR_DSIZE(2);// | DMA_TCD_ATTR_DMOD(4);
      pwm_dma.TCD->NBYTES_MLOFFYES = DMA_TCD_NBYTES_DMLOE 
        | DMA_TCD_NBYTES_MLOFFYES_MLOFF(-32)
        | DMA_TCD_NBYTES_MLOFFYES_NBYTES(16);
      pwm_dma.TCD->SLAST = -PWM_BUF_SIZE*sizeof(uint32_t);
      pwm_dma.TCD->DADDR = &FTM0_C0V;
      pwm_dma.TCD->DOFF = 8;
      pwm_dma.TCD->BITER_ELINKNO = PWM_BUF_SIZE*sizeof(uint32_t) / 16;
      pwm_dma.TCD->DLASTSGA = -32;
      pwm_dma.TCD->CITER_ELINKNO = PWM_BUF_SIZE*sizeof(uint32_t) / 16;
      pwm_dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
      pwm_dma.triggerAtHardwareEvent(DMAMUX_SOURCE_FTM0_CH0);
      pwm_dma.enable();
      pwm_dma.attachInterrupt(pwm_isr);
      Serial.begin(115200);
      //Serial.flush();
      
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      for(int i = 0; i < 10; i++) {
      if (pwm_dma.error()) Serial.println("dma error :(");
      delay(100);
    //  Serial.print("CITER: ");
    //  Serial.print(pwm_dma.TCD->CITER_ELINKNO);
    //  Serial.print(" CNT: ");
    //  Serial.print(FTM0_CNT);
    //  Serial.print(" ch0: ");
    //  Serial.print(FTM0_C0V);
    //  Serial.print(" ");
    //  Serial.print(FTM0_C0SC);
    //  Serial.print(" ch1: ");
    //  Serial.print(FTM0_C1V);
    //  Serial.print(" ");
    //  Serial.print(FTM0_C1SC);
    //  Serial.print(" ch2: ");
    //  Serial.print(FTM0_C2V);
    //  Serial.print(" ");
    //  Serial.print(FTM0_C2SC);
    //  Serial.print(" ch3: ");
    //  Serial.print(FTM0_C3V);
    //  Serial.print(" ");
    //  Serial.print(FTM0_C3SC);
    //  Serial.print(" ch4: ");
    //  Serial.print(FTM0_C4V);
    //  Serial.print(" ");
    //  Serial.print(FTM0_C4SC);
    //  Serial.print(" ch5: ");
    //  Serial.print(FTM0_C5V);
    //  Serial.print(" ");
    //  Serial.println(FTM0_C5SC);
    //  }
    //  for (int i = 0;i<PWM_BUF_SIZE;i++) {
    //    Serial.print(pwm_dma_buffer[i]);
    //    Serial.print(" ");
    //  }
    //  Serial.println();
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •