Control PWM Frequency (set it at 100hz)

Hi! I have been trying to set the PWM frequency of pin 5 on my Nano to 100hz but I have not succeeded. My project consists of a Nano, NRF24L01, LSM303, NEO-6m, L298N and a radiator fan controller from a car. The radiator fan controller works by receiving PWM-signals at 100hz. Previously I used a different fan controller and my project was working great using just analogWrite but that controller was not up to the job so now I have to use this new one. The problem is that I have some trouble setting the frequency to 100hz. I have tried using this PWM-library (GitHub - terryjmyers/PWM: Arduino Library: Modify PWM on AVR (arduino) platform) but the SetPinFrequency(enginePin, frequency); somehow messes with the other outputs as well and causes the GPS-module and L298N modules to not work (the NRF24L01 still works though). Also I do not think the output actually outputs at 100hz as the fan controller does not output a voltage when this code is ran.

The code for the project is attached (exceeded the 9000 character limit). Please forgive my spaghetti-code, it will get cleaned up as soon as the project works.

In order to test that the fan controller works I used the following code. There were no issues running with just the fan controller. Also there are no issues running the attached projects’ code with the PWM-parts removed (and as I wrote earlier the project worked with a different fan controller using analogWrite).

unsigned long cycleStart,
              onTime,             
              cycleTime,
              hz;
int val;             
const byte powerPin = 3;
byte percent = 5;
void setup()
{
  Serial.begin(9600);
  Serial.println(" Hz = 100  Duty Cycle = 25%");
  hz = 100;
  cycleTime = 1000000UL / hz;
  onTime = cycleTime / 2; 
  pinMode(powerPin,OUTPUT);
}
void loop()
{
  digitalWrite(powerPin,micros() - cycleStart < onTime); 
  if(micros() - cycleStart > cycleTime)
    cycleStart += cycleTime;
  // if there's any serial available, read it:
  if(Serial.available() > 0)
  {
     val = Serial.read();
    if(val == 'h' || val == 'H')
      hz = Serial.parseInt();
    else if(val == 'p' || val == 'P')
    {
      percent = Serial.parseInt();   
    }
    else
      while(Serial.available() > 0)
        Serial.read(); 
    cycleTime = 1000000UL / hz;
    percent = constrain(percent, 0, 100);
    onTime = cycleTime * 0.01 * percent;
    cycleStart = micros();
    Serial.print(" Hz = ");
    Serial.print(hz);
    Serial.print("  Duty Cycle = ");
    Serial.print(percent);
    Serial.println("%");
  }
}

TrollingMotor_Final_v3_PWMCONTROLLER.ino (11.1 KB)

Changing the PWM frequency of one pin requires changing the frequency of the timer that controls that pin, and that change will affect both pins, and other operations controlled by the timer.

PWM on ATmega328 based Arduinos:

  • Pins 5 and 6: controlled by timer 0
  • Pins 9 and 10: controlled by timer 1
  • Pins 11 and 3: controlled by timer 2

It sounds like you also have library conflict problems.

Get each aspect of your project working alone, then add the additional features, one at a time.

I have tried using this PWM-library (GitHub - terryjmyers/PWM: Arduino Library: Modify PWM on AVR (arduino) platform) but the SetPinFrequency(enginePin, frequency); somehow messes with the other outputs as well and causes the GPS-module and L298N modules to not work (the NRF24L01 still works though)

Also I do not think the output actually outputs at 100hz as the fan controller does not output a voltage when this code is ran.

You are correct about this, as you are using InitTimersSafe() the library will not change Timer0 and pin D5 is output A from that timer.

I’m not certain about the interactions between the library and the other code, but all the timers are altered with the Init.

You may want to try something more simple like TimerOne.h or MsTimer2.h

https://www.pjrc.com/teensy/td_libs_TimerOne.html

jremington:
Changing the PWM frequency of one pin requires changing the frequency of the timer that controls that pin, and that change will affect both pins, and other operations controlled by the timer.

PWM on ATmega328 based Arduinos:

  • Pins 5 and 6: controlled by timer 0
  • Pins 9 and 10: controlled by timer 1
  • Pins 11 and 3: controlled by timer 2

It sounds like you also have library conflict problems.

Get each aspect of your project working alone, then add the additional features, one at a time.

cattledog:
You are correct about this, as you are using InitTimersSafe() the library will not change Timer0 and pin D5 is output A from that timer.

I'm not certain about the interactions between the library and the other code, but all the timers are altered with the Init.

You may want to try something more simple like TimerOne.h or MsTimer2.h

TimerOne & TimerThree Arduino Libraries

GitHub - PaulStoffregen/MsTimer2: Run a function using a timer

From my understanding you should not mess with Timer0. I would prefer to use pin 5 for the output but since this pin is controlled using Timer0 that may not be viable? Do you have any suggestions?

Timer0 is required for delay() and millis() to work properly, but if you aren't using those functions, feel free to modify it.

Otherwise, use another timer and pin.

Here's a simple idea: forget about using AnalogWrite and create your PWM with DigitalWrite high & low.
100Hz is not a challenge for the arduino, you can use micros() for timing.
You will need to write your code as a state machine, and you MAY get a bit of jitter if other parts of the code use Delay() (they should not) or take considerable time.
Howevere a fan will not be bothered by a little jitter.

My suggestion would be to move the NeoSWSerial pins to 4 and 5, thus freeing pin 3 for 100 Hz pwm from Timer 2 on output pin B (D3).

Here is the setup for the Timer2

void setup()
{
  //Initialize Timer2
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

  // Set OC2B for Compare Match (digital pin3)
  pinMode(3, OUTPUT);
  bitSet(TCCR2A, COM2B1);//clear OC2B on up count compare match
  //set OC2B on down count compare match

  // Set mode 5-- Phase correct PWM to OCR2A counts up and down
  bitSet(TCCR2A, WGM20);
  bitSet(TCCR2B, WGM22);

  // Set up /1024 prescale
  bitSet(TCCR2B, CS20);
  bitSet(TCCR2B, CS21);
  bitSet(TCCR2B, CS22);
  
  OCR2A = 78; //Sets freq 100Hz
  //Timer 2 counts up and down for 156 total counts
  //156 x 1024 x.0625 = 9.98ms ~ 100 Hz

  //duty cycle is set by OCR2B value
  //valid values 1-77
  //change it in loop to vary duty cycle
  OCR2B = 38;//50% duty cycle

}
void loop() {}

cattledog:
My suggestion would be to move the NeoSWSerial pins to 4 and 5, thus freeing pin 3 for 100 Hz pwm from Timer 2 on output pin B (D3).

Here is the setup for the Timer2

void setup()

{
  //Initialize Timer2
  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2 = 0;

// Set OC2B for Compare Match (digital pin3)
  pinMode(3, OUTPUT);
  bitSet(TCCR2A, COM2B1);//clear OC2B on up count compare match
  //set OC2B on down count compare match

// Set mode 5-- Phase correct PWM to OCR2A counts up and down
  bitSet(TCCR2A, WGM20);
  bitSet(TCCR2B, WGM22);

// Set up /1024 prescale
  bitSet(TCCR2B, CS20);
  bitSet(TCCR2B, CS21);
  bitSet(TCCR2B, CS22);
 
  OCR2A = 78; //Sets freq 100Hz
  //Timer 2 counts up and down for 156 total counts
  //156 x 1024 x.0625 = 9.98ms ~ 100 Hz

//duty cycle is set by OCR2B value
  //valid values 1-77
  //change it in loop to vary duty cycle
  OCR2B = 38;//50% duty cycle

}
void loop() {}

Thank you a lot for your answer! Would this however not interfere with the NRF24L01 (MOSI connects to pin 11 which is also controlled by timer2)? I have no idea how the NRF24L01 actually works so I dont know if the MOSI does anything using the timer.

Would this however not interfere with the NRF24L01 (MOSI connects to pin 11 which is also controlled by timer2)? I have no idea how the NRF24L01 actually works so I dont know if the MOSI does anything using the timer.

My understanding is if you do not set pin 11 as hardware output for the timer (and the example code did not) it should function as normal and SPI MOSI will not be affected.

cattledog:
My understanding is if you do not set pin 11 as hardware output for the timer (and the example code did not) it should function as normal and SPI MOSI will not be affected.

I will try and report back! Btw, your karma is perfect now.