Arduino Due Set PWM Frequency change

I am trying to change the PWM frequency for pin 3 and 4 for Arduino DUE board. Originally it is running at 1KHz frequency. However, I want to change it to 20KHz. Any support or help would be grateful.

tonzerotnk:
Originally it is running at 1KHz frequency. However, I want to change it to 20KHz.

How about posting your code (between code tags) ?

/ select the input pin for the potentiometer

int sensorPin = A0;

// select the pin for the LED
int ledPin = 9;

// variable from the sensor
int sensorValue;
int ledValue;

void setup() {
// declare the ledPin as an OUTPUT:
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}

void loop() {
// read the value from the sensor:
sensorValue = analogRead(sensorPin);
ledValue = map( sensorValue, 0, 1023, 0, 255);
Serial.println(ledValue);
delay(100);
// fade LED
analogWrite (ledPin , ledValue);
}

To output a PWM signal, depending on the PWM frequency you are willing to output, choose between the PWM peripheral or the Timer Counter. Effectively, analogWrite() outputs a single PWM frequency !

Look carefully at the Greynomad pinout diagram to see which signals can be output.
Pin 3 ---> TIOA7 ( Timer Counter 2 Channel 1 is TC7)
Pin 4 ---> TIOB6 ( Timer Counter 2 Channel 0 is TC6)
These signals are output by Timer Counter 2.

Beware : There may be some confusion with Timer Counter names, namely e.g. TC3_Handler() is the Timer Interrupt Handler for Timer Counter 1 Channel 0, whereas TC1->TC_CHANNEL[0].TC_SR is the status register for this same timer.

Here is an example sketch to output TIOA7 over a 20 KHz frequency:

/*******************************************************************************/
/*                TIOA7 Frequency = 20 KHz over pin 3                          */
/*******************************************************************************/

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  PMC->PMC_PCER1 |= PMC_PCER1_PID34;                     // TC7 power ON - Timer Counter 2 channel 1 IS TC7 - See page 38

  PIOC->PIO_PDR |= PIO_PDR_P28;                          // The pin is no more driven by GPIO
  PIOC->PIO_ABSR |= PIO_PC28B_TIOA7;                     // Periperal type B  - See page 859

  TC2->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1  // MCK/2, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA7 on RA compare match  -- See page 883
                              | TC_CMR_ACPC_SET;           // Set TIOA7 on RC compare match

  TC2->TC_CHANNEL[1].TC_RC = 2100;  //<*********************  Frequency = (Mck/2)/TC_RC  Hz = 20 KHz
  TC2->TC_CHANNEL[1].TC_RA = 100;  //<********************   Duty cycle = (TC_RA/TC_RC) * 100  %

  TC2->TC_CHANNEL[1].TC_IER = TC_IER_CPCS;                 // Interrupt on RC compare match
  NVIC_EnableIRQ(TC7_IRQn);

  TC2->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC7 counter and enable

}

void TC7_Handler() {
  static uint32_t Count;
  TC2->TC_CHANNEL[1].TC_SR;
  Count++;
  if (Count == 20000) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));  // Toggle every 1 Hz
    Count = 0;
  }
}
void loop() {

}

Can the duty cycle be adjusted from an analog input?

Can adjust the work cycle.
0-100% of adjustable resistor
?

tonzerotnk:
Can the duty cycle be adjusted from an analog input?

Yes

tonzerotnk:
Can adjust the work cycle.
0-100% of adjustable resistor
?

Yes

I want to change the PWM frequency between 10KHz and 20KHz for pin 9 and 10 for Arduino DUE board. Any help would be grateful.

With these libraries:

  • the Graynomad pinout diagram, and you are done.

I am struggling with this too. Took me a while to find the pwm_lib (google has very bad results regarding the pwm frequency issue), only to find out that the pin I am already connected to (and cannot re-adjust) is pin5, which is PC25. And that it is not supported in pwm_lib because it's peripheral is TIOA6 and not a PWM channel.

Did not quite understand the difference between TIOA6 and TC2, or how to get this pin to generate a PWM signal at a constant frequency of >20 KHz (preferred 33KHz).
I also need to have the ability to change the duty cycle on the fly (as to say, I need a usage similar to calling analogWrite(x) to a digital pin, just not in the default 490 Hz frequency).
On the mega it was as simple as adding in the beginning:
TCCR3B = TCCR3B & B11111000 | B00000001;
I don't mind having it a little more cumbersome / less abstracted on the Due, as long as it would work.

I apologize if the answer is right under my nose, but would very much appreciate the experts' help on this.

Cheers,
Steve

You can output a PWM signal with the tc_lib from antodom or the example sketch in reply #3.

Timer Counters numeration may be confusing:
TC0 is for TIOA0,1 and 2, depending on the selected channel, the corresponding interrupt handler is TC0_Handler(),TC1_Handler() and TC2_Handler() (Note that these TCx have a different meaning)

TC1 ........TIOA3,4 and 5 ................................................TC3_Handler(), TC4_Handler() and TC5_Handler()
TC2 ........TIOA6,7 and 8 ................................................TC6_Handler(), TC7_Handler() and TC8_Handler()

Pin 5 is TIOA6 (see Graynomad pinout diagram). You can change the duty cycle with TC_RA register in loop() if needed. The difference with the PWM peripheral is that frequency or duty cycle changes on the fly will be executed exactly after the end of the previous period with PWM_CDTYUPD register (PWM Duty Cycle Update register).

Thank you.
Well, me not being an RT embedded developer, I was wondering if you could take a quick look to check if this code should work?
It passes the "verify" compilation in the IDE but I don't have the tools to actually test it except activating the whole system, which I am careful of.
Did I get the registers and numbers / channels right?

/*******************************************************************************/
/*                TIOA6 Frequency = 32.3 KHz over pin 5                          */
/*******************************************************************************/

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  PMC->PMC_PCER1 |= PMC_PCER1_PID33;                     // TC6 power ON? not sure how to change the PCER index in this line using page 38 (or others) in the datasheet.

  PIOC->PIO_PDR |= PIO_PDR_P25;                          // Assuming the "C" should come from the PIOC => PC25
  PIOC->PIO_ABSR |= PIO_PC25B_TIOA6;                     // Periperal type B as well, by page 859

// If TC_CHANNEL[1] is TIOA7, is it correct to assume that TC_CHANNEL[0] is TIOA6 ?

  TC2->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1  // MCK/2, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA6 on RA compare match  -- See page 883
                              | TC_CMR_ACPC_SET;           // Set TIOA6 on RC compare match

  TC2->TC_CHANNEL[0].TC_RC = 1300;  //<*********************  Frequency = (Mck/2)/TC_RC  rate ~ 32.2 KHz
  TC2->TC_CHANNEL[0].TC_RA = 40;  //<********************   Duty cycle = (TC_RA/TC_RC) * 40  %

  TC2->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;                 // Interrupt on RC compare match
  NVIC_EnableIRQ(TC6_IRQn);

  TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC6 counter and enable

}

void TC6_Handler() { // Is this function name a saved word?
//  static uint32_t Count;
  TC2->TC_CHANNEL[0].TC_SR; // What does this line do?

// To simplify and focus - commenting the LED lines out. They are solely for LED on/off right?
//  Count++;
//  if (Count == 20000) {
//    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));  // Toggle every 1 Hz
//    Count = 0;
//  }
}
void loop() {
  int duty;
  duty+=1;
  if (duty>100) duty=0;
  TC2->TC_CHANNEL[0].TC_RA = duty; // Would this line be enough to change the duty cycle?
  delay(1000);

}

As commented, I would also love to know:
Is the handler name hardcoded (I see no reference to it as a routine in the code preceding it).
Is it positive that the units specified in TC_RA are percentages? not some 10-nanosec-thing?
What does the TC_SR line do? Its not a function and there is no assignment in it.

Many thanks for spreading the knowledge,
Steve

Some modifications (I set a 50% duty cycle in setup()):

/*******************************************************************************/
/*                TIOA6 Frequency = 32.3 KHz over pin 5                          */
/*******************************************************************************/

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  PMC->PMC_PCER1 |= PMC_PCER1_PID33;                     // TC6 power ON? not sure how to change the PCER index in this line using page 38 (or others) in the datasheet.

  PIOC->PIO_PDR |= PIO_PDR_P25;                          // Assuming the "C" should come from the PIOC => PC25
  PIOC->PIO_ABSR |= PIO_PC25B_TIOA6;                     // Periperal type B as well, by page 859

// If TC_CHANNEL[1] is TIOA7, is it correct to assume that TC_CHANNEL[0] is TIOA6 ?

  TC2->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1  // MCK/2, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA6 on RA compare match  -- See page 883
                              | TC_CMR_ACPC_SET;           // Set TIOA6 on RC compare match

  TC2->TC_CHANNEL[0].TC_RC = 1300;  //<*********************  Frequency = (Mck/2)/TC_RC  rate ~ 32.3 KHz
  TC2->TC_CHANNEL[0].TC_RA = 650;  //<********************   Duty cycle = (TC_RA/TC_RC) * 100  % = 50%

  TC2->TC_CHANNEL[0].TC_IER = TC_IER_CPCS;                 // Interrupt on RC compare match
  NVIC_EnableIRQ(TC6_IRQn);

  TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC6 counter and enable

}

void TC6_Handler() { // Is this function name a saved word?
//  static uint32_t Count;
  TC2->TC_CHANNEL[0].TC_SR; //Read and clear status register so that the interrupt handler fires again and again

// To simplify and focus - commenting the LED lines out. They are solely for LED on/off right?
//  Count++;
//  if (Count == 20000) {
//    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));  // Toggle every 1 Hz
//    Count = 0;
//  }
}
void loop() {
  /*
  int duty;
  duty+=1;
  if (duty>100) duty=0;
  TC2->TC_CHANNEL[0].TC_RA = duty; // Would this line be enough to change the duty cycle?
  delay(1000);
  */

 // 0 = 0% < Duty cycle < 1300 = 100%

}

Thank you, I understand now.
I was expecting more errors on my part :slight_smile:

Edit:
Works like a charm.
Almost seamless: using TC_RC=1280 and sending the original computed values (0~255) * 5. (I know I'm missing 5 units in the end).

Hi everyone!
I am new member.
Now I have project about make frequency from 1hz to 10khz( this flexible frequency) and pulse wide from 30ns to 10us.
I have code for pulsewide here:

analogWrite(PULSE_OUT, PulseWide * 4096 / 1000); // PULSE_OUT is pin 13
TC_Stop(TC0,0);
TC_Configure(TC0, 0,
TC_CMR_TCCLKS_TIMER_CLOCK1 |
TC_CMR_WAVE | // Waveform mode
TC_CMR_WAVSEL_UP | // Counter running up and reset when equals to RC
TC_CMR_EEVT_XC0 | // Set external events from XC0 (this setup TIOB as output)
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
TC_CMR_BCPB_SET | TC_CMR_BCPC_CLEAR);
pulse_clk = 42 * PulseWide / 10;
TC_SetRC(TC0, 0, pulse_clk + 1);
TC_SetRB(TC0, 0, 1);
TC_Start(TC0,0);

How can i make frequency part from 30ns to 10us?
Thanks you