Go Down

Topic: Custom PWM setup (Read 2293 times) previous topic - next topic

gavinremme

Hello,

I am trying to get a pre-existing PWM function to work on a different SAMD21 board. This function seemed to work just fine on a MKRZero, but now I am using an Atmel Xplained Pro board (also with a SAMD21) but this function doesn't seem to produce a PWM signal. This should affect PA21, which is D7 on the MKRZero. PA21 is the pin I have my oscilloscope hooked to, but I'm not seeing a PWM signal. Like I was saying, it never had any problems on the MKRZero. Any ideas? Could the different bootloader be the issue?

Code: [Select]
void setup() {
  // put your setup code here, to run once:

}

void config_pwm(){

  //PWM resolution = frequency_gclock/N(TOP+1) N is clock divider which is 256 for us
 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(256) |          // Divide the 48MHz clock source by divisor 480: 48MHz/480=0.1MHz (same number as below)
                    GCLK_GENDIV_ID(4);              // Select Generic Clock (GCLK) 4
                   
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
                     
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the digital pin D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // Feed GCLK4 to TCC0 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
                     
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;    // Setup single slope PWM on TCC0
                   
  while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 9000;         // Set the frequency of the PWM on TCC0 to 10 (v)(4500 for 24ms)
 
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
 
  // Set the PWM signal to output 10us duty cycle
  REG_TCC0_CC3 = 2;         // TCC0 CC3 - on D7 (y)
 
  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
 
  // Divide the 48MHz signal by 256 giving 48MHz (5.33us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV256 |    // Divide GCLK4 by 1 Available: (1, 2, 4, 8, 16, 64, 256, 1024)
                    TCC_CTRLA_ENABLE;               // Enable the TCC0 output
                   
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
 
}

void loop() {
  // put your main code here, to run repeatedly:
  config_pwm();
}


Thank you!!

hzrnbgy

The clocking mechanism on the SAMD21 is more complex than it should be. But assuming your Xplained Pro boots up with the default 8 Mhz clock source and pre-scaled by 8 then fed to Clock Generator 0, the following code will generate a PWM signal on PA15



Quote
void timer3_init(void)
{
   // enable timer2/3 generic clock, use clock generator 0 at 1 MHz
   REG_GCLK_CLKCTRL = 0x00 | GCLK_CLKCTRL_ID_TCC2_TC3;
   while(REG_GCLK_CLKCTRL & 1<<14);
   REG_GCLK_CLKCTRL = GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3;
   REG_GCLK_CLKCTRL = 1 << GCLK_CLKCTRL_CLKEN_Pos | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3;
   
   // enable power manager for timer3
   REG_PM_APBCMASK |= PM_APBCMASK_TC3;
   
   // enable alternate pin function on PA15 for TC3 waveform output
   REG_PORT_DIRSET0 = PORT_PA15;
   PORT->Group[0].PMUX[7].reg = PORT_PMUX_PMUXO_E;
   PORT->Group[0].PINCFG[15].reg = PORT_PINCFG_PMUXEN;
   
   // reset TC3 registers
   REG_TC3_CTRLA = TC_CTRLA_SWRST;
   while(REG_TC3_STATUS & (TC_STATUS_SYNCBUSY));
   
   // run TC3 at 15625 Hz
   REG_TC3_CTRLA = TC_CTRLA_RUNSTDBY | TC_CTRLA_PRESCALER_DIV64 | TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_MPWM;
   
   // set top value, 10 Hz PWM
   REG_TC3_COUNT16_CC0 = 1562;

   // set duty cycle at 10%
   REG_TC3_COUNT16_CC1 = 156;
   
   // start timer3
   REG_TC3_CTRLA |= TC_CTRLA_ENABLE;
   
}


gavinremme

Thank you! We got it working with my original code, turns out it was a pin mapping issue I believe. I appreciate your help :)

Go Up