Go Down

Topic: How to set PWM frequency to 1 Hz on an UNO (Read 1 time) previous topic - next topic

Railroader

I would like to set a pwm output on my UNO to run at a 1 Hz frequency and then, by analogWrite, set it at 10% or 90% dutycycle. Can that be done? If so, how do I do it?

The background is my need to waist some current in a low power consuming project to keep the powering  powerbank away from cutting off. In a test project using millis() the frequency of 1Hz and 10% dutycycle does the trick. Now I want to do the same in a sketch that hangs waiting for inputs go high and go low so millis() will probably not work.
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

MartinL

#1
Nov 29, 2018, 10:45 am Last Edit: Nov 29, 2018, 10:46 am by MartinL
Hi Railroader,

The follow code sets-up digital pin D9 for 1Hz, 10% duty cycle, (using the UNO's so called "fast PWM" mode):

Code: [Select]
// Set-up fast PWM on the Arduino UNO at 1Hz on Digital pin D9
void setup() {
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 6249;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249
}

void loop() {}

To adjust the duty-cycle just change the value of the OCR1A register to a value between 0 and 62499. The output will appear at the beginning of the next timer cycle on D9.

Railroader

Thanks a lot! Why no Karma+++?
I didn't specify that the ative level is the LOW portion in my application and maybe I should have asked for 90% duty cycle. Would the code below be doing that?
Code: [Select]

 pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 56249;                                // Set the duty-cycle to 90%: 62499 *0.9 = 56249
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

dougp

Quote
Would the code below be doing that?
Based on my running demo of the sketch, yes.  With this connection, +5--/\/\/- 330Ω -->|--DIO9, the LED is mostly off.

@MartinL slightly modified sketch:

Code: [Select]


// Set-up fast PWM on the Arduino UNO at 1Hz on Digital pin D9

// arduino.cc thread: http://forum.arduino.cc/index.php?topic=582176.new;topicseen#new

void setup() {
  pinMode(6, INPUT_PULLUP);
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
                                                    // +5--/\/\/- 330Ω -->|--DIO9
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
 
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  byte tccr1a,tccr1b;
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 56241;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249

Serial.begin(9600);
Serial.print("TCCR1A - ");
Serial.println(TCCR1A,BIN);
Serial.print("TCCR1B - ");
Serial.print(TCCR1B,BIN);
}

void loop() {
  if (digitalRead(6) == 1) { // change duty cycle on-the-fly
    OCR1A = 56241;
  }
  else OCR1A = 6249;
}

Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

Railroader

At first it didn't work so I added a digitalWrite(9,HIGH); digitalWrite(9,LOW). Then things happened.
Now I run using this setup:
Code: [Select]

void setup()
// put your setup code here, to run once:
{
//1Hz 90% dutycycle 
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(WGM11);                // Enable the PWM output OC1A on digital pins 9
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 6249;                                     // Set the duty-cycle to 90%: 62499 * 9 / 10 = 56249
  delay(10);//allow pwm timers to start
////  digitalWrite(9, HIGH);
//  digitalWrite(9, LOW);
  analogWrite(9,25);
 


What is Your comment?
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

dougp

Code: [Select]

void setup()
 analogWrite(9,25);


What is Your comment?

Increase to (9,56241)
Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

MartinL

#6
Nov 30, 2018, 09:19 am Last Edit: Nov 30, 2018, 12:57 pm by MartinL
Quote
I didn't specify that the ative level is the LOW portion in my application and maybe I should have asked for 90% duty cycle. Would the code below be doing that?
Yes, but it's also possible invert the output, this just requires the COM1A0 bit to be set in the TCCR1A register.

This is the same code as above, (1Hz, 10% duty-cycle), but with an inverted output:

Code: [Select]
// Set-up fast PWM on the Arduino UNO at 1Hz on Digital pin D9 with inverted output
void setup() {
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);  // Enable the PWM output OC1A on digital pins 9 and invert output
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 6249;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249
}

void loop() {}

Railroader

Lots of thanks, MartinL  and dougp! I think I manage now. My application has been running for almost 18 hours due to the work of D9.
However, setting ICR1 and OCR1A doesn't make the output pin 9 get started. My test using dig.write( high, low) made it start. AnalogWrite also works. There is something I don't know fully.
Does analogWrite(9, x ) put the value of x into OCR1A? The range of x is 0..255 according to Arduino ref.. In that case setting OCR1A gives a higher precision in case that would be needed in the future.
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

MartinL

#8
Dec 01, 2018, 12:22 am Last Edit: Dec 01, 2018, 12:23 am by MartinL
Quote
However, setting ICR1 and OCR1A doesn't make the output pin 9 get started. My test using dig.write( high, low) made it start. AnalogWrite also works. There is something I don't know fully.
The code I provided works on my Arduino Uno with update version 1.6.23, (I tested and checked the sketch on my scope before posting). It shouldn't be necessary to use either digitalWrite() or analogWrite().

Quote
Does analogWrite(9, x ) put the value of x into OCR1A?
Yes it does, but on the AVR boards analogWrite() is set-up for 8-bit phase correct PWM, where as we're using 16-bit fast PWM mode.

Quote
The range of x is 0..255 according to Arduino ref.. In that case setting OCR1A gives a higher precision in case that would be needed in the future
Using fast PWM mode with 16-bit timer 1, in this instance provides 15-bits of resolution (log(62499 + 1)/log(2)).

Railroader

Thanks again! I probably missed something, made some mistake. Now the pulsing starts without any accesses to the output pin.
Code: [Select]
Yes it does, but on the AVR boards analogWrite() is set-up for 8-bit phase correct PWM, where as we're using 16-bit fast PWM mode.
What is "AVR boards"? Phase correct PWM? I guess using analog.Write might not work as I guessed.
Here is the code working:
Code: [Select]

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

//#define BACKLIGHT_PIN     13

hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip

// LCD geometry
#define LCD_COLS 16
#define LCD_ROWS 2
long print_cnt = millis() + 500;

void setup()
// put your setup code here, to run once:
{
//1Hz 90% dutycycle 
  pinMode(9, OUTPUT);                               // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);  // Enable the PWM output OC1A on digital pins 9 and invert output
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS12);     // Set fast PWM and prescaler of 256 on timer 1
  ICR1 = 62499;                                     // Set the PWM frequency to 1Hz: 16MHz/(256 * 1Hz) - 1 = 62499
  OCR1A = 6249;                                     // Set the duty-cycle to 10%: 62499 / 10 = 6249

  delay(10);//allow pwm timers to start
////  digitalWrite(9, HIGH);
//  digitalWrite(9, LOW);
//  analogWrite(9,25);
 
  int status;
  status = mylcd.begin(LCD_COLS, LCD_ROWS);
  if (status) // non zero status means it was unsuccesful
  {
    status = -status; // convert negative status value to positive number

    // begin() failed so blink error code using the onboard LED if possible
    hd44780::fatalError(status); // does not return
  }
  mylcd.clear();
  // initalization was successful, the backlight should be on now

  // Print start message to the LCD
  mylcd.print("Started");

  Serial.begin(115200);
  Serial.println("Measuring online");
  pinMode(A2, INPUT);//+5 from object
  pinMode(A3, INPUT);//Strobe from object
  Serial.println("Waiting for neg flank");
  while (analogRead(A3) > 512)  {
    //    Serial.println(analogRead(A3));
  }
  Serial.println("Waiting for pos flank");
  while (analogRead(A3) <= 512)
  {
    Serial.print(analogRead(A3));
  }
  Serial.println("Measuring setup and sync ready.");
}
void print_time()
{
  int hour, minute, second;
  unsigned long mill_tmp = millis();//+59L*60000L;
  if (mill_tmp > print_cnt)
  {
    print_cnt += 1000;
    mylcd.setCursor(0, 1);

    hour = mill_tmp / 3600000;
    if (hour < 10)mylcd.print(" ");
    mylcd.print(hour);
    mylcd.print(".");

    minute = (mill_tmp / 60000) % 60;
    if (minute < 10)mylcd.print("0");
    mylcd.print(minute); //print hours
    mylcd.print(".");

    second = (mill_tmp / 1000) % 60;
    if (second < 10)mylcd.print("0");
    mylcd.print(second); //print hours
    /*
      mylcd.print((mill_tmp / 60000) % 60); //print minutes
      mylcd.print(".");
      mylcd.print((mill_tmp / 1000) % 60); //print seconds
    */
    //  Serial.print(mill_tmp / 3600 000); //print hours
    //  Serial.print(".");
    //  Serial.print((mill_tmp % 60 000) / 1000); //print minutes
    //  Serial.print(" ");
  }
}
void loop()
// put your main code here, to run repeatedly:
{
  float loaded, idle;
  while (analogRead(A3) > 512) {} //wait for neg flank
  loaded = analogRead(A2) * 5.0 / 1024.0;
  //  Serial.print( analogRead(A2));Serial.print("  ");
  /* if (loaded < 3.0)
    {
     print_time();
     //    Serial.print(millis() / 60000); //print minutes
     //    Serial.print(" ");
    }*/
  mylcd.setCursor(0, 0);
  mylcd.print(loaded); mylcd.print(" ");
  Serial.print(loaded); Serial.print(" ");
  while (analogRead(A3) <= 512) {} //wait for pos flank
  idle = analogRead(A2) * 5.0 / 1024.0;
  /*  if (idle < 3.0)
    {
      print_time();
      //    Serial.print(millis() / 60000);
      //    Serial.print(" ");
    }
  */
  //  Serial.println( analogRead(A2));
  Serial.println(idle);
  mylcd.print(idle); mylcd.print(" ");
  print_time();
}
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

dougp

Phase correct PWM? I guess using analog.Write might not work as I guessed.
Good tutorial on Arduino timers and counters.
Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

Railroader

Thanks dougp. I will keep that link as a reference for the future. Fast talking videos made me drop learning Fusion360 not long ago.. Working on the hardware low level from the 1970:es I have left if it tor the young and hungry guys. Getting hold of a few crucial details without being forced to learn the entire circuit... Creating systems using known subsystems is want I aim for, asking the up to date young guys for details.
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

dougp

Fast talking videos made me drop learning Fusion360 not long ago..
+1

It's not enough that most tutorials are sped through, I swear they're edited to take out the pauses between sentences and even breathing to get to the end that much sooner.  Setting the speed to .75 , if it's still listenable, often improves matters.

Even the word tutorials is now being shortened to 'torials'.   >:( 
Everything we call real is made of things that cannot be regarded as real.  If quantum mechanics hasn't profoundly shocked you, you haven't understood it yet. - Niels Bohr

No private consultations undertaken!

MartinL

Hi Railroader,

Quote
What is "AVR boards"? Phase correct PWM? I guess using analog.Write might not work as I guessed.
AVR microcontrollers are just the family of microcontrollers used on the Arduino Uno, Nano, Mega etc..., as opposed to say ARM microcontrollers used on the Arduino Due or Zero.

The microcontroller used on the Arduino Uno has a number of PWM modes of operation. The analogWrite() function uses "phase correct PWM" that allows the duty-cycle (or phase) to be controlled from 0 to 255, but not the waveform's frequency. This is fixed at either 490Hz or 980Hz depending on which of the Uno's PWM pins you chose.

Unfortunately, the only way to control the PWM frquency and thereby create a 1Hz waveform, is to use either "fast PWM" or "phase and frequency correct PWM" modes.

Railroader

@dougp
I agree. To often those videos uses a different version and the pictures don't match. Sometimes the sharpness and details are beyond readable.
@MartinL
Thanks again. ARM and RISC controllers I heard about during my working years….
Use Your knowledge. If that's not enough, look for education.
Having knowledge, think outside the box to gain more of it. Only trains run like the train, on the rails. The rest run between the rails.

Go Up