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