Arduino Forum

Products => Arduino Due => Topic started by: buckboostbill on Feb 04, 2017, 05:12 pm

Title: Due PWM frequency
Post by: buckboostbill on Feb 04, 2017, 05:12 pm
I have a buck converter that needs 40kHz pwm. I have read almost every forum and article on the subject and I am still stuck. Everyone seems to have a different recipe with certain drawbacks or wants to push the limits just to see how fast of a fixed duty square wave they can generate.

This is a Due so I want to be able to retain the 10 bit pwm resolution or even use the 12 bit.

Most of the posts or articles are old and was wondering if there was a reasonable more accepted way of doing this?

Thanks
Title: Re: Due PWM frequency
Post by: MartinL on Feb 04, 2017, 06:23 pm
Hi buckboostbill,

The code below uses register manipulation to create a 40kHz, 50% duty cycle PWM signal on pin DAC1 (PWML0):

Code: [Select]
// Output a 40kHz PWM waveform at a resolution of 11-bits on pin DAC1 (PWML0)
void setup() {
  // PWM Set-up on pin: DAC1
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                  // Set the PWM frequency 84MHz/40kHz = 2100
  REG_PWM_CDTY0 = 1050;                                  // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

void loop() {}

This will give you 11-bit resolution, which is the best that can be achieved by the 84MHz Due at this (40kHz) frequency.
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 04, 2017, 07:10 pm
Thanks but a buck converter needs to be controlled with an analog input with variable duty cycle
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 04, 2017, 07:15 pm
It also needs to be on the pwm channels. I need several outputs. If it is really this hard to do this I guess I could settle with one of the standard values and design my magnetics around that. I believe 32kHz is an option??
Title: Re: Due PWM frequency
Post by: MartinL on Feb 04, 2017, 07:56 pm
Hi buckboostbill,

Quote
Thanks but a buck converter needs to be controlled with an analog input with variable duty cycle.
What do you mean by analog input, if you require the Due to produce a PWM output?

The duty cycle can be changed by loading the buffered duty cycle update register REG_PWM_CDTYUPD0 with a value between 0 and 2100, (0 to 100% duty cycle). Using the buffered register outputs the signal on the next timer cycle, preventing changes from causing glitches on your outputs:

Code: [Select]
REG_PWM_CDTYUPD0 = 1050;
Quote
It also needs to be on the pwm channels. I need several outputs.
This is one of the PWM channels, channel 0. It might be on the DAC1 pin, but the pins on the SAM3X8E microcontroller have multiple functions.

The PWM controller used on the Due has 8 channels. The following code outputs a 40kHz signal on all 8 channels. I've commented out the code that'll allow you to also synchronize these channels:

Code: [Select]
// Enable single slope, 11-bit resolution PWM at 40kHz on 8 channels
void setup() {
  // PWM set-up on pins DAC1, A8, A9, A10, D9, D8, D7 and D6 for channels 0 through to 7 respectively
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                                               // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P19 | PIO_ABSR_P18 | PIO_ABSR_P17 | PIO_ABSR_P16;     // Set the port B PWM pins to peripheral type B
  REG_PIOC_ABSR |= PIO_ABSR_P24 | PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21;     // Set the port C PWM pins to peripheral type B
  REG_PIOB_PDR |= PIO_PDR_P19 | PIO_PDR_P18 | PIO_PDR_P17 | PIO_PDR_P16;          // Set the port B PWM pins to outputs
  REG_PIOC_PDR |= PIO_PDR_P24 | PIO_PDR_P23 | PIO_PDR_P22 | PIO_PDR_P21;          // Set the port C PWM pins to outputs
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);                                // Set the PWM clock A rate to 84MHz (84MHz/1)
  //REG_PWM_SCM |= PWM_SCM_SYNC7 | PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4 |  // Set the PWM channels as synchronous
  //               PWM_SCM_SYNC3 | PWM_SCM_SYNC2 | PWM_SCM_SYNC1 | PWM_SCM_SYNC0; 
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CMR =  PWM_CMR_CPRE_CLKA;                  // Enable single slope PWM and set the clock source as CLKA
    PWM->PWM_CH_NUM[i].PWM_CPRD = 2100;                               // Set the PWM period register 84MHz/(40kHz)=2100;
  }
  //REG_PWM_ENA = PWM_ENA_CHID0;           // Enable the PWM channels, (only need to set channel 0 for synchronous mode)
  REG_PWM_ENA = PWM_ENA_CHID7 | PWM_ENA_CHID6 | PWM_ENA_CHID5 | PWM_ENA_CHID4 |    // Enable all PWM channels
                PWM_ENA_CHID3 | PWM_ENA_CHID2 | PWM_ENA_CHID1 | PWM_ENA_CHID0;
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTYUPD = 1050;                            // Set the PWM duty cycle to 50% (2100/2=1050) on all channels
  }
  //REG_PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop() {}
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 04, 2017, 08:33 pm
Thanks appreciate it.. I will try this right now. You can basically just pretend I'm controlling duty cycle from 0 to 100 with a pot using analog input A0,3.3, and gnd.

A buck converter will have a voltage divider from its output creating a 0-3.3 voltage range. When there are load variations the pulses needed to rapidly change duty cycle in order to maintain a constant output.
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 04, 2017, 08:48 pm
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 9; // Analog output pin that the LED is attached to

int sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)

void setup() {

 }

void loop() {
  // read the analog in value:
  sensorValue = analogRead(analogInPin);
  // map it to the range of the analog out:
  outputValue = map(sensorValue, 0, 1023, 0, 255);
  // change the analog out value:
  analogWrite(analogOutPin, outputValue);

}



This is what I am using now and it works beautifully at 1kHz..just want to change the frequency
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 04, 2017, 09:38 pm
MartinL that did work and i have a perfect 40kHz output according to my scope. Now how do I control the duty cycle with a pot?
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 06, 2017, 07:08 am
This works perfectly on my uno. I know this is a different timer. Any way to use simple prescaler change like I did in this example for an individual channel?

Thanks

int PWMpin = 9;      // PWM connected to digital pin 9
int Feedback = 0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value

void setup()

{
  TCCR1B = TCCR1B & B11111000 | B00000001;
  pinMode(PWMpin, OUTPUT);   // sets the pin as output
}

void loop()

{
  val = analogRead(Feedback);   // read the input pin
  analogWrite(PWMpin, val / 4);  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}
Title: Re: Due PWM frequency
Post by: MartinL on Feb 06, 2017, 12:57 pm
Quote
Any way to use simple prescaler change like I did in this example for an individual channel?
Yes, on the Due it's possible to change the prescaler for each timer channel using the PWM Channel Mode Register (REG_PWM_CMRx).

This divides down the Due's master clock running at 84MHz:

To divide down by 2 on channel 0:

Code: [Select]
REG_PWM_CMR0 = PWM_CMR_CPRE_MCK_DIV_2;
...or alternatively:

Code: [Select]
PWM->PWM_CH_NUM[i].PWM_CMR = PWM_CMR_CPRE_MCK_DIV_2;
... does the same thing, but is useful if you're iterating throught a number of channels.

It's possible to divide down by 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, just by changing the divisor.

If you require more refinement to your clock frequency, it's possible instead to divide down the master clock (84MHz) using one of two clock sources: CLKA or CLKB, like I did with CLKA in the example above:

Code: [Select]
REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;
You can then optionally set a prescaler and divisor (or both) for either CLKA or CLKB in the PWM Clock Register (REG_PWM_CLK), for example:

Code: [Select]
REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42);    // Set the PWM clock prescaler and divisor (84MHz/42)
If DIVA or DIVB are 0 CLKA and CLKB are turned off respectively.
If DIVA or DIVB are 1 the respective clocks are prescaled by PREA or PREB.
If DIVA or DIVB are between 2-255 the CLKA and CLKB are divided by PREA or PREB then divided by DIVA or DIVB.

The prescalers PREA and PREB are 4-bit values that range from 0 to 15 and correspond to a prescaler of 1 through to 1024. For example PWM_CLK_PREA(5) will divide the master clock (84MHz) by 32.

Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 06, 2017, 03:43 pm
Thank you! Exactly what I wanted.

I've been referring to the pwm p. 970 in the atmel manual and understand the function. I just don't understand how to turn it into code. Where is the file that we are manipulating? If I could see what is in there by default I feel like I could figure things out..
Title: Re: Due PWM frequency
Post by: MartinL on Feb 06, 2017, 04:14 pm
The PWM register definitions for the Due are stored in two Atmel files.

One file resides in the "instance" directory and includes the definitions for the peripherals registers themselves.  The other can be found in the neighbouring "component" directory and details the registers' individual bit/bitfield structures together with their associated definitions.

These can currently be found, (at least on my Windows machine) at:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\tools\CMSIS\4.0.0-atmel\Device\ATMEL\sam3xa\include\instance\pwm.h

and

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\tools\CMSIS\4.0.0-atmel\Device\ATMEL\sam3xa\include\component\pwm.h
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 07, 2017, 07:32 am
Code: [Select]
int PWMpin = 9;      // PWM connected to digital pin 9
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value



void setup()
{

  REG_PWM_CMR0 = PWM_CMR_CPRE_MCK_DIV_2;
 
  pinMode(PWMpin, OUTPUT);   // sets the pin as output
 
}

void loop()

{

  val = analogRead(Feedback);   // read the input pin
  analogWrite(PWMpin, val / 4);  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255

}


So I just inserted the code as is and messed around with both my PWMpin (PWM2-13) and atmel CMR0-CMR7 and the frequency remained 1kHz.

What arduino PWM pin is  REG_PWM_CMR0?
Title: Re: Due PWM frequency
Post by: MartinL on Feb 07, 2017, 10:15 am
Quote
What arduino PWM pin is  REG_PWM_CMR0?
The REG_PWM_CMR0 register is just the Channel Mode Register for channel 0. However, the designation what channel is assigned to what pin is determined by the PIO Controller Multiplexing tables in the SAM3X8E's datasheet. In the old datasheet it was in section 11 and in the new one it's in section 9: "Peripheral Signal Multiplexing on I/O lines".

There are 4 PIO Controller Multiplexing tables, one for each port: A, B, C and D. Furthermore, each pin can be specified as either GPIO, peripheral A, peripheral B or an extra function, for example analog IO. Channel 0 of the PWM controller can be found on peripherals pins PWML0 or its inverse PWMH0. Channel 1 on PWML1 and so on... To enable the PWM channel 0 as an output, it's necessary to:

1. First enable the peripheral's clock using the Peripheral Clock Enable Register (PCER), in the case of the PWM controller, peripheral ID 36:

Code: [Select]
REG_PMC_PCER1 |= PMC_PCER1_PID36;
2. Set the given port pin to peripheral A or peripheral B using the Peripheral AB Select Register (ABSR), (the register's detailed in the Parallel IO Controller (GPIO) section of the datasheet):

Code: [Select]
REG_PIOB_ABSR |= PIO_ABSR_P16;
3. Finally disable the GPIO for the given port pin using the Port Disable Register (PDR), (again this register's detailed in the Parallel IO Controller section):

Code: [Select]
REG_PIOB_PDR |= PIO_PDR_P16;
In my example I've chosen the DAC1 pin, but the PWML0 output is also assigned to peripheral B on pin D34, aka Port C, pin2 or PC2.
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 07, 2017, 04:34 pm

I guess you can't teach a man to fish.. just feed me I'm just not getting it. I still can't get this to work. OAgain I'm going to use 5 of these outputs, 4 fixed duty cycle for an H-bridge power converter and the main one I'm concerned with now which is an earlier buck stage in which I need to control the pulse widths with "a pot".

I was hoping to still use the arduino language like I did with the uno. I would prefer using the pwm pins. This is what I tried. Can you alter this to work?

int PWMpin = DAC1;      // PWM connected to digital dac1
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value


Code: [Select]
int PWMpin = DAC1;      // PWM connected to digital dac1
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value



void setup()
{
  REG_PMC_PCER1 |= PMC_PCER1_PID36;
  REG_PIOB_ABSR |= PIO_ABSR_P16;
  REG_PIOB_PDR |= PIO_PDR_P16;
  REG_PWM_CMR0 = PWM_CMR_CPRE_MCK_DIV_2;
  pinMode(PWMpin, OUTPUT);   // sets the pin as output
 
}

void loop()

{

  val = analogRead(Feedback);   // read the input pin
  analogWrite(PWMpin, val / 4);  // analogRead values go from 0 to 1023, analogWrite values from 0 to 255

}
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 07, 2017, 05:04 pm
This may help you understand where I am coming from..

I don't get why you would have to start from the ground up like enabling pwm. Isn't that done when you select an output with analogWrite?
Title: Re: Due PWM frequency
Post by: MartinL on Feb 07, 2017, 06:08 pm
Quote
I don't get why you would have to start from the ground up like enabling pwm. Isn't that done when you select an output with analogWrite?
The issue with analogWrite() is that it only gives you a fixed PWM frequency and an 8-bit resolution. It  doesn't really harness the full potential of the SAM3X8E's PWM controller.
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 07, 2017, 06:46 pm
Understood. I will need to change my approach. I will code with the atmel language.

After your last few posts I now fully understand this. Works great.
Code: [Select]
// Output a 40kHz PWM waveform at a resolution of 11-bits on pin DAC1 (PWML0)
void setup() {
  // PWM Set-up on pin: DAC1
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                  // Set the PWM frequency 84MHz/40kHz = 2100
  REG_PWM_CDTY0 = 1050;                                  // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

void loop() {}


0-2100 resolution is much better suited to my project.

I now need to figure out how to make REG_PWM_CDTY0 = the value from an ADC input with 0-2100 resolution instead of a fixed 1050..
Title: Re: Due PWM frequency
Post by: buckboostbill on Feb 07, 2017, 07:26 pm
I at least have 10bit ADC input now

Code: [Select]
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value

void setup() {
  // PWM Set-up on pin: DAC1
   
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                  // Set the PWM frequency 84MHz/40kHz = 2100                                   // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

void loop()
{
val = analogRead(Feedback);
REG_PWM_CDTY0 = val * 2;
}
Title: Re: Due PWM frequency
Post by: dlloyd on Feb 07, 2017, 07:54 pm
Try this for 11-bit. Note that the duty cycle range is about 0-97.5%
Code: [Select]
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value

void setup() {
  // PWM Set-up on pin: DAC1

  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                 // Set the PWM frequency 84MHz/40kHz = 2100                                   // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel

  analogReadResolution(11);                             // 11-bit, 0-2048
}

void loop()
{
  val = analogRead(Feedback);
  REG_PWM_CDTY0 = val;
}
Title: Re: Due PWM frequency
Post by: antodom on Feb 08, 2017, 06:50 pm
Hi there @buckboostbill,

Maybe you will find interesting to have a look to pwm_lib: https://github.com/antodom/pwm_lib (https://github.com/antodom/pwm_lib)

This library abstract the use of the eight hardware PWM channels available on Arduino DUE's Atmel ATSAM3X8E microcontroller. I think you can generate easily the 40 kHz PWM signals that you need using pwm_lib.

Have a look to the exampes that come with the library, and if in doubt just ask me.

I hope it helps.
Title: Re: Due PWM frequency
Post by: tcorkum on Feb 21, 2017, 11:40 pm
Try this for 11-bit. Note that the duty cycle range is about 0-97.5%
Code: [Select]
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value

void setup() {
  // PWM Set-up on pin: DAC1

  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                 // Set the PWM frequency 84MHz/40kHz = 2100                                   // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel

  analogReadResolution(11);                             // 11-bit, 0-2048
}

void loop()
{
  val = analogRead(Feedback);
  REG_PWM_CDTY0 = val;
}

Can I use this approach to get a frequency of 200KHz?
Title: Re: Due PWM frequency
Post by: dlloyd on Feb 22, 2017, 12:12 am
Sure. At 200kHz PWM, you'll get 420 steps for duty cycle.
Code: [Select]
REG_PWM_CPRD0 = 420;  //Set PWM frequency to 200kHz (84000000/200000 = 420)
Title: Re: Due PWM frequency
Post by: mbahjiman on Oct 18, 2017, 07:11 am
hi, i don't know whether i should start a new topic or just follow this, how can i produce 8 bursts of 40khz frequency, and does this have effects on other application, micros(), delay (), etc. Thank you
Title: Re: Due PWM frequency
Post by: antodom on Oct 18, 2017, 10:42 am
Hi there @mbahjiman,

Using pwm_lib (https://github.com/antodom/pwm_lib (https://github.com/antodom/pwm_lib)) you can easily generate eigth PWM independent outputs at 40 KHz. Have a look to the examples coming with the library.

In relation to micros() and delay() the library does not have any effect on them. Have a look to the README.md file coming with pwm_lib to know possible incompatibilies, the main one is with using analogWrite().
Title: Re: Due PWM frequency
Post by: mbahjiman on Oct 19, 2017, 08:40 am
Thanks Antodom, I was able to generate the wave with desired frequency, but I still don't know how am I going to detect 8 outputs, is that the work of get_period()? if it is, can I use them in if conditional?
Title: Re: Due PWM frequency
Post by: antodom on Oct 19, 2017, 11:26 am
Hi there again @mbahjiman,

Have a look to example basic_test.ino which comes with pwm_lib. This example shows you how to generate two PWM signals. You just have to add the other six signals to this exampe to generate the eight signals you want. Take into account that to know which pins and signals you can use, you must utilize the enum values you can find in pwm_def.h (explained also in file README.md):

    enum class pwm_pin: uint32_t
    {
      // PWM pins listed on Table 38-2 for Atmel ATSAM3X8E
      // datasheet available in www.atmel.com
      // NOTE: only it is possible to use one pin for each
      // PWM channel
      PWMH0_PA8 , // PWM_CH0
      PWMH0_PB12, // PWM_CH0
      PWMH0_PC3 , // PWM_CH0
      PWML0_PA21, // PWM_CH0
      PWML0_PB16, // PWM_CH0
      PWML0_PC2 , // PWM_CH0
      
      PWMH1_PA19, // PWM_CH1
      PWMH1_PB13, // PWM_CH1
      PWMH1_PC5 , // PWM_CH1
      PWML1_PA12, // PWM_CH1
      PWML1_PB17, // PWM_CH1
      PWML1_PC4 , // PWM_CH1
      
      PWMH2_PA13, // PWM_CH2
      PWMH2_PB14, // PWM_CH2
      PWMH2_PC7 , // PWM_CH2
      PWML2_PA20, // PWM_CH2
      PWML2_PB18, // PWM_CH2
      PWML2_PC6 , // PWM_CH2
      
      PWMH3_PA9 , // PWM_CH3
      PWMH3_PB15, // PWM_CH3
      PWMH3_PC9 , // PWM_CH3
      PWML3_PA0 , // PWM_CH3
      PWML3_PB19, // PWM_CH3
      PWML3_PC8 , // PWM_CH3
      
      PWMH4_PC20, // PWM_CH4
      PWML4_PC21, // PWM_CH4
      
      PWMH5_PC19, // PWM_CH5
      PWML5_PC22, // PWM_CH5
      
      PWMH6_PC18, // PWM_CH6
      PWML6_PC23, // PWM_CH6
      
      PWML7_PC24  // PWM_CH7
    };

For some channels you can chose between several pins, except for channel 7. Each channel is independent, and remember that you can ONLY use ONE pin per channel. To define each pwm object you has to put as template argument the corresponding enum value for the output pin you want for each PWM signal.

I hope it helps.
Title: Re: Due PWM frequency
Post by: RustamAxm on Nov 15, 2017, 12:24 pm
Hi buckboostbill,

What do you mean by analog input, if you require the Due to produce a PWM output?

The duty cycle can be changed by loading the buffered duty cycle update register REG_PWM_CDTYUPD0 with a value between 0 and 2100, (0 to 100% duty cycle). Using the buffered register outputs the signal on the next timer cycle, preventing changes from causing glitches on your outputs:

Code: [Select]
REG_PWM_CDTYUPD0 = 1050;
This is one of the PWM channels, channel 0. It might be on the DAC1 pin, but the pins on the SAM3X8E microcontroller have multiple functions.

The PWM controller used on the Due has 8 channels. The following code outputs a 40kHz signal on all 8 channels. I've commented out the code that'll allow you to also synchronize these channels:

Code: [Select]
// Enable single slope, 11-bit resolution PWM at 40kHz on 8 channels
void setup() {
  // PWM set-up on pins DAC1, A8, A9, A10, D9, D8, D7 and D6 for channels 0 through to 7 respectively
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                                               // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P19 | PIO_ABSR_P18 | PIO_ABSR_P17 | PIO_ABSR_P16;     // Set the port B PWM pins to peripheral type B
  REG_PIOC_ABSR |= PIO_ABSR_P24 | PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21;     // Set the port C PWM pins to peripheral type B
  REG_PIOB_PDR |= PIO_PDR_P19 | PIO_PDR_P18 | PIO_PDR_P17 | PIO_PDR_P16;          // Set the port B PWM pins to outputs
  REG_PIOC_PDR |= PIO_PDR_P24 | PIO_PDR_P23 | PIO_PDR_P22 | PIO_PDR_P21;          // Set the port C PWM pins to outputs
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);                                // Set the PWM clock A rate to 84MHz (84MHz/1)
  //REG_PWM_SCM |= PWM_SCM_SYNC7 | PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4 |  // Set the PWM channels as synchronous
  //               PWM_SCM_SYNC3 | PWM_SCM_SYNC2 | PWM_SCM_SYNC1 | PWM_SCM_SYNC0; 
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CMR =  PWM_CMR_CPRE_CLKA;                  // Enable single slope PWM and set the clock source as CLKA
    PWM->PWM_CH_NUM[i].PWM_CPRD = 2100;                               // Set the PWM period register 84MHz/(40kHz)=2100;
  }
  //REG_PWM_ENA = PWM_ENA_CHID0;           // Enable the PWM channels, (only need to set channel 0 for synchronous mode)
  REG_PWM_ENA = PWM_ENA_CHID7 | PWM_ENA_CHID6 | PWM_ENA_CHID5 | PWM_ENA_CHID4 |    // Enable all PWM channels
                PWM_ENA_CHID3 | PWM_ENA_CHID2 | PWM_ENA_CHID1 | PWM_ENA_CHID0;
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTYUPD = 1050;                            // Set the PWM duty cycle to 50% (2100/2=1050) on all channels
  }
  //REG_PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop() {}


Can you explain it to me? I can use only 8 PWM pins. And can I add 2 pins, for example 2 pins D5 and DAC1?
In my code I add pins ( PIO_ABSR_P15 and PIO_ABSR_P25), but I don't understand how use 10 PWM pins, because in DUE we have only 8 PWM channels (PWM_ENA_CHIDx). 
Title: Re: Due PWM frequency
Post by: MartinL on Nov 15, 2017, 06:07 pm
Hi RustamAxm,

Each pin on the Arduino Due's SAM3X8E microcontroller can either be General Purpose Input Output (GPIO), or alternatively can be switched to one of two peripherals: either A or B. The peripherals being analog IO, timer IO, PWM, Serial, CAN, etc...

The different peripherals are allocated to a number of specified pins, defined in the PIO Controller Multiplexing tables in the "Peripherals" section of the SAM3X8E datasheet. There are four tables one for each of the microcontroller's ports: A, B, C and D.

Like you say, the PWM Controller peripheral has 8 channels. Each channel has both low and high side (non-inverting and inverting) outputs labelled: PWMLx and PWMHx, (where x is the channel number), that are listed in the table.

To get the PWM controller going it's first necessary to enable it (REG_PMC_PCER1), specify the peripheral either A or B (REG_PIOx_ABSR) for each pin, (in this case ports B and C and the low side PWMLx pins are selected) and finally set the corresponding pins as outputs (REG_PIOx_PDR).

To use the code it's easiest to lookup your required outputs in the PIO Controller Multiplexing tables and comment out or add channel outputs you require.

As the PWM controller has only 8 channels, further PWM outputs would require you to employ the TC timers. They're also PWM capable, but less fully featured.
Title: Re: Due PWM frequency
Post by: sepehrdidevar on Jun 03, 2018, 01:19 pm
I at least have 10bit ADC input now

Code: [Select]
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value

void setup() {
  // PWM Set-up on pin: DAC1
   
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                  // Set the PWM frequency 84MHz/40kHz = 2100                                   // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

void loop()
{
val = analogRead(Feedback);
REG_PWM_CDTY0 = val * 2;
}

 

why did you write  the val*2 ?
and what is the "feedback" value?
is it converter output voltage ?
Title: Re: Due PWM frequency
Post by: sepehrdidevar on Jun 03, 2018, 01:24 pm
I at least have 10bit ADC input now

Code: [Select]
int Feedback = A0;   // feedback connected to pin 0
int val = 0;         // variable to store the read value

void setup() {
  // PWM Set-up on pin: DAC1
   
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                  // Set the PWM frequency 84MHz/40kHz = 2100                                   // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel     
}

void loop()
{
val = analogRead(Feedback);
REG_PWM_CDTY0 = val * 2;
}

 

why did you write  the val*2 ?
and what is the "feedback" value?
is it converter output voltage ?
Title: Re: Due PWM frequency
Post by: generatorlabs on May 26, 2019, 10:11 pm
MartinL,

Thank you so much for that snippet of code. It works very well. The DUE is a completely different beast from the AVR stuff and trying to move a project from a UNO to the DUE has proven to be very challenging. Even looking up these completely different timer registers is a daunting task.

I have seen a library that can do some of what you have shared. I would like to keep the code as simple as possible. My needs are simple. I need two fixed frequency PWM clocks to drive external chips. They do not have to be super precise and they will never change. I need approx 104kHz and 150kHz on each clock.

I would like to ask, what must I change/add in the code snippet to have a second timer with its own PWM freq and Duty on another unused pin, like DAC2?

Thanks for any help.

Hi buckboostbill,

The code below uses register manipulation to create a 40kHz, 50% duty cycle PWM signal on pin DAC1 (PWML0):

Code: [Select]
// Output a 40kHz PWM waveform at a resolution of 11-bits on pin DAC1 (PWML0)
void setup() {
  // PWM Set-up on pin: DAC1
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                     // Enable PWM
  REG_PIOB_ABSR |= PIO_ABSR_P16;                        // Set PWM pin perhipheral type A or B, in this case B
  REG_PIOB_PDR |= PIO_PDR_P16;                          // Set PWM pin to an output
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);      // Set the PWM clock rate to 84MHz (84MHz/1)
  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;                     // Enable single slope PWM and set the clock source as CLKA
  REG_PWM_CPRD0 = 2100;                                  // Set the PWM frequency 84MHz/40kHz = 2100
  REG_PWM_CDTY0 = 1050;                                  // Set the PWM duty cycle 50% (2100/2=1050)
  REG_PWM_ENA = PWM_ENA_CHID0;                          // Enable the PWM channel    
}

void loop() {}

This will give you 11-bit resolution, which is the best that can be achieved by the 84MHz Due at this (40kHz) frequency.