I'm working on an Arduino project and I need to write some code that controls the frequency and duty cycle of a square wave signal (pulse width modulation). I'm not allowed to use the PWM function, I have to write my own code.

I've tried this code on Proteus (virtual Arduino) but there are two problems:

  1. The frequency is always high (hundreds of Hz) even if the input voltage is zero. It never reaches 0.5 Hz or 1 Hz.

  2. The duty cycle is very high or very low. I never see 50% or 75% duty cycle on an oscilloscope. When I change the position of the potentiometer it becomes 90% or 10% (I think) and there are no other values.

Would you tell me what's wrong with this code please? I can not find my mistake:

unsigned long Time = 2000000; unsigned int Volt; float dutyCycle = 0.5;
void setup() {
  pinMode(13, OUTPUT); //set Pin 13 or On-board LED to output

// the loop function runs over and over again forever
void loop() {

  Volt = analogRead (1);    // Frequency: input voltage is through potentiometer at analong pin 1
  dutyCycle = analogRead (0) / 1023;    // dutyCycle potentiometer is at analong pin 0
  Time = ( 2000954 / (1 + Volt) )- 954; //This equations is linear. When input voltage is zero, frequency = 0.5Hz. When input voltage is 5v, f=1000Hz.

  digitalWrite(13, HIGH);   // turn the LED on 
  delayMicroseconds(Time * dutyCycle );              // wait
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delayMicroseconds(Time * (1-dutyCycle) );          // wait

1 Answer 1

dutyCycle = analogRead (0) / 1023;    // dutyCycle potentiometer is at analong pin 0

Dividing a small integer by a bigger integer leaves you with 0. If analogRead() returns 1023 you will get a result of 1. Everything else will give you 0. You need to divide by a floating point number - either 1023.0 or 1023f.

analogRead() is slow. Because it is happening between every single cycle of your square wave you can expect to never get to anywhere near 100% duty cycle. There will always be a LOW period where the analog input is being read.

You should:

  1. Fix your integer division problem
  2. Make the ADC "free running" or interrupt driven so it doesn't interfere with your PWM generation

... Or ...

  1. Fix your integer division problem
  2. Make your PWM generation timer driven, either using the OC peripheral or manually with interrupts, so it doesn't interfere with the analog reading.

... Or ...

You could even do both. Make everything as asynchronous as possible leaving your loop() free to just do the heavy work of calculating the values.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.