High Speed PWM on Arduino ATSAMD21

High Speed PWM on Arduino ATSAMD21

Set up high speed PWM (1Hz - 1MHhz) to one of your IO pins.

  • 1 comment
  • 5 respects

Components and supplies

About this project

High Speed PWM Using TCC Timer/Counters

On the Arduino with Cortex-M0 (ATSAMD21) there is more choice in frequency and speed for your PWM control than just the AnalogWrite() functions.

  • Why not using Tone() function? : this function does not support you to change the PWM duty cycle.
  • Why not using the AnalogWrite() function? : this function only supports one frequency ~ 490Hz
  • Why not use the setPwmFrequency() function : this changes the Clock pre-scaler, possibly influencing timing of other Arduino functions for time.

It took me some efforts scrolling through fora and demo's to figure it out, so I like to share.

The difficulty is to figure out the right register settings, using the datasheet, Chapter 31 - TCC timers/counters, specific 31.6.2 for setting up the PWM counters - using GCLKC4 (not used by arduino) and connecting them to the right TCCs, and to the right IO pin.

Code is self explaining, and using the dual-slope PWM mode to create glitch-free PWM signals for low (several Hz) and high speed (>>500Khz).

You can do a simple test by connecting a piezzo speaker to the IOs (for the audible frequencies).


arduino project

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() {
  // put your setup code here, to run once:

void loop() {
  // put your main code here, to run repeatedly:
int t=0;
Serial.println("\nRunning PWM 0 to 100%");
for (t=0;t<1000;t=t+1){
  REG_TCC1_CC1 = t;                              // TCC1 CC1 - on D3  - PWM signalling
  while (TCC1->SYNCBUSY.bit.CC1);                // Wait for synchronization


// 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 = 1000;                           // 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 = 500;                             // 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
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization


Piezzo Speaker Connect
Simply connect speaker between ground and the PWM output port
Peizo circuit 2 9vvdbfinrk


Similar projects you might like

High Speed Arduino RC car

by Aqib

  • 39 respects

QuickFFT: High Speed (low accuracy) FFT for Arduino

Project tutorial by abhilash_patel

  • 6 respects

PWM Sound Synthesis

by 3 developers

  • 13 respects

25 kHz 4 Pin PWM Fan Control with Arduino Uno

Project tutorial by tylerpeppy

  • 36 respects

DC Motor speed control and measurement

Project tutorial by ambhatt

  • 14 respects

Control speed and direction of a dc motor without h-bridge

Project in progress by mamífero

  • 25 respects
Add projectSign up / Login