EFK
EFK

Reputation: 23

SAMD21: how to set PWM using TCC?

I'm programming a SAMD21 and I need PWM.

When I chose pin with F function TCC0 output: PA22 - TCC0/WO[4] PA23 - TCC0/WO[5]

I successfully configured TCC0 base counter:

// enable clock for TCC0 - disable clock masking
PM->APBCMASK.reg |= PM_APBCMASK_TCC0;

// set GCLK1 as source to the TCC0 counter
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_GEN(1) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_ID(0x1A);
while(!SYSCTRL->PCLKSR.bit.DFLLRDY);

// set counter
TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV64; // setting prescaler
TCC0->WAVE.reg |=  TCC_WAVE_WAVEGEN_NPWM | TCC_WAVE_POL0;
while (TCC0->SYNCBUSY.bit.WAVE);

// set TOP (PER) value of counter - frequency
TCC0->CTRLA.bit.RESOLUTION = 0;
TCC0->PER.reg = 48'000'000 / (100 * 64) - 1; // Fpwm = Fglk / (PRESC(PER+1))  --> PER = Fglk / (Fpwm * PRESC) - 1
while (TCC0->SYNCBUSY.bit.PER);

But the problem was how to configure the compare channels - the SAMD21 has only 4 compare channels (CC) but I want output to WO[4] and WO[5].

How can I connect given compare channels to WO[x] pins?

EDIT:

I have also configured pins for mutiplexing (not sure if correctly):

PORT->Group[0].PINCFG->reg |= (1 << PIN_PA22) | (1 << PIN_PA23);
PORT->Group[0].PMUX->bit.PMUXE = (0x5 << (PIN_PA22/2));
PORT->Group[0].PMUX->bit.PMUXO = (0x5 << (PIN_PA23/2 + 1));

Upvotes: 1

Views: 1892

Answers (2)

ocrdu
ocrdu

Reputation: 2183

There is a PWM library you can re-use code from; it comes with a table (under "extras") with timers, output pins, output channels, pin multiplexers etc.

The library was written (by me) for SAMD21G-based Arduinos, but all the mappings and code you need are there, and it may help you in your efforts.

Upvotes: 0

Lundin
Lundin

Reputation: 213266

NOTE: PORT->Group[0].PMUX is an array! Which decays into a pointer to the first element if you do PMUX->bit, so it compiles, but it is wrong. You are setting the first element instead of the pin PIN_PA22/2 that you are interested in.

The routing PMUX register type in the ASF register map looks like this:

typedef union {
  struct {
    uint8_t  PMUXE:4;          /*!< bit:  0.. 3  Peripheral Multiplexing for Even-Numbered Pin */
    uint8_t  PMUXO:4;          /*!< bit:  4.. 7  Peripheral Multiplexing for Odd-Numbered Pin */
  } bit;                       /*!< Structure used for bit  access                  */
  uint8_t reg;                 /*!< Type      used for register access              */
} PORT_PMUX_Type;

Meaning you can either write to the 8 bit reg or the 4 bit bit nibbles. You seem to do the latter. If 0x05 is the "magic number" obtained by the manual, you should write this one both to the "even" and the "odd" nibble. That is:

PORT->Group[0].PMUX[PIN_PA22/2].bit.PMUXE = (0x5 << 4);
PORT->Group[0].PMUX[PIN_PA22/2].bit.PMUXO = (0x5 << 0);

Or if you will, you can alternatively use pointless ASF bloatware macros to hide away "scary" bitwise logic:

PORT->Group[0].PMUX[PIN_PA22/2].reg = PORT_PMUX_PMUXE(0x5) | PORT_PMUX_PMUXO(0x5);

If you don't get these right, there will be no activity on the pin at all.

Upvotes: 0

Related Questions