Frequency Square Wave

I'm trying to get a square wave between 1000 HZ and 100000 HZ.
I have the hardware all setup but the frequency doesn't go lower then 2000 HZ and above 4000 HZ it loses a couple hundred HZ.

This is my code:

Const int PIN = 3;

int RPM = 8000;
int PPM;
int PPS;
int Microsecs;
int interval;

void setup(){
  pinMode(PIN, OUTPUT);
  Serial.begin(9600);
}

void loop(){
  PPM = RPM;
  PPS = PPM/60;
  interval = 1000000/PPS;
  Microsecs = interval/2;
  digitalWrite(PIN, HIGH);
  delayMicroseconds(Microsecs);
  digitalWrite(PIN, LOW);
  delayMicroseconds(Microsecs);
}

When i input a direct delay instead of the microsecs it works a lot better.
Any idea What is going wrong here?

Well first you do lots of maths in the loop, so that will slow things down
Then you are using an int which is only 16 bits on uno and the likes
Last you are using digitalWrite which is notoriously "slow" (when you want really fast operations)

If you enter a fixed value for the delay, as the other variables are not used the compiler just get rid of them and you don’t have the math anymore at each loop

1 Like

Have you tried the tone() function?

1 Like

If you want accurate sqaure wave frequency at DPin-9 (1 kHz to 100 kHz), you may go with CTC-4 Mode of TC1 (Timer/Counter 1) of the MCU of UNO Board.

Sample Sketch:

void setup()
{
  pinMode(9, OUTPUT);   //output is at DPin-9  
  
  TCCR1A = 0x00;
  TCCR1B = 0x00;

  bitClear(TCCR1B, WGM13);
  bitSet(TCCR1B, WGM12);
  bitClear(TCCR1A, WGM11);
  bitClear(TCCR1A, WGM10);

  bitClear(TCCR1A, COM1A1);
  bitSet(TCCR1A, COM1A0);
 

  OCR1A = 7999;  // f = 16 MHz/(2 x N (1+OCR1A); for 1000 Hz, OCR1A = 7999
  TCNT1 = 0x0000;

  bitClear(TCCR1B, CS12); //start TC1 with prescaler N = 1
  bitClear(TCCR1B, CS11);
  bitSet(TCCR1B, CS10);
}

void loop(){}
1 Like

Yes, tone() is the obvious solution. (It may not be absolutely perfect to 1Hz... There may be errors related to rounding, or remainders after frequency-division, or whatever it's doing. The CPU clock also as accuracy limits/tolerance.)

Right... Every instruction in the loop takes time and that gets added to the delay.

If you use micros(), similar to the Blink Without Delay Example, that's not a problem but there is still a limit to how fast you can loop.

The tone() function uses timers.

This from Brett Hagman's notes on the tone() function:

The range of frequencies that can be produced depends on the microcontroller clock frequency and the timer which is being used:

MCU clock 8 bit timer Flow 16 bit timer Flow Fhigh
8 MHz 16 Hz 1 Hz (1/16 Hz) 4 MHz
16 MHz 31 Hz 1 Hz (1/8 Hz) 8 MHz

I believe the Tone function maxes out at 65535 Hz (uint16_t)?

Yes on 8 bit architecture as it’s using an unsigned int so 16 bits (but would be 32 bits on a MKR or ESP)

Well, the OP didn't say which Arduino, so...

Yes indeed

My point was that the function does not use the fixed width type (uint16_t)

Which Arduino are you using?

If you use the ESP32, you can generate frequencies from 1 HZ to 40 Mhz regardless of the code running.
Using the LEDC feature of the ESP32.

I'm using the Arduino Nano Every.
I've tried to use the Tone() function which does work.
But i still can't get a higher frequency then 500HZ.
I doubt its because i'm on the limit of what frequency the transistor and optocoupler can manage.
I do see that there is an additional wire in my gauge which is what i need to control.
It says it needs to be pulled up in case of a "Open collector ECU output", no idea what this means.
Any suggestions?

Please post a circuit diagram of what you have, and a link to the gauge you are trying to drive.



This is the gauge and the circuit i use

I would certainly try a 10K pullup to 12v on the Brown/state wire of the tachometer.

Sadly when i put a 10K resistor between the brown wire and 12V nothing works.

What transistor are you using on the high side of the gauge?

If its an NPN it should be between the gauge and ground.

Do you have a scope to look at the signals?

I'm using the 2N5401 which is a PNP transistor.
I might be able to use a mosfet, i have one laying around, do you think that would work?
I don't have a scope as i normally only use thing that are either constantly on or off.

mosfet, i have one laying around, do you think that would work?

I'm not a hardware guy, but I'm not certain there would be any advantage over what you are using.

The guide shows six wires. Can you provide the complete information of what you have connected to where?

Connect green wire to +12V supply, black wire to 12V ground ( - ), red / blue wire to Arduino output pin, 12V ground to Arduino GND. Use the Tone function to apply a 500Hz signal to the output pin.