Go Down

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

##### Nov 29, 2018, 01:37 am
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 amLast Edit: Nov 29, 2018, 10:46 am by MartinL

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 D9void 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.

#2
##### Nov 29, 2018, 11:07 pm
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

#3
##### Nov 30, 2018, 12:42 am
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#newvoid 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 = 6249Serial.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;}`
I don't trust atoms.  They make up everything.

No private consultations undertaken!

#4
##### Nov 30, 2018, 12:52 am
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

#5
##### Nov 30, 2018, 03:04 am
Code: [Select]
`void setup() analogWrite(9,25);`

What is Your comment?

Increase to (9,56241)
I don't trust atoms.  They make up everything.

No private consultations undertaken!

#### MartinL

#6
##### Nov 30, 2018, 09:19 amLast 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 outputvoid 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() {}`

#7
##### Nov 30, 2018, 06:41 pm
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 amLast 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)).

#9
##### Dec 04, 2018, 12:12 am
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     13hd44780_I2Cexp mylcd; // declare lcd object: auto locate & config exapander chip// LCD geometry#define LCD_COLS 16#define LCD_ROWS 2long 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

#10
##### Dec 04, 2018, 02:26 am
Phase correct PWM? I guess using analog.Write might not work as I guessed.
Good tutorial on Arduino timers and counters.
I don't trust atoms.  They make up everything.

No private consultations undertaken!

#11
##### Dec 04, 2018, 02:44 am
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

#12
##### Dec 04, 2018, 05:03 am
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'.
I don't trust atoms.  They make up everything.

No private consultations undertaken!

#### MartinL

#13
##### Dec 04, 2018, 12:53 pm

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.