Generate Frequency using simple HIGH-Delay-LOW-Delay-Repeat

I was trying to generate a frequency with the resolution of 0.1Hz/steps from 0 to 1kHz.

The code is below:

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

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(13,HIGH);
delay(5000);
digitalWrite(13,LOW);
delay(5000);
}

by delaying 5 seconds after triggering HIGH and LOW , it is shown in the oscilloscope the frequency is

0.1Hz (100mHz) for delay = 5000 after HIGH and LOW;

0.2Hz (200mHz) for delay = 2500 after HIGH and LOW;

up until

0.9Hz (901mHz) for delay = 555 after HIGH and LOW;

when I tried to generate 10.1 Hz, which by 1/10.1Hz and divide by 2 to have the delay at high and delay at low, which is 49 ms after high and 49ms after low. The result is I get 10.16 Hz which is fine.

But when I tried to generate frequency of 49.3Hz , I have changed from delay to delayMicroseconds and here’s my new code:

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

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(13,HIGH);
delayMicroseconds(10142);
digitalWrite(13,LOW);
delayMicroseconds(10142);
}

The frequency read by oscilloscope is : 49.01 Hz

And if I try for frequency of 587.3 Hz, the delay is 851 microseconds.

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

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(13,HIGH);
delayMicroseconds(851);
digitalWrite(13,LOW);
delayMicroseconds(851);
}

The frequency read by oscilloscope is : 582.1 Hz which is a bit diverted from my required value of 587.3 Hz.

Lets say, if I tries with frequency of 800.1 Hz the delay is 624 ms. The code is:

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

void loop() {
  // put your main code here, to run repeatedly:
digitalWrite(13,HIGH);
delayMicroseconds(625);
digitalWrite(13,LOW);
delayMicroseconds(625);
}

The frequency read by oscilloscope is : 793.7 Hz . What I want is 800.1 Hz.

Is there any other method to generate frequency of 0.1Hz of resolution with a better accuracy?

Your calculations seem to be ignoring the fact that the digitalWrite()s and the return to top of loop all take some time. If you put 2 x 500 microsecond delays inside a loop with other commands the total loop time will always be more than 1000 microseconds.

If you calculate the difference between what you expected and what you measured in microseconds/cycle instead of in frequency you might spot a fairly constant offset that you can apply for better accuracy.

Steve

There are a few things you can do to improve the precision with the approach you are taking.

  1. use the special avr macro __builtin_avr_delay_cycles() for 16x better resolution than delayMicroseconds(). It puts the delays in processor ticks of .0625 us with a 16MHz Arduino. The argument is an unsigned long.

  2. use direct port manipulation instead of digitalWrite() to toggle the output pin. Fastest, way is to use the special method of writing a bit to an input register PINX to toggle the pin. You will need to determine the correct register location for your output pin. Pin D13 is PINB register bit 5.

  3. eliminate the slight overhead of the loop() function by toggling the pin in a while() condition.

You will still need to tune the delay values to get the scope and Arduino to agree, as the Arduino's processor clock(if driven by a resonator and not a crystal) is often not very accurate.

void setup() {
  pinMode(13, OUTPUT);
}
void loop() {
  while (1){
  // toggle D13 every 500 microseconds
  PINB = B00100000;
  __builtin_avr_delay_cycles (8000000UL);
  }
}