Stumped trying to figure out timers (400 Hz)

This is my first time dealing with Arduino.

I need to convert a 0 - 5 Volt analog signal into a 400 Hz PWM signal with a duty cycle matching the input voltage (so 5V input would result in 100% duty cycle).

I have access to an Arduino Nano board and thought it would be super simple to implement this idea, just need to read voltage from an analog pin and calculate the duty cycle for the PWM.

Turns out I need to adjust the PWM frequency, and I have to say working with "timers" has me completely stumped. The rest of the Arduino programming language seems simple and straight forward, but I simply cannot wrap my head around the Eldritch horror that is timer configuration. I need help.

This is the code I have right now:

int pinvolt = 0;                                    // Declare input voltage variable
int dutyc = 0;                                      // Declare calculated duty cycle variable

void setup() {
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output

void loop() {
  pinvolt = analogRead(A0)+1;                       //reads voltage on pin A0
  dutyc = ((pinvolt*256)/1024)-1;                   //calculates correct duty cycle % (0-255)
  analogWrite(9, dutyc);                            //sets the output to the calculated duty cycle

I think my loop should get the job done, but only if the PWM output is running at 400 Hz.

I believe I need to set up the timers in the "setup" area, but I have no idea how to approach this. I've read tons of forum posts all over the web dealing with frequency adjustments on Arduino PWM but they're always super complex issues from people trying to build quadcopters and whatnot, the more I read the more confused I get. It seems there's multiple ways to do it but every post seems written under the assumption the reader fully understands timer configuration.
The few basic tutorials I could find about PWM frequency adjustments only show how to change the prescalers, and it seems you can't get to 400 Hz through prescaler adjustments.

Why change the frequency?

PWM changes the duty cycle.

If you just want to set PWM at 0% for 0 volts and up through 100% for 5V you can use
analogWrite(pwmPin, analogRead(Ax) / 4);

Why 400Hz? The output frequencys for PWM is pins 3 ,9, 10, 11 (490 Hz) pins 5 and 6 (980 Hz) on Nano.

If your application can stand 490.20Hz then it reduces to:

void setup() {}
void loop()
   analogWrite(9, analogRead(A0) / 4);

If you MUST have 400 Hz you need to calculate the number of clock cycles that is. 16 MHz / 400 Hz is 40000 clocks. Timer1 can count up to 65536 so you can use that value (-1) directly. Waveform Generation Mode (WGM) 14 lets you put your count in ICR1.

void setup()
  // Stop Timer/Counter1
  TCCR1A = 0;  // Timer/Counter1 Control Register A
  TCCR1B = 0;  // Timer/Counter1 Control Register B
  TIMSK1 = 0;   // Timer/Counter1 Interrupt Mask Register
  ICR1 = 39999;  // 40000 clocks = 400 Hz.

  OCR1A = 0;  // Default Pin 9 to 0% PWM
  OCR1B = 0;  // Default Pin 10 to 0% PWM

  // Set to Timer/Counter1 to Waveform Generation Mode 14: Fast PWM with TOP set by ICR1
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM13) | (1 << WGM12) ;

  // Set clock prescale to 1 for maximum PWM frequency
  TCCR1B |= (1 << CS10);

  // Enable PWM on Pin 9
  TCCR1A |= (1 << COM1A1);
  pinMode(9, OUTPUT);

void loop()
   OCR1A = (analogRead(A0) * 20000UL) / 1024;
1 Like

Thank you, this optimizes things a bit. Obvious when you think about it but I didn't.

I'm afraid I need 400 Hz. A small deviation of +/- 5 Hz is fine, but 490 is too high.

Ah, ok I think I'm starting to get an idea of what all those posts I read before were discussing, it's starting to make a bit more sense. My thanks.

See update for full code using WGM 14.

1 Like

Thank you so much, I actually understand what all of these commands mean now!
Your comments on the code example make all the difference. I've seen all of these before, but usually the code wouldn't have any comments and I couldn't make heads or tails of what was going on.