Good morning,
I am trying to use the following sketch in order to use any digital output as a PWM output; even if it has no PWM option.
The problem I have is that it works fine except it will never go to 100% with the pot value to Vcc 5 V.
I tried changing parameters and there is always the same value left, The Output stops at some 93% instead of 100% of Vcc.
I can't understand why, could anyone have an idea ? the problem happens on any board: Uno, Nano and even AtTiny.
The whole device behaves as if there was a resistor of about 2,5 K in series with the Pot input: I tried using a 10 K value pot and the max output I get is around 60% of max. With a 50 K value pot, max output is around 80%.
int frequency = 300; //the frequency of the cycle
int cycle_length; //how long each pwm cycle should be on for
float duty_cycle; //the percentage of power we want
int time_on; // how long the output should be high
int time_off; //how long the output should be low
float v_out; //the amount of voltage the led should draw
int pot_pin = A2; //pin with potentiometer
int pwm_pin = 3; // pin that will act like a pwm pin
void setup() {
pinMode(pot_pin, INPUT);
pinMode(pwm_pin, OUTPUT);
digitalWrite(pwm_pin, LOW);
//calculate the length of the cycle
cycle_length = 1000000/frequency; //length of one pwm cycle in microseconds
}
void loop() {
int val = analogRead(pot_pin); //get the pwm value
v_out= map(val, 0, 1023, 0, 255); //gets the pot value from 0-1024 and turns it into a value from 0-255
duty_cycle = v_out/255; // percentage of power being drawn as decimal.
time_on = duty_cycle * cycle_length; // work out the time it should be on
time_off = cycle_length-time_on; // work out the time it should be off
if(time_on > 0)
{
digitalWrite(pwm_pin, HIGH);
delayMicroseconds(time_on); // turns led on for short anount of time
}
digitalWrite(pwm_pin, LOW);
delayMicroseconds(time_off);
}
Don't mix float and int. If you are not extremely careful with this the result will not be what you intended. Try to eliminate the floats as there the risk of rounding errors is quite high. If you stay with integers the error is at least faster visible.
Since the digitalWrite() takes about 6 microseconds you can get a little closer by adjusting the times:
const int frequency = 300; //the frequency of the cycle
const int cycle_length = 1000000L / frequency; //how long each pwm cycle should be on for
const int pot_pin = A2; //pin with potentiometer
const int pwm_pin = 3; // pin that will act like a pwm pin
void setup() {
pinMode(pwm_pin, OUTPUT);
}
void loop() {
float duty_cycle = analogRead(pot_pin) / 1023.0; // percentage of power being drawn as decimal.
int time_on = duty_cycle * cycle_length; // work out the time it should be on
int time_off = cycle_length - time_on; // work out the time it should be off
if (time_on > 6) {
digitalWrite(pwm_pin, HIGH);
delayMicroseconds(time_on-6); // turns led on for short anount of time
}
if (time_off > 6) {
digitalWrite(pwm_pin, LOW);
delayMicroseconds(time_off-6);
}
}
Good morning
Sorry, I am not familiar with all those possibilities. Now is he talking about Serial prints or other kinds of prints ?
I will add some Serial prints in the sketch meanwhile.
Well, problem is now solved.
The frequency has to a multiple of 3, if not there is a rounding error which happens and the output never goes 100% high.
Thanks to all, Pylon for the rounding error clue and Awol for the suggestion of adding some Serial.prnts.
here is the sketch and it does go up to 100%:
loat frequency = 333; //the frequency of the cycle
float cycle_length; //how long each pwm cycle should be on for
float duty_cycle; //the percentage of power we want
int time_on; // how long the output should be high
int time_off; //how long the output should be low
float v_out; //the amount of voltage the led should draw
int pot_pin = A0; //pin with potentiometer
int pwm_pin = 2; // pin that will act like a pwm pin
void setup() {
Serial.begin(19200);
pinMode(pot_pin, INPUT);
pinMode(pwm_pin, OUTPUT);
digitalWrite(pwm_pin, LOW);
//calculate the length of the cycle
cycle_length = 1000000/frequency; //length of one pwm cycle in microseconds
}
void loop() {
int val = analogRead(pot_pin); //get the pwm value
v_out= map(val, 0, 1023, 0, 255); //gets the pot value from 0-1023 and turns it into a value from 0-255
Serial.print("vout ");
Serial.println(v_out);
delay(2000);
duty_cycle = v_out/255; // percentage of power being drawn as decimal.
Serial.print("dutycycle ");
Serial.println(duty_cycle);
Serial.println();
time_on = duty_cycle * cycle_length; // work out the time it should be on
Serial.print("timeon ");
Serial.println(time_on);
time_off = cycle_length-time_on; // work out the time it should be off
Serial.print("timeoff ");
Serial.println(time_off);
Serial.println();
delay(1000);
if(time_on > 0)
{
digitalWrite(pwm_pin, HIGH);
Serial.print("pwmpin is HIGH ");
Serial.println();
delayMicroseconds(time_on); // turns Output on for the said amount of time
}
digitalWrite(pwm_pin, LOW);
delayMicroseconds(time_off);
}
Good evening Awol
I will try it ! seems very clever indeed, if I want to use i on an Attiny it will be useful indeed, since there is little room in the memory.
Thanks !
Awol, I just tried it but the output does not go to 100%.
In fact the sketch does show it goes to 100% on all Serial prints, but the physical output does not !
So it works theoretically but not in practical uses !
Your solution has the same behavior.
Hardware issue ?
I tried it on all sorts of Arduino platforms, the same.
You are telling it to turn off the output pin until you turn it on again. You can't have the output stay on 100% when you turn it off. What happened to the "if (time_off > 0)"?!?
Dear John,
The time_off duration is only after an 'if' test. It is a difference between total time and time_on. If the output is positive and HIGH at 100% then no time_off will happen. In the case of 100% the time_off duration is bypassed.
Awol, the input does go to 100%, the Prints show it.