SAMD21 H-Bridge Waveform Generation

Hey everyone!

Overview
I am working on a project that utilizes an H-Bridge, and I am controlling it with a SparkFun SAMD21 Mini Dev Breakout. I currently have half of it working, as I am generating a complimentary pair with deadtime, however I need to go further and add a second complimentary pair which can be phase shifted independently of the first pair.

I also realize my current system isn't the best, it was mostly set up to just get a working 20Khz signal so I just got one with TCC0 and phase shifted the second one with TCC1. I saw @MartinL was helping out in a few threads on this, and my biggest question is how I can get the high-side/low-side w/dead time working across 2 pairs with one being phase shifted.

Scope & Diagram
Here is a scope screenshot of what I have now + a screenshot/diagram of what I am shooting for. Any help is appreciated!

Code

void setup() {
         hbridge_tccEnable2(600, 280, 280);    // 20Khz Signal w/ aggressive dead time
}

void hbridge_tccEnable2(uint32_t period, uint32_t ccn0, uint32_t ccn1) {
	
	/* Enable the generic clock for TCC0 & TCC1 */
	GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |         // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
	GCLK_GENDIV_ID(4);           // Select Generic Clock (GCLK) 4

	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

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Route GCLK 4 to timers TCC0 and TCC1
						GCLK_CLKCTRL_GEN_GCLK4 |
						GCLK_CLKCTRL_ID_TCC0_TCC1;

	/* Configure the clock prescaler for each TCC.
	   This lets you divide up the clocks frequency to make the TCC count slower
	   than the clock. In this case, I'm dividing the 8MHz clock by 16 making the
	   TCC operate at 500kHz. This means each count (or "tick") is 2us.
	*/
	TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV4_Val);
	TCC1->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV4_Val);
	
	/* Use "Normal PWM" */
	TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;
	/* Wait for bus synchronization */
	while (TCC0->SYNCBUSY.bit.WAVE) {};

	/* Use "Normal PWM" */
	TCC1->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;
	/* Wait for bus synchronization */
	while (TCC1->SYNCBUSY.bit.WAVE) {};
		
	/* Configure the frequency for the PWM by setting the PER register.
	The value of the PER register determines the frequency in the following
	way:

	frequency = GCLK frequency / (TCC prescaler * (1 + PER))

	So in this example frequency = 8Mhz / (16 * (1 + 512)) so the frequency
	is 974Hz.
	*/

	TCC0->PER.reg = period;
	while (TCC0->SYNCBUSY.bit.PER) {};
	TCC1->PER.reg = period;
	while (TCC1->SYNCBUSY.bit.PER) {};
	
	/* n for CC[n] is determined by n = x % 4 where x is from WO[x]
   WO[x] comes from the peripheral multiplexer - we'll get to that in a second.
	*/
	TCC0->CC[2].reg = ccn0;
	while (TCC0->SYNCBUSY.bit.CC2) {};
	TCC1->CC[1].reg = ccn1;
	while (TCC1->SYNCBUSY.bit.CC2) {};
	
	/* Configure PA18 and PA07 to be output. */
	PORT->Group[0].DIRSET.reg = PORT_PA18;
	PORT->Group[0].OUTCLR.reg = PORT_PA18;

	PORT->Group[0].DIRSET.reg = PORT_PA07;
	PORT->Group[0].OUTCLR.reg = PORT_PA07;
	
	/* Enable the peripheral multiplexer for the pins. */
	PORT->Group[0].PINCFG[18].reg |= PORT_PINCFG_PMUXEN;
	PORT->Group[0].PINCFG[7].reg |= PORT_PINCFG_PMUXEN;

	/* Set PA18's function to function F. Function F is TCC0/WO[2] for PA18.
	   Because it's an even numbered pin the PMUX is E (even) and the PMUX
	   index is pin number / 2, so 9.
	*/
	PORT->Group[0].PMUX[9].reg = PORT_PMUX_PMUXE_F;

	/* Set PA07's function to function E. Function E is TCC1/WO[1] for PA07.
	  Because this is an odd numbered pin the PMUX is O (odd) and the PMUX
	  index is pin number - 1 / 2, so 3.
	*/
	PORT->Group[0].PMUX[3].reg = PORT_PMUX_PMUXO_E;
	
	//	Enable TCCs
	TCC0->CTRLA.reg |= (TCC_CTRLA_ENABLE);
	while (TCC0->SYNCBUSY.bit.ENABLE) {};
	TCC1->CTRLA.reg |= (TCC_CTRLA_ENABLE);
	while (TCC1->SYNCBUSY.bit.ENABLE) {};
	//TCC1->COUNT.reg = (TCC0->COUNT.reg + (period/2));
	TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC;
	while (TCC0->SYNCBUSY.bit.COUNT) {};
	TCC1->COUNT.reg = (TCC0->COUNT.reg + (period/2 + 5));  //  Lazy shift with error correction for complimentary pair
}

@MartinL any ideas? Asking because your name pops up a lot!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.