SAM D21 problem with buffered PWM on TCC

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using TCC0 on a D21 for PWM to an H-bridge. I use the Pattern register to override the PWM signals to force an output high or low. The TCC conveniently provides buffer registers for the PWM count (CC[n]) and pattern (PATT) registers (CCB[n] and PATTB) so that changes will kick in together at the end of each PWM cycle.

 

My problem is that the PATTB register is not copied to the PATT registers at update.  CCB[n] is copied to CC[n].  The Status register has a flag to indicate that the buffer register has been written and should be copied, and in the debugger I can see this flag gets sets when writing CCB[0] but not when writing PATTB.  I have worked around this by setting PATT directly, but this is not synchronized with the change in CC[n] and could cause a serious glitch.

 

This seems like a bug in the chip, but there is no errata for it. I am using a SAMD21E16B, die rev. E.  Has anyone ever successfully used PATTB? On this die revision? I am not using ASF, but have looked at the ASF code and it doesn't do anything differently. To use this in ASF, you would ensure that double buffering is enabled in the TCC driver and call tcc_set_pattern().

 

My code:

	// Disable hardware updates from buffer registers while we set them
	TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD;
	
	TCC0->CCB[0].reg = uOnTime;	// Set PWM time
	TCC0->PATTB.reg = usPattB;
	TCC0->PATT.reg = usPattB;	// UNDONE: write to PATTB doesn't seem to work
	
	// Enable hardware updates from buffer registers
	TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD;

I have tried doing things in different order, removing the update lock, waiting on SYNCBUSY, but nothing changed.

 

This topic has a solution.
Last Edited: Sat. Jul 28, 2018 - 07:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have create a complete working demo of the issue:

#include "sam.h"

#define PWM_MAX_VALUE	100


int main(void)
{
	PORT_WRCONFIG_Type	portConfig;

	// Turn on clock to everything we might want to use
	PM->APBCMASK.reg = PM_APBCMASK_PAC2 | PM_APBCMASK_TCC0;

	// Set up TCC0 for PWM
	// No prescale
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC0_TCC1;
	TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC;
	TCC0->PER.reg = PWM_MAX_VALUE - 1;
	TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;
	TCC0->WEXCTRL.reg = TCC_WEXCTRL_OTMX(2);	// Repeat CC0 on all outputs
	TCC0->CC[0].reg = PWM_MAX_VALUE / 2;		// set up square wave on all outputs
	TCC0->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_PRESCSYNC_PRESC | TCC_CTRLA_ENABLE;
	// Connect output pins PA08 (WO[0]), PA09 (WO[1])
	portConfig.reg = 0;
	portConfig.bit.WRPINCFG = 1;
	portConfig.bit.WRPMUX = 1;
	portConfig.bit.PMUX = MUX_PA08E_TCC0_WO0;
	portConfig.bit.INEN = 1;
	portConfig.bit.PMUXEN = 1;
	portConfig.bit.PINMASK = PORT_PA08 | PORT_PA09;
	portConfig.bit.HWSEL = 0;	// lower 16 bits
	PORT->Group[0].WRCONFIG.reg = portConfig.reg;
	// Connect output pins PA14 (WO[4]), PA15 (WO[5])
	portConfig.reg = 0;
	portConfig.bit.WRPINCFG = 1;
	portConfig.bit.WRPMUX = 1;
	portConfig.bit.PMUX = MUX_PA14F_TCC0_WO4;
	portConfig.bit.INEN = 1;
	portConfig.bit.PMUXEN = 1;
	portConfig.bit.PINMASK = PORT_PA14 | PORT_PA15;
	portConfig.bit.HWSEL = 0;	// lower 16 bits
	PORT->Group[0].WRCONFIG.reg = portConfig.reg;
	
	// Force WO[1] (PA09) low, WO[5] (PA15) high
	TCC0->PATT.reg = TCC_PATT_PGE1 | TCC_PATT_PGE5 | TCC_PATT_PGV5;
	
	// Force WO[0] (PA08) high, WO[4] (PA14) low on next update
	TCC0->PATTB.reg = TCC_PATTB_PGEB0 | TCC_PATTB_PGEB4 | TCC_PATTB_PGVB0;
	
    /* Replace with your application code */
    while (1) 
    {
    }
}

The result of this code is two TCC pins will be putting out a 10kHz square wave generated by PWM, and the two others will be forced high and low. It should be PA08 high and PA14 low, but the write to PATTB has no effect, so it's PA09 low and PA15 high.

 

You don't need to measure the pins, you can see the values stepping with the debugger.  When PATTB is written, the PATTBV bit in STATUS is not set, so no update to the PATT register occurs.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Microchip has confirmed this problem with these comments:

You are right. The issue seems to be with variant B devices.
I managed to bring up a board for SAMD21E16B and was able to reproduce the issue.

 

The design team is able to reproduce the issue on simulation as well.
Unfortunately, there is no workaround as of now. PATT register has to be updated by software, may be inside an overflow interrupt (or any other update condition based on application).
 

I have tested it on a SAM C20J18A device. It works fine.
Hence it will work on other lower pin/memory variants of this device as well.

You can also use the SAMD20E16 (not the E16B). This should work since the SAMD20J18A worked fine.