Arduino Forum

Products => Arduino Zero => Topic started by: joelcrouch on Sep 09, 2015, 10:10 pm

Title: Changing Arduino Zero PWM Frequency
Post by: joelcrouch on Sep 09, 2015, 10:10 pm
I have an application where I need to output a PWM frequency >250kHz.  Using Arduino's analogWrite function only gets me to 187kHz max.  I found the Due has this function, PWMC_ConfigureClocks(), to change the frequency.  Is there something similar for the Zero?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: mantoui on Sep 10, 2015, 01:47 am
I don't think ZERO has an API to set PWM frequency.  (The API for  Teensy ARM products has an analogWriteFrequency(pin,herz),  cold comfort :( )

If you want to roll your own, there is sample code in the thread
ZERO IRrremote (https://forum.arduino.cc/index.php?topic=344552.0)
that does pwm at user-selected frequency.

Also in the core packages/arduino/hardware/samd/1.6.1/cores/arduino/Tone.cpp
there is code to set the frequency of TC5, selecting best prescaler and compare value.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 10, 2015, 11:15 am
Hi joelcrouch,

Welcome to the Arduino Zero forum.

Both the AVR and the ARM processors are capable of controlling both the phase and frequency using dual slope PWM. If you're interested in reading further, it's detailed in the SAMD21 datasheet, page 660, under the heading "Dual Slope PWM Generation". In dual slope PWM the timer counts up to a given value then counts back down to zero, and so on...

Dual slope PWM on the SAMD21 is provided by the timers TCC0, TCC1 and TCC2. These timers are clocked by one of the processor's generic clocks (GCLK). There are 8 GCLKs in total, 0 to 3 are used by Arduino, but you're free to use the others. It's possible to set up a given GCLK to feed a 48MHz (CPU clock frequency) signal to one of the timers. However, due to the high speed of your 250kHz PWM signal in relation to the 48MHz clock frequency, you'll end up with poor resolution. Here's the calculations:

The frequency for dual slope PWM is determined by:

Frequency = GCLK frequency / (2 * N * PER)       where N = prescaler value (CTRLA register)

The value in the PER register determines the maximum value the timer counts up to.

In your case N = 1 (as we'd like to clock the timer as fast as possible), therefore...

Frequency = 48MHz / (2 * 1 * 96) = 250kHz

So the PER should be set at 96 (decimal).

The resolution for dual slope PWM is given by:

Resolution = log(PER + 1)/log(2), therefore:

Resolution at 250kHz = log(96 + 1) / log(2) = 6.6 = 6 bits (rounding down)

To change the PWM pulse width (phase), just load the timer's CCBx register with a value between 0 and 96. 0 outputs 0V (0% duty cycle), 96 outputs 3.3V (100% duty cycle). Loading the CCBx register with 48 gives a 50% duty cycle.

The following code sets up GCLK 4 to feed timer TCC0 with 48MHz. The TCC0 is set up for dual slope PWM operation and connected to ouput D7. To control the PWM output just load the REG_TCC0_CCB3 register with a value between 0 and 96. Using my old multimeter I've sucessfully tested the code for 125kHz, however it's not capable of measuring 250kHz and I haven't got a scope, so I'm unable to test it at this higher frequency.

Code: [Select]
// Output 250kHz PWM on timer TCC0 (6-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  REG_TCC0_PER = 96;         // Set the frequency of the PWM on TCC0 to 250kHz
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle
  REG_TCC0_CC3 = 48;         // TCC0 CC3 - on D7
  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: joelcrouch on Sep 10, 2015, 02:03 pm
Hi MartinL,
Your code snippet works!  I measure 250kHz on oscilloscope.

Thank you,
Joel
Title: Re: Changing Arduino Zero PWM Frequency
Post by: joelcrouch on Sep 10, 2015, 02:43 pm
MartinL,
Is there an easy way to assign PWM function to another pin other than 7?  I tried changing number in your two PORT->Group.... statements above, but it didn't work.

Thanks,
Joel
Title: Re: Changing Arduino Zero PWM Frequency
Post by: joelcrouch on Sep 10, 2015, 03:04 pm
MartinL,
Running your code breaks analogWrite() function.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 10, 2015, 05:30 pm
Yes, it'll break analogWrite(), because it's also trying to use timer TCC0.

The pin mapping for the TCC0, TCC1 and TCC2 are as follows:

REG_TCC0_CCB0 - digital output D2 (Zero Pro/M0 Pro/M0 - digital pin D4)
REG_TCC0_CCB1 - digital output D5
REG_TCC0_CCB2 - digital output D6
REG_TCC0_CCB3 - digital output D7
REG_TCC1_CCB0 - digital output D4 (Zero Pro/M0 Pro/M0 - digital pin D2)
REG_TCC1_CCB1 - digital output D3
REG_TCC2_CCB0 - digital output D11
REG_TCC2_CCB1 - digital output D13

Note: On Arduino.org's Zero Pro/M0 Pro/M0, D2 and D4 are reversed.

Note that first you'll also have to enable TCC1 and TCC2 in a similar manner to TCC0. You can connect TCC2 to GCLK4 using the lines:

Code: [Select]
// Feed GCLK4 to TCC2 (and TC3)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

To connect the timers to their output pins, first enable the port multiplexer on each timer pin:

Code: [Select]
// Enable the port multiplexer for the 8 PWM channels: timer TCC0, TCC1 and TCC2 outputs
  const uint8_t CHANNELS = 8;
  const uint8_t pwmPins[] = { 2, 3, 4, 5, 6, 7, 11, 13 };
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }

Then connect all the timer outputs (TCC0, TCC1 & TCC2) to their respective output pins:

Code: [Select]
// Connect the TCC timers to the port outputs - 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_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;

The PMUX registers are arranged in pin pairs odd and even. So for example Arduino's digital pin 13 (D13) is actually the SAMD21 port A, pin 17, or PA17. An odd pin. (See Arduino Zero's schematic diagram). This is paired with its neighboring even pin PA16, (actually digital pin 11). To connect the TCC2 timer to the D13 we have to specify the even SAMD21 pin, in this case PA16 (D11), then connect the timer to D13 using the odd PORT_PMUX_PMUXO_E mask. PMUXO stands for: port multiplexer odd. The E and F suffixes specify that the timers are to be connected.

By the way, the >>1 shift left is actually divide by 2, because there is one PMUX register for each odd and even pair of pins, there's only 16 PMUX registers for 32 pins on a given port.

The g_APinDesciription() function simply converts the Arduino pin numbers into the SAMD21's port and pin representation.

Title: Re: Changing Arduino Zero PWM Frequency
Post by: mantoui on Sep 10, 2015, 07:17 pm
@MartinL, is it really necessary to configure GCLK4?  Coudln't one just use GCLK0 which is configured as 48MHz via startup.c?

inquiring minds
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 10, 2015, 11:58 pm
Hi mantoui

Thanks for pointing this out.

Yes, in this instance you could make the code more efficient by using GCLK0, but if you wanted divide down the GCLK frequency further to obtain a lower frequency using the REG_GCLK_GENDIV register, then I believe you'd have to use one of the free GCLKs. For example, in my project I divide GCLK4's 48MHz by 3 to obtain 16MHz, so that the PWM timing works out the same as the AVR processors on my Arduino Micro and Mega.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Glowtape on Nov 11, 2015, 11:59 pm
Hi!

I'm doing some research into the feasibility of programming a quadcopter flight controller based on an Arduino Zero, among other things, all the PWM business to control the ESCs.

If I understood the code above correctly, I can program TCC0 to a specific frequency, tie it to D4-D7 to control four ESCs, and still control the duty cycle of each pin via CCB0-CCB3?

On top of that, given clock division combined with this PER and CCB stuff, I should be able to get very fine duty cycle control at lower PWM frequencies? I'm interested in getting four PWMs going at rates of either 490hz or arbitrary rates below that.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Nov 12, 2015, 10:33 am
Yes, the PER/PERB and CCBx registers allow fine control over the PWM duty cycle and frequency. With TCC0, TCC1 and TCC2 it's possible to control up to 8 motors.

If you're using Electronic Speed Controllers (ESCs), setting the generic clock divisor to 3 to get 16MHz and then using the control A register prescaler of 8 is nice, because the duty cycle can be controlled by loading the CCBx registers with a number between 0-2000. This corresponds to a 0-2000us duty cycle required by most ESCs.

So for 490Hz, the PER register value is:

PER = 16MHz / (2 * 8 * 490) = 2040

where 48MHz / 3 (GCLK divisor) = 16MHz and 8 is the control A prescaler

For some reason initialising the PWM using the buffered PERB/WAVEB registers causes a delay of about 5 seconds before the output becomes active. To overcome this I initialise the PWM using the unbuffered PER/WAVE registers, but use the buffered CCBx registers to control the duty cycle. This way the PWM becomes active immediately. Using the buffered CCBx registers are necessary to prevent changes in the duty cycle from causing glitches on your motors.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Glowtape on Nov 12, 2015, 11:00 am
Cool, thanks for your help. Even tho it's less intuitive to set up than the analogWrite stuff from the Arduino library, the higher precision should be worth it. And should incur less latency from what I read.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: shiram on Dec 10, 2015, 12:38 pm
Do I need all that code just to change PWM frequency on the arduino zero?
on the Atmega it was one line:

  TCCR1B = TCCR1B & 0b11111000 | 0x01;

did anyone find a simpler way? it is really hard to adjust that complex code to my needs....
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Dec 10, 2015, 01:11 pm
Hi shiram,

On the Atmega devices the TCCRxx registers are used to set up PWM, but for complete control of the PWM phase (duty cycle) and frequency you still need to modify their ICRx and OCRxx registers.

The SAMD microcontroller is internally more flexible than the older AVR ICs, it requires that you first connect a generic clock (GCLK) to the timer peripherals. After that the activation of the port pins and the switching of the port multiplexer is analogous to setting up the TCCRxx registers on Atmegas.

Quote
it is really hard to adjust that complex code to my needs....
I might be able to help. What are the PWM requirements for your project?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: rmay_pci on Feb 11, 2016, 03:55 pm
Martini ... you appear to be an expert on the Zero and PWM so maybe there is a simpler method to accomplish what I am trying to do.  I had posted the comments below on another section of the forum and was redirected here, and had tried exactly the same thing (to change the PWM frrequency on the Zero (pin 9) that shiram has listed above based on something I found on another post:

Pins 9 and 10: controlled by timer 1 in phase-correct PWM mode (cycle length = 510)

Setting    Divisor    Frequency
0x01         1        31372.55
0x02         8        3921.16
0x03        64        490.20   <--DEFAULT
0x04       256        122.55
0x05      1024        30.64

TCCR1B = (TCCR1B & 0b11111000) | <setting>;

So I understood (wrongly, apparently) that changing TCCR1B as above would do the trick.  But this line of code doesn't compile as the compiler can't find a declaration of TCCR1B.  All I am trying to do is change the PWM frequency from the default of near 190 KHz (187 KHz is a number I've seen posted elsewhere) down to 490 Hz or 123 Hz (or similar ... somewhere in the low 100s). 

Is there a simple way to do this, or do I need to follow a much more extensive process (which I think you have outlined above but I am not too clear on all of those details).  I did find a library (PWM.h) what was supposed to do this for the Zero, but that also doesn't compile or recognize any of the library functions.  I would have thought changing a PWM frequency would be relatively easy for an Arduino, but maybe the Zero is a different animal. 187 KHz is just too high for my application.  Thanks for any help.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Feb 12, 2016, 10:58 am
Hi rmay_pci,

Pin 9 and 10 control the output of both phase and phase and frequency correct PWM on the Arduino Uno. At the risk of going off topic the following code for the Uno sets up two servos at 50Hz. The ICR1 register controls the frequency, for example: 20000 = 50Hz, 10000 = 100Hz, 2500 = 400Hz. The OCR1A and OCR1B registers control the duty cycle. If you're using servos just load them with a value between 1000 and 2000, with 1500 being the center point of the servo. Or, if you need a 50% duty cycle with a value that is half ICR1. However, using this method prevents you from using libraries that also use timer1, for instance the servo library.

Code: [Select]
void setup() {
  // Initialise timer 1 for phase and frequency correct PWM
  pinMode(9, OUTPUT);                         // Set digital pin 9 (D9) to an output
  pinMode(10, OUTPUT);                        // Set digital pin 10 (D10) to an output
  TCCR1A = _BV(COM1A1) | _BV(COM1B1);         // Enable the PWM outputs OC1A, and OC1B on digital pins 9, 10
  TCCR1B = _BV(WGM13) | _BV(CS11);            // Set phase and frequency correct PWM and prescaler of 8 on timer 1
  ICR1 = 20000;                               // Set the PWM frequency to 50Hz
  OCR1A = 1500;                               // Centre the servo on D9
  OCR1B = 1500;                               // Centre the servo on D10
}

void loop() {
  OCR1A = 1000;                               // Move the servo to min position on D9
  OCR1B = 1000;                               // Move the servo to min position on D10
  delay(1000);                                // Wait for 1 second
  OCR1A = 2000;                               // Move the servo to max position on D9
  OCR1B = 2000;                               // Move the servo to max position on D10
  delay(1000);                                // Wait for 1 second
}

Back to the Zero. It's internally more flexible than the older AVR processors, so it does require more extensive set-up, but much of it's just routing the processor's generic clock to the appropriate timer and hooking up the correct pins for output. Once that's out of the way, it's pretty much the same as the AVR.

Here's similar code for the Zero, but sets up 4 servos at 50Hz on digital pins D2, D5, D6 and D7 using the TCC0 timer. The REG_TCC0_PER register is equivalent of ICR1 and the REG_TCC0_CCBx the OCR1x registers:

Code: [Select]
// Output 50Hz PWM on timer TCC0 (14-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 4 PWM channels: timer TCC0 outputs
  const uint8_t CHANNELS = 4;
  const uint8_t pwmPins[] = { 2, 5, 6, 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - 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_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  // 20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz
  REG_TCC0_PER = 20000;      // Set the frequency of the PWM on TCC0 to 50Hz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB0 = 1500;       // TCC0 CCB0 - center the servo on D2
  while(TCC0->SYNCBUSY.bit.CCB0);
  REG_TCC0_CCB1 = 1500;       // TCC0 CCB1 - center the servo on D5
  while(TCC0->SYNCBUSY.bit.CCB1);
  REG_TCC0_CCB2 = 1500;       // TCC0 CCB2 - center the servo on D6
  while(TCC0->SYNCBUSY.bit.CCB2);
  REG_TCC0_CCB3 = 1500;       // TCC0 CCB3 - center the servo on D7
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }


Title: Re: Changing Arduino Zero PWM Frequency
Post by: rmay_pci on Feb 12, 2016, 07:54 pm
Martini,
Thanks very much for the detailed response.  I will try this code with the Zero over the weekend to see if I can get it to work. 

What we are doing is running a PID heater control algorithm using the duty cycle of the PWM output (which drives a MOSFET, which drives the heater current).  The heater power is directly proportional to the PWM duty cycle, but the system has a horribly slow response (it is more like an oven than a contact heater). 

Tuning using a normal PID approach has not worked, so we are trying to get fancier with the algorithm and stumbled with the PWM frequency on the Zero being too fast.  I tried a Due which seems to have a default PWM frequency of 1 KHz and everything works very well, but we prefer the Zero for size reasons (and because we have already made a custom "shield" with the same footprint).  So if I can get the Zero's PWM frequency down I think we will be in business.

Thanks again for the detailed explanation ... people like you who understand all these details are a very valuable resource for those of us who don't but need to get projects working.
Randy
Title: Re: Changing Arduino Zero PWM Frequency
Post by: pharaohamps on Feb 16, 2016, 01:04 am
OK, any advice on how to change the frequency on only pin 13?  I've got an application where I need its output to be in the audio band, so somewhere between 100Hz and 10kHz would work nicely.  I think I can follow the steps from the example above, but how do I change the PWM ratio?  Does analogWrite() still work?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Feb 17, 2016, 09:08 pm
Hi pharaohamps,

The following code outputs a 100Hz PWM signal from timer TCC0, on digital pin 13:

Code: [Select]
// Output 100Hz PWM on timer TCC0 digital pin D13
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 digital pin 13 (D13): timer TCC0 output
  PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port output - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;
 
  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  // 20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz
  REG_TCC0_PER = 10000;      // Set the frequency of the PWM on TCC0 to 100Hz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB3 = 1500;       // TCC0 CCB3 - center the servo on D13
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

You can change the PWM ratio or duty-cycle by loading the REG_TCC0_CCB3 register with a value between 0 and whatever value is in the REG_TCC0_PER register. For example, at 100Hz the REG_TCC0_PER register contains 10000, therefore loading the REG_TCC0_CCB3 with 5000 will give you a 50% duty-cycle. The REG_TCC0_CCB3 register can be changed with the following code:

Code: [Select]
// The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB3 = 5000;       // TCC0 CCB3 - 50% duty cycle on D13
  while(TCC0->SYNCBUSY.bit.CCB3);

Manipulating TCCx timers at the register level will most likely conflict with analogWrite(). It's really a trade-off, analogWrite() provides both useability and portability, while register manipulation ultimately gives you more flexibility.

The example above gives a 13-bit resolution at 100Hz, (that was good enough for my project), however as the SAMD21's TCC0 and TCC1 are 24-bit timers, it's possible to obtain an even higher resolution, by altering the generic clock divisor and the timer's prescaler.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: pharaohamps on Feb 25, 2016, 09:55 pm
Thanks for the help!  Am I correct in thinking that analogWrite() will still work on pins that I haven't changed manually?  I'm fine with the standard 8-bit resolution from analogWrite() even on pin 13, so writing a function to change my mapping from 0-255 to 0-1000 is trivial.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Feb 26, 2016, 10:15 am
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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: gcl8a on Feb 28, 2016, 05:36 am
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:

Code: [Select]

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).

- Another option is to note that the Arduino code (see https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/wiring_analog.c) uses a default value of 0xFF for the PER register. This can be changed manually as you have shown above, but then one has to set the CCBx register manually. Still, it saves a little of the setting things up. To do PWM on D6, for example:

Code: [Select]
 
  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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Feb 28, 2016, 05:58 pm
Thanks gcl8a, a nice combination of analogWrite() and register manipulation.

Quote
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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 09, 2016, 04:35 pm
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




Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 09, 2016, 06:04 pm
Hi glovisol,

Quote
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?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 09, 2016, 06:56 pm
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

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 09, 2016, 08:04 pm
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:

Code: [Select]
// 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:

Code: [Select]
GCLK->CLKCTRL.reg = 0x441A;
This effectively does the same thing, but is less readable.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 09, 2016, 09:08 pm
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
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 08:27 am
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
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 10, 2016, 10:42 am
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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 11:43 am
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

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 10, 2016, 12:46 pm
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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 02:00 pm
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
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 10, 2016, 02:33 pm
Quote
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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 03:48 pm
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
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 04:32 pm
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
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 04:39 pm
 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
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 10, 2016, 04:54 pm
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?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 05:34 pm
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



Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 10, 2016, 06:07 pm
Did not let me load the Word File. I try again.

Cheers,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 10, 2016, 11:52 pm
The following code will output 370Hz on pin D7 with an 11-bit resolution:

Code: [Select]
// Output 370Hz PWM on timer TCC0 (11-bit resolution) on D7
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 1 PWM channel: timer TCC0 output
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = { 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
 
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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*/
  GCLK->CLKCTRL.reg = 0x441A;                     // Added for compilation problem with Arduino M0
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  // 20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz
  REG_TCC0_PER = 2702;      // Set the frequency of the PWM on TCC0 to 370Hz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB3 = 1500;       // TCC0 CCB3 - center the servo on D7
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

Here the timer is being clocked with a 2MHz signal (48MHz/3 = 16MHz, 16MHz/8 = 2MHz), or in other words each tick has a period of 0.5uS (1/2MHz). We're using dual slope PWM, in this scenario the timer counts up to the PER register value then back down to 0 and so on. So the PER register determines how long the timer takes to cycle counting up and then back down or in other words it determines the PWM's frequency.

This is given by the formula for dual slope PWM: PWM frequency = timer frequency / (2 * PER).

If we know the PWM frequency this formula can be rewritten PER = timer frequency / (2 * PWM frequency).

Therefore PER = 2MHz / (2 * 370) = 2702. That's the value of our PER register for 370Hz.

The CCBx registers determine the duty cycle. They can be any value between 0 and PER. In this case a value of 1351 (2702/2) will give a 50% duty cycle.

The resolution of this signal is given by the formula: resolution = log(PER + 1)/log(2). For a PER register value of 2702 this gives a resolution of 11-bits.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 11, 2016, 08:09 am
Dear Martin,

Thanks a lot for the ad hoc sketch!! Just up (7.30 AM here). Will test right away.I am enclosing a PICT. showing the 370 Hz , the PWM D.C. and the mean D.C. recovered @ 10 bit.

Will post results later today.

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 11, 2016, 09:16 am
Dear Martin,

I got a big fright! Sorry to disappoint you. I uploaded the sketch and it compiled first time. Near the end of uploading  IT LOST THE COMM PORT. Again the message "Device not recognized".

I unplugged Arduino instantly from  the USB cable, re-started the PC, checked "Devices" and re-plugged the M0. This time Windows recognized it and I could reload  Blink".

I carefully inspected your script and checked it with the working one (the Word stencilled script) found nothing wrong and did a second test. Again it lost the COMM Port before completing the upload.

I recovered M0nagain and re-loaded yesterday's sketch which is quietly working now....I believe there are gross differences between the US Zero and the Italian M0 in MUX settings.

I intend to get an US Zero with twin USB's as soon as possible, so that we have a common testing ground and not work with the constant fear of losing the Micro.

Kind regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 11, 2016, 09:54 am
The one of the main differences between the M0/M0 Pro and the Zero is that the digital pins D2 and D4 are swapped.

So, on the M0 the TCC0 will output on D4, D5, D6 and D7, while the Zero will output on D2, D5, D6 and D7. Apart from that, the MUX settings should be the same.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 11, 2016, 10:10 am
The MUX settings should be the same, but are not because these last settings freezed my M0 twice. What do you suggest?

Also you did not comment on the observations on the stencilled Word doc., where you can see that some parts of the sketch are not operative: this should surely point to relevant differences. I have located Zeros in France and hope to be operative by mid next week.

Furthermore pin 7 is for system testing only. I really need 6 output pins with 200 to 400 Hz PWM ouputs & Duty Cycle variable 0 to 100%, so a pin plan must be carefully laid out, in order not to have analogWrite interferences.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 11, 2016, 11:34 am
Quote
The MUX settings should be the same, but are not because these last settings freezed my M0 twice.
Why should the M0 freezing have anything to do with the MUX settings?

Quote
Also you did not comment on the observations on the stencilled Word doc., where you can see that some parts of the sketch are not operative: this should surely point to relevant differences.
The code that I provided outputs 370Hz on D7 at a resolution of 11-bits, I've tested it on my Zero and it works. Why do you need to change the generic clock source to 8MHz, when 48MHz and the subsequent dividers are sufficient? Changing the generic clock to an 8MHz source ripples through to the timers, so you'll need to evaluate new values for the timer prescaler and PER register. That's why parts of the sketch in your Word file are inoperative.

Have you managed to get your M0 to output a 370Hz PWM signal on D7 using the code I provided?

Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 11, 2016, 11:51 am
Dear Martin,

Thanks for your questions. Here is further info.

Further to my previous post, I took the previous (safely working) sketch and patiently made the modifications towards your last sketch, changing one thing at a time. I have come to the end and the M0 reloads with no problems, so I cannot explain why your original sketch made the M0 fail twice and this criticity of course worrie me.

Apart from this, please let me know if your sketch on your M0 works as described in your post, because on this M0, some parts REALLY DO NOTHING, as per my previous stencilled Word doc.

The parts that do nothing are:

===========================================
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                   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);  

Here I can change DIV(3) to any number, but output freq. never changes. I can go from DFLL48M to OSC8M and output never changes.

==========================================

 REG_TCC0_PER = 2702;      // Set the frequency of the PWM on TCC0 to 370Hz
 while(TCC0->SYNCBUSY.bit.PER);

The PER register also is not operative and changing REG_TCC0_PER from 20000 to 1000 does not produce any change in output frequency. So the 2702 factor is useless here.

==========================================

All the remainder of the script is working well, so that, as received, the M0 has an output of 11,760 Hz.

Therefore: 11760/370 = 32 this is the missing scale factor.

To get to 370 Hz we must have a Prescaler factor of 8*32 = 256 and indeed, if I set the prescaler at 256 the M0 outputs the required 370 Hz.

kind regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 11, 2016, 12:02 pm
Quote
Apart from this, please let me know if your sketch on your M0 works as described in your post, because on this M0, some parts REALLY DO NOTHING, as per my previous stencilled Word doc.
The code that I supplied to output 370Hz PWM on D7 works. I've tested it on my Zero, my multimeter reads 370Hz.

All I can suggest is waiting until you get your Genuino Zero and the Arduino.cc IDE up and running. At least then we'll be singing from the same hymn sheet.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 11, 2016, 12:38 pm
Dear Martin,

I totally agree, it is pointless to lose time and risking failures while operating on two different machines. Will contact you again when my Genuino Zeros arrive.

Bt the way, where you from?

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 14, 2016, 07:00 pm
Dear Martin,

I ordered two Zero's in France last friday, march 11 and should be here any time now. Will advise.

Kind regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 14, 2016, 07:31 pm
Hi glovisol,

Quote
Bt the way, where you from?
I'm from England.

Quote
I ordered two Zero's in France last friday, march 11 and should be here any time now. Will advise.
Speak to you soon.

Kind regards,
MartinL
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 17, 2016, 04:57 pm
Hi, MartinL,

The two Genuino zeros arrived this afternoon. One was defective just ot of the box: the native USB port was dead, didn't even light the ON Led and I immediately sent it back.

Downloaded IDE 1.6.7, but now I have to re-learn all the works, as this animal is a lot different from the M0! I still have to figure out how to print things into the serial monitor, as a start.

Will contact you again as soon as I can get this thing alive.

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 17, 2016, 06:37 pm
Hi MartinL,

I finally sorted the Genuino Zero & loaded your sketch.

First the good news: I see 370 Hz on pin 7, but...here

 the bad news: the sketch starts only after pressing the reset button : it shhows the square wave @ 370 Hz for perhaps 3 seconds, then pin 7 goes high and stays there until I press the reset again.

All above loading on Programming Port (COM 2).  Loading on Native Port (COM14) I see the 370 Hz on pin 7 for about six seconds, then off. To see it again I must press the reset button.

Scratiching my head...

I have more news about the c.c. and org. IDES, but I leave these for later.

glovisol

Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 17, 2016, 08:46 pm
Hi MartinL

It works with Genuino Zero, but not, as we have seen last week, with Arduino M0!! This means that differences between the two IDES's and perhaps the two boards are more than currently believed.

I enclose the sketch which is the exact copy of your original. The only mystery is why the operating time is determined by the timex variable, but will investigate tomorrow. With timex = 100000 millis it works for a long time before going off. Frequency is confirmed 370 Hz and can go down to fraction of Hz manipulating the different parameters.

In the meantime my Picoscope 2000 decided to die, so more frustration. But I still had my faithful old friend, the Tektronix 2336 which tided me over.

Another important  issue: if you download IDE 1.7.8 (org.) the Atmel drivers of this IDE stay there even if you later load IDE 1.6.7 (c.c.). If you are not aware of this, all kind of strange things start happening with the Genuino (for instance it is impossible to load your sketch)....by luck I have many laptops and after getting crazy for two hours, loaded a fresh IDE 1.6.7 on a completely clean laptop and things started looking up. I reckon a good system cleanup is necessary when going from 1.7.8 to 1.6.7........

Hope to meet you on site  tomorrow.

Kind regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 18, 2016, 08:44 am
Hi MartinL,

Compliments, you really did a super job!

Herewith enclosed MINIMAL frequency change sketch that works with Genuino Zero, IDE 1.6.7. The problem with the sketch not staying "ON" yesterday was due to:

analogWrite(7, sensorValue_5);

which was conflicting with the CCBx register.

In any case the sketch outputs a fixed frequency, NOT a PWM which changes with an input pin parameter change, so some more work is necessary.

I still have a problem with sketches writing to the  Serial Monitor (Programmimg Port). Sometimes it works, sometimes it does not...

Cheers,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 18, 2016, 10:03 am
If you change the CCBx register value in your loop(), this will change the duty cycle of the PWM signal. The value of the CCBx register can be between 0 and whatever value you have in the PER register.

So for 370Hz the value in the PER is 2702, therefore if you set the CCBx register to 1351 you'll get a 50% duty cycle. 0 will give a 0V output for 0% duty cycle, while 2702 will give 3.3V for 100%. As the value of the CCBx is buffered, changes to the duty cycle won't cause any glitches on your output.

Code: [Select]
REG_TCC0_CCB3 = 1351;       // TCC0 CCB3 - 50% duty cycle on digtital pin 7 (D7)
while(TCC0->SYNCBUSY.bit.CCB3);
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 18, 2016, 12:03 pm
Why not writing a Frequency Library out of this?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 18, 2016, 01:07 pm
I guess it would be possible to write a frequency library within certain constraints. The main issue though would be in creating something that satifies everyone's requirements in terms of PWM signal frequency and resolution, not to mention output pin multiplexing.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 18, 2016, 05:16 pm
Hi MartinL,

Here is the bottom line.

1. Complete sketch with Pin 7 working @ 400 Hz and outputting 0 - 100% PWM with Analogue input A5 receiving 0 - 2500 mV.

2. PC screens showing operation.

Your script is very powerful and gives accurate results. To prepare a library, the first step is planning a strategy for pin utilisation. I think that perhaps up to six pins can be used as outputs, to match the six available Analogue inputs. The real problem is that the output pins must output INDEPENDENT PWM levels to follow the individual A0 - A5 input voltages. In any case the output resolution shown by the enclosed sketch is simply amazing, when compared to what one can get out of Arduino Uno.

Have a nice weekend.

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 20, 2016, 09:24 pm
Hi MartinL,

Based on your work, now I have the sketch with 6 input / output channels working @ 400 Hz, on output pins: 2, 3, 4, 5, 6 & 7, from registers TCC0_CCB0 up to TCC1_CCB1. I anticipated at least two months of work, but with your help & know how I am in business after two weeks! I can post this sketch for the benefit of the community if you instruct me to do so.

Furthermore with this work we became aware that the board Arduino M0, working with its out of factory bootloader and IDE 1.7.8, does not respond to out of the book ATMEL standard commands  and I am wondering if it should not be our duty to warn other users of this problem. At the end of the day, I have two useless M0 boards in my lab, which were paid good money. Not only these boards did not and do not respond, but one became completely unoperative as a result of uploading software, something that should never happen and was revived with a special trick only.

We could further analyse these problems by using these boards and uploading the perfectly working shetches you have developed and see what happens.

Thanks again,

glovisol

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 21, 2016, 12:38 am
Hi glovisol,

Glad to hear you got everything working.

Quote
Furthermore with this work we became aware that the board Arduino M0, working with its out of factory bootloader and IDE 1.7.8, does not respond to out of the book ATMEL standard commands  and I am wondering if it should not be our duty to warn other users of this problem. At the end of the day, I have two useless M0 boards in my lab, which were paid good money. Not only these boards did not and do not respond, but one became completely unoperative as a result of uploading software, something that should never happen and was revived with a special trick only.
You should be able to reconfigure the Arduino M0's for use with Arduino.cc code, by connecting their 10-pin SWD header to an Atmel ICE, then using the Arduino.cc's IDE to burn the bootloader. I burn the bootloader on my own custom SAMD21G boards this way.

Kind regards,
MartinL
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 21, 2016, 08:02 am
Hi MartinL,

Thanks for your suggestion, but I have seen that the ICE is for sale at EUR 94-. At this price it makes sense to throw the M0's into the dustbin...Since you have the facilities and could use it, I would gladly send you one.

I enclose a PC screen showing 4 of the 6 output pins. Waveforms are horrible to look at, because I am using a Picolog DrDAQ (very low bandwith) as scope, having sent back to Picotech my regular 2000 scope for repairs.

Kind regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 21, 2016, 09:15 am
Quote
Thanks for your suggestion, but I have seen that the ICE is for sale at EUR 94-. At this price it makes sense to throw the M0's into the dustbin...Since you have the facilities and could use it, I would gladly send you one.
I bought the Atmel ICE Basic kit: http://uk.farnell.com/atmel/atatmel-ice-basic/debugger-atmel-arm-avr-basic-kit/dp/2407172 (http://uk.farnell.com/atmel/atatmel-ice-basic/debugger-atmel-arm-avr-basic-kit/dp/2407172). It costs about the same as a Zero.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 21, 2016, 10:30 am
Thanks for the info! Will do.

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: cr34t0r on Mar 21, 2016, 07:04 pm
I've been looking into PWM modes, can anyone confirm that the dual slope PWM mode is the same thing as phase correct PWM mode? Are these just different terms used by different vendors?


Also, @MartinL

I am trying to control 2 DC motors via a motor driver. I tried to decipher your code on the first page but don't understand why or if there is a need in my case to wait for clock syncs, port multiplexers, etc?

Can I just set the PER register (to 1.2k), select the PWM mode, select the prescaler, and select the clock?

Thank you!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 22, 2016, 10:43 am
Quote
I've been looking into PWM modes, can anyone confirm that the dual slope PWM mode is the same thing as phase correct PWM mode? Are these just different terms used by different vendors?
Dual slope PWM is the same as "phase and frequency correct" PWM on the AVR microcontrollers. Atmel seems to use a number of different terms to describe the same thing. On the Arduino Due's SAM3X8E for instance it's called "center aligned".

Quote
I tried to decipher your code on the first page but don't understand why or if there is a need in my case to wait for clock syncs, port multiplexers, etc?
The SAMD21 is internally more flexible than the older AVR processors. Peripherals such as timers, serial communication ports can be multiplexed to different pins, so it's necessary to hook them up first. Once that's out of the way though the basic principle is the same.

If you've previously been using code for the AVR, it's likely that it's using a base (timer) frequency of 16MHz. So you can divide GCLK4 by 3 to give 16MHz and leave the timer prescaler at 1 (no further division). The PER register needs to be loaded with 400, as frequency = 16MHz/(2 * 400) = 20kHz. The CCBx registers can be loaded with any value between 0 and 400 for a 0 to 100% duty cycle. The following code outputs 20kHz, 50% duty cycle, with a resolution of 8-bits on pins D2, D5, D6 and D7:

Code: [Select]
// Output 20kHz PWM on timer TCC0 (8-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 4 PWM channels: timer TCC0 outputs
  const uint8_t CHANNELS = 4;
  const uint8_t pwmPins[] = { 2, 5, 6, 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - 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_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  // 400 = 20kHz
  REG_TCC0_PER = 400;      // Set the frequency of the PWM on TCC0 to 50Hz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB0 = 200;       // TCC0 CCB0 - 50% duty cycle on D2
  while(TCC0->SYNCBUSY.bit.CCB0);
  REG_TCC0_CCB1 = 200;       // TCC0 CCB1 - 50% duty cycle on D5
  while(TCC0->SYNCBUSY.bit.CCB1);
  REG_TCC0_CCB2 = 200;       // TCC0 CCB2 - 50% duty cycle on D6
  while(TCC0->SYNCBUSY.bit.CCB2);
  REG_TCC0_CCB3 = 200;       // TCC0 CCB3 - 50% duty cycle on D7
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 1 giving 16MHz (62.5ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: cr34t0r on Mar 22, 2016, 01:43 pm
Is it necessary to divide the GCLK4 by 3 or is it okay to keep the original 48MHz and have the PER register loaded with 1,200 instead? 48MHz/(2*1200)=20kHz

If I am not mistaken, a higher value in the PER register results in a higher resolution (in this case 1,200 results in a 10 bit resolution as opposed to 8 bit)?

Also, if possible, can you direct me to some resources to better understand the macros you are using and tutorials on setting up clocks/timers for the SAMD21?

The datasheet for the SAMD21 and the gclk.h file reference on the Atmel website doesn't really offer a comprehensive guidance for first time users IMO.

BTW +1 Karma, thank you so much for your help!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 22, 2016, 03:46 pm
Quote
Is it necessary to divide the GCLK4 by 3 or is it okay to keep the original 48MHz and have the PER register loaded with 1,200 instead? 48MHz/(2*1200)=20kHz

If I am not mistaken, a higher value in the PER register results in a higher resolution (in this case 1,200 results in a 10 bit resolution as opposed to 8 bit)?
Yes, using a higher clock frequency for the timer will provide a higher resolution, in your case 10-bit resolution.

Quote
Also, if possible, can you direct me to some resources to better understand the macros you are using and tutorials on setting up clocks/timers for the SAMD21?

The datasheet for the SAMD21 and the gclk.h file reference on the Atmel website doesn't really offer a comprehensive guidance for first time users IMO.
The only documentation I was able to find was the SAMD21 datasheet. It was a case of a lot of experimentation, looking at the datasheet for registers then hunting for its corresponding definition in the Atmel files, such as "gclk.h".
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 22, 2016, 04:05 pm
Hi MartinL,

I have been studying the SAMD21 data sheet & Arduino Zero schematic, but either my eyes fail me due to old age, or the connection between pin D7 (Arduino) and the Micro is missing.... Perhaps you do know where pin 7 is coming from... All the other DX pins are accounted for.

Cheers,

glovisol

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 22, 2016, 06:03 pm
D7 on the Zero is coming from PA21, that's pin 30 on the SAMD21G chip itself.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: cr34t0r on Mar 22, 2016, 06:22 pm
@MartinL,

I am having a hard time figuring out the correct mappings for the timers/ports/pins for the use of TCC2 for the digital pins 11 and 13...

Thanks!

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 23, 2016, 10:23 am
Hi cr34t0r,

To connect GCLK4 to the TCC2 timer (and TC3), use:

Code: [Select]
// Feed GCLK4 to TCC2 (and TC3)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

To enable the port multiplexer on pins 11 and 13:

Code: [Select]
PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;

To connect timer TCC2 to digital pins D11 and D13:

Code: [Select]
PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
Title: Re: Changing Arduino Zero PWM Frequency
Post by: cr34t0r on Mar 23, 2016, 06:38 pm
Hi MartinL,

I tried to run my motors from the Zero and it didn't work. I realized that it was because the motor driver pwm pins were defined as D9 and D10 as opposed to D11 and D13.

I looked into the pin connections and it looks like D9 is attached to TCC1 and D10 is attached to TC3.

I realized, for example, there is no REG_TCC1_WAVE register for the TC3 timer and I am confused as to how to set up everything for the TC3 timer/D10 pin. Please advise, thanks! 


Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 24, 2016, 10:00 am
Hi MartinL,

As correctly stated in your posts, Resolution is a function of the PER parameter. In other words, the larger the number of steps in the PER register, the larger the Resolution. You have blueprinted a powerful method which allows, within limits, to set the PWM frequency and resolution addressing the SAMD registers directly.

My question is: what happens when the SAMD is addressed by the IDE?. With the IDE there are very few choices, never the less you can have up to 12 Bit output resolution with a fixed PWM frequency as high as 187 KHz. I am enclosing a (horrible, due to the small bandwith of the DrDAQ used as scope/freq.meter) PC screen showing PWM at this frequency with 12 Bit input / output resolution. I am also enclosing a "conventional" sketch using standard IDE commands. How are they doing it?

It appears, once more, there are many ways to skin a cat..

Best regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 24, 2016, 11:06 am
Hi c34t0r,

Quote
I looked into the pin connections and it looks like D9 is attached to TCC1 and D10 is attached to TC3.
The TC timers are a completely different peripheral from the TCC timers and come with their own set of registers. The TC timers are capable of PWM, albeit with reduced functionality.

The issue with using the TC timers is that they can be set as an 8-bit, 16-bit and 32-bit counter, however you can only set the PER (period) register in 8-bit mode for PWM. This means that if you set the timer to 8-bit mode you'll have control over your frquency, but you'll lose resolution. If on the other hand you use the 16-bit or 32-bit modes you'll gain resolution, but have not so much control over your frequency.

If possible it would be better to use the TCC registers for PWM, as they provide much more flexibiltity.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 24, 2016, 11:53 am
Hi glovisol,

The resolution of the PWM can be increased by using the raising the base (timer) frequency.

In some of the examples, I divided the generic clock (GCLK) by 3 to get 16MHz and then down further to 2MHz using the TCC timer's prescaler. The 16MHz GCLK allows the SAMD21 to mimic the older AVR microcontrollers found on the Arduino Uno and Mega. Using a 2MHz base frequency is useful for controlling brushless motors and servos and is convenient as the CCBx register values can be loaded with a value between 1000-2000, which corresponds to the 1000uS to 2000uS PWM pulses that servos and brushless electronic speed controller expect. Resolution is determined by log(PER + 1)/log(2). So even in this case for a servo at 50Hz, the resolution is log(20000 + 1)/log(2) = 14-bit resolution, which for most servos is good enough.

However, in terms of resolution the SAMD21 can do a lot better than this. If you set TCC timer's base frequency to 48MHz, (GCLK divisor = 1, TCC prescaler = 1) and correspondingly raise the value of the PER and CCBx registers you'll obtain a much higher resolution. The TCC0 and TCC1 registers are 24-bit counters, while TCC2 is 16-bit, so the PER and CCBx registers can be set to a high value.

I don't see how the analogWriteResolution() changes the resolution for analogWrite() to 12-bits? Looking at the analogWrite() code, it appears as though the Arduino core is setting the TC timer counter to 8-bit mode and the PER register to it's maximum (for 8-bits) at 0xFF (hex) or 255 (decimal), with the generic clock running at 48MHz. This is what's giving you the frequency of 48MHz/(255 + 1) = 187.5kHz and also a resolution of log(255 + 1)/log(2) = 8-bits. The higher resolution values appear to be just shifted back down to 8-bits. Using analogWriteResolution(12) together with analogWrite() is just mapping your inputs, it's not giving you a better output resolution, which remains at 8-bits.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: cr34t0r on Mar 24, 2016, 03:35 pm
Hi MartinL,

Thank you so much for all the guidance. I physically remapped some of the pins on my board and was able to get both motors running!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Mar 29, 2016, 10:31 am
The higher resolution values appear to be just shifted back down to 8-bits. Using analogWriteResolution(12) together with analogWrite() is just mapping your inputs, it's not giving you a better output resolution, which remains at 8-bits.

Hi MartinL,

Thanks for the detailed explanation on Resolution. I understand that with the c.c. IDE the TC timers are used (single slope) for PWM, but I do not understand the meaning of the quoted sentence: how can they claim 12 Bit resolution if, in fact, it remains @ 8 Bit?  am going to quickly test this.

Kind regards,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 29, 2016, 11:08 am
Hi glovisol,

Quote
how can they claim 12 Bit resolution if, in fact, it remains @ 8 Bit?
I was a bit suprised as well. analogWrite() is using the TC timers in 8-bit mode, so there's no way to get an output resolution of 12-bits.

So even with analogWriteResolution() set to 12-bits, analogWrite() is simply mapping your inputs from 4096 steps down to 256, before passing the value on to TC registers. You're actually only getting 8-bit resolution.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: michael231 on Apr 01, 2016, 01:23 am
Hi Martin L,

I am using your example code in order to generate PWM for 2 channels at a frequency of 150kHz which is a big step forward for me.  However, I need the channels to have a 180 degree phase difference, when one channel is HIGH the other needs to be LOW for the same amount of time and vice versa.  Is it possible to do something like this within the setup?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Apr 01, 2016, 10:46 am
Hi michael231,

Quote
Is it possible to do something like this within the setup?
Yes it is. The SAMD21's TCC timers are highly configurable. The PWM examples that I've described only scratches the surface of what's possible.

The issue here is that there are number of different permutations and combinations of outputs that can be used. Do you have a preference for which two digital pins you'd like to use?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: michael231 on Apr 02, 2016, 06:13 pm
Do you have a preference for which two digital pins you'd like to use?
Yes I am using pins 6 and 7 an I'm trying to get it such that the duty ratio on pin 7 is 1 - the duty ratio of pin 6. so if pin 6 is at .9 then pin 7 is at .1
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Apr 03, 2016, 10:50 am
Hi MartinL,

I had a hint two days ago, and I tested extensively, but I am sure that AREF does not work with Register Direct Addressing. All the following do not work:

AR_INTERNAL1V0;

AR_INTERNAL1V65;

AR_INTERNAL2V23;

AR_EXTERNAL;

From testing the Reference voltage is fixed at 3.3 V and cannot be changed with the regular commands.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Apr 03, 2016, 03:52 pm
With reference to my previous post, to set the REFERENCE VOLTAGE the following TESTED expressions must be used, as suggested  by Aloyse Tech:

 
Replace  AR_INTERNAL2V23 with:
     ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;      // Gain Factor Selection
     ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 1/1.48 VDDANA = 1/1.48* 3V3 = 2.2297
   

Replace AR-EXTERNAL with:
     ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;      // Gain Factor Selection
     ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_AREFA_Val;
     

Replace AR_INTERNAL1V0 with:
     ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;      // Gain Factor Selection
     ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val;   // 1.0V voltage reference
   

Replace AR_INTERNAL1V65 with
     ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;      // Gain Factor Selection
     ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC1_Val; // 1/2 VDDANA = 0.5* 3V3 = 1.65V
   

Factors 1.48 & 0.5 must be used to correct PWM as required.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Apr 03, 2016, 11:56 pm
Hi michael231,

In order to produce an inverting and non-inverting signal on D6 and D7, you need to additionally change two registers: the Waveform Extention Control register (REG_TCC0_WEXCTRL) and the driver control register (REG_TCC0_DRVCTRL).

Timer TCC0 has four channels CC0 - CC3, which can be routed to 8 waveform outputs WO[0] - WO[7]. By default:

CC0 => WO[0]
CC1 => WO[1]
CC2 => WO[2]
CC3 => WO[3]
CC0 => WO[4]
CC1 => WO[5]
CC2 => WO[6]
CC3 => WO[7]

D6 and D7 are connected to WO[6] and WO[7] and therefore connected to separate channels CC2 and CC3. However, this configuration can be changed by configuring the output matrix in the Waveform Extension Control register:

Code: [Select]
// Set the output matrix so that D6 and D7 are set to output CC0
  REG_TCC0_WEXCTRL |= TCC_WEXCTRL_OTMX(0x2);

If the output matrix is set to 0x2, then the CC0 channel is routed to all waveform outputs.

CC0 => WO[0]
CC0 => WO[1]
CC0 => WO[2]
CC0 => WO[3]
CC0 => WO[4]
CC0 => WO[5]
CC0 => WO[6]
CC0 => WO[7]

Now both D6 and D7 are connected to the same channel: CC0.

To get a non-inverted and inverted output it's necessary to invert one of the waveform outputs. I've chosen D7 to be the inverted output:

Code: [Select]
// Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
  REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;

The example code below sets up D6 as the non-inverted and D7 as the inverted output. As you're using such a high frequency (150kHz), I've switched the code from dual slope PWM to single slope PWM. This improves your resolution from 7-bits to 8-bits. This is the maximum resolution the SAMD21 can produce at this PWM frequency. Also, as the code uses single slope PWM, it's now not necessary to reverse the channel polarity.

Anyway, here's the code:

Code: [Select]
// Output 150kHz with non-inverting and inverting PWM on timer TCC0 (8-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 pins D6 and D7
  PORT->Group[g_APinDescription[6].ulPort].PINCFG[g_APinDescription[6].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port output D6 and D7 - port pins are paired odd PMUXO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

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

  // Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;        // Setup single slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);                // Wait for synchronization

  // Set the output matrix so that D6 and D7 are set to output CC0
  REG_TCC0_WEXCTRL |= TCC_WEXCTRL_OTMX(0x2);
 
  // Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
  REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 319;                             // Set the frequency of the PWM on TCC0 to 150kHz
  while(TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
 
  // Set the PWM signal to output 75% duty cycle on D6, 25% on D7
  REG_TCC0_CCB0 = 239;                            // TCC0 CCB0 - on output on D6 and inverted output on D7
  while(TCC0->SYNCBUSY.bit.CCB0);                 // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Apr 04, 2016, 10:11 am
Hi glovisol,

Thanks for the information about the analog reference.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Apr 04, 2016, 11:17 am
Hi MartinL,

Here enclosed PCTS of your new sketch at low frequency. (My repaired Pico scope should arrive tomorrow....).

Compliments for a fantastic performance!

Code below is example for FREQUENCY MODULATION with voltage from pin A5

 //OPERATION
void loop()
{
 digitalWrite(Pin13LED, HIGH);
 Serial.begin(9600);
 analogReadResolution(BitX); //Sets the INPUT resolution equal to the OUTPUT resolution.
 Vy_5 = analogRead(sensorValue_5);
 float Vz_5 =(Vy_5 * CalX/PERX);
 Serial.print("Channel/5, Bits = ");
 Serial.print(Vy_5);
 Serial.print(" and mV = ");
 Serial.println(Vz_5);
 Serial.println("....................");
 REG_TCC0_PER = (Vy_5*12);
 while(TCC0->SYNCBUSY.bit.CCB0);


glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Apr 04, 2016, 03:29 pm


Of course if you change:

 REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;       
  while (TCC0->SYNCBUSY.bit.WAVE);                           

with:

  /*REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |
                                 TCC_WAVE_WAVEGEN_DSBOTTOM;   
  while (TCC0->SYNCBUSY.bit.WAVE);

You have double slope operation at half the frequency
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 05, 2016, 12:19 pm
Quote
I don't see how the analogWriteResolution() changes the resolution for analogWrite() to 12-bits? Looking at the analogWrite() code, it appears as though the Arduino core is setting the TC timer counter to 8-bit mode and the PER register to it's maximum (for 8-bits) at 0xFF (hex) or 255 (decimal), with the generic clock running at 48MHz. This is what's giving you the frequency of 48MHz/(255 + 1) = 187.5kHz and also a resolution of log(255 + 1)/log(2) = 8-bits. The higher resolution values appear to be just shifted back down to 8-bits. Using analogWriteResolution(12) together with analogWrite() is just mapping your inputs, it's not giving you a better output resolution, which remains at 8-bits.
Hi MartinL,

As previously anticipated, I have tested AnalogWriteResolution() with Arduino Zero. The PWM frequency remains indeed constant @ 187.4 KHz. I have made a simple mod. to the sketch given as example for AnalogWriteResolution() to see Resolution @ 8/10/& 12 Bit. Since max. AnalogRead can go up to 10 Bit only, it is of course impossible to demonstrate by measurement that Write can go up to 12 Bit. But, with AnalogWriteResolution(10) indeed the PWM goes in steps of 99(%)/1023, as it can be seen in the enclosed PC screens. So it remains to be figured out how the command can pull Resolution from 8 to 10 Bit without altering the PWM frequency.

The Scope shows PWM percentage. One bit is equivalent to 0.096774% and indeed, even if difficult because of noise, this interval was appreciated.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 05, 2016, 01:56 pm
Hi glovisol,

Thanks for the test. As I thought, it shows that analogWrite() PWM is only working to a resolution of 8-bits, whatever analogWriteResolution() is set to.

It's possible to show that analogWriteResolution() doesn't work.

What analogWriteResolution() is doing is just shifting analogWrite()'s input right by the user defined resolution - 8 bits. So for example, if we set our resolution to 12-bits then analogWrite()'s input is shifted by: 12 - 8 = 4 bits.

Shifting right means we lose the last or least signficant 4 bits of information, as they're effectively shunted off the register. These 4-bits represent an analogWrite() value between 0-15. We can test if this information has been lost by setting the resolution to 12-bits, then setting analogWrite()'s value to a number between 0-15. If the information has been lost, the PWM output should remain at 0V, 0Hz.

Sure enough, setting analogWrite()'s value to 15, 15 >> 4 = 0, the PWM outputs 0V, 0Hz:

Code: [Select]
void setup() {
  analogWriteResolution(12);    // Should set PWM resolution to 12-bits
  analogWrite(9, 15);           // Output PWM on digital pin D9
}

void loop() {}

While setting the analogWrite()'s value to 16, 16 >> 4 = 1, so we get 187.5kHz again:

Code: [Select]
void setup() {
  analogWriteResolution(12);    // Should set PWM resolution to 12-bits
  analogWrite(9, 16);           // Output PWM on digital pin D9
}

void loop() {}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 05, 2016, 04:30 pm
Hi Martin,

Thanks for your additional info. As far as the 12Bit Resolution setting is concerned, I agree 100%: in fact, if you look closely at my PC screens, you see that the 12 Bit section goes 4 by 4, while the 8 bit & the 10 Bit sections go 1 by 1.

My point was and is: with my test it appears that AnalogWriteResolution is able to at least get 10 bits with PWM frequency still at 187 KHz: this is verified by the Scope screens as well. Therefore one could have, with the simple AnalogWriteResolution command Input Resolution = Output Resolution = 10 Bit. In fact if you look at PC screen "100" enclosed, you see measured by the scope a duty cycle of 9.478% (mean value, noise is a problem) for 99 Bit, which well agrees with 99*100/1023 = 9.677%.

Your opinion on this would be very well appreciated.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 05, 2016, 11:11 pm
Hi glovisol,

My apologies, I mistook, 1023 steps is for 10-bit, not 8-bit.

The reason that AnalogWriteResolution() doesn't work for 10-bit (or 12-bit) operation lies in the code for analogWrite() in the Arduino file: "wiring_analog.c".

analogWrite() employs both TC and TCC timers to cover most of the digital pins, but in both cases it sets the period (PER) register to 255 (0xFF).

Resolution = log(PER + 1)/log(2)

So in this case the timers' resolution is log(255 + 1)/log(2) = 8-bits

For this reason alone, it's not possible for the timers to output a higher resolution.

Then there's the issue of analogWriteResolution(). This allows you to change the range of values that can be provided as an argument to analogWrite() function. So you can enter a number between 0-255 for 8-bits, 0-1023 for 10-bits and 0-4095 for 12-bits. However, analogWrite() just right shifts the value back to 8-bits (0-255), before passing it on to the timer. So in effect analogWriteResolution() isn't giving you any improvement in timer resolution at all.

For example try setting analogWriteResolution() to 10-bits, then setting the value of analogWrite() to 1. You'd expect to see a tiny pulse at 0.0976% duty cycle (1/1024), but you don't, what you actually get is 0V. Same goes if you enter 2 or 3. Only when you enter 4 do you get a PWM signal at 0.39% duty cycle (4/1024 = 1/256). This is because two least significant bits of analogWrite()'s value have been right shifted, this means that any value between 1-3 have been lost shifting from 10-bit down to 8-bit resolution.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 06, 2016, 08:58 am
Sorry, was uploaded while on draft.
glovisol



Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 06, 2016, 09:11 am
Sorry, was uploadeds while on draft.

apologies,

glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 07, 2016, 10:40 am
Code: [Select]
/*glovisol- This sketch in the public domain.
This minimal sketch tests the PWM resolution of
Arduino ZERO and the behaviour of analogWriteResolution
according to the theoretical table below, where Bit%
is the single PWM percentage step at a given resolution.
BITS     RANGE       BIT %
8        255         0,39216
9        511         0,19569
10      1023         0,09775
11      2047         0,04885
12      4095         0,02442 */

//Set analogWriteResolution()
int Res = 10;
//Set arbitrary Value within Resolution range chosen
int Value = 504;
void setup()
{
 pinMode(3, OUTPUT);
}
void loop()
{
 analogWriteResolution(Res);
 analogWrite(3, Value);
 delay(4000);}


Hi MartinL,

Your last upload is fully confirmed by experiment: my previous statement that Arduino Zero can at least reach 10 Bit PWM resolution with analogReadResolution was wrong.

Using the above sketch one obtains the following:

BITS                    PIN D3 VALUE              DUTY CYCLE %

8                         100                            39.15
8                         101                            39.55  DIFFERENCE: 0.4% as per theory

9                         250                            48.97
9                         251                            48.97  DIFFERENCE: 0
9                         252                            49.34  DIFFERENCE: 0.4% as per theory

10                       500                             48.93
10                       501                             48.93  DIFFERENCE: 0
10                       502                             48.93  DIFFERENCE: 0
10                       503                             48.93  DIFFERENCE: 0
10                       504                             49.33  DIFFERENCE: 0.4% as per theory

I enclose the PC screens showing the above, with Picoscope showing Duty Cycle percentages. I spared  myself and everybody, because of the obvous results, the 12 Bit test.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 07, 2016, 04:28 pm
Code: [Select]
/* G. LOVISOLO - "glovisol" - Work based on original research by MartinL whose
expertise, help and guidance is gratefully acknowledged. This sketch in the Public Domain.
VER. 1.5 - 06.05.2016--- GENUINO ZERO (c.c.)SAMD21 PWM RESOLUTION DEMONSTRATOR.
Provides settable resolution variable duty cycle squarewave on output pin D3.
This minimal sketch tests the PWM resolution of Arduino ZERO by direct SAMD21 control
according to the theoretical table below, where Bit% is the single PWM percentage
step at a given resolution.
BITS     RANGE       BIT %
8        255         0,39216
9        511         0,19569
10      1023         0,09775
11      2047         0,04885
12      4095         0,02442
14     16383         0,006104
16     65535         0,001526  */

int PERX = 65535;                 //Set Resolution parameter here.
int Value = 32917;                //PWM value to check actual PWM resolution.
int timex = 4000;                 //Set iteration time.

//SETTING UP
void setup()
{
 //Setting the INTERNAL Reference Voltage @ 2.2297V.
 ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val;      // Gain Factor Selection
 ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INTVCC0_Val; // 1/1.48 VDDANA = 1/1.48* 3V3 = 2.2297
 REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |       // Generic clock Divisor: calculated by PPWM tool.
                   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 |   // 48MHz clock source selected by PPWM.
                     GCLK_GENCTRL_ID(4);          // Select GCLK4.
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization.

  // Enable the port multiplexer for one channel: timer TCC1 outputs.
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = {3};
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
   PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
   PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

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

  REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC1 outputs
                   TCC_WAVE_WAVEGEN_DSBOTTOM;  // Setup dual slope PWM on TCC1
  while (TCC1->SYNCBUSY.bit.WAVE);             // Wait for synchronization
 
  REG_TCC1_PER = PERX;                        //Resolution for TCC1 Register. 
  while(TCC1->SYNCBUSY.bit.PER);

  REG_TCC1_CCB1 = (PERX/2);       // TCC1 CCB1 - 50% PWM on D3
  while(TCC1->SYNCBUSY.bit.CCB1);
 
// Divide the Divisor processed Clock signal by the Prescaler factor and enable the outputs.
    REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV16 |  // Prescaler Factor from the PPWM tool.
                      TCC_CTRLA_ENABLE;            // Enable the TCC0 output.
  while (TCC1->SYNCBUSY.bit.ENABLE);             // Wait for synchronization.
  delay(5000);
 }

//OPERATION
void loop()
{
 REG_TCC1_CCB1 = (Value);      //Output pin D3 
 while(TCC1->SYNCBUSY.bit.CCB1);
 delay(timex);
}


Hi MartinL,

As a practical counter-proof, shown above is a minimal sketch for checking the PWM resolution of the sketches developed by yourself. Using the above sketch, one obtains the following:

BITS               PIN D3 VALUE         FREQUENCY         DUTY CYCLE%

10                       500                   488.5 Hz              48.93
                          501                                             49.03 DIFFERENCE: 0.1% as per theory

11                      1000                  244.1 Hz               48.88
                         1001                                             48.93 DIFFERENCE: 0.05% as per theory

12                      2000                  122 Hz                  48.87
                         2001                                             48.89 DIFFERENCE: 0.02% as per theory

16                     32653                     7.625 Hz            49.85
                        32917                                             50.26 DIFFERENCE: 0.41% see below

At 16 Bit the difference between two following steps is only 0,001526%, outside the measurement capabilities of the Picoscope. I therefore resorted to check the difference between two PRIME Values:

(32917-32653)*0.001526 = 0.41%

Hi Martin,

This is the counter-proof that any resolution above 8Bit can be achieved with the scheme  given by yourself. PC screens enclosed.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: peladilla on May 10, 2016, 01:38 pm
hi and thanks to all

I have a question, what I have to include to use this register REG_GCLK_GENDIV ??

I don't get find it


sorry me english, thanks !!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on May 10, 2016, 04:04 pm
Hi there,

Can you please better explain? I sincerely do not understand your question.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: siam_thom on Aug 21, 2016, 08:50 am
Hi,

I am using Atmel Studio 7 to program the Arduino Zero. I am trying to confirm the PWM output is working by blinking the L LED at PORTA_17, as I don't have an oscilloscope. So far I have had no success. Can someone help please either with an Arduino sketch or C code.

I am using TCC0 and trying to generate a normal PWM.

Programming in Atmel Studio, I don't have the Pin function to convert the pin numbers. I believe I have the correct group, pin, and pmux numbers. I also have not been able to get the TCC0 intterupts working.

Code: [Select]

PORT->Group[0].PINCFG[17].bit.PMUXEN = 1;
PORT->Group[0].PMUX[8].reg = PORT_PMUX_PMUXO_F;

//Another way to write this is using the WRCONFIG register. 

PORT->Group[0].WRCONFIG.reg = (uint32_t)(PORT_WRCONFIG_HWSEL | PORT_WRCONFIG_WRPINCFG |
PORT_WRCONFIG_WRPMUX | PORT_PMUX_PMUXO_E_Val << PORT_WRCONFIG_PMUX_Pos |
PORT_WRCONFIG_PMUXEN | 0x1 << (17-16) );




I have tried multiple combinations of timer counters and no cigar.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: siam_thom on Aug 22, 2016, 12:32 am
I have got the LED to blink using the TCC0 interrupts. I had to use GCLK0 and enable the TCC0 bus clock to get this to work. I also was not able to directly route W0 (the PWM signal) to the LED. The match and overflow interrupts should be equivalent to the signal I want.

I have tried to configure the LED to toggle every 1s with a PWM period of 2s. The LED definitely doesn't toggle every 1s. The prescaler is set to 256 on the 48MHz clock and my PER is 375,000 with 50% duty cycle. Help please, thanks in advance.


Code: [Select]


#include "sam.h"

int genClock = 0;
int pwmPER =  375000;

void syncCount(){
     TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_NONE;
     while(TCC0->SYNCBUSY.bit.CTRLB);
     TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC;
      while(TCC0->SYNCBUSY.bit.CTRLB);
      while (TCC0->SYNCBUSY.bit.COUNT);
}

int main(void)
{
REG_PORT_DIRSET0 |= PORT_PA17;       // define portA x as output

// PORT->Group[0].PINCFG[20].bit.PMUXEN = 1;
// PORT->Group[0].PMUX[10].reg = PORT_PMUX_PMUXE_E;

// enable the TCC0 bus clock
PM->APBCMASK.reg |= PM_APBCMASK_TCC0;

GCLK->CLKCTRL.reg = (uint16_t)(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(genClock) |
GCLK_CLKCTRL_ID_TCC0_TCC1);
while (GCLK->STATUS.bit.SYNCBUSY);

// disable TCC0 to configure.
TCC0->CTRLA.reg &=~(TCC_CTRLA_ENABLE);
while (TCC0->SYNCBUSY.bit.ENABLE);

// set TCC counter direction up
TCC0->CTRLBCLR.reg &= ~TCC_CTRLBCLR_DIR;
while(TCC0->SYNCBUSY.bit.CTRLB);

TCC0->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;
while (TCC0->SYNCBUSY.bit.WAVE);

TCC0->PER.reg = pwmPER;
while (TCC0->SYNCBUSY.bit.PER);

TCC0->CC[0].reg = pwmPER*0.5;
while (TCC0->SYNCBUSY.bit.CC0);

NVIC_SetPriority(TCC0_IRQn, 0);
NVIC_EnableIRQ(TCC0_IRQn);
TCC0->INTFLAG.reg |= TCC_INTFLAG_MC0 | TCC_INTFLAG_OVF;
TCC0->INTENSET.reg = TCC_INTENSET_MC0 | TCC_INTENSET_OVF;

TCC0->COUNT.reg = 0x00000000;
while(TCC0->SYNCBUSY.bit.COUNT);

// Enable TCC0 and prescaler now that is configured
TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_ENABLE;
while (TCC0->SYNCBUSY.bit.ENABLE);

    while (1)
    {
syncCount();
    }
}

void TCC0_Handler(){
REG_PORT_OUTTGL0 = PORT_PA17;

TCC0->INTFLAG.reg |= TCC_INTFLAG_OVF | TCC_INTFLAG_MC0;
}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: siam_thom on Aug 22, 2016, 09:43 pm
Figured out the timing issue. The clocks weren't setup properly. Copied over the SystemInit function from the Arduino startup.c into the system_samd21.c file.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: johncornetto on Aug 30, 2016, 07:03 pm
To MartinL or others:

Is there a relatively straightforward way to increase/decrease the maximum/minimum frequency of the tone function in the standard IDE library without having to change features of the bootloader?  I ask you because of your insightful advice about how to increase/decrease the max/min frequency of PWM on this thread.

If tone's max/min frequency can be easily modified, do you have a particular Arduino model/version that you recommend for such modification?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 01, 2016, 04:18 pm
Quote
Is there a relatively straightforward way to increase/decrease the maximum/minimum frequency of the tone function in the standard IDE library without having to change features of the bootloader?  I ask you because of your insightful advice about how to increase/decrease the max/min frequency of PWM on this thread.

If tone's max/min frequency can be easily modified, do you have a particular Arduino model/version that you recommend for such modification?
The Arduino.cc website states that on the Zero the Tone() is capable of outputting a 50% duty cycle PWM at frequencies from 41Hz to 275kHz.

It depends what you consider to be straightfoward? Using register manipulation it's possible to get both higher and lower frequencies, but Tone() does seem pretty flexible in this regard. What frequency range are you intending to use?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: johncornetto on Sep 01, 2016, 09:29 pm
I have no particular frequency in mind; rather, I hope to explore the possible use of the tone function as a frequency generator within a sketch without having to alter the bootloader (as in many PWM frequency alterations).

Hence, I was, and am, hoping that there is a call function (or its equivalent) in the ( or a ) IDE library that allows a static (constant) or a dynamic (variable) change/alteration in the *prescaler* to vary the clock "divide by" (say from divide by 128 to some other appropriate multiple of 2 number).

I hope to do this as needed either by a particular sketch or even dynamically *within* a particular sketch, I hope, without directly altering the bootloader if possible.

Some of the information about register manipulation and/or varying the clock "divide by" is likely in the data sheet for each chip, so I would greatly appreciate some guidance as to which chip allows, preferably by means of an IDE library, a not-too-complicated way of implementing a clock "divide by" variable and/or register manipulation (if that makes the most sense).

If you do *not* think that the ZERO or UNO can do that, then is there anything in, or out, of the Arduino family that can do what I hope via an IDE library?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 01, 2016, 11:12 pm
Quote
Hence, I was, and am, hoping that there is a call function (or its equivalent) in the ( or a ) IDE library that allows a static (constant) or a dynamic (variable) change/alteration in the *prescaler* to vary the clock "divide by" (say from divide by 128 to some other appropriate multiple of 2 number).
I had a look at the source code for the Zero's tone() function. It dynamically alters the timer's prescaler to generate the specified frequency and appears to be remarkably similiar to your requirements.

The tone() function is in the Zero's "Tone.cpp" file, which (on my Windows machine at least) is located at:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.6.6\cores\arduino\Tone.cpp.

Title: Re: Changing Arduino Zero PWM Frequency
Post by: R0BERT0 on Sep 07, 2016, 12:43 pm
Hello!!!! i have a question.
I try it but i can change pwm frequency.

Can use this code in arduino mkr1000 board?
Thanks
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 07, 2016, 04:17 pm
Hi ROBERTO

Quote
Can use this code in arduino mkr1000 board?
Yes you can. However, while the port pin mapping (for example PA00, PA01, etc...) of the Zero and MKR1000 are the same, (as they share the same microcontroller), the Arduino mapping (D0, D1, etc...) for both boards is different.

The other issue is that you've got less pins to chose from, as the MKR1000 has fewer pins broken out than the Zero and some of these might also need to be configured as the default I2C or SPI port.

When selecting the timer pins you can use table 6.1 PORT Function Multiplexing from the SAMD21 datasheet for the SAMD21G. The conversion from port pin to Arduino pin can be found either in the MKR1000's "variant.cpp" file, or alternatively in the board's schematic. A PDF version of the MKR1000's schematic can be found here: https://www.arduino.cc/en/uploads/Main/MKR1000-schematic.pdf (https://www.arduino.cc/en/uploads/Main/MKR1000-schematic.pdf).

Anyway, here's a list of the 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.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: SSPW on Sep 18, 2016, 03:15 am
MartinL

Thanks for this tutorial. I have to admit, it a had to read it many times to get the details.

If anybody is interested, i have got 100Hz PWM with 0.16us resolution running on 4 pins on the Adafruit Feather M0 boards.

https://cdn-learn.adafruit.com/assets/assets/000/030/921/original/adafruit_products_2772_pinout_v1_0.png?1457305814 (https://cdn-learn.adafruit.com/assets/assets/000/030/921/original/adafruit_products_2772_pinout_v1_0.png?1457305814)

D11/D13 runs on TCC2
D10/D12 runs on TCC0

GCLK4 running at 48MHz
Both timers are using prescaler of 4 and PER=60000

Sketch sets D10 at 1000us, D11 at 2000us, D11/13 at 1500us pulse widths.

Runs fine in 1.6.11 IDE
Title: Re: Changing Arduino Zero PWM Frequency
Post by: rjastap on Sep 23, 2016, 03:29 pm
Hi MartinL.
Would you help to set this up?
I need 50khz on D9 pin, in my board this pin is mapped to PA07. What value I need to modify to alter duty cycle after making the configuration?
Thanks in advance.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 24, 2016, 10:11 am
Hi rjastap,

The following code sets-up 50kHz, at 50% duty cycle on D9:

Code: [Select]
// Output 50kHz PWM on timer TCC1 (9-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 PWM channel on pin D9 
  PORT->Group[g_APinDescription[9].ulPort].PINCFG[g_APinDescription[9].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC1 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E peripherals specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[9].ulPort].PMUX[g_APinDescription[9].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;

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

  // Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
  REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NPWM;        // Setup single slope PWM on TCC1
  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: 959 = 50kHz
  REG_TCC1_PER = 959;      // Set the frequency of the PWM on TCC1 to 50kHz
  while(TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC1_CC1 = 480;      // Set the duty cycle of the PWM on TCC0 to 50%
  while(TCC1->SYNCBUSY.bit.CC1);
 
  // Enable TCC1 timer
  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
}

void loop() { }

Digital pin 9, or PA07 uses timer TCC1 on perhiperal E. The timer is clocked a 48MHz, as the generic clock divisor and the timer prescaler are set to 1. I've also set-up the timer work in normal PWM mode, rather than dual slope, as at 50kHz this will increase your resolution by 1-bit.

The formula for working out the frequency (in normal PWM mode) is:

timer frequency = generic clock frequency / (N * (PER + 1))

where:
N = timer prescaler (in this case 1)
PER = value in the period (PER) register

We can therefore rearrange this formula to find the PER value for a given timer frequency:

PER = 48MHz / 50kHz - 1 = 959

This will give you a 50kHz PWM output.

Loading the counter compare 1 register (REG_TCC1_CC1) with a value half that of the PER gives a 50% duty cycle.

To change the duty cycle during operation however, it's better to load the buffered counter compare register, in this case REG_TCC1_CCB1. Note the 'B'. For example, to set the duty cycle to 25%:

Code: [Select]
REG_TCC1_CCB1 = 240;                      // Load the CCB1 register with a new value
while(TCC1->SYNCBUSY.bit.CCB1);                  // Wait for synchronization

The buffered register loads the new value into the unbuffered CC1 register at the beginning of each timer cycle. Loading the CC1 register directly (not using the buffered register) may cause glitches on you output PWM waveform when changing the duty cycle.

The value of the CCB1 register can range from 0 = 0% duty cycle (0V), through to the value in the PER register, in this case 959 = 100% duty cycle (3.3V).

This configuration will give out an output resolution of 9-bits.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: rjastap on Sep 27, 2016, 07:32 am
Hi MartinL.

Thank you so much for taking your time to write that beautiful explanation.


I was in a hurry , so I kept trying to configure it and I got to understand the whole concepts exactly as you explained.

Finally I made an aproximation to 20kHz that sastisfy my requirements.

This is the code I've got.

Code: [Select]

void pwmconfig()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 port multiplexer
 PORT->Group[g_APinDescription[9].ulPort].PINCFG[g_APinDescription[9].ulPin].bit.PMUXEN = 1;  // D9(PA07)
  
  // Connect the TCC1 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
 PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin>>1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E; // D8
  
  // Feed GCLK4 to TCC0 and TCC1         ??? Is there any way to feed GCLK4? only to 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

  // Single slope PWM operation

   REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NPWM;

   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:
  //
  REG_TCC1_PER = 2400;      // Set the frequency of the PWM on TCC1 to
  while(TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC1_CCB1 = 1200;       // TCC0 CCB0 - 50% duty cycle on D2
  while(TCC1->SYNCBUSY.bit.CCB1);


  // Divide the 48MHz signal by 1 giving 48MHz
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}


And I made a function to replace analogWrite(i,9);

Code: [Select]

void set_pwm(int i){
  REG_TCC1_CCB1 = i;       // TCC0 CCB0 - 50% duty cycle on D2
  while(TCC1->SYNCBUSY.bit.CCB1);

}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: DR49 on Oct 08, 2016, 11:04 am
Hi MartinL,

I am interested in your code since I need to generate a number (N) of pulses at a given frequency (20 to 45 KHz more or less), on a given PIN of my M0PRO board.

I tried to compile your example (dual slope PWM over PIN 7) to start with.

I unfortunately got the following error message.

Could you help ?

Many thanks,

Daniel

Arduino : 1.7.9 (Windows XP), Carte : "Arduino M0 Pro (Programming Port)"

Gene_frequence_1.ino: In function 'void setup()':

Gene_frequence_1.ino:24:22: error: 'GCLK_CLKCTRL_ID_TCC0_TCC1' was not declared in this scope

Erreur lors de la compilation.

 
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 08, 2016, 07:47 pm
 Hi Daniel,

The reason why the code doesn't compile is because you're using Arduino.org's IDE and M0 Pro board, rather than Arduino.cc's IDE and Arduino/Genuino Zero.

Arduino split into two companies, Arduino.org and Arduino.cc (and are currently seeking an amicable resolution), but the Zero and M0 Pro never fully escaped the consequences. The main differences between the boards being that the digital pins D2 and D4 are reversed and that the M0 Pro doesn't have the additional ATN pin. In addition, the two boards use a different bootloader.

Unfotunately, it also appears that Arduino.org software doesn't use some of the Atmel register definitions for the SAMD21 microcontroller found on both boards and that's what's causing the error.

Personally, I think the easiest solution is to download the Arduino.cc IDE and take the following steps below to burn the Arduino.cc bootloader on to your M0 Pro. However, this will have the effect that that the digital pins: D2 and D4 will be reversed. So, calling D2 in your sketch will effect the D4 pin and vice-versa. Following this you should be able to compile and upload the above sketch without any errors. It also has the added benefit that you'll be using the same software as most of the other Zero/M0 Pro users on this forum.

To burn the Arduino.cc bootloader on to your Arduino M0 Pro:

1. Download the Arduino.cc IDE (currently version 1.6.12)
2. In the menu go to Tools->Board: ...->Boards Manager
3. Install the "Arduino SAMD Boards (32-bit ARM Cortex M0+)" package (currently version 1.6.9)
4. Close the Boards Manager
5. Select Tools->Board: ..., then select "Arduino/Genuino Zero (Programming Port)
6. Plug your USB into your M0 Pro's programming port and the other end into your PC
7. Select Tools->Port and select your M0 Pro's COM port
8. Select Tools->Programmer..., and select "Atmel EDBG"
9. Select Tools->Burn Bootloader
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Maverick123 on Oct 30, 2016, 08:29 pm
Hi MartinL

I have been trying to read up on how to implement PWM on an M0. I have learnt heaps up to this point from this thread. I have included some of the code posted to create my own code to drive 7 PWM pins of an Adafruit Feather M0: PA15, PA20, PA07, PA18, PA16, PA19 and PA17 if I am to believe the following figure:
Adafruit M0 proto (https://cdn-learn.adafruit.com/assets/assets/000/030/921/original/adafruit_products_2772_pinout_v1_0.png?1457305814)

However I get the following errors when verifying my code (see attachment). Could you point me out what I am doing wrong, and more importantly, point out how to fix this error?


Arduino: 1.6.8 (Mac OS X), Board: "Adafruit Feather M0 (Native USB Port)"

error: 'REG_TCC0_CC5' was not declared in this scope
  REG_TCC0_CC5 = 600;                             // TCC0 CC5 - on D05
error: 'REG_TCC0_CC6' was not declared in this scope
 REG_TCC0_CC6 = 600;                             // TCC0 CC6 - on D06
error: 'volatile struct TCC_SYNCBUSY_Type::<anonymous>' has no member named 'CC6'
 while (TCC0->SYNCBUSY.bit.CC6);             // Wait for synchronization  

exit status 1
Error compiling for board Adafruit Feather M0 (Native USB Port).

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 30, 2016, 10:59 pm
Hi Maverick123,

The reason why you're getting the errors, is because timer TCC0 only has 4 channels for the waveform outputs: WO[0] to WO[3]. These 4 channels are then repeated for waveform outputs: WO[4] to WO[7]. So counter compare registers CC5 and CC6 registers don't exist, they instead correspond to CC1 and CC2.

This means that digtal pin D05 uses REG_TCC0_CC1 and D06 the register REG_TCC0_CC2.

Note that each CCx register requires write synchronization, for example:

Code: [Select]
while (TCC0->SYNCBUSY.bit.CC1);
Also in your loop() section, it's possible to use the buffered CCBx registers, as these allow the waveform's duty cycle to be changed at the beginning of each waveform period and therefore prevents glitches from appearing on your output. Note that these buffered registers also have to be write synchronized.

Title: Re: Changing Arduino Zero PWM Frequency
Post by: Maverick123 on Oct 30, 2016, 11:37 pm
Hi MartinL,

thank you for your quick response. Changing CC5 and CC6 to CC1 and CC2 works like a charm.

I do still have some questions about your second remark:
do you mean that in the setup I should use
Code: [Select]
while (TCCx->SYNCBUSY.bit.CCy)
for every pin?

Should I, in the loop() section, use
Code: [Select]
REG_TCCx_CCBy in stead of REG_TCCx_CCy
since this will write to the same buffer as was initialized during setup?
and should I then also use
Code: [Select]
while(TCCx->SYNCBUSY.bit.CCy)
after each change in PWM duty cycle?

P.S. now that I take a better look at the code, I see that both D6 (PA20) and D10 (PA18) now use TCC0_CC2. Isn't this a problem? Could I use PA04: TCC0 CC0 and PA10: TCC1 CC0 instead of PA20 to create 8 independent PWM signals?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 31, 2016, 09:56 am
Hi Maverick123,

On the SAMD21 each of the peripherals, such as SERCOM modules or the TCC/TC timers are internally connected to the processor through a single digital interface, on the what is know as the APB bus, (Advanced Peripheral Bus). This bus is clocked using a synchronous clock.

The peripherals themselves are also clocked by a core clock supplied by the generic clock controller. In order to read and write to some of the peripheral registers, these clocks need to be synchronized.

After writing to a peripheral register that requires write synchronization, it's necessary to check that clocks have synchronized. This can be achieved in the case of the TCC timer, either by polling the register's SYNCBUSY bit or generating an associated interrupt. If you do not check that synchronization has occured, then in the case of the TCC timer it risks another write to the same register being discarded. In other peripherals such as the TC timer it risks stalling the bus interface if you try to immediately read or write to the peripheral again.

The TCCx timers' CCx and CCB registers require write synchonization. So after each write I poll the register's SYNCBUSY bit with the line:

Code: [Select]
while (TCCx->SYNCBUSY.bit.CCy);
Quote
P.S. now that I take a better look at the code, I see that both D6 (PA20) and D10 (PA18) now use TCC0_CC2. Isn't this a problem?
Yes, then it's necessary to reassign either D6 (PA20) or D10 (PA18) to another pin.

Quote
Could I use PA04: TCC0 CC0 and PA10: TCC1 CC0 instead of PA20 to create 8 independent PWM signals?
Yes, using either PA04 or PA10 as an output for TCC0/WO[0] would work.

In the loop() section to use the buffered registers, just add a 'B' to the CC in the register name and poll the corresponding CCBx SYNCBUSY bit. So just as you mentioned it's:

Code: [Select]
REG_TCCx_CCBy = 600;
while (TCCx->SYNCBUSY.bit.CCBy);
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Maverick123 on Oct 31, 2016, 12:00 pm
Hi MartinL,

thanks again for the quick response and the elaborate answer. If it is possible I would like to use 8 pins for PWM.

If I want to use:


Would the following code work?

Code: [Select]
PORT->Group[PORTA].PMUX[04 >> 1].reg = PORT_PMUX_PMUXE_E; //E PA04 (TCC0/ WO[0])
PORT->Group[PORTA].PMUX[10 >> 1].reg = PORT_PMUX_PMUXE_E; //E PA10 (TCC1/ WO[0])


Since PA04 and PA10 are not of the kind D6 (PA20), what would be the equivalent description using:

Code: [Select]
PORT->Group[g_APinDescription[z].ulPort].PMUX[g_APinDescription[z].ulPin >> 1].reg = PORT_PMUX_PMUXE_E;

or should z in these cases be:
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 31, 2016, 02:21 pm
Hi Maverick123,

Quote
Would the following code work?
Yes.

Quote
Since PA04 and PA10 are not of the kind D6 (PA20), what would be the equivalent description using:
As PA04 is the A3 pin and PA10 is D1, it's also possible to use:

Code: [Select]
PORT->Group[g_APinDescription[A3].ulPort].PMUX[g_APinDescription[A3].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
PORT->Group[g_APinDescription[1].ulPort].PMUX[g_APinDescription[1].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;


Title: Re: Changing Arduino Zero PWM Frequency
Post by: Maverick123 on Oct 31, 2016, 09:58 pm
Hi MartinL,

Thank you for your response. It has been really helpful. I do have a question about setting up the Feather M0 as an SPI slave, but I will post that question in a more appropriate thread.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: DR49 on Nov 13, 2016, 10:14 pm
Hi MartinL
I followed your recommendations upthere and your code is working perfectly on my m0pro board. I can generate a frequency of 40 khz with dedicated duty cycle. Nice.
Now I need to start / stop the timers so that I get a burst of let say 30 ms. The idea being to create a fsk2 modulation, generating frequency burts for example :
F1 (39 Khz) for 0 / 33ms
F2 (41 khz) for 1 /33 ms
So could you help me to manage start / stop on relevant timers ?
DR49
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Nov 14, 2016, 09:37 am
Hi DR49,

To stop the (TCC0) timer:

Code: [Select]
REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_STOP;
while(TCC0->SYNCBUSY.bit.CTRLB);

To restart the timer again:

Code: [Select]
REG_TCC0_CTRLBSET = TCC_CTRLBSET_CMD_RETRIGGER;
while(TCC0->SYNCBUSY.bit.CTRLB);
Title: Re: Changing Arduino Zero PWM Frequency
Post by: avandalen on Dec 13, 2016, 12:52 pm
Who can make a Fast 8/10-bit PWM DAC for the Arduino Zero?

It would be nice to have a library for the Arduino zero, equal to this library:
http://www.avdweb.nl/arduino/hardware-interfacing/fast-pwm-dac.html (http://www.avdweb.nl/arduino/hardware-interfacing/fast-pwm-dac.html)


Title: Re: Changing Arduino Zero PWM Frequency
Post by: joverbee on Jan 04, 2017, 02:24 pm
building further on the theme of the topic (and borrowing greatly from it) I tried to implement a 50/50 duty cycle PWM clock output which is triggered on the rising edge of an incoming signal on pin 4 of an Arduino M0 Pro and which is stopped on the falling edge.
Doing this with a software interupt and invoking STOP and RETRIGGER commands works fine but is very slow (as was expected from previous arduino uno experience).
So I tried to set this up with the EVSYS events, using one event to RETRIGGER and hook it up to the rising edge on pin4 (extint14) via the EIC and another event invoking STOP on the falling edge. However it seems this STOP command stops the timer forever so that it can't be RETRIGGERED later. The retriggering using both event works fine.

Anyone with experience that can help me out here as I am running out of ideas...
(minimum code reproducing my issue attached)

Code: [Select]

//test program to create a 50/50 clock pulse
//with a period 2xPIXTIME+1
//that starts/stops on the rising/falling edge on the IRQ pin
//aim is to get the response to IRQ as fast as possible
//for now it seems approx 250ns on scope (why so long? still 12 clockcycles?)
//and the pulse train only works for one cycle on IRQ
//for Arduino M0 pro

//what I have so far is hacked together from several bits and pieces and probably contains superficial instructions
#define IRQ 4//line sync interupt on pin 2 CAREFUL for Arduino M0 Pro pin 4 and 2 are swapped!!!!!
#define PIX 9//clock output generated
#define PIXTIME 3200 //defines the period of 1 clock pulse

void setup() {
  // put your setup code here, to run once:
  pinMode(PIX, OUTPUT);
  pinMode(IRQ,INPUT);
  pinMode(2,INPUT); //to be sure with the 2/4 confusion
  noInterrupts(); // disable all interrupts while setting up

  //use event channels to start and stop the timer connected to EXTINT
  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
  EVSYS->CTRL.reg = EVSYS_CTRL_SWRST;       // reset
 
  REG_PM_APBAMASK |= PM_APBAMASK_EIC;        // PM_APBAMASK for the EIC
 
  //connect IRQ pin to EIC via port mux
  PORT->Group[g_APinDescription[IRQ].ulPort].PINCFG[g_APinDescription[IRQ].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[IRQ].ulPort].PMUX[g_APinDescription[IRQ].ulPin >> 1].reg |= PORT_PMUX_PMUXE_A;
  // connect PIX pin to TCC1 output via port mux
  PORT->Group[g_APinDescription[PIX].ulPort].PINCFG[g_APinDescription[PIX].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[PIX].ulPort].PMUX[g_APinDescription[PIX].ulPin >> 1].reg = PORT_PMUX_PMUXO_E;
 
  attachInterrupt(IRQ, NULL, HIGH);          //I suppose this avoids setting it up all manually even though we don't want a software ISR

  // set up GCLK4
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Take full 48Mhz speed
                    GCLK_GENDIV_ID(1);            //
  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
 
  // 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

  //and to evsys ch0 (this is essential to make it work!)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_EVSYS_0;
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  //and for evsys ch1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_EVSYS_1;
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  //and for EIC (needed?)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID( GCLK_CLKCTRL_ID_EIC);
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // normal PWM operation
  REG_TCC1_WAVE |= TCC_WAVE_POL(0x0) |         // Don't Reverse the output polarity on all TCC1 outputs
                    TCC_WAVE_WAVEGEN_NPWM;    // Setup normal PWM on TCC1
  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 period of the pulse
  REG_TCC1_PER = PIXTIME*2+1;      // Set the frequency of the PWM on TCC1 to 2xpixtime
  while(TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth
  REG_TCC1_CC1 = PIXTIME;       // 50% duty cycle
  while(TCC1->SYNCBUSY.bit.CC1);

  //Event ch0 on rising edge 
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                               // Attach TCC1_EV0 (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC1_EV_1) ;               
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_RISING_EDGE |                 // Rising edge detection
                      EVSYS_CHANNEL_PATH_SYNCHRONOUS |                   // Set event path as synchronous
                      EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_14) |  // Set event generator (sender) as external interrupt 14 (which connects to pin 2 = pa14
                      EVSYS_CHANNEL_CHANNEL(0);                          // Attach the generator (sender) to channel 0
  //Event ch1 on falling edge
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(2) |                                // Attach TCC1_EV0 (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC1_EV_0) ;               
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_FALLING_EDGE |                // Falling edge detection
                      EVSYS_CHANNEL_PATH_SYNCHRONOUS |                   // Set event path as synchronous
                      EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_14) |  // Set event generator (sender) as external interrupt 14 (which connects to pin 2 = pa14
                      EVSYS_CHANNEL_CHANNEL(1);                          // Attach the generator (sender) to channel 0

//stop works ONCE only, but then doesnt retrigger again,
//is this a documented feature? datasheet is not very expanded on this feature
//doing a software stop and retrigger works fine
//both EV channels work, retrigger works well, but STOP avoids the timer to be retriggered: WHY?
  REG_TCC1_EVCTRL |= TCC_EVCTRL_TCEI0 |               // Enable the TCC1 event input EV0
                    TCC_EVCTRL_TCEI1 |                // Enable the TCC1 event input EV1
                    TCC_EVCTRL_EVACT0_RETRIGGER |     //retrigger on EV0
                    TCC_EVCTRL_EVACT1_STOP   ;         //stop on EV1 (this is where it fails, putting OFF or RETRIGGER here does what is expected)
                    //TCC_EVCTRL_TCINV0 |             // Invert the event input                 
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization
 
//enable interupt...totally unsure what to do here (what is needed and what is superficial or wrong)
  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO14;      // Enable event output on external interrupt 14=pin2 pa14
  //REG_EIC_CTRL=0x2; //enable EIC 
  //NVIC_SetPriority(EIC_IRQn, 0);              // Set the Nested Vector Interrupt Controller (NVIC) priority for TC3 to 0 (highest)
  //NVIC_EnableIRQ(EIC_IRQn);
 
  //enable the outputs
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
 
}

void loop() {
  // put your main code here, to run repeatedly:
}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: joverbee on Jan 05, 2017, 04:09 pm
made some progress...but still stuck at one point (and needed to use a software ISR which I wanted to avoid)
-The PWM output STARTS on falling edge and STOPS on rising edge (good) and this keeps going as long as we receive falling and rising edges on IRQ
-The response time is ~200ns (good, but could be better)

One remaining problem: the state of the PIX pin on the output of the TCC1 depends on what it was when STOP was received. Is there a way to force the output to a given state, i.e. write the WOx state inside the ISR while the timer is stopped?
Code: [Select]

//test program to create a 50/50 clock pulse
//with a period 2xPIXTIME+1
//that starts/stops on the rising/falling edge on the IRQ pin
//aim is to get the response to IRQ as fast as possible
//for now it seems approx 250ns on scope (why so long? still 12 clockcycles?)
//and the pulse train only works for one cycle on IRQ
//for Arduino M0 pro

//what I have so far is hacked together from several bits and pieces and probably contains superficial instructions
#define IRQ 4//line sync interupt on pin 2 CAREFUL for Arduino M0 Pro pin 4 and 2 are swapped!!!!!
#define PIX 9//clock output generated
#define PIXTIME 3200 //defines half period of 1 clock pulse
#define RED 7

void setup() {
  // put your setup code here, to run once:
  pinMode(PIX, OUTPUT);
  pinMode(RED, OUTPUT);
  digitalWrite(RED,HIGH);
  pinMode(IRQ,INPUT);
  pinMode(2,INPUT); //to be sure with the 2/4 confusion
  noInterrupts(); // disable all interrupts while setting up

  //use event channels to start and stop the timer connected to EXTINT
  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
  //EVSYS->CTRL.reg = EVSYS_CTRL_SWRST;       // reset
 
  //REG_PM_APBAMASK |= PM_APBAMASK_EIC;        // PM_APBAMASK for the EIC
 
  // connect PIX pin to TCC1 output via port mux
  PORT->Group[g_APinDescription[PIX].ulPort].PINCFG[g_APinDescription[PIX].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[PIX].ulPort].PMUX[g_APinDescription[PIX].ulPin >> 1].reg = PORT_PMUX_PMUXO_E;
 
  attachInterrupt(IRQ, lineISR, HIGH);          //I suppose this avoids setting it up all manually even though we don't want a software ISR

  // set up GCLK4
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Take full 48Mhz speed
                    GCLK_GENDIV_ID(1);            //
  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
 
  // 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

  //and to evsys ch0 (this is essential to make it work!)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_EVSYS_0;
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  //and for evsys ch1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_EVSYS_1;
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  //and for EIC (needed?)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID( GCLK_CLKCTRL_ID_EIC);
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // normal PWM operation
  REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) |         // Don't Reverse the output polarity on all TCC1 outputs
                    TCC_WAVE_WAVEGEN_NPWM;    // Setup normal PWM on TCC1
  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 period of the pulse
  REG_TCC1_PER = PIXTIME*2+1;      // Set the frequency of the PWM on TCC1 to 2xpixtime
  while(TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth
  REG_TCC1_CC1 = PIXTIME;       // 50% duty cycle
  while(TCC1->SYNCBUSY.bit.CC1);

  // Set the output matrix so during stop the PIX output is low
  //REG_TCC0_WEXCTRL |= TCC_WEXCTRL_OTMX(0x2); 
  //REG_TCC1_DRVCTRL |= 0x000000FF;

  //Event ch0 on rising edge 
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                               // Attach TCC1_EV0 (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC1_EV_0) ;               
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_FALLING_EDGE |                 // Rising edge detection
                      EVSYS_CHANNEL_PATH_SYNCHRONOUS |                   // Set event path as synchronous
                      EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_14) |  // Set event generator (sender) as external interrupt 14 (which connects to pin 2 = pa14
                      EVSYS_CHANNEL_CHANNEL(0);                          // Attach the generator (sender) to channel 0
  //Event ch1 on falling edge
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(2) |                                // Attach TCC1_EV0 (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC1_EV_1) ;               
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_EDGSEL_RISING_EDGE |                // Falling edge detection
                      EVSYS_CHANNEL_PATH_SYNCHRONOUS |                   // Set event path as synchronous
                      EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_14) |  // Set event generator (sender) as external interrupt 14 (which connects to pin 2 = pa14
                      EVSYS_CHANNEL_CHANNEL(1);                          // Attach the generator (sender) to channel 0

  REG_TCC1_EVCTRL |= TCC_EVCTRL_TCEI0 |               // Enable the TCC1 event input EV0
                    TCC_EVCTRL_TCEI1 |                // Enable the TCC1 event input EV1
                    TCC_EVCTRL_EVACT0_START |         //start on EV0
                    TCC_EVCTRL_EVACT1_STOP ;          //stop on EV1
                    //TCC_EVCTRL_TCINV0 |             // Invert the event input                 
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization

//enable interupt...totally unsure what to do here (what is needed and what is superficial or wrong)
  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO14;      // Enable event output on external interrupt 14=pin2 pa14
 
  //enable the counter, note that in event mode as here, the timer only really starts when start event is received
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  interrupts();
}

void loop() {
  // put your main code here, to run repeatedly:
}

void lineISR(){
  //IRQ is HIGH, this means the timer is stopped by now
  //can we write WO[x] high? Now the WO[x] state depends on its state when we stopped
  REG_TCC1_COUNT = 2*PIXTIME+1;      // set counter to preset value so we are ready when we receive a START event
  while(TCC1->SYNCBUSY.bit.COUNT);
  while((REG_PORT_IN0 & PORT_PA14)); //wait till PA14 goes low again
  }
 
Title: Re: Changing Arduino Zero PWM Frequency
Post by: joverbee on Jan 07, 2017, 11:33 am
ok, almost there
I think there is an error on p686 of the SAMD21 datasheet which states that a STOP command will put the output in a state defined by DRVCTRL NREx and NVx. This is only partially true as it works for a software CMD_STOP, but NOT for an event generated STOP command (as wrongly implied in the datasheet).

The below code now starts the PWM on a falling edge on pin4 (IRQ) with a response time of around 180ns (9 clock cycles) which I can compensate for exactly to create a PWM which is exactly in sync with the IRQ signal.
The only remaining issue is that now I have to stop the timer via a software interrupt which seems to take as long as 6us (!) for receiving the interrupt, testing the level of IRQ and invoking a CMD_STOP. This means that at 1us pulses I receive up to 6 unwanted pulses on the rising edge of IRQ.

Anyone an idea how to significantly improve the response time of an external interrupt? I checked the code for attachinterrupt and I would need to replace the EIC_IRQn_handler but that will likely break all other interupts....in AVR we had a specific low level handler for each interrupt separately...

Code: [Select]

//test program to create a 50/50 clock pulse
//with a period 2xPIXTIME+1
//that starts/stops on the rising/falling edge on the IRQ pin
//aim is to get the response to IRQ as fast as possible
//for now it seems approx 180 ns on scope (why so long? still 9 clockcycles?)
//for Arduino M0 pro

//what I have so far is hacked together from several bits and pieces and probably contains superficial instructions
#define IRQ 4//line sync interupt on pin 2 CAREFUL for Arduino M0 Pro pin 4 and 2 are swapped!!!!!
#define PIX 9//clock output generated (PA07, on TCC1 WO[1])
#define PIXTIME 24 //defines half period of 1 clock pulse  (24=1us) (can be as small as COMP)
#define RED 7
#define COMP 9; //compensation for nr of clockcycles it takes for the timer to retrigger on external interupt

void setup() {
  // put your setup code here, to run once:
  pinMode(PIX, OUTPUT);
  pinMode(IRQ,INPUT);
  noInterrupts(); // disable all interrupts while setting up

  //use event channel to retrigger the TCC connected to EXTINT
  REG_PM_APBCMASK |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
 
  // connect PIX pin to TCC1 output via port mux
  PORT->Group[g_APinDescription[PIX].ulPort].PINCFG[g_APinDescription[PIX].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[PIX].ulPort].PMUX[g_APinDescription[PIX].ulPin >> 1].reg = PORT_PMUX_PMUXO_E;
 
  attachInterrupt(IRQ, lineISR, CHANGE);          //cause a retrigger event both on rise and falling edge

  // set up GCLK4
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Take full 48Mhz speed
                    GCLK_GENDIV_ID(1);            //
  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
 
  // Feed GCLK4 to TCC1 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC1 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;  // Feed GCLK4 to TCC1 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  //and to evsys ch0 (this is essential to make it work!)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to EVSYS
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_EVSYS_0;
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // normal PWM operation
  REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) |         // Don't Reverse the output polarity on all TCC1 outputs
                    TCC_WAVE_WAVEGEN_NPWM;    // Setup normal PWM on TCC1
  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 period of the pulse
  REG_TCC1_PER = PIXTIME*2+1;      // Set the frequency of the PWM on TCC1 to 2xpixtime+1
  while(TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth
  REG_TCC1_CC1 = PIXTIME;       // 50% duty cycle
  while(TCC1->SYNCBUSY.bit.CC1);

  REG_TCC1_DRVCTRL |= TCC_DRVCTRL_NRV1 |TCC_DRVCTRL_NRE1 ; //NRV1 determines that in the stop state the output is high
 
  //Event ch0 on interupt change 
  REG_EVSYS_USER = EVSYS_USER_CHANNEL(1) |                               // Attach TCC1_EV0 (receiver) to channel 0 (n + 1)
                   EVSYS_USER_USER(EVSYS_ID_USER_TCC1_EV_0) ;               
  REG_EVSYS_CHANNEL = EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as synchronous
                      EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_14) |  // Set event generator (sender) as external interrupt 14 (which connects to pin 2 = pa14
                      EVSYS_CHANNEL_CHANNEL(0);                          // Attach the generator (sender) to channel 0
 
  REG_TCC1_EVCTRL |= TCC_EVCTRL_TCEI0 |               // Enable the TCC1 event input EV0
                    TCC_EVCTRL_EVACT0_RETRIGGER;      //retrigger
  while (TCC1->SYNCBUSY.bit.ENABLE);                  // Wait for synchronization

  REG_EIC_EVCTRL |= EIC_EVCTRL_EXTINTEO14;      // Enable event output on external interrupt 14=pin2 pa14
 
  //enable the counter, note that in event mode as here, the timer only really starts when start event is received
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  interrupts();
}

void loop() {
  // put your main code here, to run repeatedly:
}

void lineISR(){
  if ((REG_PORT_IN0 & PORT_PA14)){ //only do something on rising edge (but interupt needs to trigger also on falling edge for the event system to retrigger the counter)
    REG_TCC1_CTRLBSET = TCC_CTRLBSET_CMD_STOP; //stop the timer, this puts output in the state defined by nonrcovfault in REG_TCC1_DRVCTRL
    //it takes up to ~6us before the timer is stopped...really????
    while(TCC1->SYNCBUSY.bit.CTRLB);
    REG_TCC1_COUNT = COMP;      // clear counter so we are ready when we receive a RETRIGGER/START event, comp compensates for nr of clockcycles it takes for the event system
    //to retrigger the counter
    while(TCC1->SYNCBUSY.bit.COUNT);
  }
}

Title: Re: Changing Arduino Zero PWM Frequency
Post by: avandalen on Jan 31, 2017, 12:12 pm
I have made a fast PWM-DAC library for the Arduino SAMD that makes life easier:

Fast PWM-DAC for the SAM15x15 and Arduino Zero (http://www.avdweb.nl/arduino/hardware-interfacing/samd21-pwm-dac.html)

The library is easy to use; the following code creates a 10bit DAC on pin 5 of the Arduino Zero and sets output to 1.65V:

Code: [Select]
SAMDpwmDAC myDAC = SAMDpwmDAC(3, 10, 5);
myDAC.write(512); // 512/1024 * 3.3V = 1.65V
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Jan 31, 2017, 02:56 pm
Have downloaded, will test & upload results. It looks fantastic work to me!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Adminius on Feb 15, 2017, 12:51 pm
Hi,

i need to invert TCCx outputs.

so, i think i have to modify this lines:
Code: [Select]
REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) | // Reverse the output polarity on all TCC0 outputs
TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE);

REG_TCC1_WAVE |= TCC_WAVE_POL(0xF) | // Reverse the output polarity on all TCC1 outputs
TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC0
while (TCC1->SYNCBUSY.bit.WAVE);


I think 0xF is for CC3 | CC2 | CC1 | CC0, but TCC1 and TCC2 have no CC3/CC2..., is 0xF in case of TCC1/TCC2 wrong?! May be 0x3?

So, if i will to invert, let's say TCC0_CC2, so i have to use 0xB (B1011) instead of 0xF, right?

another case:
i've configured TCC0_CC0 pin in inverted mode (0xF ?!)
and the i will try to add TCC0_CC1 in non-inverted mode. So how can i do it?

Wenn i try to use 0x0 (?!) it overwrites TCC_CC0 also. How can I do it?



 
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Feb 15, 2017, 01:28 pm
Hi Adminius,

Quote
I think 0xF is for CC3 | CC2 | CC1 | CC0, but TCC1 and TCC2 have no CC3/CC2..., is 0xF in case of TCC1/TCC2 wrong?! May be 0x3?
You're right, it should be 0x03 for TCC1 and TCC2.

Quote
i've configured TCC0_CC0 pin in inverted mode (0xF ?!)
and the i will try to add TCC0_CC1 in non-inverted mode. So how can i do it?
Do you require inverting and non-inverting outputs from the same channel?

To do this you need to modify the timer TCC0's output matrix and then invert one of the outputs:

Code: [Select]
// Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;        // Setup single slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE);                // Wait for synchronization

// Set the output matrix so that D6 and D7 are set to output CC0
REG_TCC0_WEXCTRL |= TCC_WEXCTRL_OTMX(0x2);
 
// Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;

Modifying of the output matrix is optional depending on what output pins you're using.

The full example is provided earlier in this thread #84: https://forum.arduino.cc/index.php?topic=346731.75 (https://forum.arduino.cc/index.php?topic=346731.75)
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Adminius on Feb 15, 2017, 03:45 pm
thank you for answer, MartinL!

in my understanding (DualSlope PWM):
inverted: PER==>0==>PER
non-inverted: 0==>PER==>0


i will be able to invert only one ore two pins on the same timer. e.g. #D2, D5, D6, #D7

if i use your example:
Code: [Select]
// Set the output matrix so that D6 and D7 are set to output CC0
REG_TCC0_WEXCTRL |= TCC_WEXCTRL_OTMX(0x2);
 
// Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;


D7 will be inverted, but stays connected to CC0, like D2,D5 and D6...right?

can i use DRVCTRK without WEXCTRL?
Code: [Select]
// Invert the driver on TCC0/WO[4], which by coincidence happens to be Arduino digtal pin D2
REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN4;
// Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;


i need 4 outputs: D2=CC0, D5=CC1, D6=CC2, D7=CC3...
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Feb 15, 2017, 04:46 pm
Hi Adminius,

Quote
in my understanding (DualSlope PWM):
inverted: PER==>0==>PER
non-inverted: 0==>PER==>0
Dual slope PWM on the SAMD21 outputs the opposite to single slope (normal) PWM, which is why it's necessary to reverse the polarity of the signal.

By default in dual slope mode (without reversing the polarity), as you go from 0 ==> CCx and CCx ==> 0 the WO(x) timer output goes low, while anything from CCx ==> PER and PER ==> CCx the output is high. Note CCx = counter compare.

In single slope mode as you go from 0 ==> CCx the output is high, while from CCx ==> PER the output is low.

Quote
can i use DRVCTRK without WEXCTRL?
Yes, that's possible.

If you don't set the output matrix, the TCC0 timer has 4 channels are connected to 8 outputs WO[0] to WO[7], (channels 0-3 are repeated on 4-7):

WO[0] = D4 or A3
WO[1] = D3 or A4
WO[2] = D1
WO[3] = D0
WO[4] = D2
WO[5] = D5
WO[6] = D6
WO[7] = D7

TCC1 has 2 channels:

WO[0] = D8
WO[1] = D9

TCC2 also has 2 channels:

WO[0] = D11
WO[1] = D13
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Adminius on Feb 18, 2017, 06:15 pm
Thanks for your help!

i've made a PWM library for Zero/M0

https://github.com/Adminius/DimmerZero (https://github.com/Adminius/DimmerZero)
Title: Re: Changing Arduino Zero PWM Frequency
Post by: techguystuff on May 06, 2017, 05:48 am
I've been following this thread closely, looking for a solution for my problem, but I haven't had any luck yet. I am trying to drive a MOSFET bridge, so I need to take advantage of the dead time insertion feature of the TCC timers on the SAMD21. I'm almost there; I'm using D6 and D7 and my sketch is outputting the frequency and pulse width I need (the sketch just scans both frequency and PWM), but I have not been able to enable the Dead Time without killing my waveforms.

I've attached my sketch, and I'm looking at the WEXCTRL register that has the high side and low side clocks to insert, as well as the DTIENx bits. Since for OTMX I used 0x02, I thought I needed to enable DTIEN2 bit, but that doesn't work. Neither does any of the other 3 bits. I do not get any transitions on the output lines.

In the sketch, this line works (but with no dead time, transitions are coincident):
REG_TCC0_WEXCTRL |= TCC_WEXCTRL_OTMX(0x2);

Commenting it and uncommenting this line, which inserts 15 cycles on both the high side and low side transitions, just rails the outputs to high:
REG_TCC0_WEXCTRL = (0x0f<<24) | (0x0f<<16) | TCC_WEXCTRL_DTIEN2;

Maybe there is something else I need to do to get DTI to work. Anybody have a clue?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 07, 2017, 08:28 pm
Hi techguystuff,

The SAMD21 datasheet isn't at all clear, but after some experimentation it turns out that dead time insertion is split between low side pins (D2, D3, D1 and D0) and high side pins (D4, D5, D6 and D7). Therefore it's necessary to chose a low and high sided pin from each group:

For example, using the output matrix set to 0:

CC0: D2 - D4
CC1: D3 - D5
CC2: D1 - D6
CC3: D0 - D7

Setting the Dead Time Insertion Enable bit(s) (TCC_WEXCTRL_DTIENx) corresponding output matrix 0's CCx channels, allows the Dead Time Low Side Output (TCC_WEXCTRL_DTLS(value)) and the Dead Time High Side Output (TCC_WEXCTRL_DTHS(value)) bifields to take effect.

This is achieved using the following line:

Code: [Select]
// Enable dead time insertion for channel CC3 (D0 and D7) and set the low/high dead time insertion on each pin
REG_TCC0_WEXCTRL |=  TCC_WEXCTRL_DTHS(100) | TCC_WEXCTRL_DTLS(100) | TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_OTMX(0x0);

Here's the example code:

Code: [Select]
// Output 150kHz with non-inverting and inverting PWM on timer TCC0 with dead time insertion (8-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 pins D0 and D7
  PORT->Group[g_APinDescription[0].ulPort].PINCFG[g_APinDescription[0].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port output D0 and D7 - port pins are paired odd PMUXO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[1].ulPort].PMUX[g_APinDescription[1].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;// | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;// | PORT_PMUX_PMUXE_F;

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

  // Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);                // Wait for synchronization

  // Set the output matrix so that D0 and D7 are set to output CC3 and enable low and high dead time insertion
  REG_TCC0_WEXCTRL |=  TCC_WEXCTRL_DTHS(100) | TCC_WEXCTRL_DTLS(100) | TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_OTMX(0x0);
 
  // Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
  //REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 319;                             // Set the frequency of the PWM on TCC0 to 150kHz
  while(TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle on D0 and D7
  REG_TCC0_CCB3 = 159;                            // TCC0 CCB3 - normal output on D0 and inverted output on D7
  while(TCC0->SYNCBUSY.bit.CCB3);                 // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop(){}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 08, 2017, 09:39 am
Further to my last post, it's also possible to mix and match the pins on the low (D2, D3, D1 and D0) and high side (D4, D5, D6 and D7) using the output matrix.

For example, you can set the output matrix to only output from timer TCC0 on channel CC0, by setting the TCC_WEXCTRL_OTMX(value) bitfield to 2:

CC0: D2, D3, D1, D0 - D4, D5, D6, D7

It's now possible to output on any low side pin, on say D3 and also on any high side, for instance D7 and still retain the dead time insertion functionality. However, it's now necessary to set not only the TCC_WEXCTRL_DTIEN3 bit for D7 (normally on CC3), but also the TCC_WEXCRL_DTIEN1 bit for D3 (normally on CC1):

Code: [Select]
// Set the output matrix so that D3 and D7 are set to output CC0 and enable low and high dead time insertion
REG_TCC0_WEXCTRL |=  TCC_WEXCTRL_DTHS(100) | TCC_WEXCTRL_DTLS(100) |
  TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_DTIEN1 | TCC_WEXCTRL_OTMX(0x2);

Here's the example code:

Code: [Select]
// Output 150kHz with non-inverting and inverting PWM on timer TCC0 with dead time insertion (8-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 pins D3 and D7
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port output D3 and D7 - port pins are paired odd PMUXO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_E;// | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;// | PORT_PMUX_PMUXE_F;

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

  // Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);                // Wait for synchronization

  // Set the output matrix so that D3 and D7 are set to output CC0 and enable low and high dead time insertion
  REG_TCC0_WEXCTRL |=  TCC_WEXCTRL_DTHS(100) | TCC_WEXCTRL_DTLS(100) |
    TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_DTIEN1 | TCC_WEXCTRL_OTMX(0x2);
 
  // Invert the driver on TCC0/WO[7], which by coincidence happens to be Arduino digtal pin D7
  //REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 319;                             // Set the frequency of the PWM on TCC0 to 150kHz
  while(TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle on D3, 50% on D7
  REG_TCC0_CCB0 = 159;                            // TCC0 CCB0 - on output on D3 and inverted output on D7
  while(TCC0->SYNCBUSY.bit.CCB0);                 // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop(){}

Title: Re: Changing Arduino Zero PWM Frequency
Post by: techguystuff on May 08, 2017, 03:48 pm
MartinL,

I am in awe of your datasheet reading skills. Truly. You have helped me quite a bit, thank you!

After playing with this a bit, I noticed that tone() stopped working since it too uses TCC0. So to recover the use of tone() (I've peppered the UI with calls to tone() already) would it be possible to use TCC1 or TCC2 for DTI since(from one of your earlier posts):

TCC1 has 2 channels:

WO[0] = D8
WO[1] = D9

TCC2 also has 2 channels:

WO[0] = D11
WO[1] = D13

What would the configuration be to use either of these pairs of pins?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 08, 2017, 05:12 pm
Hi techguystuff,

Quote
I noticed that tone() stopped working since it too uses TCC0.
I thought the tone() function only uses timer TC5 on the Arduino Zero?

Quote
would it be possible to use TCC1 or TCC2 for DTI
Unfortunately it's only possible to use timer TCC0, as TCC1 and TCC2 don't support either dead time insertion or the output matrix.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: techguystuff on May 08, 2017, 08:47 pm
MartinL,

Right! So it is. Turns out I have been moving the PWM pins around quite a bit trying to find the best combination, and my last stop just happened to be on the same pin I had configured tone() to use. I've now moved it up to D10, and all is good now. Thanks for all your help!

BTW, not sure why, but I had to disable the inversion of D7 in order to get an inverted waveform (compared to D3) on D7:

// REG_TCC0_DRVCTRL |= TCC_DRVCTRL_INVEN7;

My target frequency is variable, from 10K to 25K (nominally 15K), PWM less than 30% (nominally 10%). So I use these lines:

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 48e6/15e3;                  // Set the frequency of the PWM on TCC0 to 15kHz
  while(TCC0->SYNCBUSY.bit.PER);            // Wait for synchronization
 
  // Set the PWM signal to output 10% duty cycle on D3, 90% on D7
  REG_TCC0_CCB0 = (48e6/15e3)*0.10;      // TCC0 CCB0 - on output on D3 and inverted output on D7
  while(TCC0->SYNCBUSY.bit.CCB0);          // Wait for synchronization

Deadtime only needs to be about 200ns, so I'm only using a setting of 10 cycles (at 48MHz):

  REG_TCC0_WEXCTRL =  TCC_WEXCTRL_DTHS(10) | TCC_WEXCTRL_DTLS(10) |
    TCC_WEXCTRL_DTIEN3 | TCC_WEXCTRL_DTIEN1 | TCC_WEXCTRL_OTMX(0x2);

This gives absolutely perfect waveforms!

Again, thank you very much for your help.

Sincerely,
Carlos
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 09, 2017, 09:07 am
Hi Carlos,

Glad you got it working.

Quote
BTW, not sure why, but I had to disable the inversion of D7 in order to get an inverted waveform (compared to D3) on D7:
Then I guess provided you choose a pin from the low and high side, the SAMD21 does the inversion for you and activating the inverter isn't necessary. As I haven't got an oscilloscope, it's difficult for me to see if the signals are inverted or non-inverted. Thanks for the information.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 10, 2017, 06:33 am
Hello Friends! I'm new in arduino world and trying to make a code for pwm 2 outputs(pin 5 and pin 6), 20hz pwm frec.
Reading atmel SAMD21G datasheet found RAMP2 operation could be a solution for my problem, but is a hard work a lots configuration of matches, modes, etc.
Upload picture show my idea how output should be work and that 90° off phases needed between W[5] and W[6] output.
I have problem to implement output idea due can't make a correct register configuration.
Someone can tell me if i'm in a right way, or suggest other solution, code examples, edit my code, etc? Any help is welcome.

Thanks!  


Code: [Select]

#include <Adafruit_NeoPixel.h>

#define Pin13LED 13
#define NEOPIXEL 8  // Neopixel

int PERx = 32000; // PER=TOP value just for example

volatile int cont =0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, NEOPIXEL);   // Create an strip object

void setup()
{
  strip.begin();
  strip.show(); // neopixel off
  
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Generic clock divisor D = 1
    GCLK_GENDIV_ID(4);          // Selecciono el clock gen 4
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Duty cicle 50%
    GCLK_GENCTRL_GENEN |        // habilito gclk4
    GCLK_GENCTRL_SRC_XOSC32K |    // selecciono clock de 32Khz
    GCLK_GENCTRL_ID(4);         // Selecciono el clock gen 4
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  // Habilito el multiplexor para 2 canales de PWM con Timer TCC0
  const uint8_t CHANNELS = 2;
  const uint8_t Pin_5 = 5;  // TCC0;CCB1;Odd
  const uint8_t Pin_6 = 6;  // TCC0;CCB2;Even
  PORT->Group[g_APinDescription[Pin_5].ulPort].PINCFG[g_APinDescription[Pin_5].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[Pin_6].ulPort].PINCFG[g_APinDescription[Pin_6].ulPin].bit.PMUXEN = 1;

  // Conecto el timer TCC0 con los puertos de salida
  PORT->Group[g_APinDescription[5].ulPort].PMUX[g_APinDescription[5].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

  // Conecto el GCLK con TCC0
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |
     GCLK_CLKCTRL_GEN_GCLK4 |
     GCLK_CLKCTRL_ID_TCC0_TCC1;
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  
  // Single slope PWM: timer TCC0
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM |
    //TCC_WAVE_CIPEREN |  // Diferentes PER en ciclo A y B
    TCC_WAVE_POL0 | // Change polarity output
    TCC_WAVE_POL1;
    TCC_WAVE_RAMP_RAMP2;  // RAMP2 operation
  while (TCC0->SYNCBUSY.bit.WAVE);    // Espero sincronizacion

  // Configuro valor PER
  REG_TCC0_PER = PERx;
  while(TCC0->SYNCBUSY.bit.PER)   // Espero sincronizacion

  delay(50);

  // Configuro valor CCBx
  REG_TCC0_CC1 = (PERx/2);       // TCC0 CCB1 - 50% PWM on PIN 5
  while(TCC0->SYNCBUSY.bit.CC1);
  REG_TCC0_CC2 = (PERx/2);    // TCC0 CCB2 - 50% PWM on PIN 6
  while(TCC0->SYNCBUSY.bit.CC2);

  
  // Configuro preescaler y habilito las salidasTCC_CTRLBSET_IDXCMD_DISABLE |
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |
    TCC_CTRLA_CPTEN0 |  // capture channel 0 enable
    //TCC_CTRLBSET_IDXCMD_DISABLE |
    TCC_CTRLA_ENABLE;
  while (TCC0->SYNCBUSY.bit.ENABLE);  // Espero por sincronizacion

  delay(50);
  
}

void loop()
{
  
  REG_TCC0_CCB1 = (PERx/2);                       // OUTPUT pin 5
  while(TCC0->SYNCBUSY.bit.CCB1);
  REG_TCC0_CCB2 = (PERx/2);                     //OUTPUT pin 6  
  while(TCC0->SYNCBUSY.bit.CCB2);

}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Aug 10, 2017, 08:13 am
Go here for a simplified tutorial on Arduino Zero PWM frequency change.

https://www.picotech.com/support/topic24051.html

Cheers,

Glovisol
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 10, 2017, 03:24 pm
Thanks glovisol! I based my current code in that tutorial.
Unfortunately can't found correct reg configuration for 90° unphased output.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: dlabun on Aug 10, 2017, 10:02 pm
@b0rn4 You should post your question on the Atmel forum as well, they are pretty helpful with features and questions that involve the SAMD registers.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 10, 2017, 10:43 pm
@diabun Done!! Thanks!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Aug 12, 2017, 06:26 pm
Hi diabun,

Please better explain what you wish to do: from your description it seems you only need to obtain two 20 Hz squarewaves shifted by 90°. Is this all, or do you also need the waveforms to change in duration driven by another signal?

If you only need the two 90° shifted squarewaves with FIXED duty cycle you only have to add adequate delay between the two.....

Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 15, 2017, 02:00 am
Hi @glovisol! I'm gona try to better explain. Sorry for my english, not is the best  :smiley-sweat:

Like picture show, i need a two output 90° shifted squarewaves.
Respect PWM drive,i need start in 0% and increment, 1 step per sec, for 1 min, from 0us to 107us (period max). In other words, need 60 steps of 1,78us active period increase.
That's especification not are most relevant for my problem. I have a hard time triying to make the two output 90° shifted with 49875us(aprox..) of dead time each.
My duty cicle is veery small and can't make dead time insertion.

Hope clarify my first post. I'm at your disposal for any other question.

Cheers
Title: Re: Changing Arduino Zero PWM Frequency
Post by: glovisol on Aug 15, 2017, 11:32 am
Hi Born4,

The easiest & simplest way to obtain your result is to use a Standard Digital Phase Shiìfter using two type "D" flip-flops and one inverter. This can be implemented with just two digital packages. If you are willing to add this simpe hardware I can sedn you the schematic diagram.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 15, 2017, 12:30 pm
Hi! @glovisol

I appreciate your idea, but this signal generation is just a part for a biggest program to run a hardware.
Unfortunately already design PCB and buy SMD components. My only option is finish the job by software.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: darkhawk on Aug 18, 2017, 03:43 pm
I'm having some real issues understanding the multiplexing and how to setup output PWM pins.

Here's what I'm trying to do.
I'm trying to build a motor quadrature encoder that reads in 2 sensors (magnetic sensors), once per revolution.
I then determine the timing between 2 revolutions, and then output 2 quadrature signals but increase the timing so that for 1 revolution, I output 32 periods. (ie for that revolution time, I output 32 pulses)

Now, for the tough part. For the quadrature outputs, I need 2 PWM signals, 50% duty cycle, with them being 90 degrees out of phase from each other. This also changes depending on which way the motor is spinning (lets call them channel A and B, for a clockwise motor, channel A would lead B by 90 degrees, for counterclockwise, channel A would lag B by 90 degrees).

I have code that detects the sensors I'm using just fine and generates the required information to actually be able to output the frequencies, however I'm just not familiar enough with the SAMD21 and muxing the signals to be able to just go any further.

I'm using an Adafruit Feather M0 (specifically the one with WIFI as I might output this to the cloud for fun), and I would prefer using pins 11 and 13 for the PWM output pins.

Any help or information on setting this up would be greatly appreciated.

EDIT: I should mention I'm fairly familiar with using Arduino and coding in general, but I've never used a MCU that is advanced as the SAMD21 with the ability to MUX so many pins and capabilities before.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 18, 2017, 05:19 pm
Hi darkhawk,

I'm having some similar insue.
I recommend read this atmel application note (http://www.atmel.com/Images/Atmel-42357-Using-the-Timer-Counter-for-Control-Applications_ApplicationNote_AT07690.pdf) have a usefull information.
I guess ramp2 or ramp2A can resolve your problem.
I try to make some code but still don't work totally as i want. However maybe can help you.
Code: [Select]


void setup() {
 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Generic clock divisor D = 1
    GCLK_GENDIV_ID(4);          // Selecciono el clock gen 4
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Duty cicle 50%
    GCLK_GENCTRL_GENEN |        // habilito gclk4
    GCLK_GENCTRL_SRC_XOSC32K |    // selecciono clock de 32Khz
    GCLK_GENCTRL_ID(4);         // Selecciono el clock gen 4
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  // Habilito el multiplexor para 2 canales de PWM con Timer TCC0
  const uint8_t CHANNELS = 2;
  const uint8_t Pin_5 = 5;  // TCC0;CC1;W0;Odd
  const uint8_t Pin_6 = 6;  // TCC0;CC2;W1;Even
  PORT->Group[g_APinDescription[Pin_5].ulPort].PINCFG[g_APinDescription[Pin_5].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[Pin_6].ulPort].PINCFG[g_APinDescription[Pin_6].ulPin].bit.PMUXEN = 1;

  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E peripherals specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[5].ulPort].PMUX[g_APinDescription[5].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

  // Conecto el GCLK con TCC0
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |
     GCLK_CLKCTRL_GEN_GCLK4 |
     GCLK_CLKCTRL_ID_TCC0_TCC1;
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

 
  // Single slope PWM: timer TCC0
  REG_TCC0_WAVE |= //TCC_WAVE_WAVEGEN_NPWM |
      TCC_WAVE_POL0 |
      TCC_WAVE_RAMP_RAMP2A; // RAMP2A operation
  while (TCC0->SYNCBUSY.bit.WAVE);    // Espero sincronizacion


  // Configuro valor PER
  REG_TCC0_PER = 32000;
  while(TCC0->SYNCBUSY.bit.PER)   // Espero sincronizacion

  delay(50);

  // Configuro valor CCx
  REG_TCC0_CC0 = 16000;       // TCC0 CCB1 - 50% PWM on PIN 5
  while(TCC0->SYNCBUSY.bit.CC0);
  delay(50);


  // Configuro preescaler y habilito las salidas
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |
    TCC_CTRLA_ENABLE;
  while (TCC0->SYNCBUSY.bit.ENABLE);  // Espero por sincronizacion

  delay(50);

}

void loop() {

}


Title: Re: Changing Arduino Zero PWM Frequency
Post by: darkhawk on Aug 18, 2017, 05:58 pm
My biggest issue is defining which pins to use for the output, and then getting that output there.
Unfortunately, no where can I find a good, easy to understand idea of what goes where, how it gets there, or what to do. I do have a table for the Zero (which matches the M0 board I have pin for pin) which does state the TCC0 and waveform outputs, but translating that into the code is well above my understanding at this time. Normally I could piece it together through examples, but I just get further confused because there aren't good examples of it.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 18, 2017, 08:44 pm
Hi darkhawk,

you should read previews pages in this post,they explain very well how configure and what pin can be use for PWM.

cheers
Title: Re: Changing Arduino Zero PWM Frequency
Post by: b0rn4 on Aug 19, 2017, 09:33 pm
Finally i made some progress!! Outputs are 90° shifted.
However have a little insue. After each PER reg configuration need some delay, i don't not why, but if you miss that, code don't work properly.
My idea is put all configuration needed in a ISR, but can't use delay there.
What is our recomendation?

Regards.

Code: [Select]

#include <Adafruit_NeoPixel.h>

#define Pin13LED 13
#define NEOPIXEL 8  // Neopixel

Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, NEOPIXEL);   // Create an strip object

int PER1 = 32000; // PER1 controla el ciclo de la salida
int PER2 = 16000; // PER2 controla el timer que se activa cada 1s

void setup() {
  strip.begin();
  strip.show(); // neopixel off
 
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Generic clock divisor D = 1
    GCLK_GENDIV_ID(4);          // Selecciono el clock gen 4
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Duty cicle 50%
    GCLK_GENCTRL_GENEN |        // habilito gclk4
    GCLK_GENCTRL_SRC_XOSC32K |    // selecciono clock de 32Khz
    GCLK_GENCTRL_ID(4);         // Selecciono el clock gen 4
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  // Habilito el multiplexor para 2 canales de PWM con Timer TCC0
  const uint8_t CHANNELS = 2;
  const uint8_t Pin_5 = 5;  // TCC0;CC1;W0;Odd
  const uint8_t Pin_6 = 6;  // TCC0;CC2;W1;Even
  PORT->Group[g_APinDescription[Pin_5].ulPort].PINCFG[g_APinDescription[Pin_5].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[Pin_6].ulPort].PINCFG[g_APinDescription[Pin_6].ulPin].bit.PMUXEN = 1;

  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E peripherals specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[5].ulPort].PMUX[g_APinDescription[5].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

  // Conecto el GCLK con TCC0
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |
     GCLK_CLKCTRL_GEN_GCLK4 |
     GCLK_CLKCTRL_ID_TCC0_TCC1;
  while (GCLK->STATUS.bit.SYNCBUSY);    // Espero sincronizacion

  // Single slope PWM: timer TCC0 cuenta hasta PER1
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;             
  while (TCC0->SYNCBUSY.bit.WAVE);    // Espero sincronizacion

  // Normal frecuency generator: timer TCC1 cuenta hasta PER2
  REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NFRQ;
  while (TCC1->SYNCBUSY.bit.WAVE);

 // Configuro matriz de salida y habilito la insercion de tiempo muerto en el canal 1
 REG_TCC0_WEXCTRL |= TCC_WEXCTRL_DTIEN1;

  // Configuro valor PER1
  REG_TCC0_PER = PER1;
  while(TCC0->SYNCBUSY.bit.PER)   // Espero sincronizacion
  delay(25);

  // Configuro valor PER2
  REG_TCC1_PER = PER2;
  while(TCC1->SYNCBUSY.bit.PER)   // Espero sincronizacion
  delay(25);

  // Configuro valor CCx
  REG_TCC0_CC1 = 24000;       // TCC0 CC1 - controla PWM del PIN 5
  while(TCC0->SYNCBUSY.bit.CC1);
  REG_TCC0_CC2 = 8000;        // TCC0 CC2 - controla PWM del PIN 6
  while(TCC0->SYNCBUSY.bit.CC2);

  //habilito interrupciones en TCC1
  REG_TCC1_INTENSET = TCC_INTENSET_OVF; //configuro interrupcion por desbordamiento en TCC1
//  NVIC_SetPriority(TCC1_IRQn, 0);// Set the Nested Vector Interrupt Controller (NVIC) priority
    NVIC_EnableIRQ(TCC1_IRQn);

  // Configuro preescaler y habilito las salidas
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |
    TCC_CTRLA_ENABLE;
  while (TCC0->SYNCBUSY.bit.ENABLE);  // Espero por sincronizacion
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |
    TCC_CTRLA_ENABLE;
  while (TCC1->SYNCBUSY.bit.ENABLE);  // Espero por sincronizacion

}

void loop() {

}

void TCC1_Handler() { //Interrupcion cada 1s PER2 (1s)
  static bool b;
  pinMode(Pin13LED, OUTPUT);
  digitalWrite(Pin13LED, b=!b);
 
  REG_TCC1_INTFLAG = TC_INTFLAG_OVF; //Habilito nuevamente las interrupciones

}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: darkhawk on Aug 21, 2017, 04:00 pm
While they are 90 degrees...they're only a 25% duty cycle.


Wouldn't work for my application either, as I need 50% duty cycle and 90 degree offset.

Still haven't identified a way to make it work for my application.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: bnn1044 on Sep 21, 2017, 04:27 pm
hi:MartinL
this code seen to affect my ADC 3 input.
any idea?


// Output 250kHz PWM on timer TCC0 (6-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  REG_TCC0_PER = 96;         // Set the frequency of the PWM on TCC0 to 250kHz
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle
  REG_TCC0_CC3 = 48;         // TCC0 CC3 - on D7
  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Sep 22, 2017, 09:43 am
Hi bnn1044,

Have you tried using another analog input?

You could also try changing the PWM to another digital pin, but this may require the use of another TCC timer (TCC1 or TCC2), or a different channel on TCC0.

At 250kHz the PWM signal is going pretty fast, does your application require this speed or could it use a slower frequency? What sort of load/device is your PWM driving?

I'd also try to keep any analog wiring separate as they come off the board.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: sergun2311 on Oct 17, 2017, 06:54 pm
Hello Everyone,

I am working with SAM15x15 which uses same MCU as Zero does. I am needed to have PWM at PA23 pin. I cant understand how to change code which I found in that topic for my needs.  Can someone help me please?

Code: [Select]
// Output 250kHz PWM on timer TCC0 (6-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 D7
  PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;
  
  // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  REG_TCC0_PER = 96;         // Set the frequency of the PWM on TCC0 to 250kHz
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
  
  // Set the PWM signal to output 50% duty cycle
  REG_TCC0_CC3 = 48;         // TCC0 CC3 - on D7
  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
  
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 18, 2017, 10:07 am
Hi sergun2311,

In the SAMD21 Datasheet's "PORT Function Multiplexing" table 7-1, you'll see that pin PA23 uses timer TCC0/W0[4] on peripheral F. Timer TCC0 has 4 channel outputs WO[0]..WO[3] that that correspond to counter compare registers CC0..CC3, these repeat for channels WO[4]..WO[7]. Therefore WO[4] uses the corresponding counter compare register REG_TCC0_CC0.

In your code, to switch the pin from GPIO to the peripheral multiplexer just change the 21 to 23:

Code: [Select]
// Enable the port multiplexer for the SCL pin
PORT->Group[PORTA].PINCFG[23].bit.PMUXEN = 1;

...and to select the TCC0 timer on peripheral F:

Code: [Select]
// Connect the TCC0 timer to pin SCL - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2
PORT->Group[PORTA].PMUX[23 >> 1].reg |= PORT_PMUX_PMUXO_F;

Here we just divide the port number 23 by 2 (23 >> 1), as there are 32 port pins, but only 16 PMUX registers. Each PMUX register can control two port pin pairs: odd (in this case PA23) and even (PA22).

Finally just change the counter compare register and the subsequent synchronization bit from CC3 to CC0:

Code: [Select]
// Set the PWM signal to output 50% duty cycle
REG_TCC0_CC0 = 48;         // TCC0 CC0 - on SCL
while (TCC0->SYNCBUSY.bit.CC0);                // Wait for synchronization
Title: Re: Changing Arduino Zero PWM Frequency
Post by: sergun2311 on Oct 18, 2017, 11:44 am
Hi MartinL,

Thanks a lot, you helped me so much.
In your explanation you've misprinted about channel WO[4], it is WO[5] but it pushed me to fix it and understand it better.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 18, 2017, 12:31 pm
Hi sergun2311,

Quote
In your explanation you've misprinted about channel WO[4], it is WO[5]
I misread the datasheet, thanks for the correction. So it should be register CC1 instead.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: sergun2311 on Oct 24, 2017, 10:16 am
Hi MartinL,

Now I have PWM with frequency 200 kHz on PA23. I am also needed to have PWM output on PA22 with much lower frequency (>10kHz). Did I understood it right that it is impossible because they works with the same timer?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 24, 2017, 11:26 am
Hi sergun2311,

That's right, on the SAMD21 it's not possible to have different PWM frequencies on the same timer. This is because although a timer may have a number of output channels, it only has one period (PER) register that determines frequency.

Your options in this instance are either to choose a different pin using a different TCC timer, or alternatively switch one of the pins to peripheral E (rather than F) and use the TC4 timer instead. Although the TC timers have less functionality that the PWM oriented TCC ones, they're still capable of outputting basic PWM waveforms at a given frequency.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Jimbee on Mar 02, 2018, 07:30 pm
Hi MartinL,

I have tried your PWM example on my feather M0 and it works great.  I am trying to control the frequency/speed of a stepper motor with a analog pot attached to A1.  I have increased the PWM frequency to 90KHz to get higher rpm out of the stepper.  As I rotate the pot the PWM output gets erratic looking at the PWM output with a scope the PWM output starts and stops.  Here is what I am using to change the frequency.

PotValue = analogRead(Pot);
REG_TCC1_PER = PotValue;      // Set the frequency of the PWM
  while(TCC1->SYNCBUSY.bit.PER);

Please let me know your thoughts.

Thanks.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Mar 03, 2018, 09:48 am
Hi Jimbee,

If you're changing the frequency during operation then just use the buffered period (PERB) register instead:

Code: [Select]
PotValue = analogRead(Pot);
REG_TCC1_PERB = PotValue;      // Set the frequency of the PWM
while(TCC1->SYNCBUSY.bit.PERB);

Using the buffered PERB register will cause the frequency change to happen at the beginning of each timer cycle (overflow) and should prevent the erratic PWM output that you're seeing on your scope.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: VincentVega on Mar 16, 2018, 04:31 pm
Hi everyone,

thank you all (and especially MartinL) for your work here and helping me understand this topic. It was a lot to read and understand, and my results tell me I am not even there...

My situation:
I am working on a Zero and need to create a 20kHz pwm signal to pins D3 (PA09) and D5 (PA15).
After a long study on the SAMD21 data sheet I thought I could take the TCCO CC1 (which should address WO1 for D3/PA09 and WO5 for D5/PA15) to do so.
The test results tell me I was wrong. But I can not figure it out...

Here is my code:

Code: [Select]
void setup()
{

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 port multiplexer
 PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;  // D3(PA09)
 PORT->Group[g_APinDescription[5].ulPort].PINCFG[g_APinDescription[5].ulPin].bit.PMUXEN = 1;  // D5(PA15)
 
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
 PORT->Group[g_APinDescription[5].ulPort].PMUX[g_APinDescription[5].ulPin>>1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E; // D5
 
  // Feed GCLK4 to TCC0 and TCC1         ??? Is there any way to feed GCLK4 only to TCC0?
  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

  // Single slope PWM operation

   REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_DSBOTTOM;

   while (TCC0->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:
  //
  REG_TCC0_PER = 1200;      // Set the frequency of the PWM on TCC1 to
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB1 = 600;       // TCC0 CCB0 - 50% duty cycle on D2
  while(TCC0->SYNCBUSY.bit.CCB1);


  // Divide the 48MHz signal by 1 giving 48MHz
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}


I would be happy about any kind of help, thank you in advance!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: VincentVega on Mar 16, 2018, 05:21 pm
Sometimes some fresh air helps...
After a short break I found some copy paste errors and got it up working.
To be able to control the pwm on both pins separately, I changed D3 to TCC1 CBB1 and now I am able to do what I wanted.

Here is my now working code:
Code: [Select]
void setup()
{
  pinMode(13,INPUT);

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 port multiplexer
 PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;  // D3(PA09)
 PORT->Group[g_APinDescription[5].ulPort].PINCFG[g_APinDescription[5].ulPin].bit.PMUXEN = 1;  // D5(PA15)
 
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F; // D3
  PORT->Group[g_APinDescription[5].ulPort].PMUX[g_APinDescription[5].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F; // D5
 
  // Feed GCLK4 to TCC0 and TCC1         ??? Is there any way to feed GCLK4 only to TCC0?
  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

  // Single slope PWM operation

   REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;
   while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization
   REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NPWM;
   while (TCC0->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:
  //
  REG_TCC0_PER = 2400;      // Set the frequency of the PWM on TCC0 to
  while(TCC0->SYNCBUSY.bit.PER);

  REG_TCC1_PER = 2400;      // Set the frequency of the PWM on TCC1 to
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB1 = 1600;       // TCC0 CCB0 - 50% duty cycle on D2
  while(TCC0->SYNCBUSY.bit.CCB1);

  REG_TCC1_CCB1 = 800;       // TCC0 CCB0 - 50% duty cycle on D2
  while(TCC0->SYNCBUSY.bit.CCB1);


  // Divide the 48MHz signal by 1 giving 48MHz
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}


Thank you all again for your provided knowledge! I would not have been able to do this without this topic.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: farlane on Apr 19, 2018, 11:18 am
Having a bit of a problem using the TCC0 timer on the SAMD21G18 which is on the Arduino Tian. I use the TCC0 in dual slope mode, using only buffered registers to generate waveforms on two pins to avoid corrupting the waveforms. Generating the waveforms is not a problem, but i see some strange effects regarding the PER/PERB register.

According to the datasheet the PERB register is copied to the PER register on an UPDATE event (BOTTOM for dual slope mode). This should be the same for the other buffered registers CCB[X].

What i am seeing is that the CC registers (when read) actually do reflect the actual value of the compare values written using the CCB registers.

The PER register however always reads back as 0xFFFFFF, its reset and also maximum value. (Directly writing to the PER register works like one would expect and reads back correctly.)

Any of you guys have a clue as to what is going on there?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Apr 19, 2018, 04:25 pm
Hi farlane,

I ran a small test. It looks like a read of the PER register returns the last value written to it, but like you mention it's not updated directly from the PERB register during double buffering, as described in the SAMD21 datasheet.

This suggests to me that perhaps internally the period double buffering isn't actually using the PER register.

Looks like another "undocumented feature" in the SAMD21 datasheet.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: farlane on Apr 19, 2018, 04:58 pm
Hi farlane,

I ran a small test. It looks like a read of the PER register returns the last value written to it, but like you mention it's not updated directly from the PERB register during double buffering, as described in the SAMD21 datasheet.

This suggests to me that perhaps internally the period double buffering isn't actually using the PER register.

Looks like another "undocumented feature" in the SAMD21 datasheet.
Hi Martin, thanks for your time and troubles double checking this. A very strange behavior indeed given that the CC[X] registers are in fact updated with their buffered counterparts ...

Cudos & thanks again :)
Title: Re: Changing Arduino Zero PWM Frequency
Post by: farlane on May 08, 2018, 06:05 pm
This suggests to me that perhaps internally the period double buffering isn't actually using the PER register.
I just got confirmation from the Microchip support desk, and i quote:

Quote
Actually the PER value is loaded from PERB register in UPDATE condition. If you check the WO pin, you can observe that.

Unfortunatley there is some issue with reading the PER register due to some synchronisation issue.

So the read-back values of the PER and PERB registers are not reliable. When you read the PER/PERB register, it may not give the actual/updated value.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: johnyrobot on Oct 12, 2018, 03:20 pm
Dear Forum,

I am having a problem configuring the PWM frequency in ARDUINO ZERO.

The ports I use are D8 and D111  , I want to make the PWM with more than 30Khz .

Can someone help me out with a small snippet of code ? I would really appreciate your help , because I am really confused with the timers in ATSAMD21


Kind regards
Johny
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 12, 2018, 06:06 pm
Hi johnyrobot,

Here's the code that outputs 30kHz PWM, 50% duty-cycle on D8 and D11:

Code: [Select]
// Output 30kHz PWM on timer TCC1/W0[0] (D8) and TCC0/WO[6] (D11) (10-bit resolution)
void setup()
{
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                     GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = 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

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4
                      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

  // Enable the port multiplexer for the PWM channel on pin D8 and D11
  PORT->Group[g_APinDescription[8].ulPort].PINCFG[g_APinDescription[8].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC1 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E peripherals specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;

  // Normal (single slope) PWM operation: timers countinuously 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
  TCC0->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC1
  while (TCC0->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: 1600 = 30kHz
  TCC1->PER.reg = 1599;                            // Set the frequency of the PWM on TCC1 to 30kHz
  while (TCC1->SYNCBUSY.bit.PER);                  // Wait for synchronization
  TCC0->PER.reg = 1599;                            // Set the frequency of the PWM on TCC0 to 30kHz
  while (TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization

  // The CCx register value corresponds to the pulsewidth in microseconds (us)
  TCC1->CC[0].reg = 799;                           // Set the duty cycle of the PWM on TCC1 to 50%
  while (TCC1->SYNCBUSY.bit.CC0);                  // Wait for synchronization
  TCC0->CC[2].reg = 799;                           // Set the duty cycle of the PWM on TCC1 to 50%
  while (TCC0->SYNCBUSY.bit.CC0);                  // Wait for synchronization
             
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable TCC1 timer
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  TCC0->CTRLA.bit.ENABLE = 1;                     // Enable TCC0 timer
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: johnyrobot on Oct 14, 2018, 12:28 pm
Wuooo thank you Martin!

I will try this and let you know .

When I change these settings to the timers TCC1 and TCC0 will this affects any other devices? Like for example SPI?

Thank you very much!

Warm Regards

Johnyrobot
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 14, 2018, 01:08 pm
Hi Johnyrobot,

Quote
When I change these settings to the timers TCC1 and TCC0 will this affects any other devices? Like for example SPI?
The TCC timer counters are separate peripherals and independent from the SPI, or for that matter I2C or Serial.

It also won't affect the timing functions delay(), millis() or micros(), as they use the systick timer on the SAMD21.

Kind regards,
Martin
Title: Re: Changing Arduino Zero PWM Frequency
Post by: johnyrobot on Oct 14, 2018, 05:57 pm
Hi Martin,

Thank you very much , your codes works perfect!!!

Can you please explain me the maths behind these :

TCC1->CC[0].reg = 799;    // What is the math behind it?

TCC1->PER.reg = 1599;   // What is the math behind it?


Kind Regards
Johnyrobot

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 14, 2018, 06:26 pm
Hi Johnyrobot,

The timers TCC0 and TCC1 are being clocked by generic clock 4 (GCLK4) at 48MHz.

The formula for calculating the PWM frequency (in the SAMD21 datasheet) is:

PWM frequency = GCLK frequency / (N * (PER + 1))

where:
GCLK frequency => generic clock frequency fed to the timer, (in our case: 48MHz)
N => timer prescaler (in our case: 1)
PER => the value of the timer's period (PER) register

Rearranging the formula:

PER = GCLK frequency / (N * PWM frequency) - 1

therefore:

PER = 48000000 / (1 * 30000) - 1 = 1599

The timers count up from 0 up to 1599, before being reset back to 0, then from 0 to 1599 oncemore and so on. There's a single PER register for each timer.

The counter compare or CC registers determine the PWM waveform's duty-cycle. There is one CC register for each timer channel. Timer TCC0 has 4 channels: (0..3) and TCC1 has 2: (0..1).

The duty-cycle can be set between 0 (0%) and the value in the PER register (100%).

Therefore, for a 50% duty-cycle the CC register is simply set to half the PER register, in our case 1599/2 = 799.

By the way, if you want to change the duty-cycle or period during operation, then it's best to use the buffered PERB and CCBx registers. The buffered registers are loaded into their PER and CC counterparts only at the beginning of the timer cycle (know as an update), thereby preventing glitches from appearing on your outputs.

Code: [Select]
TCC1->CCB[0].reg = 399;                           // Set the duty cycle of the PWM on TCC1 to 25%
while (TCC1->SYNCBUSY.bit.CCB0);                  // Wait for synchronization

Kind regards,
Martin
Title: Re: Changing Arduino Zero PWM Frequency
Post by: RicoW on Oct 16, 2018, 11:20 am
Hello MartinL,

love your work on the Zero, and I need your help as well.
I want to send a simple clock signal over port 2 with 8 MHz. The code I modified from yours is this:
Code: [Select]
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(6) | GCLK_GENDIV_ID(4);
while (GCLK->STATUS.bit.SYNCBUSY);
REG_GCLK_GENCTRL = GCLK_GENCTRL_OE | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(4);
while (GCLK->STATUS.bit.SYNCBUSY);
PORT->Group[g_APinDescription[2].ulPort].PINCFG[g_APinDescription[2].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXO_H;


I need the signal for communication with an IC, but it does not respond. I want to eliminate all possible error sources. Can you tell me what I need to change here?

Greetings, Rico
Title: Re: Changing Arduino Zero PWM Frequency
Post by: johnyrobot on Oct 16, 2018, 04:33 pm
Hello Martin!

Thank you very much for everything !! Your example works like a charm!!

Mucho gracias!!

Johnyrobot
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 16, 2018, 04:55 pm
Hi Rico,

All you need to do is just change:

Code: [Select]
PORT_PMUX_PMUXO_H
to...

Code: [Select]
PORT_PMUX_PMUXE_H
The MKRZero has digital pin 2 on port PA10. This is an even port (rather than an odd one), hence the E instead of the O.

I tested your code on the my Arduino Zero and it works, giving an 8MHz output.

Kind regards,
Martin
Title: Re: Changing Arduino Zero PWM Frequency
Post by: CoCoZero on Oct 19, 2018, 02:21 pm
Hello everyone,

First I'd like to thank you all for every information you shared on this forum (special thanks to MartinL for all the well explained information he already has provided).

I am facing some issues with my code. My goal is to control 4 motors using PWM, in order to keep the basic functions of ARDUINO I understood that i needed to let the TCC0 as it is. That is why I decided to use only TCC1 and TCC2.
With information I picked up on this forum I developped this code below
   
Code: [Select]

// pins 2|5 control motor 1 with pwm on PIN 3
// pins 7|8 control motor 2 with pwm on PIN 4 
// pins 6|9 control motor 3 with pwm on PIN 11     
// pins 10|12 control motor 4 with pwm on PIN 13

REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 4 PWM channels: timer TCC1 and TCC2 outputs
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[4].ulPort].PINCFG[g_APinDescription[4].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC timers to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F; // To connect TCC1 to PIN 3 and PIN 4
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E; // To connect TCC2 to pin 11 and pin 13

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

  // Feed GCLK4 to TCC2 (and TC3)
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 (and TC3)
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 (and TC3)
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization


  /*TCC_WAVE_POL(0xF) is for CC3 | CC2 | CC1 | CC0
      TCC1 and TCC2 only have CC0 and CC1 so we need to use
      TTC_WAVE_POL(0x3)
      By default in dual slope mode (without reversing the polarity), as you go from 0 ==> CCx and CCx ==> 0 the WO(x) timer output goes low, while anything from CCx ==> PER and PER ==> CCx the output is high. Note CCx = counter compare.
  */
  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC1_WAVE |= TCC_WAVE_POL(0x3) |         // Reverse the output polarity on all TCC1 outputs
                   TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC1
  while (TCC1->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC2_WAVE |= TCC_WAVE_POL(0x3) |         // Reverse the output polarity on all TCC2 outputs
                   TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC2
  while (TCC2->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:
  REG_TCC1_PER = 1200;         // Set the frequency of the PWM on TCC1 to 25kHz
  while (TCC1->SYNCBUSY.bit.PER);                // Wait for synchronization
  REG_TCC2_PER = 1200;         // Set the frequency of the PWM on TCC1 to 25kHz
  while (TCC2->SYNCBUSY.bit.PER);                // Wait for synchronization


  // Set the PWM signal to output 50% duty cycle
  REG_TCC1_CCB0 = 600;         // TCC1 CC1 - on D3
  while (TCC1->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  REG_TCC1_CCB1 = 600;         // TCC1 CC2 - on D4
  while (TCC1->SYNCBUSY.bit.CCB1);                // Wait for synchronization
  REG_TCC2_CCB0 = 600;         // TCC2 CC1 - on D11
  while (TCC2->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  REG_TCC2_CCB1 = 600;         // TCC2 CC2 - on D13
  while (TCC2->SYNCBUSY.bit.CCB1);                // Wait for synchronization

  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization

  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC2 output
  while (TCC2->SYNCBUSY.bit.ENABLE);              // Wait for synchronization


   // pins 2|5 control motor 1 with pwm on PIN 3
   // pins 7|8 control motor 2 with pwm on PIN 4 
   // pins 6|9 control motor 3 with pwm on PIN 11     
   // pins 10|12 control motor 4 with pwm on PIN 13

  I commande the motors using bloc of code like :

Code: [Select]

  digitalWrite(2, LOW);
  digitalWrite(7, HIGH);
  digitalWrite(5, HIGH);
  digitalWrite(8, LOW);
  digitalWrite(6, LOW);
  digitalWrite(10, HIGH);
  digitalWrite(9, HIGH);
  digitalWrite(12, LOW);


 My problem is that PIN 12 seems to be in HIGH state whether or not I use the commande digitalWrite(12, LOW);.
  First I have seen that my code never initialize the PIN mode to output so I tried to use the commande pinMode(12, OUTPUT);
  It worked for the PIN 12 but it seems to disable the PWM each time the programme uses the commande digitalWrite(12, LOW);
  My guess is that I have somehow connected the PIN 12 with the pin 11 so maybe the error comes from the line :
 
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
 
  I can't be sure of that because I don't realy understand how this line works...
  Does someone has an idea of how to disconnect PIN 11 and PIN 12 ?
 
  Thank you in advance!
  Corentin
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 19, 2018, 02:39 pm
Hi CoCoZero,

The timing functions delay(), millis() and micros() use the systick timer on the SAMD21, so it's possible to use TCC0 in addition to TCC1 and TCC2.

Unlike the Uno and other AVR based Arduinos it's usually not necessary to set the pin to an output before using the pins for PWM. However, if you need to pull the outputs low as soon as possible during initialisation, then it's easiest just to set the GPIO ports directly:

Code: [Select]
REG_PORT_DIRSET0 =  PORT_PA08 | PORT_PA09 | PORT_PA16 | PORT_PA17;     // Configure the motor channels as outputs                  
REG_PORT_OUTCLR0 = PORT_PA08 | PORT_PA09 | PORT_PA16 | PORT_PA17;      // Set the motor channels low (0V)
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 19, 2018, 03:11 pm
Hi Corentin,

Quote
My guess is that I have somehow connected the PIN 12 with the pin 11 so maybe the error comes from the line :
Not sure what the issue is, but digital pin 12 isn't connected in any way to pins 11 and 13.

Digital pins 11 and 13 are a pair (PA16 and PA17), as are pins 10 and 12 (PA18 and PA19).
Title: Re: Changing Arduino Zero PWM Frequency
Post by: CoCoZero on Oct 19, 2018, 05:42 pm
Hi MartinL,

Thank you for your quick answer and your explanation about the TCC0 timer, it could be useful later, but now that I've something almost working with TCC1 and TCC2 I will try to keep it that way.

Anyway, after your remark I check the pin 12 on my arduino and on my diferent shileds and I found out my last shield is responsible for the problem...

Once again thank you for your answer !
Title: Changing Arduino Zero PWM Frequency
Post by: Miruta22 on Oct 26, 2018, 11:52 am
Hi, I've been looking for information on generating several PWMs with different sources and I don't find anything about that.

1.Could you generate 3 PWM, one of 15Khz, another of 10Khz and the last of 1Khz without any disturbances between them? I need the most precision possible using arduino.

2.Could it be done with different clock sources?

It is the first MKRzero arduino and I still do not know it.

Thanks for help.

Regards!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Oct 28, 2018, 05:24 pm
Hi Miruta22,

If you require the highest PWM resolution possible on all three channels then it's necessary to 3 separate timers: TCC0, TCC1 and TCC2. The timers operate independently from one another.

The following code for the MKRZero outputs 15kHz on D2, 10kHz on D3 and 1kHz on the MOSI pin with a 50% duty-cycle:

Code: [Select]
// Output 15kHz, 10kHz and 1kHz PWM on timers TCC0, TCC1 and TCC2 respectively
void setup()
{
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                     GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = 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 3 PWM channels: timer TCC0 outputs
  const uint8_t pwmPins[] = { 2, 3, MOSI };
  for (uint8_t i = 0; i < 3; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - 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_F | PORT_PMUX_PMUXE_E;
  PORT->Group[g_APinDescription[MOSI].ulPort].PMUX[g_APinDescription[MOSI].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = 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

  // Feed GCLK4 to TCC2 and TC3
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC2 and TC3
                      GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                      GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed GCLK4 to TCC2 and TC3
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  TCC0->PER.reg = 3199;        // Set the frequency of the PWM on TCC0 to 15kHz
  while (TCC0->SYNCBUSY.bit.PER);
  TCC1->PER.reg = 4799;        // Set the frequency of the PWM on TCC1 to 10kHz
  while (TCC1->SYNCBUSY.bit.PER);
  TCC2->PER.reg = 47999;       // Set the frequency of the PWM on TCC2 to 1kHz
  while (TCC2->SYNCBUSY.bit.PER);
 
  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  TCC0->CC[3].reg = 1599;       // TCC0 CC3 - 50% duty cycle on D3
  while (TCC0->SYNCBUSY.bit.CC3);
  TCC1->CC[0].reg = 2399;       // TCC1 CC0 - 50% duty cycle on D2
  while (TCC1->SYNCBUSY.bit.CC0);
  TCC2->CC[0].reg = 23999;      // TCC2 CC0 - 50% duty cycle on MOSI
  while (TCC2->SYNCBUSY.bit.CC0);
 
  TCC0->CTRLA.bit.ENABLE = 1;                     // Enable the TCC0 counter
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable the TCC1 counter
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  TCC2->CTRLA.bit.ENABLE = 1;                     // Enable the TCC2 counter
  while (TCC2->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: chuqdd on Nov 26, 2018, 07:34 pm
Hi, MartinL,

I need to generate 100KHz (50% duty cycle is fine for me) on D5 pin of Arduino MKR1300, on which D5 is PB11 of SAMD21G18A

I've tried fumbling after a few code examples in this forum, but with no success.

Wondering if you can provide me any guidance on this?

Thank you very much
Title: Re: Changing Arduino Zero PWM Frequency
Post by: chuqdd on Nov 26, 2018, 10:11 pm
Hi, MartinL,


I now get 100KHz on D5 (PB11?) of MKR1300 using the following modified code, but also get 25KHz on D6 (PA20?)

how to get rid of the unwanted 25KHz on D6(PA20?)?

Thank you very much




// Output 250kHz PWM on timer TCC0 (6-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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 D7
//  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[5].ulPort].PINCFG[g_APinDescription[5].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
//  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  REG_TCC0_PER = 240;         // Set the frequency of the PWM on TCC0 to 250kHz
  while (TCC0->SYNCBUSY.bit.PER);                // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle
//  REG_TCC0_CC3 = 48;         // TCC0 CC3 - on D7
//  while (TCC0->SYNCBUSY.bit.CC3);                // Wait for synchronization
  REG_TCC0_CC1 = 120;         // TCC0 CC3 - on D7
  while (TCC0->SYNCBUSY.bit.CC1);                // Wait for synchronization
 
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }
Title: Re: Changing Arduino Zero PWM Frequency
Post by: chuqdd on Nov 26, 2018, 10:25 pm
Hi, MartinL,


Please disregard my previous post, the 25KHz signal on D6 is due to my shield.

Thanks

Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on Jan 26, 2019, 06:02 pm
The pin mapping for the TCC0, TCC1 and TCC2 are as follows:

REG_TCC0_CCB0 - digital output D2 (Zero Pro/M0 Pro/M0 - digital pin D4)
REG_TCC0_CCB1 - digital output D5
REG_TCC0_CCB2 - digital output D6
REG_TCC0_CCB3 - digital output D7
REG_TCC1_CCB0 - digital output D4 (Zero Pro/M0 Pro/M0 - digital pin D2)
REG_TCC1_CCB1 - digital output D3
REG_TCC2_CCB0 - digital output D11
REG_TCC2_CCB1 - digital output D13
Hi Martin,

I'm using MKR1000 and the pin map seems to be different. I need to drive PWM on pin PA8 which seems to use register TCC0 CCB0 for duty cycle and correspond to D11 Arduino port for MKR1000, but I can't get any PWM signal out on PA8. (But I can toggle the PA8 with direct port manipulation)

I did these modifications to your code to direct PWM to PA11:

  // Enable the port multiplexer for the digital pin PA8
  PORT->Group[g_APinDescription[11].ulPort].PINCFG[g_APinDescription[11].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to digital output D0 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXE_F;

  // Set the PWM signal to output 50% duty cycle
  REG_TCC0_CCB0 = pulsewidth;         // TCC0 CCB0 - on PA8

Is there a way to set the PORT command with generic SAMD21 pin numbers instead of using g_APinDescription() and Arduino pin numbers? The Arduino pin/port mapping is constantly changing between board version and it's very confusing


Thanks!

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jan 26, 2019, 06:24 pm
Hi evi7538,

To configure port pin PA08 without referencing Arduino pins:

First enable the port multiplexer:

Code: [Select]
PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 1;
...then switch the port multplexer to the correct peripheral. In this case the port is even (port eight) and the TCC0/W[0] is on switch E (TC/TCC), (I/O Multiplexing and Considerations table in the SAMD21 datasheet):

Code: [Select]
PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_E;
If you prefer the PORTA definition can be replaced by 0, and likewise PORTB with 1.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on Jan 26, 2019, 06:36 pm
Martin, thank you for quick response. I also just realized that I need to use TCC2 for PA8 (is this correct?). Still no luck. Here is my code.

Do I need to enable the pin with pinMode(11, OUTPUT) for PWM?

Any help is much appreciated!!!


Code: [Select]


 pinMode(11, OUTPUT);

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 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

 PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 1;
 PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_E;

  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC2_TC3;   // Feed GCLK4 to TCC2 and TC3
   while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC2_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTH;    // Setup dual slope PWM on TCC0
  while (TCC2->SYNCBUSY.bit.WAVE);               // Wait for synchronization

   REG_TCC2_PER = period;         // Set the frequency of the PWM on TCC to 250kHz
  while (TCC2->SYNCBUSY.bit.PER);                // Wait for synchronization
  
  // Set the PWM signal to output 50% duty cycle
  REG_TCC2_CCB0 = pulsewidth;         // TCC2 CCB0 - on PA8
  while (TCC2->SYNCBUSY.bit.CC3);                // Wait for synchronization
  
  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC output
  while (TCC2->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jan 26, 2019, 07:22 pm
Timer TCC1 on channel 0 (CC0/CCB0) is the other timer available on port PA08, it's on peripheral switch position F:

Code: [Select]
PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_F;
Just change TCC2 to TCC1 and set the counter compare and counter compare buffered registers to CC0 and CCB0 respectively.

You'll also need to connect the TCC1 timer to the generic clock, by changing the GCLK_CLKCTRL_ID_TCC2_TC3 register bitfield to GCLK_CLKCTRL_ID_TCC0_TCC1 instead:

Code: [Select]
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC
                   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

Unlike the AVR Arduino boards, on the SAMD21 it isn't necessary to use the pinMode() function to activate the pin as an output, so this line can be deleted.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on Jan 26, 2019, 07:33 pm
WOW, it actually works now, thank you so much, Martin!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: manuelx10 on May 10, 2019, 04:27 pm
Hi,

I am looking to migrate my project from Arduino MEGA2560 to MKR ZERO.
My sketch controls 3 motor driver ICs and does it via 3 separate pins per driver IC, and sets up the pins  to 20KHz via timers, and then directly modify the duty cycle in the program to adjust the speed.

Right now i have this for my MEGA2560:

Code: [Select]


  // Timer 4 (TCCR4) configuration controls pins 6, 7, and 8.
  // PWM frequency calculation: [ 16MHz / 1 (prescaler) / 2 (phase-correct) / 400 (top) = 20kHz ]
  int eightOnes = 255;          // Equivalent to 11111111 in binary
  TCCR3A &= ~eightOnes;         // Set the eight bits in register to 0
  TCCR3B &= ~eightOnes;         // Set the eight bits in register to 0
  ICR3 = (F_CPU/20000)/2;       // Top = 400 = (16Mhz/20Khz)/2
  TCCR3A = _BV(COM3A1)          // pin 6 - non-inverted PWM output   
         | _BV(COM3B1)          // pin 7 - non-inverted PWM output
         | _BV(COM3C1)          // pin 8 - non-inverted PWM output
         | _BV(WGM31);          // mode 10: phase correct PWM with ICR4 as Top
  TCCR3B = _BV(WGM33)
         | _BV(CS30);           // Prescaler = 1     



and control the duty cycle by updating OCR3A, OCR3B, OCR3C:

Code: [Select]


// speed = range between -400 to 400
OCR3A = speed
OCR3B = speed



Anyone could help with a solution or could point me the right direction? thanks!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 11, 2019, 10:17 am
Hi manuelx10,

Here's some example code for the MKR Zero that outputs 20kHz PWM on digital pins 2, 3 and 4:

Code: [Select]
// MKR Zero: Output 20kHz PWM on timer TCC0 (8-bit resolution) on 3 channels on D4 (0), D2 (2) and D3 (3)
void setup()
{
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                     GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = 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 3 PWM channels: timer TCC0 outputs on D4, D2 and D3
  PORT->Group[g_APinDescription[2].ulPort].PINCFG[g_APinDescription[2].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[4].ulPort].PINCFG[g_APinDescription[4].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // Peripheral F specifies the timer: TCC0
  PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = 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
  TCC0->WAVE.reg = TCC_WAVE_POL(0xF) |            // Reverse the output polarity on all TCC0 outputs
                   TCC_WAVE_WAVEGEN_DSBOTTOM;     // Set up dual slope PWM on TCC0
  while (TCC0->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: 16MHz / (1 * (399 + 1) * 2) = 20kHz
  TCC0->PER.reg = 399;                            // Set the frequency of the PWM on TCC0 to 20kHz
  while (TCC0->SYNCBUSY.bit.PER);                 // Wait for synchronization

  // Set the CC[x] registers for 50% duty-cycle
  TCC0->CC[0].reg = 199;                          // TCC0 CC0 - 50% duty cycle on D4
  while (TCC0->SYNCBUSY.bit.CC0);                 // Wait for synchronization
  TCC0->CC[2].reg = 199;                          // TCC0 CC1 - 50% duty cycle on D2
  while (TCC0->SYNCBUSY.bit.CC2);                 // Wait for synchronization
  TCC0->CC[3].reg = 199;                          // TCC0 CC2 - 50% duty cycle on D3
  while (TCC0->SYNCBUSY.bit.CC3);                 // Wait for synchronization

  // Divide the 16MHz signal by prescaler 1 giving 16MHz (62.5ns) TCC0 timer tick
  TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1;    // Divide GCLK4 by 1
 
  TCC0->CTRLA.bit.ENABLE = 1;                     // Enable the TCC0 timer
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop()   // Change TCC0 timer channel 0 between 25% and 75% duty-cycle using buffered CCB[0] register
{
  TCC0->CCB[0].reg = 99;                          // TCC0 CCB0 - 25% duty cycle on D4
  while (TCC0->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
  TCC0->CCB[0].reg = 299;                         // TCC0 CCB0 - 75% duty cycle on D4
  while (TCC0->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
}

I've set up the generic clock 4 (GCLK4) to generate 16MHz to timer TCC0, thereby providing the same resolution as the Mega, (although the MKR Zero is capable of much higher resolution if required). It also uses dual slope PWM, the same as phase correct PWM on the AVR microcontrollers.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: manuelx10 on May 12, 2019, 11:00 am
Hi MartinL,

Thanks for the quick reply! and the sample sketch, great starting point.


What is the difference between using the CC.reg and CCB.reg?

Also, is the delay and while loop for wait for sync necessary? will this affect simultaneous / instant control of the 3 pwm ?

Code: [Select]
while (TCC0->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 12, 2019, 11:57 pm
Quote
What is the difference between using the CC.reg and CCB.reg?
The CCBx.reg are buffered counter compare registers that automatically update the CCx only at the beginning of a new timer cycle (period). This allows the PWM duty-cycle to be changed during operation without causing glitches to appear on the PWM output. Changes by writing to CCx registers directly by contrast take effect (almost) immediately at the output.

Quote
Also, is the delay and while loop for wait for sync necessary?
The delay isn't necessary, as it's only in this example for demonstration purposes.

The register synchronisation is sometimes required, because CPU and its corresponding AHB/APB bus system is driven by a clock signal that is asynchronous respect the generic clock that drives the given peripheral. In this case the CPU clock domain is asynchronous with respect to GCLK4 that drives the TCC0 timer peripheral.

After writing to a TCC0 register that requires write synchronisation, the CPU can wait for the corresponding CCBx SYNCBUSY bit to clear before proceeding. This ensures that the written data has been successfully clocked into the TCC0 register.

However, register synchronisation happens anyway, whether you poll the corresponding SYNCBUSY bit or not, therefore waiting for the SYNCBUSY bit to clear in a while loop is only really necessary if there is a chance TCC0 register could be written two times in quick succession.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: embring on May 25, 2019, 07:07 pm
Thanks for this topic !! And all the excellent information from Martin L!

I try to drive two motors with two Motor driver cards Adafruit MD 8871, to be able to drive more powerful electric motors. 3,5 A each. I need 4 separate channels with PWM to drive both motors forward and backwards.
I use a small Adafruit ItsyBitsy M0 connected via I2C to have a more flexible motor driver.
I have almost got everything to work, but it seems that the channels are connected in some way, and only two channels work.. I think its the connection pins between the M0 chip and the pins that I really don't can figure out.. I get perfect 20k PWM signals from your code but I don't get 4 separate channels.

Any one has any clue?  (Part of the code I use from this topic)
I have attached a Screendump width the pin configuration on the Adafruit ItsyBitsy M0.. And looked in the ATSAMD21G18 32-bit Cortex M0+ data sheet but I still don't get it... :(

REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 4 PWM channels: timer TCC0 outputs
  const uint8_t CHANNELS = 4;
  const uint8_t pwmPins[] = { 11, 13, 10, 12 };    // CCB0 = 11, CCB13 = 5, CCB2 = 10, CCB3 = 12   ????
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins].ulPort].PINCFG[g_APinDescription[pwmPins].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
  PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
 



  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  // 400 = 20kHz
  REG_TCC0_PER = 400;      // Set the frequency of the PWM on TCC0 to 20kHz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB0 = 0;       // TCC0 CCB0 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB0);
  REG_TCC0_CCB1 = 0;       // TCC0 CCB1 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB1);
  REG_TCC0_CCB2 = 0;       // TCC0 CCB2 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB2);
  REG_TCC0_CCB3 = 0;       // TCC0 CCB3 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 1 giving 16MHz (62.5ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization


Title: Re: Changing Arduino Zero PWM Frequency
Post by: embring on May 25, 2019, 10:57 pm
I found the problem!  (some hours later... ) Now the code is working!

When I changed pins so they corresponded to the ItsyBitsy M0 layout (PAXX to Digital pin) and the M0 data sheet and the E or F column in the Table 6-1. PORT Function Multiplexing in the ATMEL SAMD21G
data sheet. Previous pairs  (11,13 ) and (10,12) of pins had the same (W O numbers)

TCC0 (W O 0) E PA08 = 4
TCC0 (W O 1) E PA09 = 3
TCC0 (W O 2) F PA18 = 10
TCC0 (W O 3) F PA19 = 12 

CODE:

// Output 20kHz PWM on timer TCC0 (8-bit resolution)
void setup()
{
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    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 4 PWM channels: timer TCC0 outputs
  const uint8_t CHANNELS = 4;
  const uint8_t pwmPins[] = { 4, 3, 10, 12 };    // TCC0 (W O 0) E PA08 = 4, TCC0 (W O 1) E PA09 = 3, TCC0 (W O 2) F PA18 = 10, TCC0 (W O 3) F PA19 = 12   
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins].ulPort].PINCFG[g_APinDescription[pwmPins].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
  PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
 
  // 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_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:
  // 400 = 20kHz
  REG_TCC0_PER = 400;      // Set the frequency of the PWM on TCC0 to 20kHz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB0 = 0;       // TCC0 CCB0 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB0);
  REG_TCC0_CCB1 = 0;       // TCC0 CCB1 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB1);
  REG_TCC0_CCB2 = 0;       // TCC0 CCB2 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB2);
  REG_TCC0_CCB3 = 0;       // TCC0 CCB3 - 200 = 50% duty cycle on D2   (0-400)
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 1 giving 16MHz (62.5ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on May 29, 2019, 04:08 am
Hi Martin,

I got the PWM routine working fine on Zero with your help (https://forum.arduino.cc/index.php?topic=346731.msg4034058#msg4034058 ). But I now need to synchronize my ADC measurements with PWM signal. As a reminder, I have the PWM directed to the PA08:

  PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 1;
  PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_F;

For synchronization I'm trying to do direct read of the bit 08 of the PA register, something like:
PwmOut = REG_PORT_OUT0 & (1 << 8 );

But I'm getting only constant values, 0 or 1 depending on how I set the port values in the setup(). The PWM routine does not seem to affect the state of the REG_PORT_OUT0 register, although I can clearly see the PWM squarewave signal on the PA08 pin, so I guess the MUX connects the PA8 to the timer output instead of the port register?

So how do I read the state of the PA08 pin when it's driven by timer in the PWM mode?

I wanted to try the simplest method first (which is just polling the PA08 register bit in a while loop) but I know that there are other ways to do that which are a bit more complicated and have other consequences:
- I can connect PA08 electrically to some other pin and do digital read, but for that I will need to modify the board.
- I can trigger ADC read by an interrupt from the counter overflow, this will slightly increase the execution time (every us counts in my application)

Your help is much appreciated!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 29, 2019, 10:20 am
Hi evi7538,

The easiest way to trigger an ADC conversion from a PWM output is to use the SAMD21's event system. The event system is a 12-channel highway that can be used to allow peripheral-to-peripheral communication without CPU intervention.

It's possible to set up a peripheral to be the event sender (generator) and any number of peripherals to be the event receiver (user), on a given event channel (0 to 11). The peripherals can be configured to send and receive events on a number of peripheral specific criteria.

Here's some example code that sets up timer TCC1 to output a 1Hz PWM signal on port PA08. The TCC1 is configured so that it triggers an event each time it overflows (every second) on event channel 0. The ADC timer is initialised to start a conversion on analog pin A0 each time it receives this event from the TCC1 timer (on event channel 0). Once the ADC conversion is complete, the it generates an interrupt to output the result to the console:

Code: [Select]
// Use event system to trigger ADC conversion every second from a 1Hz PWM signal
void setup()
{
  SerialUSB.begin(115200);
  while(!SerialUSB);
 
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral

  // Intialise the ADC - generic clock already routed to ADC
  ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV512 |    // Divide Clock ADC GCLK by 512 (48MHz/512 = 93.7kHz)
                   ADC_CTRLB_RESSEL_12BIT;         // Set ADC resolution to 12 bits
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for synchronization
  ADC->SAMPCTRL.reg = 0x00;                        // Set max Sampling Time Length to half divided ADC clock pulse (5.33us)
  ADC->INPUTCTRL.bit.MUXPOS = 0x0;                 // Set the analog input to A0
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for synchronization

  NVIC_SetPriority(ADC_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for ADC to 0 (highest)
  NVIC_EnableIRQ(ADC_IRQn);         // Connect ADC to Nested Vector Interrupt Controller (NVIC)

  ADC->INTENSET.reg = ADC_INTENSET_RESRDY;        // Set ADC to generate an interrupt when the result is ready
 
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) |         // Divide the 48MHz clock source by divisor 1: 48MHz/3=16MHz
                     GCLK_GENDIV_ID(4);           // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  GCLK->GENCTRL.reg = 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

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = 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

  // Enable the port multiplexer for the TCC1 PWM channel 0 on port pin PA08
  PORT->Group[PORTA].PINCFG[8].bit.PMUXEN = 1;
 
  // Connect the TCC1 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[PORTA].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_F;// | PORT_PMUX_PMUXO_F;

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_ADC_START);              // Set the event user (receiver) as ADC

  ADC->EVCTRL.reg |= ADC_EVCTRL_STARTEI;                                   // Enable the ADC start conversion on event input

  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TCC1_OVF) |        // Set event generator (sender) as timer TCC1 overflow
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0
 
  TCC1->EVCTRL.reg |= TCC_EVCTRL_OVFEO;                                    // Enable the TCC1 overflow event output
                     
  // 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
 
  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  TCC1->PER.reg = 15999999;                         // Set the frequency of the PWM on TCC1 to 1Hz
  while (TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  TCC1->CC[0].reg = 7999999;                        // TCC1 CC0 - 50% duty cycle
  while (TCC1->SYNCBUSY.bit.CC0);
 
  // Divide the 16MHz signal by 1 giving 16MHz (62.5ns) TCC1 timer tick and enable the outputs
  TCC1->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1;      // Divide GCLK4 by 1

  ADC->CTRLA.bit.ENABLE = 0x01;                    // Enable the ADC
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for synchronization

  TCC1->CTRLA.bit.ENABLE = 1;                       // Enable the TCC1 timer
  while (TCC1->SYNCBUSY.bit.ENABLE);                // Wait for synchronization
}

void loop() {}

void ADC_Handler()
{
  while (ADC->STATUS.bit.SYNCBUSY);                 // Read synchronization
  SerialUSB.println(ADC->RESULT.reg);               // Output the result
  ADC->INTFLAG.bit.RESRDY = 1;                      // Clear the RESRDY interrupt flag
}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on May 29, 2019, 02:18 pm
Martin, thank you for good suggestion but sorry, I forgot to mention, I'm using an external 20-bit ADC through SPI. But I guess may be I can still use your idea (without initializing the ADC ) but somehow redirect the overflow event to a different event channel that I can just read directly in the interrupt handler function? I still use the internal ADC for other purposes so I don't want to disturb it with PWM.

Another thing: can I just poll that specific event bit from the channel without using interrupts?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 29, 2019, 03:02 pm
Hi evi7538,

Quote
I'm using an external 20-bit ADC through SPI.
No problem, is the ADC conversion started through an SPI command, or with an external sync pulse from the microcontroller?

Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on May 29, 2019, 03:03 pm
Hi evi7538,

No problem, is the ADC conversion started through an SPI command, or with an external sync pulse from the microcontroller?


The ADC starts and it is controlled entirely through SPI

I guess another possibility (if I still have to use interrupt handler anyway to clear the overflow) is just to trigger the interrupt by TCC1 overflow and run the ADC conversion in the interrupt handler without using the event system.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 29, 2019, 03:34 pm
In that case, the simplest method is to call the TCC1 timer's Interrupt Service Routine (ISR) each time it overflows.

Just add this code before you enable TCC1 timer, in the setup() portion of your code:

Code: [Select]
NVIC_SetPriority(TCC1_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC1 to 0 (highest)
NVIC_EnableIRQ(TCC1_IRQn);         // Connect TCC1 to Nested Vector Interrupt Controller (NVIC)

TCC1->INTENSET.reg = TC_INTENSET_OVF;          // Enable TCC1 overflow interrupt

And this function to implement the ISR itself:

Code: [Select]
void TCC1_Handler()                  // Interrupt Service Routine (ISR) for timer TCC1
{     
  // Put your timer overflow (OVF) code here:     
  // ... 
  TCC1->INTFLAG.bit.OVF = 1;         // Clear the OVF interrupt flag
}

In this instance it isn't necessary to use the event system, as sending the SPI command requires CPU intervention in the ISR.

This should work well for low to medium PWM frequencies. If however you intend to use a high PWM frequency, the rapid calling of ISR can overwhelm the processor. In this scenario, it's necessary to route the TCC1's overflow event (via the event system) to the Direct Memory Access Controller (DMAC) and get the DMAC to automatically send out the SPI command to the ADC.

Using the event system and DMAC together allows the construction of completely autonomous peripheral systems that operate with a minimum of CPU intervention.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on May 29, 2019, 04:50 pm
Thank you, Martin, I will give it a try. My PWM period is 16us so the ISR should not bee too overwhelming for the CPU I think.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: evi7538 on May 30, 2019, 12:07 am
I got it all working, thanks a lot for your help, Martin!
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 31, 2019, 11:07 am
Hi evi7538,

Glad to hear you got it working.

Out of interest I decided to have a go at getting the PWM to generate a synchronised 2 byte SPI command output, in order to simulate sending an SPI conversion signal (including register sub-address and command bytes) to an external device. This all occurs without CPU intervention (the loop() function is empty) and runs like a clockwork machine.

The code uses timer TCC0 and all of its 4 channels to generate the necessary timing. Channel 2 generates a 62.5kHz PWM output and channel 3 the SPI chip select output. Channels 0 and 1 are set up to route their compare match events through the event system to DMA channels 0 and 1. The DMA channels 0 and 1 simply output the first (sub-address) and second (command) bytes on the SPI port respectively.

(Unfortunately, it's not possible to use a single DMA channel to send two SPI bytes, as the SAMD21 doesn't support DMA burst mode).

On my Arduino Zero, the code outputs a 62.5kHz PWM signal on digtial pin D6 (yellow), the SPI chip select on D7 (light blue), SPI's SCK (pink) and MOSI (dark blue):

(https://forum.arduino.cc/index.php?action=dlattach;topic=346731.0;attach=310674)
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 31, 2019, 11:08 am
Here's the code:

Code: [Select]
// Use event system to use 62.5kHz PWM signal to trigger DMAC to send SPI ADC conversion
#include <SPI.h>

typedef struct                              // DMAC descriptor structure
{
  uint16_t btctrl;
  uint16_t btcnt;
  uint32_t srcaddr;
  uint32_t dstaddr;
  uint32_t descaddr;
} dmacdescriptor ;

// DMAC_CH_NUM = 12 channels on SAMD21
volatile dmacdescriptor wrb[DMAC_CH_NUM] __attribute__ ((aligned (16)));        // Write-back DMAC descriptors
dmacdescriptor descriptor_section[DMAC_CH_NUM] __attribute__ ((aligned (16)));  // DMAC channel descriptors
dmacdescriptor descriptor __attribute__ ((aligned (16)));                       // Place holder descriptor

uint8_t data[2] = { 0x68, 0x77 };            // Dummy SPI command: 0x68 = Register address, 0x77 = Command

void setup()
{
  SPI.begin();
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));   // SPI at 10MHz
 
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral

  DMAC->BASEADDR.reg = (uint32_t)descriptor_section;                // Set the descriptor section base address
  DMAC->WRBADDR.reg = (uint32_t)wrb;                                // Set the write-back descriptor base adddress
  DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf);      // Enable the DMAC and priority levels
 
  DMAC->CHID.reg = DMAC_CHID_ID(0);                                 // Select DMAC channel 0
  // Set DMAC channel 0 to priority level 3 (highest), to trigger on TCC0 overflow and to trigger every beat
  DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(3) |                   
                      DMAC_CHCTRLB_TRIGACT_BEAT |
                      DMAC_CHCTRLB_EVIE |
                      DMAC_CHCTRLB_EVACT_TRIG;

  DMAC->CHID.reg = DMAC_CHID_ID(1);                                 // Select DMAC channel 1
  // Set DMAC channel 1 to priority level 3 (highest), to trigger on TCC0 overflow and to trigger every beat
  DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(3) |
                      DMAC_CHCTRLB_TRIGACT_BEAT |
                      DMAC_CHCTRLB_EVIE |
                      DMAC_CHCTRLB_EVACT_TRIG;

  descriptor.descaddr = (uint32_t)&descriptor_section[0];          // Circular descriptor
  descriptor.srcaddr = (uint32_t)&data[0] + 1;
  descriptor.dstaddr = (uint32_t)&SERCOM4->SPI.DATA.reg;
  descriptor.btcnt = 1;
  descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_BYTE | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_VALID;
  memcpy(&descriptor_section[0], &descriptor, sizeof(descriptor));

  descriptor.descaddr = (uint32_t)&descriptor_section[1];          // Circular descriptor
  descriptor.srcaddr = (uint32_t)&data[1] + 1;
  descriptor.dstaddr = (uint32_t)&SERCOM4->SPI.DATA.reg;
  descriptor.btcnt = 1;
  descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_BYTE | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_VALID;
  memcpy(&descriptor_section[1], &descriptor, sizeof(descriptor));
 
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) |         // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                     GCLK_GENDIV_ID(4);           // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  GCLK->GENCTRL.reg = 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

  // Feed GCLK4 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = 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

  PORT->Group[g_APinDescription[6].ulPort].PINCFG[g_APinDescription[6].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC timers to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
 
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_DMAC_CH_0);              // Set the event user (receiver) as DMAC channel 0

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_DMAC_CH_1);              // Set the event user (receiver) as DMAC channel 0

  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TCC0_MCX_0) |      // Set event generator (sender) as timer TCC0 match compare channel 0
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0

  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TCC0_MCX_1) |      // Set event generator (sender) as timer TCC0 match compare channel 1
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 0
 
  TCC0->EVCTRL.reg |= TCC_EVCTRL_MCEO0 |                                   // Enable the TCC0 match compare channel 0 output
                      TCC_EVCTRL_MCEO1;                                    // Enable the TCC0 match compare channel 1 output
                                           
  // Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
  TCC0->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC0
  while (TCC0->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:
  TCC0->PER.reg = 767;                          // Set the frequency of the PWM on TCC0 to 62.5kHz
  while (TCC0->SYNCBUSY.bit.PER);               // Wait for synchronization
  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  TCC0->CC[2].reg = 384;                        // TCC0 CC2 - 50% duty cycle
  while (TCC0->SYNCBUSY.bit.CC2);               // Wait for synchronization
  TCC0->CC[3].reg = 129;                        // TCC0 CC3 - set duty cycle
  while (TCC0->SYNCBUSY.bit.CC3);               // Wait for synchronization 
  TCC0->DRVCTRL.reg |= TCC_DRVCTRL_INVEN7;      // Invert CC3 = WO[7]
  TCC0->CC[0].reg = 1;                         // TCC0 CC0 - set duty cycle
  while (TCC0->SYNCBUSY.bit.CC0);               // Wait for synchronization
  TCC0->CC[1].reg = 49;                         // TCC0 CC1 - set duty cycle
  while (TCC0->SYNCBUSY.bit.CC1);               // Wait for synchronization
 
  // Divide the 16MHz signal by 1 giving 16MHz (62.5ns) TCC0 timer tick and enable the outputs
  TCC0->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1;      // Divide GCLK4 by 1

  TCC0->CTRLA.bit.ENABLE = 1;                       // Enable the TCC0 timer
  while (TCC0->SYNCBUSY.bit.ENABLE);                // Wait for synchronization

  DMAC->CHID.reg = DMAC_CHID_ID(0);                 // Select DMAC channel 0
  DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;         // Enable DMAC channel 0
  DMAC->CHID.reg = DMAC_CHID_ID(1);                 // Select DMAC channel 1
  DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE;         // Enable DMAC channel 1
}

void loop() {}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: dev_000 on May 31, 2019, 01:30 pm
Dear MartinL,

Great work with this topic. I have learnt quite a lot reading through. I have some troubles understanding a few things.


I am using an Adafruit M0 Feather board and I am trying to get a 100kHz waveform on D10, D12. I am having trouble understanding the section of the code where you connect TCC timer to the ports. I am not sure how to go about doing that for any board(with a custom mapping) for any particular pin. Will you please help explain that or point to where in the Atsamd21 datasheet, I should be reading for clarification.

I am attaching the variant.cpp file for pin Mapping for Adafruit M0 if that helps.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on May 31, 2019, 02:27 pm
Hi dev_000,

Looking at the Adafruit Feather M0 schematic: https://cdn-learn.adafruit.com/assets/assets/000/040/553/original/arduino_schem.png?1490994398 (https://cdn-learn.adafruit.com/assets/assets/000/040/553/original/arduino_schem.png?1490994398), it can be seen that digital pin D10 is connected to SAMD21's port pin PA18 and D12 to PA19.

By default the microcontroller's pins are set to General Purpose Input Output (GPIO), however the pins can also be switched over to act as peripheral inputs and outputs. Switching over from GPIO to peripheral IO is performed by setting the PMUXEN (Peripheral Multiplexer Enable) bit in a given pin's CONFIG (Configuration) register:

This can be set either for an Arduino style digtial pin:

Code: [Select]
// Set PMUXEN for digital pin D10
PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].bit.PMUXEN = 1;

...or using the SAMD21's port pin:

Code: [Select]
PORT->Group[PORTA].PINCFG[18].bit.PMUXEN = 1;    // Set PMUXEN for port pin PA18
The SAMD21's datasheet contains a table, called "Table 6-1 PORT Function Multiplexing". It lists each of the microcontroller's pins against peripherals labelled A to H. Going down the table we can see that port pin PA18 (D10) has a timer TCC0/WO[2] on peripheral F. WO[2] indicates that this pin uses TCC0 channel 2. Port pin PA19 (D12) has timer TCC0/WO[3], also on peripheral F.

On the SAMD21 the port pins a paired as odd and even, starting PA00 and PA01, PA02 and PA03 and so on... Port pin PA18 on D10, happens to be paired with PA19 on D12. Using the pins' PMUX register we can activate both peripheral outputs. Each PMUX register is 8-bits wide with the upper nibble (4-bits) determining the ODD port peripheral IO: A through to H and the lower nibble determining the EVEN port peripheral IO, again: A through to H.

Therefore to activate the even PA18 (D10) and the odd PA19 (D12) as PWM outputs, it's necessary to set both the even and odd nibbles to peripheral F.

Again, this can be set either for an Arduino style digtial pins:

Code: [Select]
PORT->Group[g_APinDescription[10].ulPort].PMUX[g_APinDescription[10].ulPin >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
...or using the SAMD21's port pins:

Code: [Select]
PORT->Group[PORTA].PMUX[18 >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
Each port has 32 pins (PA00-PA31) (although not all port pins are available on some SAMD21 variants), this means there are 32 port pin CONFIG registers (one for each pin), but only 16 PMUX registers (one for each port pair), the logical shift right ">>" essentially divides the value by to 2, to account for half the number of PMUX registers.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: dev_000 on May 31, 2019, 02:55 pm
Thanks a lot for taking the time to explain that beautifully. Got it working fine at 100kHz.

Just a small correction. In the newer datasheet (http://ww1.microchip.com/downloads/en/DeviceDoc/SAMD21-Family-DataSheet-DS40001882D.pdf), its Table 7-1. I had searched for Table 6-1 and couldn't find it. For anyone searching for it in the future, search for "PORT Function Multiplexing" in the datasheet.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: NicoCaldo on Jun 18, 2019, 10:40 am
Hi MartinL,
your post has been super useful to me to understand how PWM on Arduino Zero works.

I'm trying to make a LED (pin 8 ) blink with PWM, changing the frequency to value visible to the eyes. Is it possible in some ways?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 18, 2019, 12:29 pm
Hi Nico,

Quote
I'm trying to make a LED (pin 8 ) blink with PWM, changing the frequency to value visible to the eyes. Is it possible in some ways?
If your intention is to simply blink an LED, then it's possible to run the TC or TCC timers at a lower frequency.

Here's some code that blinks an LED at 0.5Hz, 1 second on, 1 second off on digital pin D8. It uses the TCC1 timer in Normal Frequency (NFRQ) mode, this mode simply toggles the D8 output each time the timer overflows (every second):

Code: [Select]
// Toggle LED output 1 second on, 1 second off (0.5Hz) on digital pin D8 using timer TCC1
void setup()
{
  // Feed GCLK0 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK0 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for the TCC1 PWM channel 0 (digital pin D8), SAMD21 pin PA06
  PORT->Group[g_APinDescription[8].ulPort].PINCFG[g_APinDescription[8].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;
 
  TCC1->PER.reg = 46874;                          // Set TCC1 timer to overflow every 1 second: 48MHz / (1024 * 46874 + 1) = 1Hz
  while(TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
 
  TCC1->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1024; // Set TCC1 prescaler to 1024
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() {}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: NicoCaldo on Jun 19, 2019, 04:25 pm
Amazing, thanks a lot.
And then I simply use the AnalogWrite for PWM as always right?

Last question; if I would like to use the digital pins 2-3 for the same purpose, do I need to change the value to another timer or is the code the same?

Thanks again
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 19, 2019, 09:21 pm
Hi Nico,

Quote
And then I simply use the AnalogWrite for PWM as always right?
Unfortunately the issue with the analogWrite() function, is that it will set up a predefined timer and frequency on a given pin, this will interfere with code above.

Quote
if I would like to use the digital pins 2-3 for the same purpose, do I need to change the value to another timer or is the code the same?
That depends if you need to change the signal's frequency (period) or duty-cycle. Changing the frequency affects the whole timer and all of its output channels, while the duty-cycle can be set for each individual channel.

What would you like the LED outputs to do? What frequency (range) and duty-cycle do you intend to use?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: NicoCaldo on Jun 20, 2019, 08:44 am
Quote
Unfortunately the issue with the analogWrite() function, is that it will set up a predefined timer and frequency on a given pin, this will interfere with code above.
I've tried the analogWrite(8,x) with your code and it works fine. Changing the value of x between 0 and 255 change the time which the led stay on in a period of one second.

Quote
What would you like the LED outputs to do? What frequency (range) and duty-cycle do you intend to use?
The purpose is the same as the pin 8. I need to make two led connected on pin 2 and 3 blinks with the PWM so, it would be amazing if I can set the frequency to 0.5Hz as on the previous example and then I make it blink slower or faster changing the duty cycle with analogWrite(2,x) and analogWrite(3,x)

Does the changing of the PWM frequency on pins 2 and 3 will change the running time of my whole firmware (like will change delays) or is it unlinked from the actual clock speed of the board?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 21, 2019, 09:20 am
Hi Nico,

Quote
I've tried the analogWrite(8,x) with your code and it works fine. Changing the value of x between 0 and 255 change the time which the led stay on in a period of one second.
The analogWrite() function sort of works in this instance, because it's using the same timer (TCC1). However, behind the scenes it does change the timer's configuration and should now be giving you an on/off time of around 1.4s (0.7Hz).

If you require control over the duty-cycle of the signal on digital pin D8 then it's better to set up TCC1 for Normal PWM (NPWM) operation. Changes to the duty-cycle are made by loading the Counter Compare Buffered Channel 0 (CCB0) register with a value between 0 (0%) and the Period (PER) register (100%).

Here's the code to do that:

Code: [Select]
// Set up timer TCC1 to output a 0.5Hz signal on digtal pin D8
void setup()
{
  // Feed GCLK0 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK0 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for the TCC1 PWM channel 0 (digital pin D8), SAMD21 pin PA06
  PORT->Group[g_APinDescription[8].ulPort].PINCFG[g_APinDescription[8].ulPin].bit.PMUXEN = 1;
 
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[8].ulPort].PMUX[g_APinDescription[8].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E;

  TCC1->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;        // Setup single slope PWM on TCC1
  while (TCC1->SYNCBUSY.bit.WAVE);                // Wait for synchronization
 
  TCC1->PER.reg = 93749;                          // Set the TCC1 timer to overflow every 1 second: 48MHz / (1024 * 93749 + 1) = 0.5Hz
  while(TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization

  TCC1->CC[0].reg = 46875;                        // Set the TCC1 duty cycle to 50% (PER / 2)
  while(TCC0->SYNCBUSY.bit.CC0);                  // Wait for synchronization
 
  TCC1->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1024; // Set TCC1 prescaler to 1024
  TCC1->CTRLA.bit.ENABLE = 1;                     // Enable the TCC1 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop()
{
  TCC1->CCB[0].reg = 23437;                       // Set the TCC1 duty cycle to 25% (PER / 4)
  while(TCC0->SYNCBUSY.bit.CCB0);                 // Wait for synchronization 
  delay(4000);                                    // Wait for 4 seconds
  TCC1->CCB[0].reg = 70312;                       // Set the TCC1 duty cycle to 75% (PER * 3 / 4)
  while(TCC0->SYNCBUSY.bit.CCB0);                 // Wait for synchronization 
  delay(4000);                                    // Wait for 4 seconds
}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 21, 2019, 09:36 am
Regarding obtaining the same output on D2 and D3:

On D2 change the pin output configuration to:

Code: [Select]
PORT->Group[g_APinDescription[2].ulPort].PINCFG[g_APinDescription[2].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;

...and the timer and timer channel to TCC0, channel 0, for example:

Code: [Select]
TCC0->CCB[0].reg = 23437;                       // Set the TCC0 duty cycle to 25% (PER / 4)
while(TCC0->SYNCBUSY.bit.CCB0);                 // Wait for synchronization

On D3 change the pin output configuration to:

Code: [Select]
PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;

... and the timer and timer channel to TCC1, channel 1, for example:

Code: [Select]
TCC1->CCB[1].reg = 23437;                       // Set the TCC1 duty cycle to 25% (PER / 4)
while(TCC1->SYNCBUSY.bit.CCB1);                 // Wait for synchronization

Making changes to the TCCx timers won't affect the timing delay(), delayMicroseconds(), millis() or micros() functions, as they're generated using the SAMD21's Systick (System Tick) timer.
Title: Re: Changing Arduino Zero PWM Frequency
Post by: NicoCaldo on Jun 24, 2019, 08:45 am
Amazing, thanks a lot MartinL
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Nilab on Jun 24, 2019, 07:18 pm
Hi MartinL
How to set d3 and d4 pin to 20kHz or 30khz  (arduino M0)

and change duty cycle in loop to control the motor with high resolution (10 bit) for example
can i connect to it to tcc0 ? or i need tcc0 to d4 and tcc1 to d3 ?

Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 25, 2019, 09:45 am
Hi Nilab,

Here's an example of that generates 30kHz PWM on digital pins D3 and D4, it provides 10-bit resolution (log(1600)/log(2)) on both outputs.

Note that on the Arduino M0 board's D2 and D4 pins are reversed with respect to the Arduino Zero's, (I've accounted for that in the code):

Code: [Select]
// Output 30kHz PWM on digital pins D3 and D4 on Arduino.org M0 board
// Note: D2 and D4 are reversed on Arduino MO with respect to Arduino Zero
void setup()
{
  // Feed GCLK0 to TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0 as a clock source
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0
                      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 D3 and D4
  PORT->Group[g_APinDescription[3].ulPort].PINCFG[g_APinDescription[3].ulPin].bit.PMUXEN = 1;
  PORT->Group[g_APinDescription[4].ulPort].PINCFG[g_APinDescription[4].ulPin].bit.PMUXEN = 1;

  // D3 is on ODD port pin PA09 and TCC0/WO[1] channel 1 is on peripheral E
  // D4 is on EVEN port pin PA14 and TCCO/WO[4] channel 0 is on peripheral F
  PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E;
  PORT->Group[g_APinDescription[4].ulPort].PMUX[g_APinDescription[4].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F;

  // Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
  TCC0->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC1
  while (TCC0->SYNCBUSY.bit.WAVE);                 // Wait for synchronization
 
  TCC0->PER.reg = 1599;                            // Set the frequency of the PWM on TCC0 to 30kHz: 48MHz / (1599 + 1) = 30kHz
  while (TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
 
  // The CCx register value corresponds to the pulsewidth in microseconds (us)
  TCC0->CC[1].reg = 800;                          // TCC0 CC1 - 50% duty cycle on D3
  while (TCC0->SYNCBUSY.bit.CC1);                 // Wait for synchronization
  TCC0->CC[0].reg = 800;                          // TCC0 CC0 - 50% duty cycle on D4
  while (TCC0->SYNCBUSY.bit.CC0);                 // Wait for synchronization
 
  TCC0->CTRLA.bit.ENABLE = 1;                     // Enable the TCC0 counter
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop()
{
  // Using buffered counter compare registers (CCBx)
  TCC0->CCB[1].reg = 400;                         // TCC0 CCB1 - 25% duty cycle on D3
  while (TCC0->SYNCBUSY.bit.CCB1);                // Wait for synchrnoization
  TCC0->CCB[0].reg = 400;                         // TCC0 CCB1 - 25% duty cycle on D4
  while (TCC0->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
  TCC0->CCB[1].reg = 1200;                        // TCC0 CCB1 - 75% duty cycle on D3
  while (TCC0->SYNCBUSY.bit.CCB1);                // Wait for synchrnoization
  TCC0->CCB[0].reg = 1200;                        // TCC0 CCB1 - 75% duty cycle on D4
  while (TCC0->SYNCBUSY.bit.CCB0);                // Wait for synchronization
  delay(1000);                                    // Wait for 1 second
}
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Nilab on Jun 25, 2019, 11:08 am
Sorry MartinL ! I am not to said! I use robotdyn m0-mini  robotdyn_arduino_zero_arduino_m0 (https://robotdyn.ru/catalog/arduino/boards/kontroller_samd21_m0_mini_32_bit_arm_cortex_m0_core_sovmestima_s_arduino_zero_arduino_m0.html)

in attachement Schematic
d3 connected to "pa09"
d4 connected to "pa08"
Is that means my d3 and d4 pin on peripheral E ? for timer tcc0?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 25, 2019, 01:25 pm
Hi Nilab

Quote
in attachement Schematic
d3 connected to "pa09"
d4 connected to "pa08"
Is that means my d3 and d4 pin on peripheral E ? for timer tcc0?
Yes that's right. In this instance the TCC0 channels 0 and 1 are now on peripheral E for pins D4 and D3 respectively, the same as the Arduino Zero.

Just replace the Peripheral Multiplexing (PMUX) lines in the above example with the following line:

Code: [Select]
// D3 is on ODD port pin PA09 and TCC0/WO[1] channel 1 is on peripheral E
// D4 is on EVEN port pin PA08 and TCCO/WO[0] channel 0 is on peripheral E
PORT->Group[g_APinDescription[3].ulPort].PMUX[g_APinDescription[3].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
Title: Re: Changing Arduino Zero PWM Frequency
Post by: Nilab on Jun 25, 2019, 08:10 pm
Thanx MartinL
But i have another question
Arduino Due have  simple interrrupt config by timer

Code: [Select]

Timer3.attachInterrupt(myHandler).setFrequency(10000).start();


How do that? in Zero ?
Title: Re: Changing Arduino Zero PWM Frequency
Post by: MartinL on Jun 26, 2019, 08:59 am
Hi Nilab,

Here's a link to an explanation of how to configure the TCC timer to handle interrupts, the description is on post #20: https://forum.arduino.cc/index.php?topic=589655.15 (https://forum.arduino.cc/index.php?topic=589655.15).

This disscussion is for PWM generation on the SAMD51 microcontroller, but the TCC interrupt code on the SAMD21 is identical.