Changing arduino Due PWM frequency for DC motor control

Hi,

I am working on projet where i need to control 3 dc motors with PWM frequeny around ,(15 Khz) on PIN (5,6,9 or 10).
I am using arduino Due and multimoto (motor driver).

I tried many forums and make many search to change frequency froùm 1Khz to 15 khz but still I cant resolve this problem.

The best I found is PWM_LIb in this link:

but with this lib I can control only two pin (6 : PWML7_PC24 // PWM_CH7 and 9 : PWML4_PC21, // PWM_CH4).

Could you please help to resolve this problem ?

Bests Regards,

Hi @hyakoubi

Is this because your Multimoto motor driver shield only supports PWM input on digital pins 5, 6, 9 and 10?

Are any other digital pins available on this shield, for example 7 and 8? Since I imagine that @antodom 's library would support these.

Hi Martin,

Availaible pin on this shield are only 5,6,9 and 10.
An example in this link :

thanks,

Hi @hyakoubi

The reason for asking is to do with the internals of the SAM3X8E used on the Arduino Due. Digital pins 6 an 9 use the microcontroller's PWM Controller, while pins 5 and 10 the TC timers.

I'll see if I can tweak the Due's analogWrite() function to operate at 15kHz. If not, I can provide you with register level code that'll do the same thing.

Hi @hyakoubi

I tried to tweaking the analogWrite() function to change the PWM frequency, since it's possible with AVR microcontrollers (Uno/Mega, etc...), but it turns out for the Due's implementation this isn't so easy.

Here's a code example that outputs 15kHz PWM on pins D5 and D10. The duty-cycle is set by setting the respective timers' TC_RA and TC_RB registers between 0 and 2800, (with this code you're getting a much higher resolution):

// Set up the Arduino Due's digital pins D5 and D10 for 15kHz PWM
void setup() 
{
  PMC->PMC_PCER1 |= PMC_PCER1_PID34 | PMC_PCER1_PID33;    // Enable peripheral TC6 (TC2 Channel 0) and TC7 (TC2 Channel 1)
  PIOC->PIO_ABSR |= PIO_ABSR_P29 | PIO_ABSR_P25;          // Switch the multiplexer to peripheral B for TIOA6 (D5) and TIOB7 (D10)
  PIOC->PIO_PDR |= PIO_PDR_P29 | PIO_PDR_P25;             // Disable the GPIO on the corresponding pins

  TC2->TC_CHANNEL[0].TC_CMR = TC_CMR_ACPC_SET |           // Set TIOA on counter match with RC0                           
                              TC_CMR_ACPA_CLEAR |         // Clear TIOA on counter match with RA0
                              TC_CMR_WAVE |               // Enable wave mode
                              TC_CMR_WAVSEL_UP_RC |       // Count up with automatic trigger on RC compare
                              TC_CMR_EEVT_XC0 |           // Set event selection to XC0 to make TIOB an output
                              TC_CMR_TCCLKS_TIMER_CLOCK1; // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  TC2->TC_CHANNEL[1].TC_CMR = TC_CMR_BCPC_SET |           // Set TIOB on counter match with RC0                              
                              TC_CMR_BCPB_CLEAR |         // Clear TIOB on counter match with RB0                             
                              TC_CMR_WAVE |               // Enable wave mode
                              TC_CMR_WAVSEL_UP_RC |       // Count up with automatic trigger on RC compare
                              TC_CMR_EEVT_XC0 |           // Set event selection to XC0 to make TIOB an output
                              TC_CMR_TCCLKS_TIMER_CLOCK1; // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  TC2->TC_CHANNEL[0].TC_RC = 2800;                        // Set the PWM frequency to 15kHz: 42MHz / 15kHz = 2800 
  TC2->TC_CHANNEL[1].TC_RC = 2800;                        // Set the PWM frequency to 15kHz: 42MHz / 15kHz = 2800

  TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Enable the timer TC6 (TC2 Channel 0)
  TC2->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Enable the timer TC7 (TC2 Channel 1)
}

void loop() 
{
  TC2->TC_CHANNEL[0].TC_RA = 700;                         // Set the duty-cycle to 25% on D5
  TC2->TC_CHANNEL[1].TC_RB = 700;                         // Set the duty-cycle to 25% on D10
  delay(2000);                                            // Wait for 2 seconds
  TC2->TC_CHANNEL[0].TC_RA = 2100;                        // Set the duty-cycle to 75% on D5
  TC2->TC_CHANNEL[1].TC_RB = 2100;                        // Set the duty-cycle to 75% on D10
  delay(2000);                                            // Wait for 2 seconds
}

Note that there's a slight anomoly with the TC timers, in that it's not possible set the output to 0V by setting the respective TC_RA and TC_RB registers to zero. There's always a tiny slither of a pulse at the start of the timer cycle, (similar to fast PWM mode on the Uno or Mega).

The workaround is to set the timer to clear at the start to of the cycle with the line for D5:

TC2->TC_CHANNEL[0].TC_CMR &= ~TC_CMR_ACPC_Msk;     // Clear the ACPC bitfield
TC2->TC_CHANNEL[0].TC_CMR |= TC_CMR_ACPC_CLEAR;    // Clear D5 output

and for D10:

TC2->TC_CHANNEL[1].TC_CMR &= ~TC_CMR_BCPC_Msk;     // Clear the BCPC bitfield
TC2->TC_CHANNEL[1].TC_CMR |= TC_CMR_BCPC_CLEAR;    // Clear D10 output

Reinstate the PWM output oncemore with the respective TC_CMR_ACPC_SET or TC_CMR_BCPC_SET bitfields.

Thank you for your help

Hi MartinL,

I will need your help again about using encoder with this 3 motors if you can ?

Controlling the 3 motors with 15 Hz PWM works well in open loop but when i tried to use PID I used encoder with PIN 22,23 for exeample motor 1.The motors make too much noise (hissing).

There is a confusion between the use of PWM at 15 Hz and interrupt for counts with encoder but I cant find the solution ?

Motor 1 : PIN ( pwm 9, dir 2) encoder (phase A 22, Phase B 23)
Motor 2 : PIN ( pwm 10, dir 3) encoder (phase A 24, Phase B 25)
Motor 3 : PIN ( pwm 5, dir 4) encoder (phase A 26, Phase B 27)

I use software called Matlab Simulink with Sample time Te= 0.01 and (Ts=0.1 for encoder)

Any idea ?
Many thanks,

Hi @hyakoubi

Do you mean 15kHz?

Is your DC motor driver able to accept higher PWM frequencies? Switching to above 20kHz or so, would energise the motor coils at a frequency outside of (human) audible hearing range.

yes I mean 15Khz.

For 20Khz it's not wotking too with the same PIN for Motor 1 : PIN ( pwm 9, dir 2) encoder (phase A 22, Phase B 23), stil hissing.

But I changed the encoder PIn to (phase A 44, Phase B 45) it seems to wrok even if my PID parameters not working in this case.

for :
Motor 1 : PIN ( pwm 9, dir 2) encoder (phase A 44, Phase B 45)
Motor 4 : PIN ( pwm 6, dir 7) encoder (phase A 42, Phase B 43)

the problem is resolved because I think the pwm channel for PIN (42,43,44,45) is not same pwm channel for PWM pin 9 and 6.

But for PWM pin 10 and 5, I am still facing the same noise (hissing) with many frequncies tested : 15 Khz,20 Khz, 32125 Hz...
and tested many other PIn for encoder .

Motor 2 : PIN ( pwm 10, dir 3) encoder (phase A 24, Phase B 25) HISSING
Motor 3 : PIN ( pwm 5, dir 4) encoder (phase A 26, Phase B 27) Hissing

Hi @hyakoubi

Just to clarify, so you're saying that you're getting no hissing on pins 6 and 9 using PWM library, but you are getting hissing on pins 5 and 10 with the PWM from the TC timers?

I am using Matlab/Simulink software not arduino IDE, but I cant see source code in Matlab.

yes, pin 6 and 9 no noise bacause it's related to PWM clock and noise on Pin 5 and 10. because it's TC Timers.

Noise only when I use encoder (phase A 24, Phase B 25), (phase A 26, Phase B 27) without encoder no noise.

Hi @hyakoubi

I think it's because your closed-loop control system is making very fast, fine adjustments to the TC timers' duty-cycles. However, unlike the PWM controller used by the PWM library, the TC timers have no buffered TC_RA and TR_RB duty-cycle registers. This means any changes in duty-cycle occur immediately at the output rather than at the beginning of each timer cycle, causing glitches on their respective outputs.

The solution is to only set the new duty-cycle for the TC timers in their respective overflow interrupt service routines:

// Set up the Arduino Due's digital pins D5 and D10 for 15kHz PWM with RC compare interrupts
volatile uint32_t dutycycleTC6 = 0, dutycycleTC7 = 0;

void setup() 
{
  PMC->PMC_PCER1 |= PMC_PCER1_PID34 | PMC_PCER1_PID33;    // Enable peripheral TC6 (TC2 Channel 0) and TC7 (TC2 Channel 1)
  PIOC->PIO_ABSR |= PIO_ABSR_P29 | PIO_ABSR_P25;          // Switch the multiplexer to peripheral B for TIOA6 (D5) and TIOB7 (D10)
  PIOC->PIO_PDR |= PIO_PDR_P29 | PIO_PDR_P25;             // Disable the GPIO on the corresponding pins

  TC2->TC_CHANNEL[0].TC_CMR = TC_CMR_ACPC_SET |           // Set TIOA on counter match with RC0                           
                              TC_CMR_ACPA_CLEAR |         // Clear TIOA on counter match with RA0
                              TC_CMR_WAVE |               // Enable wave mode
                              TC_CMR_WAVSEL_UP_RC |       // Count up with automatic trigger on RC compare
                              TC_CMR_EEVT_XC0 |           // Set event selection to XC0 to make TIOB an output
                              TC_CMR_TCCLKS_TIMER_CLOCK1; // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  TC2->TC_CHANNEL[1].TC_CMR = TC_CMR_BCPC_SET |           // Set TIOB on counter match with RC0                              
                              TC_CMR_BCPB_CLEAR |         // Clear TIOB on counter match with RB0                             
                              TC_CMR_WAVE |               // Enable wave mode
                              TC_CMR_WAVSEL_UP_RC |       // Count up with automatic trigger on RC compare
                              TC_CMR_EEVT_XC0 |           // Set event selection to XC0 to make TIOB an output
                              TC_CMR_TCCLKS_TIMER_CLOCK1; // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 42MHz)

  TC2->TC_CHANNEL[0].TC_RC = 2800;                        // Set the PWM frequency to 15kHz: 42MHz / 15kHz = 2800 
  TC2->TC_CHANNEL[1].TC_RC = 2800;                        // Set the PWM frequency to 15kHz: 42MHz / 15kHz = 2800

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

  TC2->TC_CHANNEL[0].TC_IER |= TC_IER_CPCS;               // Enable RC compare interrupt for TC6 (TC2 Channel 0)

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

  TC2->TC_CHANNEL[1].TC_IER |= TC_IER_CPCS;               // Enable RC compare interrupt for TC7 (TC2 Channel 1)

  TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Enable the timer TC6 (TC2 Channel 0)
  TC2->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Enable the timer TC7 (TC2 Channel 1)
}

void loop() 
{
  dutycycleTC6 = 700;                                      // Set the duty-cycle to 25% on D5 on next timer cycle
  dutycycleTC7 = 700;                                      // Set the duty-cycle to 25% on D10 on next timer cycle
  delay(2000);                                             // Wait for 2 seconds
  dutycycleTC6 = 2100;                                     // Set the duty-cycle to 75% on D5 on next timer cycle
  dutycycleTC7 = 2100;                                     // Set the duty-cycle to 75% on D10 on next timer cycle
  delay(2000);                                             // Wait for 2 seconds
}

void TC6_Handler()                                         // ISR TC6 (TC2 Channel 0)
{
  if (TC2->TC_CHANNEL[0].TC_SR & TC_SR_CPCS)               // Check for RC compare condition
  {
    TC2->TC_CHANNEL[0].TC_RA = dutycycleTC6;               // Set the duty-cycle to on D5
  }
}

void TC7_Handler()                                         // ISR TC7 (TC2 Channel 1)
{ 
  if (TC2->TC_CHANNEL[1].TC_SR & TC_SR_CPCS)               // Check for RC compare condition
  {
    TC2->TC_CHANNEL[1].TC_RB = dutycycleTC7;               // Set the duty-cycle on 10
  }
}

If there's still an issue, or if the motors on the TC timers still sound a bit different, then it could be that TC timers need to be configured to work in dual slope rather than single slope mode. Luckily the TC timers are capable of both.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.