I am trying to change the frequency of the PWM output from an Arduino Uno R3 (Been using Pin 9) to 200 Hz with a duty cycle of 20%. This is for an ESC that is connected to a 12V motor, and I know the ESC works as if I input a 200 HZ wave from a waveform generator the motor runs. However, I have been finding it difficult to do the same thing with an Arduino Uno and I believe it is due to the frequency being a default of 490 Hz, so I need a way to change that frequency to 200 Hz. Are there any libraries or specific ways to do so?
Thanks!
-
1please add your code to the post ... format as "code"jsotola– jsotola2022年10月27日 23:23:50 +00:00Commented Oct 27, 2022 at 23:23
-
Haven't used a library for this yet, but you can find promising results in a web search. Like this librarychrisl– chrisl2022年10月27日 23:59:21 +00:00Commented Oct 27, 2022 at 23:59
2 Answers 2
You can also use my newly-created AVR_PWM library to shield you from dealing with complex hardware registers
For example
#include "AVR_PWM.h"
#if ( PWM_USING_ATMEGA2560 )
// Pins tested OK in Mega
//#define pinToUse 12 // Timer1B on Mega
//#define pinToUse 11 // Timer1A on Mega
//#define pinToUse 9 // Timer2B on Mega
//#define pinToUse 2 // Timer3B on Mega
//#define pinToUse 3 // Timer3C on Mega
//#define pinToUse 5 // Timer3A on Mega
//#define pinToUse 6 // Timer4A on Mega
//#define pinToUse 7 // Timer4B on Mega
#define pinToUse 8 // Timer4C on Mega
//#define pinToUse 46 // Timer5A on Mega
//#define pinToUse 45 // Timer5B on Mega
//#define pinToUse 44 // Timer5C on Mega
#elif ( PWM_USING_ATMEGA_32U4 )
// Pins tested OK on 32u4
//#define pinToUse 5 // Timer3A on 32u4
#define pinToUse 9 // Timer1A on 32u4
//#define pinToUse 10 // Timer1B on 32u4
#else
// Pins tested OK on Nano / UNO
#define pinToUse 9 // Timer1A on UNO, Nano, etc
//#define pinToUse 10 // Timer1B on UNO, Nano, etc
//#define pinToUse 5 // Timer0B on UNO, Nano, e
//#define pinToUse 3 // Timer2B on UNO, Nano, etc
#endif
//creates pwm instance
AVR_PWM* PWM_Instance;
float frequency;
float dutyCycle;
void setup()
{
Serial.print(F("\nStarting PWM_Basic on "));
Serial.println(BOARD_NAME);
Serial.println(AVR_PWM_VERSION);
//assigns PWM frequency of 200 Hz and a duty cycle of 0%
PWM_Instance = new AVR_PWM(pinToUse, 200, 0);
}
void loop()
{
frequency = 200;
dutyCycle = 20;
PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);
delay(10000);
dutyCycle = 90;
PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);
delay(10000);
}
If you are prepared to use Timer 2 (Pin D3) this code will do it:
void setup()
{
pinMode (3, OUTPUT);
TCCR2A = bit (WGM20) | bit (WGM21) | bit (COM2B1); // fast PWM, clear OC2B on compare
TCCR2B = bit (WGM22) | bit (CS20) | bit (CS21) | bit (CS22); // prescaler of 1024
OCR2A = 77; // zero relative
OCR2B = 15; // 20% duty cycle
} // end of setup
void loop()
{
// do other stuff here
}
More information on the Atmega328P timers on my page about timers.
The figure 77 is obtained from:
16000000 / 1024 / 78 = 200.32
In other words, the prescaler of 1024 is divided into the clock of 16 MHz, and then we count up to 78, giving a result of just over 200. I actually use 77 because the timer is zero-relative (it counts from 0 to 77, giving 78 counts). This means the frequency is slightly out (it will be 200.32 Hz rather than 200 but that could be close enough).
You could also do it with Timer 1 (pin D10) to get slightly more accurate results, like this:
void setup()
{
pinMode (10, OUTPUT);
// mode 15
TCCR1A = bit (WGM10) | bit (WGM11) | bit (COM1B1); // fast PWM, clear OC1B on compare
TCCR1B = bit (WGM12) | bit (WGM13) | bit (CS11); // fast PWM, prescaler of 8
OCR1A = 10000 - 1; // what to count to
OCR1B = 2000 - 1; // duty cycle
} // end of setup
void loop()
{
// do other stuff here
}
In this case I have used a prescaler of 8 and a count-up value of 10000, as follows:
16000000 / 8 / 10000 = 200
In this case the answer is exactly 200, which will be more accurate than the 200.32 obtained from using Timer 2. That is because Timer 1 is a 16-bit timer and has higher resolution.