[solved]PWM inittiation on registers

Hello all, I have read many topics but none of them suited me 100%. On most topics there are plenty of lines of code that (probably) I do not need. I would like to get PWM signal with 12 bit resolution and a 100kHz frequency on pin 3. How to do it in the simplest way? I do not mean to modify the variant.h file.

Please help and greet :)

A few thoughts:

For a 100 KHz frequency, with the Timer Counter controller, the max resolution will be 42MHZ/100KHz = 420, and with a PWM(H or L) the max resolution will be 84MHz/100KHz = 840 ---> far from a 12 bit resolution = 2 exp12 = 4096.

The second point is that Arduino pin 3 is TIOA7 ---> It is the output of the Timer Counter 2 channel1 (TC7) limited to a max resolution of 420.

So it is impossible? I thought that the Arduino Due is a very powerful - in arduino uno (atmega328) its no problem to make pwm 10bit about 65kHz. Maybe on another pin ?

lolakr:
in arduino uno (atmega328) its no problem to make pwm 10bit about 65kHz.

I don’t think so. 10-bit resolution gives you 2 exp10 = 1024, and 16 MHz/1024 = 15625 Hz, far from 65 KHz.

The main point to consider is the precision you need. With a DUE, and a PWM frequency of 100 KHz, your duty will be programmable in a range between 0 and 840. The PWM_CDTY register is a 23_bit register, although only the first 16-bit are significant, AND 0<PWM_CDTY<PWM_CPRD = 840 in this example.

See Sam3x datasheet page 1046.

Ok, I understand.

ard_newbie: I don't think so. 10-bit resolution gives you 2 exp10 = 1024, and 16 MHz/1024 = 15625 Hz, far from 65 KHz.

Sorry for this mistake. This value probably was related to the ADC.

So I have to reduce the frequency or resolution? Maybe new example ~82kHz and 10bit resolution. Now it is correct with the equation. What is the diffrence of PWM(H or L) and what is the difference with PWM and Timer Counter controller ?

The most obvious difference is that the Timer Counter controller produces PWM pulses thru TIOAx/TIOBx pins and the PWM controller produces PWM pulses thru PWMHx/PWMLx pins (see pinout diagram).

There are important differences, e.g.: the TC controller provides a quadrature decoder feature and the PWM controller has a synchro feature.

Read (several times) Timer Counter and PWM sections of Sam3x datasheet.

This is over 50 pages but I will try as long as necessary :slight_smile:
Sam3x8 is a very powerful uC and I did not expect so many possibilities of configuration.
So far, I have come up with something like that:

PWM->PWM_CLK = 0x10001;
PWM->PWM_CPRD0 = 1024;
PWM->PWM_CDTY0 = 0;
PWM->PWM_ENA = 1;

Is it a good way? Please help, it is not easy for a beginner :slight_smile:

Header files for all peripherals are included in your IDE, it is much better to use them rather than magic numbers.

For every peripheral you are using, you need first to power on, then (for safety) you disable the part of the peripheral you will be using, then you set all registers according to your requirements, and finally you enable the part of the peripheral you selected.

As an example, I choose PWML2 that you can find on your pinout diagram on arduino pin 43. I selected a 100 KHz frequency (note that you have several possibilities to achieve this frequency depending on the divisor DIVA), I selected a duty cycle of 50%.

/*******************************************************************************/
/*          PWML2 on Arduino pin 43 - F = 100 KHz , Duty cycle = 50%           */
/******************************************************************************/

void setup () {
  
  // Datasheet page 973
  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral=B
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                    // PWM power on , see datasheet page 38
  
  PWM->PWM_DIS = PWM_DIS_CHID2;         // Disable PWM channel 2

  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral=B
  PMC->PMC_PCER0 |= PMC_PCER0_PID11;                    // PIOA power on
  
  PIOA->PIO_PDR |= PIO_PDR_P20;                         // The GPIO don't drive the pin, this is the peripheral

  PIOA->PIO_ABSR |= PIO_ABSR_P20;                       // Set PWM pin perhipheral type B , datasheet page 974

  // Set registers for PWM channel 2 
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);    // Set the PWM clock rate to 84MHz (84MHz/1). Adjust DIVA for the resolution you are looking for

  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_CPRE_CLKA;     // The period is left aligned, clock source as CLKA on channel 2

  PWM->PWM_CH_NUM[2].PWM_CPRD = 840;                  // Channel 2 : Set the PWM frequency (84MHz/1)/PWM_CPRD = 100KHz ;

  PWM->PWM_CH_NUM[2].PWM_CDTY = 420;                  // Channel 2: Set the PWM duty cycle to x%= (CDTY/ CPRD)  * 100 % = 50%;

  PWM->PWM_ENA = PWM_ENA_CHID2;

  // Alternately, you can use this format :  REG_PWM_CPRD2 = 840;

}


void loop() {
  
}

Thank you very much :) What if I want to simultaneously use PWML2 PWMH2? Are they dependent? And if I wanted to use another pwm channel should it look like that:

void setup () {

  // Datasheet page 973
  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral=B
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                    // PWM power on , see datasheet page 38

  PWM->PWM_DIS = PWM_DIS_CHID2;         // Disable PWM channel 2

  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral=B
  PMC->PMC_PCER0 |= PMC_PCER0_PID11;                    // PIOA power on

  PIOA->PIO_PDR |= PIO_PDR_P20;                         // The GPIO don't drive the pin, this is the peripheral

  PIOA->PIO_ABSR |= PIO_ABSR_P20;                       // Set PWM pin perhipheral type B , datasheet page 974

  // Set registers for PWM channel 2
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);    // Set the PWM clock rate to 84MHz (84MHz/1). Adjust DIVA for the resolution you are looking for

  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_CPRE_CLKA;     // The period is left aligned, clock source as CLKA on channel 2

  PWM->PWM_CH_NUM[2].PWM_CPRD = 840;                  // Channel 2 : Set the PWM frequency (84MHz/1)/PWM_CPRD = 100KHz ;

  PWM->PWM_CH_NUM[2].PWM_CDTY = 420;                  // Channel 2: Set the PWM duty cycle to x%= (CDTY/ CPRD)  * 100 % = 50%;

  PWM->PWM_ENA = PWM_ENA_CHID2;

  // Alternately, you can use this format :  REG_PWM_CPRD2 = 840;


PWM->PWM_DIS = PWM_DIS_CHID3;  
// Select Instance=PWM; Signal=PWMH3 (channel 3); I/O Line=PA9
PIOA->PIO_PDR |= PIO_PDR_PA9;
PIOA->PIO_ABSR |= PIO_ABSR_PA9;
PWM->PWM_CH_NUM[3].PWM_CMR = PWM_CMR_CPRE_CLKA;
PWM->PWM_CH_NUM[3].PWM_CPRD = 840;
PWM->PWM_CH_NUM[2].PWM_CDTY = 420;
PWM->PWM_ENA = PWM_ENA_CHID3;

And now when I want to modify duty cycle in loop() i have to change only this ? e.g. PWM->PWM_CH_NUM[2].PWM_CDTY = 300;

You can use simultaneously PWMHx/PWMLx provided you set properly the I/O lines.

Your code for PWMH3 seems correct, although you choose PA9 which is the TX0 for Serial (RX/TX). Better choose e.g. PC9, Arduino pin 42.

Writing in PWM_CDTYx register is possible while the channel is disabled. After validation of the channel, you must use PWM_CDTYUPDx register to update the duty cycle idem for CPRD with PWM_CPRDUPDx.

void setup () {
 
  // Datasheet page 973
  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral=B
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                    // PWM power on , see datasheet page 38
 
  PWM->PWM_DIS = PWM_DIS_CHID2;         // Disable PWM channel 2

  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral=B
  PMC->PMC_PCER0 |= PMC_PCER0_PID11;                    // PIOA power on
 
  PIOA->PIO_PDR |= PIO_PDR_P20;                         // The GPIO don't drive the pin, this is the peripheral

  PIOA->PIO_ABSR |= PIO_ABSR_P20;                       // Set PWM pin perhipheral type B , datasheet page 974

  // Set registers for PWM channel 2
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);    // Set the PWM clock rate to 84MHz (84MHz/1). Adjust DIVA for the resolution you are looking for

  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_CPRE_CLKA;     // The period is left aligned, clock source as CLKA on channel 2

  PWM->PWM_CH_NUM[2].PWM_CPRD = 1024;                  // Channel 2 : Set the PWM frequency (84MHz/1)/PWM_CPRD = 100KHz ;

  PWM->PWM_CH_NUM[2].PWM_CDTY = 0;                  // Channel 2: Set the PWM duty cycle to x%= (CDTY/ CPRD)  * 100 % = 50%;

  PWM->PWM_ENA = PWM_ENA_CHID2;

  // Alternately, you can use this format :  REG_PWM_CPRD2 = 840;


PWM->PWM_DIS = PWM_DIS_CHID3; 
// Select Instance=PWM; Signal=PWMH3 (channel 3); I/O Line=PC9
 PMC->PMC_PCER0 |= PMC_PCER0_PID13;
PIOC->PIO_PDR |= PIO_PDR_P9;
PIOC->PIO_ABSR |= PIO_ABSR_P9;
    // Set the PWM clock rate to 84MHz (84MHz/1). Adjust DIVA for the resolution you are looking for

PWM->PWM_CH_NUM[3].PWM_CMR = PWM_CMR_CPRE_CLKA;
PWM->PWM_CH_NUM[3].PWM_CPRD = 1024;
PWM->PWM_CH_NUM[3].PWM_CDTY = 0;
PWM->PWM_ENA = PWM_ENA_CHID3;
}
void loop() {
  // put your main code here, to run repeatedly:
  for (int i =0 ; i<1024;i++)
  {
  REG_PWM_CDTYUPD2=i;
  REG_PWM_CDTYUPD3=i;
  delay(50);
  }
}

I started this code and the signal on PA9 is inverted relative to PA20 and on PC9 there is no signal. Where did I make a mistake?

You first enable PWM channel 2 with PWM->PWM_ENA = PWM_ENA_CHID2; then you enable PWM channel 3 with PWM->PWM_ENA = PWM_ENA_CHID3; Therefore channel 2 is no more enable !!

Edit : your sketch works fine, code is correct !!

I'm not sure. Because of channel 2 is disabled it should not be signal on PA20? In order to rapair this I should change this

PWM-> PWM_ENA = PWM_ENA_CHID3;

to PWM-> PWM_ENA | = PWM_ENA_CHID3; ? Why there are inverted? I wanted to do 2 non-inverted,Independent channels on PA20 and PC9.

PWM High x is the invert of PWM Low x. When the output of PWMH2 = 1, the output of PWML2 = 0.

If you want two non inverted signals, select e.g. PWMH2 (pin 39) and PWMH3 (pin 41) with same clock divisor and prescaler.

So there is no way to set PWMH 1 to a 30% duty cycle and PWML1 to 30% duty cycle in the same time?
And when I set:

PWM_CPRD = 1024;
PWM_CDTY = 256;

25% duty cycle
for PWMH it mean 25% LOW , 75% HIGH
When it’s PWML its 25% HIGH, 75% LOW?

If you want two PWM outputs with the same frequency and duty cycle, you need two channels.

PWMH/PWML inverted is very useful in 2-bit Gray Up/Down Counter. Note that figure 38.6 page 981 of Sam3x datasheet is wrong, you will find a correct figure page 962 of Sam4S datasheet (The two uc share a very similar PWM controller).

I read again the datasheet and a "0" has no effect on PWM_DIS and PWM_ENA, only a "1", your code is correct.

Ok, I understand. I was looking on bad column on pinout diagram from this forum :P (fail :/ ) When I want to set peripheral type A Should i do it this way? e.g.

PIOE->PIO_ABSR &= ~(PIO_ABSR_PE15);

Do you know where(In what file?) all these declarations(e.g. PMC_PCER1_PID36) are ?

Header files :

Here

And PIOE->PIO_ABSR &= ~(PIO_ABSR_P15); is the way to select peripheral type A for PE15, but all pins are not broken out, AFAIK you can only access to PIOA, B, C and D.

Some 3rd party arduino DUE compatible boards have more broken out pins like this one:

Now I can boldly experiment. Thank you for your help and full commitment :)

lolakr: So there is no way to set PWMH 1 to a 30% duty cycle and PWML1 to 30% duty cycle in the same time? And when I set:

PWM_CPRD = 1024;
PWM_CDTY = 256;

25% duty cycle for PWMH it mean 25% LOW , 75% HIGH When it's PWML its 25% HIGH, 75% LOW?

PWMH1 and PWML1 are complementary outputs. If PWMH1 is high at the moment, PWML1 is low and vice versa. You do not set the duty cycle of PWMH1 and PWML1 independently. You set the duty cycle of PWM Channel 1. Your two parameters listed PWM_CPRD and PWM_CDTY only tell part of the story. I'm assuming for the time being your waveform is left aligned with your period length being 1024 clock cycles. The PWMHx starting state (high/low) is defined by a flag in the parameter PWM_CMRx when the counter starts. The PWMHx output will then switch states when the count reaches 256. When the count reached 1024, we cycle back to the beginning.

I have already addressed the issue back in March in the article: http://forum.arduino.cc/index.php?topic=386981.0 describing implementating multichannel PWM. In a nutshell, I was running the 8 PWM channels up to 1 MHz. If have to have 12-bit resolution, your max frequency will be 20.5 KHz. I also include a heavily commented sketch that covers PWM setup in more detail.