PWM frequency adjust and steps

Hello Arduino guys,

I would like to ask about float frequency from library PWM.h (there is an example of project in this library PWM Frequency Library Example by Riham )

This library works fine, only I would need to get float or double frequency. For example set the PWM freq on 109 Hz and then to do steps plus or minus by at least 0.5 Hz. But the best if I can change frequency on 109.1, then 109.2 Hz and so on.

I want to have two buttons: plus and minus and after push the button you will go down or up from 109 Hz on 108.5 or 109.5 Hz, and after next button push again +/- 0.5 Hz. But the best for me to have steps by 0.1 Hz.

Problem is if I can set frequency in float or double value.

When I changed int32_t frequency = 109 as float frequency, it does not help.

Can anybody help me please?

Or is there any different way how I can make steps in 0.1 or 0.5 Hz by pushing plus or minus button?

For example for me is the best

Thank you

Basic view on PWM example (without buttons)

#include <PWM.h>

int motor = 9; // Pin with PWM output

int32_t frequency = 109; // freq in Hz

void setup()

{



InitTimersSafe();



bool success = SetPinFrequencySafe(motor, frequency);


if(success) {

pinMode(13, OUTPUT);

digitalWrite(13, HIGH);

}

}

void loop()

{



pwmWrite(motor, 128);

}

delay(30);

}

The code is clearly very incomplete. For instance, it's not clear how the SetPinFrequencySafe() function is defined and which variable type it accepts as its second argument, i.e. frequency. If that's an integer, then obviously you'll never be able to set the frequency as a fraction - it'll always be whole numbers only.

What is the expected minimum and maximum PWM frequency do you wish to achieve?

Hello,

minimum etc 45.8, maximum 109 Hz.

Is not there any different way to get for example 49.8 Hz and then change frequency on 50.2 Hz?

For example with another library than PWM.h...

This example I only copied from link I attached

An Arduino Uno would be more than capable of achieving PWM resolution in the 0.0001 Hz increments in that range. Do you have equipment to measure the PWM frequency in that resolution?

Frequency will drive rpm of VDC motor. So yes, you can measure rpm of motor very precisely.
1 Hz = 10 rpm. I need to get the speed 33,33 behind the gearbox that is 14:1

That means motor has to have speed 466,62 rpm, that means 46,662 Hz :slight_smile: Thats why I need to have very soft steps

Try this one out. Put a potentiometer on A0, and the output PWM on D10

int analogPin = A0;
int val = 0;
uint16_t freqTop = 0;

void setup() {
  // put your setup code here, to run once:
  PWM_init();
}

void loop() {
  // put your main code here, to run repeatedly:
  val = analogRead(analogPin);
  freqTop = map(val, 0, 1023, 2080, 6250);
  OCR1A = freqTop;
  OCR1B = freqTop/2;
  delay(50);
}

void PWM_init(void)
{
  /***************************
  Initialize the PWM hardware
  ***************************/

  // set OC1B output pin
  DDRB |= 1<<PORTB2;

  // set TIMER1 for Fast PWM mode, non-inverted output on OC1B pin
  TCCR1A = 1<<WGM11 | 1<<WGM10 | 1<<COM1B1 | 0<<COM1B0;
  TCCR1B = 1<<WGM13 | 1<<WGM12;

  // disable interrupts
  TIMSK1 = 0<<OCIE1B | 0<<OCIE1A | 0<<TOIE1;

  // set TOP value to generate initial 40 Hz [250 KHz / 6250]
  OCR1A = (6250 - 1);

  // set initial duty cycles at 50%
  OCR1B = 6250/2;

  // start TIMER1 at 250 KHz [DIV64]
  TCCR1B = 1<<WGM13 | 1<<WGM12 | 0<<CS12 | 1<<CS11 | 1<<CS10;

  return;
}

You can change the value of val however you want (through buttons, potentiometer, etc). This example uses potentiometer on A0. As long as its between 0 and 1023, you should be able to get 40 Hz to 120 Hz on the PWM

val = analogRead(analogPin);
// Generating Two 180° Out of Phase Variable-Frequency 
// Square Waves on Timer1 of an Arduino UNO (Pins 9 and 10)
// Good for frequencies from 0.2 Hz to 500 kHz.
// Written June 1st, 2020 by John Wasser

void TwoPhaseBegin()
{
  digitalWrite(9, LOW);
  pinMode(9, OUTPUT);
  digitalWrite(10, LOW);
  pinMode(10, OUTPUT);

  // Stop Timer/Counter1
  TCCR1A = 0;  // Timer/Counter1 Control Register A
  TCCR1B = 0;  // Timer/Counter1 Control Register B
  TIMSK1 = 0;  // Timer/Counter1 Interrupt Mask Register

  // Set Timer/Counter1 to Waveform Generation Mode 8: 
  // Phase and Frequency correct PWM with TOP set by ICR1
  TCCR1B |= (1 << WGM13);  // WGM=8
  TCCR1A |= (1 << COM1A1);  // Normal PWM on Pin 9
  TCCR1A |= (1 << COM1B1) | (1 << COM1B0); // Inverted PWM on Pin 10

  TwoPhaseFrequency(1000.0);  // Default to 1 kHz
}

bool TwoPhaseFrequency(float frequency)
{
  byte prescaleBits; // 1, 2, 3, 4, 5
  uint16_t prescaleFactor;  // 1, 8, 64, 256, 1024
  uint32_t top32;

  // Find the smallest prescale factor that will fit the TOP value within 16 bits.
  // frequency = F_CPU / (2 * prescale *  (TOP+1))
  // TOP = (F_CPU / (2UL * prescale * frequency)) - 1;

  prescaleBits = 1;
  prescaleFactor = 1;  // Used for 123 Hz to 500 kHz
  top32 = F_CPU / (2UL * prescaleFactor * frequency) - 1;
  if (top32 > 65535UL) // Too many clocks to count in 16 bits?
  {
    prescaleBits = 2;
    prescaleFactor = 8;  // Used for 16-122 Hz
    top32 = F_CPU / (2UL * prescaleFactor * frequency) - 1;
    if (top32 > 65535UL) // Too many clocks to count in 16 bits?
    {
      prescaleBits = 3;
      prescaleFactor = 64;  // Used for 2-15 Hz
      top32 = F_CPU / (2UL * prescaleFactor * frequency) - 1;
      if (top32 > 65535UL) // Too many clocks to count in 16 bits?
      {
        prescaleBits = 4;
        prescaleFactor = 256; // Only used for 1 Hz
        top32 = F_CPU / (2UL * prescaleFactor * frequency) - 1;
        if (top32 > 65535UL) // Too many clocks to count in 16 bits?
        {
          prescaleBits = 5;
          prescaleFactor = 1024;
          top32 = F_CPU / (2UL * prescaleFactor * frequency) - 1;
          if (top32 > 65535UL) // Too many clocks to count in 16 bits?
          {
            return false;
          }
        }
      }
    }
  }

  //  Serial.print("Freq: ");
  //  Serial.print(frequency);
  //  Serial.print(" prescale: ");
  //  Serial.print(prescaleFactor);
  //  Serial.print(" TOP: ");
  //  Serial.println(top32);

  if (top32 < 16)
    return false; // Require at least 16 levels of PWM

  TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // Clear the three clock select bits
  TCCR1B |= prescaleBits; // Set clock prescale to prescaleBits

  ICR1 = top32;
  OCR1A = top32 / 2;
  OCR1B = (top32 / 2) + 1; // Prevent shoot-through
  return true;
}

void setup()
{
  Serial.begin(115200);

  TwoPhaseBegin();  // Start output at 1000 Hz
}

void loop()
{
  // Call TwoPhaseFrequency(uint16_t frequency) any time to change the output frequency
}

And you expect to have stable rpm with that accuracy ?
Consider synchronising two motors ....

It works very well. Thank you :slight_smile:

I changed configuration and my steps are in 0,01 Hz, thats great.

Only maybe not clever question.
Why 250 kHz?

@johnwasser Thank you, I will test it as well :slight_smile:

No particular reason except for resolution available for the PWM timer. You can play with the number since it TIMER1 is 16 bit which gives you a lot of possible combinations

There are only five values of prescale you can use on Timer1: 1, 8, 64, 256, 1024

You usually pick the smallest prescale that keeps your TOP value below 17 bits (you only have 16 bits for the value of TOP.

You can't use prescale 1 because it only goes down to 16MHz / 65536 = 244 Hz.

Prescale 8 (2 MHz clock) goes down to 30.5 Hz so that is what I would have used. TOP = 43667 for 45.8 Hz and TOP = 18348 for 109 Hz so you would have 25,319 steps between 45.8 Hz and 109 Hz.

Prescale 64 (250 kHz clock) goes down to 3.8 Hz which is overkill for your purposes (45.8-109 Hz).