PWM using TCC0 on Feather M4 CAN

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Engeen
 
Posts: 1
Joined: Sun May 16, 2021 5:55 pm

PWM using TCC0 on Feather M4 CAN

Post by Engeen »

Just wanted to post a snippet on configuring TCC0 on the Feather M4 CAN Express. Not so much source code out there yet as it is a bit unique with its ATSAME51J19 but I see many benefits with a standard clock of 120 MHz and CAN peripheral built-in.

Once finding the libraries in C:\Users\%USERNAME%\AppData\Local\Arduino15\packages\adafruit\tools\CMSIS-Atmel\1.2.2\CMSIS\Device\ATMEL\same51\include\ and searching register names the correlation with the datasheet became a bit clearer. The configurations of the TCC0 is a lot similar to the SAMD51 but configuring the GCLK and PORT mapping is a bit different. A lot of help already from shawnhymel's blog and his reference to MartinL on the arduino forms leading into the right direction.

Here's my 20 kHz with configurable dutycycles for pin 12 (PA22) and 13 (PA23), where pin 13 is inverted:

Code: Select all

int period = 48*50 - 3;

void setupTimers()
{
  // Enable and configure generic clock generator 6
  REG_GCLK_GENCTRL6 = GCLK_GENCTRL_IDC |        // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |       // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL |    // Set the 120MHz clock source
                     4;                         // Divide

  while (GCLK->SYNCBUSY.reg);                   // Wait for synchronization

  // Feed GCLK4 to TCC0 and TCC1
  REG_GCLK_PCHCTRL25 = GCLK_PCHCTRL_BANNED |      // Enable GCLK4 to TCC0 and TCC1
                     GCLK_PCHCTRL_GEN_GCLK6;    // Select GCLK5
                     
  while (GCLK->SYNCBUSY.reg);                   // Wait for synchronization

  /////// TCC0 //////
  // Divide counter by 1 giving 48 MHz (20.83 ns) on each TCC0 tick
  TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV1_Val);

  // Use "Normal PWM" (single-slope PWM): count up to PER, match on CC[n]
  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM | TCC_WAVE_POL(1 << 3);         // Select NPWM as waveform, invert channel 3
  while (TCC0->SYNCBUSY.bit.WAVE);                // Wait for synchronization

  // Set the period (the number to count to (TOP) before resetting timer)
  TCC0->PER.reg = period;
  while (TCC0->SYNCBUSY.bit.PER);

  // Set PWM signal duty cycle
  // n for CC[n] is determined by n = x % 4 where x is from WO[x]
  TCC0->CC[2].reg = period * dutycycleW / 255;
  TCC0->CC[3].reg = period * (255-4) / 255;
  while (TCC0->SYNCBUSY.bit.CC2 || TCC0->SYNCBUSY.bit.CC3);
  
  ///////// PINS //////
  // Enable the port multiplexer for PA22
  PORT->Group[PORTA].PINCFG[22].reg |= PORT_PINCFG_PMUXEN;
  //PORT->Group[PORTA].PINCFG[22].reg &= ~PORT_PINCFG_PMUXEN; // disable mux

  // Connect TCC0 timer to PA22.
  // Odd pin num (2*n + 1): use PMUXO
  // Even pin num (2*n): use PMUXE
  PORT->Group[PORTA].PMUX[22/2].reg = PORT_PMUX_PMUXE(MUX_PA22G_TCC0_WO2);

  
  // Enable the port multiplexer for PA23
  PORT->Group[PORTA].PINCFG[23].reg |= PORT_PINCFG_PMUXEN;

  // Connect TCC0 timer to PA23.
  // Odd pin num (2*n + 1): use PMUXO
  // Even pin num (2*n): use PMUXE
  PORT->Group[PORTA].PMUX[23/2].reg |= PORT_PMUX_PMUXO(MUX_PA23G_TCC0_WO3); 
  

  /////// TCC1 //////
  // Divide counter by 1 giving 48 MHz (20.83 ns) on each TCC0 tick
  /*TCC1->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV1_Val);

  // Use "Normal PWM" (single-slope PWM): count up to PER, match on CC[n]
  TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM | TCC_WAVE_POL(63);         // Select NPWM as waveform, invert all channels
  while (TCC1->SYNCBUSY.bit.WAVE);                // Wait for synchronization

  // Set the period (the number to count to (TOP) before resetting timer)
  TCC1->PER.reg = period;
  while (TCC1->SYNCBUSY.bit.PER);

  // Set PWM signal duty cycle
  // n for CC[n] is determined by n = x % 4 where x is from WO[x]
  TCC1->CC[3].reg = period * 254 / 255;
  while (TCC1->SYNCBUSY.bit.CC3);
  
  ///////// PINS //////
  // Enable the port multiplexer for PA23
  PORT->Group[PORTA].PINCFG[23].reg |= PORT_PINCFG_PMUXEN;

  // Connect TCC0 timer to PA23. Function F is TCC0/WO[2] for PA18.
  // Odd pin num (2*n + 1): use PMUXO
  // Even pin num (2*n): use PMUXE
  PORT->Group[PORTA].PMUX[23/2].reg |= PORT_PMUX_PMUXO(MUX_PA23F_TCC1_WO7); 
*/
  
  // Enable output (start PWM)
  TCC0->CTRLA.reg |= (TCC_CTRLA_ENABLE);
  //TCC1->CTRLA.reg |= (TCC_CTRLA_ENABLE);
  
  // Wait for synchronization*/
  while (TCC0->SYNCBUSY.bit.ENABLE || TCC1->SYNCBUSY.bit.ENABLE);
}
SDS00002.png
SDS00002.png (18.23 KiB) Viewed 58 times
SDS00003.png
SDS00003.png (18.77 KiB) Viewed 58 times

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”