Changing Arduino Zero PWM Frequency

I had a quick look at the analogWrite() code. I think analogWrite() will work provided a given output pin doesn’t use the same timer that you’re controlling using register manipulation. Should timer conflict be an issue, the SAMD21 is sufficiently flexible on some pins to switch to a different timer, for example digital pin 13 can be switched to output from timer TCC2 instead.

The peripheral multiplexing table can be found on pages 20-22 of the SAMD21 datasheet.

MartinL: Thanks for all the help on this. It's nice to know where to start looking to figure out the registers.

I think I have found a couple of "simpler" solutions, where the actions are really the same, but you can use some of the Arduino machinery to ease some of the coding.

  • The easiest is to just use a pre-scaler on the TCCx of choice, which does not break analogWrite() from my tests:
void setup() {
...
 analogWrite(6, 0); //uses the Arduino code, which sets up GLCK0 for TCC0

 REG_TCC0_CTRLA &= ~TCC_CTRLA_ENABLE;   // Disable the TCC0 output
 while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization

 REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV64 |    // Divide GCLK0 by 64
                   TCC_CTRLA_ENABLE;                    // Enable the TCC0 output
 while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

This will change the default PWM frequency to about 3kHz for all of the pins that use TCC0 for PWM. analogWrite() works as normal, ranging from 0..255. With other choices of pre-scalers, you can get a decent set of PWM frequencies.

Question: Will this break anything else? Since I'm only pre-scaling TCC0, I don't see it affecting GLCK0, per se. If it doesn't, this would be a good solution for several posters who don't necessary care about the exact frequency, but just need something slower (my motor driver can only do 20kHz, max; 3kHz is fine for testing).

  analogWrite(6, 0); //again, use the Arduino machinery
  // The CCBx register value corresponds to the positive clock cycles
  REG_TCC0_PER = 4096;       //D6 is on TCC0; set the period 
  while(TCC0->SYNCBUSY.bit.PER);

  //25% duty cycle
  // The CCBx register value corresponds to the positive clock cycles
  REG_TCC0_CCB2 = 1024;       //D6 is on TCC0 CCB2 
  while(TCC0->SYNCBUSY.bit.CCB2);
  delay(1000);

  //75% duty cycle
  // The CCBx register value corresponds to the positive clock cycles
  REG_TCC0_CCB2 = 3072;       //D6 is on TCC0 CCB2 
  while(TCC0->SYNCBUSY.bit.CCB2);

Using analogWrite() will reset PER to 0xFF, which will clobber any custom value for PER. Still, it only affects the pins on the same TCCx, so it might be easier.

Thanks gcl8a, a nice combination of analogWrite() and register manipulation.

Question: Will this break anything else? Since I'm only pre-scaling TCC0, I don't see it affecting GLCK0, per se.

Pre-scaling the TCC0 timer won't affect GCLK0, but the GCLK0 settings shouldn't be altered as they're used by the Arduino core.

Dear MartinL,

Compliments for your work! This info is sorely needed as ther is little on site for Arduino M0/M0 PRO.

I downloaded both your 50Hz & 100Hz sketches, but they do not compile & stop here:

GCLK_CLKCTRL_ID_TCC0_TCC1;

the message being: Arduino:1.7.8 (Windows 7), Scheda:“Arduino M0”

sketch_M0_PWM_100Hz_1.ino: In function ‘void setup()’:

sketch_M0_PWM_100Hz_1.ino:24:22: error: ‘GCLK_CLKCTRL_ID_TCC0_TCC1’ was not declared in this scope

This being the only complaint…any suggestion? I am using IDE 1.7.8.

glovisol

Hi glovisol,

I downloaded both your 50Hz & 100Hz sketches, but they do not compile & stop here:

I just rechecked the code on my Arduino Zero using Arduino.cc's IDE, version 1.6.7 and it compiles OK.

I think the issue might be to do with the fact that you're using Arduino.org's IDE, version 1.7.8.

The definition of GCLK_CLKCTRL_ID_TCC0_TCC1 is actually in an Atmel file called "gclk.h". It's referenced by the Arduino Zero's core, so you shouldn't have to add any includes to your sketch.

On my machine it's currently stored under the directory: C:\Users\Computer\AppData\Local\packages\arduino\tools\CMSIS\4.0.0-atmel\Device\ATMEL\samd21\include\component\gclk.h.

It might be that Arduino M0 Pro/M0 core doesn't reference this file? As I'm not using Arduino.org's IDE its difficult for me to find out where the problem lies. Have you selected the correct board type in the IDE?

Dear MartinL,

Thanks for your prompt reply. Before resorting to Arduino.org's 1.7.8 IDE, I tried for ONE WEEK to have the Arduino M0 (the one without the debug USB connector) work with the 1.6.7 IDE by Arduino.cc, but I was almost driven crazy by continous instability of the COMM port, which would never stay put, so really nothing would work. I painstakingly followed the procedure several times, which in the end makes you fix "board type" and "native Port", but had no success. I use W7 Pro.

Out of desperation I downloaded IDE 1.7.8 and got two boards working faultlessly in one hour. I wrote a quick sketch that would read sensors @ 10 Bit on A0-A5 and write values @ 10 Bit (analogWrite(10)) on as many output pins, but the 187.4 Khz PWM is killing me, as I have to drive Optos. This is a Galvanic Isolator application for precision data logging. If you go to the Picotech forum you will see a lot of my work: https://www.picotech.com/support/forum31.html

I need to bring the PWM freq.down to the 100 Hz region and your work was just getting me started. Well, I have downloaded the Atmel datasheet, the over 1000 pages of it, and will start studying tomorrow...unless you offer me a turnaround.

Kind regards & keep up the good work!

glovisol

Unfortunately the Arduino Zero and M0 use different bootloaders. To use the M0 with the Arduino.cc IDE, I believe you need to burn the bootloader using a programmer such as the Atmel ICE on its SWD port.

If the definition GCLK_CLKCTRL_ID_TCC0_TCC1 is an issue, you could try replacing this:

// 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

with this:

GCLK->CLKCTRL.reg = 0x441A;

This effectively does the same thing, but is less readable.

Dear MartinL,

Thanks a lot!! It works like magic. It compiled at once and the entire sketch loaded immediately. There are obvious differences between the two bootloaders & in the Port Function Multiplexing in the two IDES, as well as, I suspect, in pin enabling. In fact the pins that should have changed frequency are different and those changed show PWM @ 11.76 Khz. It is late in Italy now and I shall investigate tomorrow (afraid of mistakes when tired!). I shall also download the entire sketch tomorrow.

Thank you for your help and fast but extremely effective information!

Kind regards,

glovisol

Hi MartinL,

sketch worked all night, but this morning I attempted to change the 48 Mhz clock divisor and I lost the board. The board is not recognized any more by two different PC’s( loaded with IDE 1.7.8) and it is most likely that something in the bootloader went wrong. I know for sure, because the second Arduino M0 board, which I keep as reference, works well on both PC’s and uploads normally. I shall now organize a bootloader (so I shall be able to work with IDE 1.6.7 as well) and then report back.

I enclosed sketch using your script.

Cheers,

glovisol

sketch_Galv_Isol_3_DEMO.ino (8.41 KB)

On the Arduino Zero, if you double tap the reset button it will force the SAMD21 to enter the bootloader, (rather than your sketch). I don’t know if this is also the case with the Arduino M0?

I ran the set-up portion of your sketch (without the SerialUSB communication) and it works fine. In your code you’ve used the timer prescaler of 64. This takes the 16MHz GCLK4 signal down to 250kHz. As we’re using dual slope PWM, (counter counts up then down and so on), we can calculate the output frequency as follows:

250kHz / (2 * 20000) = 6.25Hz

where:
250kHz is the frequency of timer TCC0
20000 is the value in the PER register
2 is because we’re using dual slope PWM

My (very) basic multimeter reads a frequency somewhere in the region of about 6Hz.

Hi MartinL

From my tests right before the crash I found the following, that might be interesting:

With all the unmodified set-ups of your sketch, on this version of Arduino M0 & 1.7.8 IDE, yesterday evening I got 17.5 KHz.

When I started moving constants, nothing happened, save for Prescaler = 64, that brought me down to 1.25 KHz.

I use Picotech PC scope 2000 series, with Picoscope software which reads frequency, Duty Cycle, and many other parameters, so I am pretty sure of freq. readings, apart from seeing the waveform on the display.

Also I noticed that reading on pin 2 kept at 175.4 Khz, while the 17.5 Khz came on pin 4, thus these pins are used differently on the two IDES & Boards.

Same applies to the controls:

Timer TCC must be another one here. The PER register command does not operate, so another must be used. The only one that works here is PRESCALER, where DIV8 and DIV64 make the difference.

I am reading Section 6 of the SAMD21 to get a better ida of the works.

Today I have ordered an original Genuino Zero, coming from UK, then it will be possible to tabulate the differences between Boards, IDEs. etc. It will be an interesting exercise.

Unfortunately the "double click" reset trick does not work in my board, which stays recognized no longer by the USB.

Kind regards,

glovisol

My apologies, I now see the problem. It's to do with the fact that in the loop() section of your code you're calling the analogWrite() function. (I commented this section of code out when I ran the test that outputs 6Hz).

analogWrite() also uses the TCC timers. So if you happen to use an analogWrite() pin that also uses the TCC0 timer, it will conflict with the register manipulation. That's why we're getting different results and has probably caused your M0 to crash.

I imagine the reason your USB port isn't working, is probably due to the fact that the sketch has crashed. Normally on the native USB port the sketch looks for the port to be opened at 1200bps to enter bootloader mode, but if the sketch has crashed it can't do this. This happened to my Zero once, while messing around with some critical registers. I got it working again by pressing reset button between the compile and upload phases, although it took quite a number of goes to get the timing right and it working again.

Thanks for your message! You are right I should have been more careful before re-using the analogWrite!

Anyway I realized that, when connected to the PC via USB, the LED “L” is pulsating: I measured with the scope and found a 0,35 V squarewave across it with a freq. of 26.9Hz: this meant that the goddam thing was probably working!!

So I connected back the pot, put the scope on pin 2 and saw the PWM changing with input voltage!! So it is working with the last DEMO I uploaded yesterday. I placed the pot at 1.25V to inputs
A0 - A5 and measurements are as follows.

OUT PWM PINS 0 & 1: 0V.
OUT PWM PIN 2, 3,6 &7: Square Wave 3.3 Vpp (of course!) - Freq. 1.27 KHz - Duty Cycle: 37.7%
OUT PWM PIN 4&5: Square Wave 3.3Vpp - Freq. 161.4 KHz - Duty Cycle 37.7%
OUT PWM PIN 8,9,10,11 & 12: 0V
OUT PWM PIN 13: Square wave 3.3Vpp - Freq. 26.92 Hz - Duty Cycle: 67% (reciprocal of 37%).

So, to sumarize, the sketch did not crash, as it is still working now, but the sketch does not allow communications thru USB!

Now I am going to make you laugh: two hours ago I got the message that the UK supplier had sent the payment back to me “because they cannot ship to Italy due to legal reasons…” Will we have smugglers now to smuggle Arduinos together with cocaine? What a mad world.

I will run the reset button to death now…

Have a nice evening,

glovisol

Now I am going to make you laugh: two hours ago I got the message that the UK supplier had sent the payment back to me "because they cannot ship to Italy due to legal reasons..." Will we have smugglers now to smuggle Arduinos together with cocaine? What a mad world.

I've seen the UK supplier's website saying they won't ship to Italy. It might be worth querying it, since you can easily find Italian distributors selling Genuino products in Italy.

Hi there,

I am back in business. I pulsed the RESET input with an NE555 pulser outputting 0.5 mS wide pulses @ 200Hz approx. (amplitude 2.5 V). USB disconnected & M0 fed with 8 V on Vin. I let it fry for 5 minutes. As soon as I connected to USB it was recognized again and I again pushed RESET manually to make sure I had removed the offending sketch.

Back to work: I am reading the book, but it takes time. My first question: the SAMD21 has several oscillators. If I used the OSC 32K (32.768 KHz) I would end up with a PWM frequency of 32768/282 = 116 Hz without touching anything else and playing it safe with analogWrite… Why use a syncro Phaselock @ 48 MHz, if I need low freq. PWM?

Cheers,

glovisol

OK, I tried....I understand now there is only ONE oscillator, e.g. the 8 MHz oscillator here.

I am taking no chances now and am only writing on Pin 7. No more crash problems.

But I see that the prescaler GLK_TCC (now at 64) can also do 256 and 1024. Since with 64 I am at 1.5 Khz already, with 1024 I could go down to 80 Hz approx., not bad... Fact is the prescaler really takes 6a as maximum and does not respond to the higher ratios, so I think the mode of operation is wrong.

glovisol

OK, solved it.

Now the prescaler is at 1024 and PWM is down to 92 Hz. It has been a long day.

Now we must discover why the REG_TCC0_PER do not work.

Thanks a lot for your continuing assistance: you really set me up in business!

Kind regards,

glovisol

Glad to hear you got it working again.

The timer's prescaler gives you the following divisors: 1, 2, 4, 8, 16, 64, 256 and 1024. It's also possible to take the GCLK lower with the GCLK divisor in the REG_GCLK_GENDIV register.

The generic clock can be taken from a number of sources. In my example code it's sourced from the 48MHz DPLL.

What PWM frequency are you looking for on D7?

Hi MartinL,

In reality I tried all of the mentioned oscillators, but on the Arduino Board is used a micro where only the mask of the 8MHz oscillator is enclosed, whick locks the 48 MHz clock. This is the only clock we have on board.

At present I am using the N=256 prescaler and I get 367.6 Hz, but with N=1024 I can go down to 82 Hz.

I have prepared the enclosed WORD file with all sketch entries , showing what is working and what is not working: have a look. I have yet to figure out how I get the 367.6 Hz.

Of course the sketch shows very coarse frequency control for now, but I expect we can improve a lot!

In any case this fills the bill for my application, because even the cheapest optocoupler will easily work at 370 hz.

Have a nice evening, kind regards,

glovisol

Did not let me load the Word File. I try again.

Cheers,

glovisol

Stencilled sketch file 1.doc (29.5 KB)