I've been trying to use PWM to control a fan from my MKR1000, and I was wondering if anyone knew which pins were connected to which timer. I ask because I want to control a computer fan which requires a 25kHz (or thereabouts - more 24-28kHz) PWM frequency.
I know how to change the TOP/prescaler values of a timer to get the right PWM, but only on a Mega2560 and an Uno. I also know there are 7 configurable timers on a SAMD21, but I can't seem to find any helpful diagram to detail which timer goes to which pin. On the MKR1000 pins, that is - I've already found the basic pinout diagrams for the SAMD21 in the datasheet
The following code will give you a 50% duty cycle PWM signal, at 25kHz on pin D6 of your MKR1000:
// Output 25kHz PWM on digital pin D6 using timer TCC0 (10-bit resolution)
void setup()
{
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
REG_GCLK_GENCTRL = 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 TCC0 PWM channel 2 (digital pin D6), SAMD21 pin PA20
PORT->Group[g_APinDescription[6].ulPort].PINCFG[g_APinDescription[6].ulPin].bit.PMUXEN = 1;
// Connect the TCC0 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[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg |= /*PORT_PMUX_PMUXO_F |*/ PORT_PMUX_PMUXE_F;
// Feed GCLK4 to TCC0 and TCC1
REG_GCLK_CLKCTRL = 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
// Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
// Each timer counts up to a maximum or TOP value set by the PER (period) register,
// this determines the frequency of the PWM operation:
// 1919 = 25kHz
REG_TCC0_PER = 1919; // Set the frequency of the PWM on TCC0 to 25kHz
while(TCC0->SYNCBUSY.bit.PER);
// The CCBx register value determines the duty cycle
REG_TCC0_CCB2 = 959; // TCC0 CCB2 - 50% duty cycle on D6
while(TCC0->SYNCBUSY.bit.CCB2);
// Divide the 48MHz signal by 1 giving 48MHz (20.8ns) TCC0 timer tick and enable the outputs
REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop() { }
To change the duty cycle just load the buffered update register for timer TCC0 channel 2 with a number between 0 (0% duty cycle) and 1919 (100%):
That's more complete a solution than I could have hoped for, MartinL - thank you! I'm waiting on a fet with a lower threshold voltage before I can try this out, I'll let you know how the bench test goes when I can!
As an aside, could you tell me more about the code? I'm looking at the number of times GCLK4 is chosen and enabled, and it seems a lot. I understand the syncing while statements, PER register counter etc (it all looks like what I've seen before), but I'm less certain about what the multiplexer is required for. Is it there just because there are more pins on the chip than pins on the arduino?
Why does the generic clock have to be set to a 50/50 duty cycle before the sketch progresses through port enabling and TCC0 connection?
And why is the 48MHz broken down by the divisor again before the TCC0 is finally enabled as an output? Are we setting up the PWM on one clock then moving it to a further one?
This is really interesting! I knew the SAMD21 was a more complex beast but never thought it would be this impressive.
The Arduino core code uses generic clocks 0 to 3. This leaves GCLKs 4 to 7 free. I've just arbitrarily chosen GCLK4. The initial code just selects 48MHz as the the generic clock's source and connects it to timer TCC0.
The SAMD21 pins can be switched between either GPIO or a selection of peripheral functions from A through to H. What pin is assigned to what peripheral function is described in the Table 7.1 Port Function Multiplexing in the SAMD21's datasheet.
The pins are switched from GPIO to a perhipheral, by setting pin's associated PMUXEN (Pin Mulitplexer Enable) bit in its PINCFGx (Pin Configuration) register.
Timers TCC0, TCC1 and TCC2 are assigned to either peripheral E or F. However, this is where things get a bit more confusing, as there are only 16 PMUX (Peripheral Multiplexer) registers assigned to 32 port pins. So each neighbouring odd and even pin share a PMUX register, for instance SAMD21 port A pins PA00 (even) and PA01 (odd) share PMUX0, PA2 and PA3 share PMUX1 and so on... In the example above port pins PA20 and PA21 share PMUX10. (g_APinDescription[6].ulPin = 20, 20 >> 1 = 10).
As it's possible to divide the generic clock down, setting the GCLK_GENCTRL_IDC bit just reestablishes the output duty cycle at 50%, but isn't really necessary in this instance as we're dividing by 1.
It's possible to divide the 48Mhz clock source by both the generic clock divisor and the timer's own prescaler.
Thank you for flagging up the datasheet entry, I tried to read through and got a bit bogged down. I found the peripheral table and couldn't make head or tail of it, but your explanation's very clear and it makes a lot more sense now.
Hi, the code from MartinL works great for pin 6 ... thanks a lot for this! Unfortunately when I try to use it on digital pins 5, 4 or 3 it does not seem to work. I changed the PINCFG and PMUX assignments including the odd/even flag, but no signal is coming out For pin 2 however it does work. Can someone point me out what am I overlooking?