Last active
August 24, 2021 23:18
-
-
Save nanase/b80dea702c8933dab5bd13c6e4f9ed31 to your computer and use it in GitHub Desktop.
Triangle wave output by PWM on Seeeduino XIAO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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