38kHz PWM output

Hello,

I am new to the arduino community. I have some experience working with ARM, bare metal microcontrollers.

I bought an Arduino Nano 33 IoT for the ease of the WiFiNINA library. For my project, I would like to set up a 38kHz PWM output to control IR emitters. Unfortunately, the IRremote library is not compatible with this board currently.

It is my understanding, based on looking in the variant header file for the board, that the oscillator frequency is 32kHz. Without using any other external hardware, is it possible to configure a 38kHz PWM signal, and if so, how?

Any help is greatly appreciated!

Thanks,
Jasper

Hi Jasper,

I found the blog post below and got it to run on an Adafruit Feather M0 which is also a SAMD21. It will need some tweaking to get it to a pin on the Arduino Nano 33. Just running it on the Arduino Nano 33 did not work. The pins are different.

I did not want you to wait to long. Maybe we can figure this out together.

It is my understanding, based on looking in the variant header file for the board, that the oscillator frequency is 32kHz. Without using any other external hardware, is it possible to configure a 38kHz PWM signal, and if so, how?

The SAMD21 microcontroller on the Nano33 runs crystalless, so it instead uses its standard, internal, on-chip 32.768kHz oscillator (OSC32K). This clock is then ramped (multiplied) up to 48MHz internally using an on-chip Digtal Frequency Locked Loop (DFLL48M). The 48MHz clock source is allocated to generic clock GCLK0 (in the Arduino core's "startup.c" file). The initial lines in the code below connect the 48MHz GCLK0 to the TCC0 and TCC1 timers.

Here's some example code that outputs 38kHz PWM on the Nano 33's D7 pin:

// Output 38kHz PWM on digital pin D7 on Nano33
void setup()
{
  // Feed GCLK0 at 48MHz to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0 as a clock source
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK0 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for pins D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;

  // D7 is on EVEN port pin PA06 and TCC1/WO[0] channel 0 is on peripheral E
  PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg = /*PORT_PMUX_PMUXO_E |*/ PORT_PMUX_PMUXE_E;
  
  // Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
  TCC1->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC1
  while (TCC1->SYNCBUSY.bit.WAVE);                 // Wait for synchronization
  
  TCC1->PER.reg = 1262;                            // Set the frequency of the PWM on TCC1 to 38kHz: 48MHz / (1262 + 1) = 38kHz
  while (TCC1->SYNCBUSY.bit.PER);                  // Wait for synchronization
  
  TCC1->CC[0].reg = 630;                          // TCC1 CC0 - 50% duty cycle on D7
  while (TCC1->SYNCBUSY.bit.CC0);                 // Wait for synchronization
  
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable the TCC1 counter
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() 
{ 
  // Using buffered counter compare registers (CCBx)
  TCC1->CCB[0].reg = 315;                         // TCC1 CCB1 - 25% duty cycle on D7
  while (TCC1->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
  TCC1->CCB[0].reg = 945;                         // TCC1 CCB1 - 75% duty cycle on D7
  while (TCC1->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
}

Hi Martin, thanks for the help. Works like a charm. :slight_smile:

Jasper, if you switch between 0 and 50% duty cycle, you can make little "packages" of 38kHz.