Accurate 100 Hz square wave generator

Hello,

trying to get accurate 100Hz from Arduino, but having a bit of trouble.
So my first approach was to set pin high, wait 5ms, set it low and wait another 5ms.
The problem is, the resulting frequency is a actually a bit higher (102,4) - according to various frequency counters.
Why is that? If anything, I would actually expect it to be slower (didn't counting on the instruction time for setting the registers).
Anyone has a solution?

Thanks,
Jonas

tone(pin, frequency)
// wonders how accurate it would be?

Try using micros() instead.I see better results when I code clocks that way.
If you use blink without delay style coding, will help also.

As for the speed you see, no way to tell without seeing your code.

// declare variable names, pinModes, etc.
void loop(){
currentMicros = micros();
if ( (currentMicros - nextMicro) >= interval){
PIND = 0x00010000;  // toggle D4 - writing 1 to PINx toggles a bit
nextMicro = nextMicro + interval; // time to next flip
}
//
// do other stuff while waiting 
//
} // end loop

The clock speed may not be precise and temperature will effect it. The time to execute functions (like digitalWrite) may also need taking into consideration.
Another tip is to disable interrupts if possible (you would need to use micros for delay then) and put pin on/off code in it's own while loop as when the normal loop() ends I think it goes of and does other things before entering loop again so you cannot rely on it for exact timing.

CrossRoads:
Try using micros() instead.

That - and CrossRoads' code shows the right way to do it.

What degree of accuracy do you want? You may need to choose an
Arduino with a quartz crystal based clock (not the current Uno's for example,
although they can have their ceramic resonator replaced with a crystal).

Timer1 can be configured to a wide range of frequencies and duty cycles
on pin 9 or 10 (on the Uno).

void setup ()
{
  pinMode (9, OUTPUT) ;
  pinMode (10, OUTPUT) ;
  TCCR1A = 0xE2 ;  // pins 9 and 10 in antiphase, mode 14 = fast 16 bit
  TCCR1B = 0x1A ;  // clock divide by 8
  ICR1  = 20000-1 ;  // 20000 * 8 = 160000 clocks = 10ms
  OCR1A = 10000-1 ;  // 50% duty cycle for pin 9
  OCR1B = 10000-1 ;  // 50% duty cycle for pin 10
  TCNT1 = 0x0000 ;
}

void loop ()
{}

Gives a 100Hz antiphase signal on pins 9 and 10 that's immune to jitter from
interrupts and only limited in accuracy by the system clock.

Hey, I need to simulate a pulse output from a motorcycle crankshaft speed sensor.

This 100 hz code can be 6000 RPM. I'll need a few more, lower frequencies as well, down to
to 50 hz.

I just have a couple of questions.

  • Will this simulate a pulse from a hall sensor or am I mis-understanding?
  • To get the lower frequencies, I can just change the multiplier in ICR1?

I'm not that up to speed on Timer1, yet. Can anyone tell me how to get 50, 60, 70, 80 and 90 hz, also. I would be very grateful for the tip.

ICR1 = 20000-1 ; // 20000 * 8 = 160000 clocks = 10ms

almo1010:
I'm not that up to speed on Timer1, yet. Can anyone tell me how to get 50, 60, 70, 80 and 90 hz, also. I would be very grateful for the tip.

ICR1 = 20000-1 ; // 20000 * 8 = 160000 clocks = 10ms

The missing information is that the CPU frequency is 16 MHz = 16000000 Hz
100 Hz => 10 ms => 160000 clocks => /8 = 20000 ==> ICR1 = 20000-1

50 Hz => 20 ms => 320000 clocks => /8 = 40000 ==> ICR1 = 40000-1

think you can fill in the missing values spots by now...


(why not add a potmeter to you analog port and calculate the value constantly

void setup ()
{
  pinMode (9, OUTPUT) ;
  pinMode (10, OUTPUT) ;
  TCCR1A = 0xE2 ;  // pins 9 and 10 in antiphase, mode 14 = fast 16 bit
  TCCR1B = 0x1A ;  // clock divide by 8
  ICR1  = 20000-1 ;  // 20000 * 8 = 160000 clocks = 10ms
  OCR1A = 10000-1 ;  // 50% duty cycle for pin 9
  OCR1B = 10000-1 ;  // 50% duty cycle for pin 10
  TCNT1 = 0x0000 ;
}

void loop ()
{
  int pot = analogRead(A0);
  int  hz = map(pot, 0, 1023, 50, 100);
  uint16_t clocks = 16000000UL/ Hz ;
  uint16_t val = clocks/8;

  ICR1 = val -1;
  OCR1A = val/2 - 1 ;  // 50% duty cycle for pin 9
  OCR1B = val/2 - 1 ;  // 50% duty cycle for pin 10

/*
  Serial.print(pot);
  Serial.print("\t");
  Serial.print(Hz);
  Serial.print("\t");
  Serial.print(clocks);
  Serial.print("\t");
  Serial.print(val);
  Serial.println();
*/
}

A second potmeter could be used to set the duty cycle (left as homework :wink:

Hi, mrkva what part of the world do you come from, if you have 50Hz AC mains then you can produce a 100Hz output from that.

You haven't said if the arduino is doing anything else apart from producing the 100Hz, if it is doing anything else then that will also effect to the timing, so a mains based 100Hz would be easier.

I also think there are IC's which with a crystal can give you 50Hz/60Hz.

Hope this helps.

Tom.... :slight_smile:

That's it! Thanks. I didn't know which direction that 20000-1 should go. (or was it that -1 that should change?) It will be easy now with that clue. And yes, control will be a pot to simulate crank speed changes and another pot simulates throttle position changes. The bike has CAN bus, and this will be an interface.

Then using map() and a 2D array() I look up values to apply gain multipliers, just like fuel maps on the bike.

I appreciate the help and the extra code!

Cheers.

Note that map() uses integer math and truncates values. Might cause some side effects, but I do not expect serious ones as you scale down quite some factor.

I too would vote on you just simply using the tone() library.

Keep in mind that the accuracy (which you have to define for your application) of any method given in this posting thread is based on the assumption that your crystal is oscillating at exactly 16,000,000 Hz, which will not be the case as there is always some variation in any crystal/circuit.

The current rev3 mega and uno boards don't even use a quartz crystal but rather a ceramic resonator, so frequency accuracy will have even a larger variation.

Exactly. I count on the the integer math in map(). The algorithm I made uses attachInterrupt(), and counts each event for 200 milliseconds. If count is under 10, the crank speed is not high enough. Restart the count.

For 10 events, subtract 10 and get the zero position for the y axis, etc.

And then I do the same for the voltage representing 1/2 throttle, say (2.5 volts - 2.5) give the zero for x in the array.

This will be able to meter in things like, alcohol injection or Nitrous Oxide, or even a supercharger
to dial that against fueling tables on a dynamometer to make best power.

Hi, mrkva ,sorry it seems this thread has been HIJACKED....
Have you had any success with your project.

Tom........ :slight_smile:

PS, Come on guys, I know both problems are to do with timing, but different implementations, if I was mrkva I'd have a good reason to be completely disillusioned (could have used stronger word). Probably is because he has gone quiet, although its only been nearly 24hours.

I've just never looked at forum life that way. Sun's only gone around once, Mate.
He is probably still coding against the anti-phase clock answer. :slight_smile:

But, on that subject, is there a way to use a more accurate external clock.

I wish he would say if any of this helped him. It certainly did me.