1
votes

I'm programming 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 compare channels - SAMD21 have 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?

Thanks for answer

EDIT:

I have also configured pins to 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));
2
I haven't used PWM on these parts, but I'm almost certain that the problem is that you need to write to the PMUX register for the given pin. There's a magic number, that corresponds to a magic letter, that corresponds to the peripheral you want to route there. And yeah it is quite obscure. Check the part called I/O multiplexing in the manual and see which magic numbers and letters you need.Lundin
If you are using ASF bloatware libs, then something along the lines of PORT->Group[0].PMUX[PIN_PA22/2].reg = PORT_PMUX_PMUXE(x), where x is the magic number from the routing table in the manual.Lundin
Forgot to mention it but I allready did it (not sure if correctly thou): 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)); EFK
Looks weird. PORT->Group[0].PMUX->bit.PMUXE is the upper nibble of 4 bits in a 8 bit register, PMUXO is the lower nibble. So why the strange shift by PIN_PA22/2, shouldn't it just be a shift by 4 in the PMUXE case and no shift at all in the PMUXO case? If the magic number 5 is the one you need to write. That shift is what the bloatware macros PORT_PMUX_PMUXE and PORT_PMUX_PMUXO do. Shift by 4 or 0 respectively.Lundin
(And yeah, this register hardware layout deserves some manner of worst design award)Lundin

2 Answers

0
votes

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.

0
votes

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.