Hi new_to_due,
Implementing the RAMP2 code got me thinking that it would be possible to implement the two interleaved channels, just by simply changing the waveforms' duty-cycle period every other cycle in software in the interrupt service routine. This is instead of using fancy hardware functionality like dead-time insertion or RAMP2 operation.
I've written two sketches that implement your waveforms using this simpler method. The Arduino Due implementation I've posted on the Arduino Due forum, in the thead I linked to above.
The Arduino Zero/Feather M0 implementation here uses D12 and D13 using timers TCC0 and TCC2 on the separate respective channels. The two timers should be synchronized to each other by the fact that they're both clocked from generic clock 4 (GCLK4), I just attempt to start them as close as possible to each other by simply enabling one after the other, before synchronizing their respective registers.
This code outputs the same waveforms on D12 and D13 using timers TCC0 channel 3 and TCC2 channel 1:
// Use the TCC0 and TCC2 to generate two interleaved PWM signals of differing duty-cycle and period
void setup()
{
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) | // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
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 2 PWM channels on D12 and D13
PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;
// Connect the TCC2 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[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;// | PORT_PMUX_PMUXE_E;
PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E;// | PORT_PMUX_PMUXE_E;
// 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
// Feed GCLK4 to TCC2 and TC3
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC2 and TC3
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC2_TC3; // Feed GCLK4 to TCC2 and TC3
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)
//NVIC_SetPriority(TCC2_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC2 to 0 (highest)
//NVIC_EnableIRQ(TCC2_IRQn); // Connect TCC2 to Nested Vector Interrupt Controller (NVIC)
TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Set the PWM output to normal (single slope) PWM mode
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Set the PWM output to normal (single slope) PWM mode
while (TCC2->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC0->PER.reg = 119; // Set the frequency of the PWM on PER to 120us for the first channel on D12
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC2->PER.reg = 119; // Set the frequency of the PWM on PERB to 120us for the second channel on D13
while (TCC2->SYNCBUSY.bit.PER); // Wait for synchronization
// The CCx register values corresponds to the pulse width in microseconds (us)
TCC0->CC[3].reg = 0; // Set initial pulse width to 0us for the first channel on D12
while (TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
TCC2->CC[1].reg = 0; // Set initial pulse width to 0us for the second channel on D13
while (TCC2->SYNCBUSY.bit.CC1); // Wait for synchronization
TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 16MHz/16 = 1MHz
TCC_CTRLA_PRESCSYNC_PRESC; // Set the reset/reload to trigger on prescaler clock
TCC2->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV16 | // Set prescaler to 16, 16MHz/16 = 1MHz
TCC_CTRLA_PRESCSYNC_PRESC; // Set the reset/reload to trigger on prescaler clock
TCC0->CTRLA.bit.ENABLE = 1; // Enable the TCC0 counter
TCC2->CTRLA.bit.ENABLE = 1; // Enable the TCC2 counter
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop() {
// Output a burst of two PWM pulses on each channel every second
TCC0->INTENSET.bit.OVF = 1; // Enable overflow (OVF) interrupts on TCC0
delay(1000); // Wait 1 second
}
void TCC0_Handler()
{
static uint32_t counter;
if (TCC0->INTENSET.bit.OVF && TCC0->INTFLAG.bit.OVF)
{
if (counter == 0 || counter == 2)
{
counter++; // Increment the counter and continue
TCC0->CCB[3].reg = 59; // Set pulse width to 60us for the first channel on D12
while (TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC2->CCB[1].reg = 0; // Set the pulse width to 0us for the second channel on D13
while (TCC2->SYNCBUSY.bit.CCB1); // Wait for synchronization
TCC0->PERB.reg = 119; // Set the period to 120us for the first channel on D12
while (TCC0->SYNCBUSY.bit.PERB); // Wait for synchronization
TCC2->PERB.reg = 119; // Set the period to 120us for the second channel on D13
while (TCC2->SYNCBUSY.bit.PERB); // Wait for synchronization
}
else if (counter == 1 || counter == 3)
{
counter++; // Increment the counter and continue
TCC0->CCB[3].reg = 0; // Set pulse width to 0us for the first channel on D12
while (TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC2->CCB[1].reg = 599; // Set the pulse width to 600us for the second channel on D13
while (TCC2->SYNCBUSY.bit.CCB1); // Wait for synchronization
TCC0->PERB.reg = 7027; // Set the period to 7028us for the first channel on D12
while (TCC0->SYNCBUSY.bit.PERB); // Wait for synchronization
TCC2->PERB.reg = 7027; // Set the period to 7028us for the second channel on D13
while (TCC2->SYNCBUSY.bit.PERB); // Wait for synchronization
}
else
{
counter = 0; // Reset the counter
TCC0->CCB[3].reg = 0; // Set pulse width to 0us for the first channel on D12
while (TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC2->CCB[1].reg = 0; // Set the pulse width to 0us for the second channel on D13
while (TCC2->SYNCBUSY.bit.CCB1); // Wait for synchronization
TCC0->PERB.reg = 119; // Set the period to 120us for the first channel on D12
while (TCC0->SYNCBUSY.bit.PERB); // Wait for synchronization
TCC2->PERB.reg = 119; // Set the period to 120us for the second channel on D13
while (TCC2->SYNCBUSY.bit.PERB); // Wait for synchronization
TCC0->INTENCLR.bit.OVF = 1; // Disable overflow (OVF) interrupts on TCC0
}
TCC0->INTFLAG.bit.OVF = 1; // Clear the interrupt flag
}
}