How can I control the LED's frequency?

Hello everyone.

I want to implement a led that operates at a specific frequency, from 1 to 70 Hz.

I used the function that " delay", "delaymicros", "if time >n sec => on, else off structure"...
But there were a limitation.

In case of "delay",
this function can use only milliseconds unit.
However, if it is 6Hz, it must operate each 83.3333...ms ,50% duty cycle.
It occurs an error of 0.3333ms.

In case of delaymicros,
This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times.

Last,
In case of If structure,
I used the currentmillis, but it was occur the operate delay.
I don't understand this phenomenon.

The method does not matter.

All I need is a way to implement the LEDs, which operates at intervals of 1 to 70 Hz.

Thank you :slight_smile:


// These variables store the flash pattern
// and the current state of the LED


int ledPin1 =  11;      // the number of the LED pin
int ledState1 = LOW;             // ledState used to set the LED
double previousMillis1=0;
double previousMillis2 = 0;        // will store last time LED was updated
double OnTime1 = ((1000.00/10)/2);           // milliseconds of on-time
double OffTime1 = ((1000.00/10)/2);          // milliseconds of off-time


void setup() 
{
 // set the digital pin as output:
 pinMode(ledPin1, OUTPUT);      
Serial.begin(115200);      // open the serial port at 9600 bps:  
}

void loop()
{
 // check to see if it's time to change the state of the LED

double currentMillis = micros()/1000.0;
 // Serial.print(previousMillis1);
  // Serial.print("A");
   //Serial.print(currentMillis);
  //Serial.print("\n");
   //Serial.print(micros());
 if((ledState1 == HIGH) && (currentMillis - previousMillis1 >= OnTime1))
 {
   ledState1 = LOW;  // Turn it off
   previousMillis1 = currentMillis;  // Remember the time
   digitalWrite(ledPin1, ledState1);  // Update the actual LED
   //Serial.print(previousMillis1); 
 }
 else if ((ledState1 == LOW) && (currentMillis - previousMillis1 >= OffTime1))
 {
   ledState1 = HIGH;  // turn it on
   Serial.print(currentMillis);
   Serial.print("A");
   Serial.print(previousMillis1);
   Serial.print("\n");
   previousMillis1 = currentMillis;   // Remember the time
   digitalWrite(ledPin1, ledState1);      // Update the actual LED
   //Serial.print(previousMillis2-previousMillis1);
   //Serial.print("\n");
   //Serial.print(digitalWrite);
   //Serial.print("\n");
  // Serial.print(previousMillis2); 
 }
 
 
}

Hi,
Welcome to the forum.

Please read the first post in any forum entitled how to use this forum.
http://forum.arduino.cc/index.php/topic,148850.0.html then look down to item #7 about how to post your code.
It will be formatted in a scrolling window that makes it easier to read.

For 70Hz = period of 14.28mS = 14280uS
So delay is 7.14mS= 7140uS
Why are you worrying abut performance at 3uS?

Thanks.. Tom.. :slight_smile:

First thank you for your comment.
I modified my post.

and then..
Yes I know that ,
the case of 70Hz was occur the 7140us delay.

How can I control the led that was blink at that time?

As I said, the delay function only supports millisecond units.

Hi,

cgy326:
How can I control the led that was blink at that time?

Use millis, forget delay as that is blocking code.
"Control" do you mean how can you adjust the frequency?
Use a 10K potentimeter on an analog input and use the analog reading to adjust your delay interval.
Or do you mean how to actually output and drive an LED?
Thanks.. Tom.. :slight_smile:

TomGeorge:
Hi,Use millis, forget delay as that is blocking code.
"Control" do you mean how can you adjust the frequency?
Use a 10K potentimeter on an analog input and use the analog reading to adjust your delay interval.
Or do you mean how to actually output and drive an LED?
Thanks.. Tom.. :slight_smile:

I mean how to actually output and drive an LED.

Hi,
Can you tell us your electronics, programming, Arduino, hardware experience?

Have you googled? arduino output to LED

You need to do some research yourself.

But try here..

Tom... :slight_smile:

TomGeorge:
Hi,
Can you tell us your electronics, programming, Arduino, hardware experience?

Have you googled? arduino output to LED

You need to do some research yourself.

But try here..

Blinking the LED | Arduino Lesson 2. LEDs | Adafruit Learning System

Tom... :slight_smile:

I am a beginner of arduino.

but...
Did you read my post?
I try to many things to do.
delay, delaymicros, millis, micros,......

But I can't find the solution....
So I asked for help here.

And I've already used the post you uploaded.
However, it also failed to implement the correct frequency and caused a delay.

If I entered 17Hz, I want the code to work correctly.
The range of the input value is 1 ~ 70Hz.

thank you :slight_smile:

If you need it accurate, be aware that even if your code is perfect the arduino's clock is usually a resonator, not a crystal. It can be off by 5% or more. you can get an arduino with a crystal, but it costs more.

With respect to the problem of getting an exact X Hz signal when you can measure time only in intervals of Y, this is an interesting math problem and somewhat related to drawing a straight line on a pixelated display. The problem is aliasing.

Let's say you want a 63Hz signal. Each half-cycle is 7936.(some decimals) microseconds. Now, if you just use 7936 or 7937, the signal is 63.004 or 62.996 - which frankly is pretty damn close and better than the resolution of your resonator anyway. But, to make it a little more accurate you can store an error amount as a 16-bit signed value, being '1/32k of a microsecond'.

Start the error offset at zero. If the error offset is >=0, then do a short pulse of 7936 microseconds and subtract from the offset the amount by which the pulse is short. If it's <0, do a long pulse and add the amount by which a long pulse is long. You'll wind up with a train of sightly-too-long and slightly-too-shot signals. These offset amounts are precalculated constants.

But as I said - if you are using microseconds anyway for a <70Hz signal, then this kind of numerical accuracy is not really the problem. The problem is almost certainly in your code.

The first issue is may be this:

if(micros() - lastPulseUs >= pulseUs) {
  do some stuff;
  lastPulse = micros();
}

The problem here is that if "do some stuff" takes more than a microsecond, the timing will be off, because micros() will have moved on while"do some stuff" is happening. Instead use

if(micros() - lastPulseUs >= pulseUs) {
  do some stuff;
  lastPulse += pulseUs;
}

The other problem may be that "do some stuff" is just taking too long. A typical mistake is to do floating-point calculations in the condition of the loop. Another mistake is to perform analog reads every iteration through the loop.

if(micros() - (1000000.0 / (analogRead(A0)/1024.0*70 + 1)) >= pulseUs) {
  do some stuff;
  lastPulse += pulseUs;
}

Calculate a pulseUs and stuff it in a variable. For even more speed, you can do it all as 16 bits - but this limits how low the frequency can be.

Which Arduino and which pin for LED?

Oh - and now I actually looked at your code.

You are using floating point for everything. Floating point is slow. And you are printing to serial.out. Printing to serial is slow. And compounding all this, you are printing floating point numbers to serial. This means they need to be converted into text, which is not a trivial job.

If 3us is not accurate enough for the job (an error of 333kHz), then an arduino is not accurate enough for the job.

PaulMurrayCbr:
If you need it accurate, be aware that even if your code is perfect the arduino's clock is usually a resonator, not a crystal. It can be off by 5% or more. you can get an arduino with a crystal, but it costs more.

With respect to the problem of getting an exact X Hz signal when you can measure time only in intervals of Y, this is an interesting math problem and somewhat related to drawing a straight line on a pixelated display. The problem is aliasing.

Let's say you want a 63Hz signal. Each half-cycle is 7936.(some decimals) microseconds. Now, if you just use 7936 or 7937, the signal is 63.004 or 62.996 - which frankly is pretty damn close and better than the resolution of your resonator anyway. But, to make it a little more accurate you can store an error amount as a 16-bit signed value, being '1/32k of a microsecond'.

Start the error offset at zero. If the error offset is >=0, then do a short pulse of 7936 microseconds and subtract from the offset the amount by which the pulse is short. If it's <0, do a long pulse and add the amount by which a long pulse is long. You'll wind up with a train of sightly-too-long and slightly-too-shot signals. These offset amounts are precalculated constants.

But as I said - if you are using microseconds anyway for a <70Hz signal, then this kind of numerical accuracy is not really the problem. The problem is almost certainly in your code.

The first issue is may be this:

if(micros() - lastPulseUs >= pulseUs) {

do some stuff;
 lastPulse = micros();
}




The problem here is that if "do some stuff" takes more than a microsecond, the timing will be off, because micros() will have moved on while"do some stuff" is happening. Instead use



if(micros() - lastPulseUs >= pulseUs) {
 do some stuff;
 lastPulse += pulseUs;
}





The other problem may be that "do some stuff" is just taking too long. A typical mistake is to do floating-point calculations in the condition of the loop. Another mistake is to perform analog reads every iteration through the loop.



if(micros() - (1000000.0 / (analogRead(A0)/1024.0*70 + 1)) >= pulseUs) {
 do some stuff;
 lastPulse += pulseUs;
}




Calculate a pulseUs and stuff it in a variable. For even more speed, you can do it all as 16 bits - but this limits how low the frequency can be.

I'm not good at English.
So I can't understand whole of your answer....
But,I think you know exactly the point of my question.

Could you give me the complete code to drive the LED to the above conditions?
I didn't understand it because suddenly used an analog signal. (analogRead(A0) <- at this part)

// These variables store the flash pattern
// and the current state of the LED


const byte ledPin1 =  11;      // the number of the LED pin
byte ledState1 = LOW;             // ledState used to set the LED

const float blink_Hz = 10; // 10 Hz

// at the maximum 70hz, this will introduce an error on the order of .007% - more than accurate enough
const unsigned long halfCycle_us = (unsigned long) (1000000.0 / blinkHz / 2);
unsigned long prev_us;

void setup()
{
 // set the digital pin as output:
 pinMode(ledPin1, OUTPUT);      
Serial.begin(115200);      // open the serial port at 9600 bps:  
  prev_us = micros();
}

void loop()
{
  if(micros() - prev_us <= halfCycle_us) {
    return;
  }
  prev_us += halfCycle_us;

 if(ledState1 != LOW)
 {
   ledState1 = LOW;  // Turn it off
   digitalWrite(ledPin1, ledState1);  // Update the actual LED
   //Serial.print(previousMillis1);
 }
 else
 {
   ledState1 = HIGH;  // turn it on
   //Serial.print(currentMillis);
   //Serial.print("A");
   //Serial.print(previousMillis1);
   //Serial.print("\n");
   digitalWrite(ledPin1, ledState1);      // Update the actual LED
   //Serial.print(previousMillis2-previousMillis1);
   //Serial.print("\n");
   //Serial.print(digitalWrite);
   //Serial.print("\n");
  // Serial.print(previousMillis2);
 }
}

The analog read was because I had not read your code, and I was assuming that you were getting the frequency off a potentiometer. I see that actually your code just uses a constant 10Hz.

Hi,
Why do you need a 0 to 70Hz pulse?
What is the application?

What is your native language?

In your original code you use pin1 as output.
Do not use pin1 as an output , this is a progrmming pin along with pin0.
Use pin 3.
Edit. Sorry, to much blood in the coffee stream.

Thanks.. Tom.. :slight_smile:

If you really need some instrumentation, then perhaps I could suggest this:

// These variables store the flash pattern
// and the current state of the LED


const byte ledPin1 =  11;      // the number of the LED pin
byte ledState1 = LOW;             // ledState used to set the LED

const float blink_Hz = 10; // 10 Hz

// at the maximum 70hz, this will introduce an error on the order of .007% - more than accurate enough
const unsigned long halfCycle_us = (unsigned long) (1000000.0 / blinkHz / 2);
unsigned long prev_us;

unsigned long start_count_us;
byte countCycles = 0;


void setup()
{
 // set the digital pin as output:
 pinMode(ledPin1, OUTPUT);     
 Serial.begin(115200);      // open the serial port at 9600 bps: 
 prev_us = micros();
 start_count_us = micros();
}

void loop()
{
  const unsigned long us = micros();
  if(us - prev_us <= halfCycle_us) {
    return;
  }
  prev_us += halfCycle_us;
  countCycles ++;

  // every five seconds
  if(us - start_count_us >= 5000000)  {
     Serial.print("there have been ");
     Serial.print(countCycles);
     Serial.print("half-cycles in the past ");
     Serial.print(us - start_count_us);
     Serial.print(" microseconds.");
     start_count_us = us;
     countCycles = 0;
  }

 if(ledState1 != LOW)
 {
   ledState1 = LOW;  // Turn it off
   digitalWrite(ledPin1, ledState1);  // Update the actual LED
 }
 else
 {
   ledState1 = HIGH;  // turn it on
   digitalWrite(ledPin1, ledState1);      // Update the actual LED
 }
}

Try this for a quick starter (and for gits & shiggles). Don't ask for a translation :slight_smile:

// set for 17 Hz
// microsPeriod = 500000 / Hz, 70 Hz = 7142UL

uint32_t sTime, microsPeriod = 29411UL;

void setup()
{
  //Serial.begin(9600);
  pinMode (11,OUTPUT); 
}
void loop()
{
  if(micros() - sTime > microsPeriod)
  {
    bitSet(PINB,3);
    sTime += microsPeriod;
  }
}

uno and digital

and

Thank you all,
If you can not give it a try, I'll ask for help again!

Hi,
@edgemoron.
bitSet sets the output HIGH
where are you clearing it to go LOW?

Tom.. :slight_smile:

@TomGeorge:
Setting a bit in a PINx register toggles the pin (if set as OUTPUT). Period is 1/2 blink freq.