MKR Zero - PWM Burst

Hello guys and girls,

i have a MKR Zero and want to generate a PWM Burst (Frequency 1kHz and duty cycle of 30 %).

My target is to generate exactly 300 Pulses or 500 Pulses and so on, when a button is pressed.

I already read the topic on : Changing Arduino Zero PWM Frequency - Arduino Zero - Arduino Forum

But there is always a continious PWM generation. My example is already generating a 1kHz with 30% duty cycle, but continiously.

Can anyone help me please?

/*


//Output 1kHz 30% duty cycle
void setup() 
{ 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    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_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual 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 = 24000;                          // Set the frequency of the PWM on TCC0 to 1kHz
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
  
  // Set the PWM signal to output 30% duty cycle
  REG_TCC0_CC3 = 8000;         // TCC0 CC3 - on D7
  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
  
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

Hi Igoll88,

Probably the easiest way to generate a burst of 300 PWM pulses, is to activate the timer's interrupt service routine (ISR), get it to count the number of timer pulses and shut itself down when the pulse count reaches 300.

Here's some code that generates 300 pulses at 1kHz, 30% duty-cycle, every second on digital pin D6:

// Output a burst of 300 pulses at 1kHz, 30% duty-cycle on digital pin D6
void setup() {
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 3: 48MHz/1=48MHz
                     GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = 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 TCC0 PWM channel 2 (digital pin D6), SAMD21 pin PA20
  PORT->Group[g_APinDescription[6].ulPort].PINCFG[g_APinDescription[6].ulPin].bit.PMUXEN = 1;
  
  // Connect the TCC0 timer to the port outputs - 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_PMUXE_F;

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = 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

  NVIC_SetPriority(TCC0_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC0 to 0 (highest) 
  NVIC_EnableIRQ(TCC0_IRQn);         // Connect TCC0 to Nested Vector Interrupt Controller (NVIC)

  TCC0->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;          // Setup normal single slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);                 // Wait for synchronization
  
  TCC0->PER.reg = 47999;                           // Set the frequency of the PWM on TCC0 to 1Hz ((48MHz/1000)-1=47999)
  while (TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization 
   
  TCC0->CC[2].reg = 0;                             // Initialially set 0% duty cycle on D6 
  while (TCC0->SYNCBUSY.bit.CC2);                  // Wait for synchronization 
  
  TCC0->CTRLA.bit.ENABLE = 1;                      // Enable the TCC0 counter
  while (TCC0->SYNCBUSY.bit.ENABLE);               // Wait for synchronization 
}

void loop() {
  // Output a burst of 300 PWM pulses at 1kHz, 30% duty cycle every second
  TCC0->INTENSET.bit.OVF = 1;                     // Enable overflow (OVF) interrupts on TCC0  
  delay(1000);                                    // Wait 1 second
}

void TCC0_Handler()
{
  static volatile uint32_t counter;
  
  if (TCC0->INTENSET.bit.OVF && TCC0->INTFLAG.bit.OVF)
  {
    if (counter == 0)
    {
      counter++;
      TCC0->CCB[2].reg = 14399;                        // TCC0 CCB2 - 30% duty cycle on D6 (using buffered CCBx register)
      while (TCC0->SYNCBUSY.bit.CCB2);                 // Wait for synchronization     
    }
    else if (counter < 300)                           // Have we counted up to 300?
    {
      counter++;                                      // Increment the counter and continue     
    }
    else
    {
      counter = 0;                                    // Reset the counter     
      TCC0->CCB[2].reg = 0;                           // TCC0 CCB2 - 0% duty cycle on D6 (using buffered CCBx register)
      while (TCC0->SYNCBUSY.bit.CCB2);                // Wait for synchronization  
      TCC0->INTENCLR.bit.OVF = 1;                     // Disable overflow (OVF) interrupts on TCC0     
    } 
    TCC0->INTFLAG.bit.OVF = 1;                        // Clear the interrupt flag   
  } 
}