SAMD21 Fault Blanking RAMP to "mix" two square waves

Hi smerrett79,

The simplest way to interleave two PWM signals on to a single channel output is to use both the TCC timer's duty-cycle and period circular buffers.

In this mode both the Counter Compare: CCx and buffered equivalent: CCBx are loaded with the required duty-cycles. The same goes for the period registers: PER and PERB. At the end of each timer cycle the timer switches or cycles between them effectively interleaving two channels on to a single output.

Here's an example that sets up circular buffers for CC[0]/CCB[0] and PER/PERB on timer TCC0, output on D2 (Arduino Zero) or port pin PA14. The two interleaved channels output 50% duty-cycle at 100Hz and 200Hz frequencies:

// Use the TCC0 timer with circular CC and PER buffers to interleave two PWM signals on to a single channel (channel 0)
void setup()
{
  // Feed GCLK0 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for the PWM channel TCC0 WO[0] on D2
  PORT->Group[g_APinDescription[2].ulPort].PINCFG[g_APinDescription[2].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 TCC0
  PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;

  // Interleave TCC0 two PWM signals on to a single channel using the CC and PER circular buffers
  TCC0->WAVE.reg = TCC_WAVE_CICCEN0 |              // Enable the circular counter compare CC0 <--> CCB0 buffer 
                   TCC_WAVE_CIPEREN |              // Enable the circular period PER <--> PERB buffer
                   TCC_WAVE_WAVEGEN_NPWM;          // Set the PWM output to normal (single slope) PWM mode
  while (TCC0->SYNCBUSY.bit.WAVE);                 // Wait for synchronization

  TCC0->PER.reg = 239999;                          // Set the frequency of the PWM on PER to 200Hz on D2
  while (TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
  TCC0->PERB.reg = 479999;                         // Set the frequency of the PWM on PERB to 100Hz on D2
  while (TCC0->SYNCBUSY.bit.PERB);                 // Wait for synchronization
  
  TCC0->CC[0].reg = 120000;                        // Set the duty-cycle to 50%
  while (TCC0->SYNCBUSY.bit.CC0);                  // Wait for synchronization
  TCC0->CCB[0].reg = 240000;                       // Set the duty-cycle to 50%
  while (TCC0->SYNCBUSY.bit.CCB0);                 // Wait for synchronization

  TCC0->CTRLA.bit.ENABLE = 1;                      // Enable the TCC0 counter
  while (TCC0->SYNCBUSY.bit.ENABLE);               // Wait for synchronization
}

void loop() {}

Here's the output on the scope:

InterleavingPWM .png

To change the period or duty-cycle over set number of cycles without disrupting the output, it's possible use the event system to get a second timer to count TCC0 cycles and get this second timer to call an interrupt service routine, in order to change the CC[0]/CCB[0] and/or PER/PER[0] registers.

InterleavingPWM .png