Skip to content

Instantly share code, notes, and snippets.

@nanase
Last active August 24, 2021 23:18
Show Gist options
  • Save nanase/b80dea702c8933dab5bd13c6e4f9ed31 to your computer and use it in GitHub Desktop.
Save nanase/b80dea702c8933dab5bd13c6e4f9ed31 to your computer and use it in GitHub Desktop.
Triangle wave output by PWM on Seeeduino XIAO
// Code from: https://www.hackster.io/voske65/high-speed-pwm-on-arduino-atsamd21-859b06
/*
HIGH FREQ PWM SETUP FOR MKR1000 - SAMW25 = SAMD21 with some other pinning
MKR1000's pins than can be defined as TCC timer pins and their associated channel (WO[X])
unless stated otherwise the TCC timers are on peripheral F:
A0 - PA02 - None
A1 - PB02 - None
A2 - PB03 - None
A3 - PA04 - TCC0/WO[0] (same channel as TCC0/WO[4])
A4 - PA05 - TCC0/WO[1] (same channel as TCC0/WO[5])
A5 - PA06 - TCC1/WO[0]
A6 - PA07 - TCC1/WO[1]
D0 - PA22 - TCC0/WO[4] (same channel as TCC0/WO[0])
D1 - PA23 - TCC0/WO[5] (same channel as TCC0/WO[1])
D2 - PA10 - TCC1/WO[0]
*D3 - PA11 - TCC1/WO[1]
D4 - PB10 - TCC0/WO[4] (same channel as TCC0/WO[0])
D5 - PB11 - TCC0/WO[5] (same channel as TCC0/WO[1])
D6 - PA20 - TCC0/WO[6] (same channel as TCC0/WO[2])
D7 - PA21 - TCC0/WO[7] (same channel as TCC0/WO[3])
D8 - PA16 - TCC0/WO[6] (same channel as TCC0/WO[2]) on peripheral F, TCC2/WO[0] on peripheral E
D9 - PA17 - TCC0/WO[7] (same channel as TCC0/WO[3]) on peripheral F, TCC2/WO[1] on peripheral E
D10 - PA19 - TCCO/WO[3] (same channel as TCC0/WO[7])
*D11 - PA08 - TCC1/WO[2] (same channel as TCC1/WO[0]) on peripheral F, TCC0/WO[0] on peripheral E
D12 - PA09 - TCC1/WO[3] (same channel as TCC1/WO[1]) on peripheral F, TCC0/WO[1] on peripheral E
D13 - PB22 - None
D14 - PB23 - None
Note the timer TCC0 has only 4 channels (0-3 and 4-7 are the same),
while TCC1 and TCC2 each have 2, giving you 8 channels in total.
*/
void setup() {
setupTimers();
}
void loop() {
for (uint16_t i = 0; i < 256; i++) {
REG_TCC1_CC1 = i;
delayMicroseconds(1);
while (TCC1->SYNCBUSY.bit.CC1) ;
}
for (uint16_t i = 0; i < 256; i++) {
REG_TCC1_CC1 = 255 - i;
delayMicroseconds(10);
while (TCC1->SYNCBUSY.bit.CC1) ;
}
}
// Output PWM 24Khz on digital pin D3 and D11 using timer TCC1 (10-bit resolution)
void setupTimers() {
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide the 48MHz clock source by divisor N=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 digital pin D3 and D11 **** g_APinDescription() converts Arduino Pin to SAMD21 pin
PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
// PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
// Connect the TCC1 timer to digital output D3 and D11 - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg = PORT_PMUX_PMUXO_E; // D3 is on PA11 = odd, use Device E on TCC1/WO[1]
// PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXE_F; // D11 is on PA08 = even, use device F on TCC1/WO[0]
// 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
// Dual slope PWM operation: timers countinuously count up to PER register value then down 0
REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) | // Reverse the output polarity on all TCC0 outputs
TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC0
while (TCC1->SYNCBUSY.bit.WAVE) ; // Wait for synchronization
// Each timer counts up to a maximum or TOP value set by the PER register,
// this determines the frequency of the PWM operation: Freq = 48Mhz/(2*N*PER)
REG_TCC1_PER = 256; // Set the FreqTcc of the PWM on TCC1 to 24Khz
while (TCC1->SYNCBUSY.bit.PER) ; // Wait for synchronization
// Set the PWM signal to output , PWM ds = 2*N(TOP-CCx)/Freqtcc => PWM=0 => CCx=PER, PWM=50% => CCx = PER/2
// REG_TCC1_CC1 = 128; // TCC1 CC1 - on D3 50%
// while (TCC1->SYNCBUSY.bit.CC1) ; // Wait for synchronization
// REG_TCC1_CC0 = 500; // TCC1 CC0 - on D11 50%
// while (TCC1->SYNCBUSY.bit.CC0) ; // Wait for synchronization
// Divide the GCLOCK signal by 1 giving in this case 48MHz (20.83ns) TCC1 timer tick and enable the outputs
REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC1->SYNCBUSY.bit.ENABLE) ; // Wait for synchronization
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment