Creating Square Waves with Programmable Frequencies?

Write your own function like this:

void squareWave(int freq) {
  float x = 500/freq;
  digitalWrite(speedoPin, HIGH);
  delay(x);
  digitalWrite(speedoPin, LOW);
  delay(x);
}

This code will give you a square wave with 50% duty cycle

I'll be using attachInterrupt to monitor the speed, so I'm worried that will throw off the timing of a programming in my own squarewave using delay(xx).... When the ISR is executing, the square wave will get thrown off, won't it?

You will be changing a pin state to make a square wave edge? Why would it differ?

GoForSmoke:
You will be changing a pin state to make a square wave edge? Why would it differ?

Sorry, I'm not following your point? I will be using an attachInterrupt to monitor the original speedometer pulse, then looking to program a PWM output frequency to drive a speedometer guage....

Switch around your logic then, give the pin change job to a timer interrupt at your known interval, then in your loop check the speedo.
The timer then has 'higher priority' over the speedo check.

Also if you continually run the timer, you can disable timer0 ( millis / delay... ) and count intervals of your own timer unless you need the finer resolution.

Using PWM will be easier I think, the lost microseconds can just be fixed every couple of seconds/minutes

PWM runs at about 490 Hz and gives more or less time to HIGH than LOW. To change the PWM frequency as opposed to the PWM duty cycle you must change some clocks you might not want changed.

At the speeds you give, it'd be easier to toggle a pin by repeating time-out code and then maybe varying the time-out.

5 Hz is 200 ms, 50 Hz is 20 ms.
1 ms is 5 analog reads and processing. 1 ms is 500 digital reads with processing. 1 ms may be 100 to 1000 times through loop().
And you want to change a pin every 20 to 200 ms. Yeah, you can do that. You need 8 bytes RAM per timer though.

There are other ways. If you use intervals of ms in powers of 2 then you can mask the top bits of millis() off and rollover to 0 will come sooner. millis() & 0x0000007F rolls over every 128 ms.

Ok, after a little testing, it seems the original frequency can range from about 5Hz to as fast as about 300Hz. The idea is measure that frequency, adjust it a bit based on the setting of a trim pot, and create an adjusted frequency to output. With that as the goal, please comment on this as an approach:

//measures timing of square wave on pin 20 (interrupt 3)
//adjustes that speed based on setting of pot on pin A0
//creates a square wave with adjusted frequency on pin 21

volatile int adjustedFreq;  
volatile int latestSpeed;
volatile int lastSpeed;
unsigned long startTime;
unsigned long now;
int speedoPin =21;


void setup() {
  pinMode(speedoPin, OUTPUT);
  attachInterrupt(3, iSr, CHANGE);}
 
 
void loop()
//creates square 
{
  startTime=micros(); now=startTime;
  while (now-startTime<adjustedFreq) {speedoPin=HIGH; now=micros();}
  startTime=micros(); now=startTime;
  while (now-startTime<adjustedFreq) {speedoPin=LOW; now=micros();}
} 
  
void iSr()
//calculates length of most recent pulse and adjusts it based on pot setting on A0
{
  latestSpeed=micros();
  adjustedFreq=(latestSpeed-lastSpeed)*(analogRead(A0)/512);  
  lastSpeed=latestSpeed;
}

Moderator edit:
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags added.

If you want to change a pin, you have to write to the pin. At least that looks like what you might think you're coding with speedoPin=HIGH (funny, it started as 21) and LOW.

You might want to slow down a little and check your syntax against actual C/C++.

The tone library seems like the easiest way to produce a variable frequency square wave.

To generate a 300 Hz wave you need to switch LOW to HIGH to LOW etc 600 times a second, once every 1,666 usec. 5 Hz, every 100,000 usec.

pseudocode:
loop()
{
now = micros();
if ( now - lastTime >= halfCycle )
{
// change the pin
lastTime=now;
}
// any other code like watching for serial available or sensor data goes here
}

Learn BlinkWithoutDelay to learn to switch things on and off at proper times together. You want to generate frequency and monitor it at the same time, the basics are in BlinkWithoutDelay.

But for your use, change millis() timing to micros(). Accuracy will be 4 usec but how much does that matter when the interval is well over 1000?

Ok, using that logic from BlinkWithoutDelay, how about coding it like this? Based on the math posted above, it would seem that the procesor is plenty fast enough to handle an interrupt 300 times per second while still executing the loop code, right?

const int trimPot = A0;            //voltage divider to adjust speedometer output
const int speedPin =  21;          //pin for new pulse going to speedometer
int pulseState = LOW;             //newPulseState used to set the pulse
unsigned long currentMicros = 0;   //to measure new pulse    
unsigned long previousMicros= 0;   //to measure new pulse  
unsigned long currentSpeed = 0;    //to measure incoming pulse
unsigned long previousSpeed = 0;   //to measure incoming pulse
unsigned long interval = 0;        //calculated new pulse length

void setup() 
{
pinMode(speedPin, OUTPUT);   
attachInterrupt (3, iSr, CHANGE);
}

void loop()
{
   currentMicros = micros();  //get the current micros count
   if(currentMicros - previousMicros > interval) //check to see if it's time to change 
    {
    previousMicros = currentMicros;   //keep track of the last time pulse changed
    if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;
    digitalWrite(speedPin, pulseState);  //flip the state of the new sqaure wave
    }
}
 
void iSr()
{
  currentSpeed=micros();
  interval=(currentSpeed-previousSpeed)*(analogRead(trimPot)/512);  
  previousSpeed=currentSpeed;
}

You need to make variables accessed by the ISR volatile, as in volatile unsigned long currentMicros.

A 104 usec analog read in an ISR? Oh no. Interrupts are off inside an ISR.

How about set a flag in the ISR that tells your regular code it's time to cook the interval?

if(currentMicros - previousMicros >= interval)

Appreciate your help. How about like this:

const int trimPot = A0;            //voltage divider to adjust speedometer output
const int speedPin = 21;          //pin for new speed pulse train
int pulseState = LOW;             // state used to set the pulse
volatile unsigned long currentMicros = 0;   //to measure new pulse    
volatile unsigned long previousMicros= 0;   //to mark prior pulse  
unsigned long currentSpeed = 0;    //to measure incoming pulse
unsigned long previousSpeed = 0;   //to mark prior pulse
unsigned long interval = 1;        //original pulse length
float adjustPercent = 1; //adjustment to original pulse length

void setup() 
{
pinMode(speedPin, OUTPUT);   
pinMode(13, OUTPUT);
attachInterrupt (3, iSr, CHANGE);
}

void loop()
{
   currentMicros = micros();  //get the current micros count
   adjustPercent = (analogRead(trimPot)/512);
   interval=interval*adjustPercent;
   if(currentMicros - previousMicros > interval) //check to see if it's time to change 
    {
    previousMicros = currentMicros;   //keep track of the last time pulse changed
    if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;
    digitalWrite(13, pulseState); //show pulse train on built in LED
    digitalWrite(speedPin, pulseState);  //flip the state of the new sqaure wave
    }
}
 
void iSr()
{
  currentSpeed=micros();
  interval=(currentSpeed-previousSpeed);  //how long since last change
  previousSpeed=currentSpeed;
}

ArduinoTom:
Appreciate your help. How about like this:

const int trimPot = A0;            //voltage divider to adjust speedometer output

const int speedPin = 21;          //pin for new speed pulse train
int pulseState = LOW;             // state used to set the pulse
volatile unsigned long currentMicros = 0;   //to measure new pulse   
volatile unsigned long previousMicros= 0;   //to mark prior pulse 
unsigned long currentSpeed = 0;    //to measure incoming pulse
unsigned long previousSpeed = 0;   //to mark prior pulse
unsigned long interval = 1;        //original pulse length
float adjustPercent = 1; //adjustment to original pulse length

void setup()
{
pinMode(speedPin, OUTPUT);   
pinMode(13, OUTPUT);
attachInterrupt (3, iSr, CHANGE);
}

void loop()
{
   currentMicros = micros();  //get the current micros count
   adjustPercent = (analogRead(trimPot)/512);
   interval=interval*adjustPercent;
   if(currentMicros - previousMicros > interval) //check to see if it's time to change
    {
    previousMicros = currentMicros;   //keep track of the last time pulse changed
    if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;
    digitalWrite(13, pulseState); //show pulse train on built in LED
    digitalWrite(speedPin, pulseState);  //flip the state of the new sqaure wave
    }
}

void iSr()
{
  currentSpeed=micros();
  interval=(currentSpeed-previousSpeed);  //how long since last change
  previousSpeed=currentSpeed;
}

interval needs to be volatile and decide what it's really for.

pulseState should be type byte.

   adjustPercent = (analogRead(trimPot)/512);
   interval=interval*adjustPercent;

The above code may run many times before interval is up, the dial does not set frequency and the ISR interval change is another mystery, also there's better ways than float.

Are you wanting to set the frequency with a dial? And the ISR to read the frequency? Or control frequency at least partly through the ISR?

Is that variable interval supposed to be changed by the ISR (triggered by writes in the code after interval is up) and then changed/initialized next time through loop()?

C code ^ is bitwise xor.
bool A xor bool B returns 1 if A and B are different, 0 if the same.

You could try
pulseState ^= 1; // flips pulseState between 0 and 1
or if that doesn't compile:
pulseState = pulseState ^ 1; // flips pulseState between 0 and 1

You can use timer 1 to generate the signal you want directly. Here is the code I use:

#include "arduino.h"

#if defined(__AVR_ATtiny85__)

// Output is on physical pin 6 (= PB1/MISO/OCR1A)

#else

// Output is on Arduino pin 9 (= PB1/OC1A)
const int ocr1aPin = 9;

#endif

// Set the frequency that we will get on pin OCR1A
void setFrequency(uint16_t freq)
{
  uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

#if defined(__AVR_ATtiny85__)
  // Code for attiny85
  uint16_t prescalerVal = 1;
  uint8_t prescalerBits = 1;
  uint32_t maxVal = 256;
  while (requiredDivisor > maxVal)
  {
    ++prescalerBits;
    maxVal <<= 1;
  }
  
  uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
  TCCR1 = (1 << CTC1) | prescalerBits;
  GTCCR = 0;
  OCR1A = top;
#else
  // Code for atmega328p
  uint16_t prescalerVal;
  uint8_t prescalerBits;
  if (requiredDivisor < 65536UL)
  {
    prescalerVal = 1;
    prescalerBits = 1;
  }
  else if (requiredDivisor < 8 * 65536UL)
  {
    prescalerVal = 8;
    prescalerBits = 2;
  }
  else if (requiredDivisor < 64 * 65536UL)
  {
    prescalerVal = 64;
    prescalerBits = 3;
  }
  else if (requiredDivisor < 256 * 65536UL)
  {
    prescalerVal = 256;
    prescalerBits = 4;
  }
  else
  {
    prescalerVal = 1024;
    prescalerBits = 5;
  }

  uint16_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
  TCCR1A = 0;
  TCCR1B = (1 << WGM12) | prescalerBits;
  TCCR1C = 0;
  OCR1A = (top & 0xFF);
#endif
}

// Turn the frequency on
void on()
{
#if defined(__AVR_ATtiny85__)
  TCNT1 = 0;
  TCCR1 |= (1 << COM1A0);
#else
  TCNT1H = 0;
  TCNT1L = 0;  
  TCCR1A |= (1 << COM1A0);
#endif
}

// Turn the frequency off and turn off the output
void off()
{
#if defined(__AVR_ATtiny85__)
  TCCR1 &= ~(1 << COM1A0);
#else
  TCCR1A &= ~(1 << COM1A0);
#endif
}

void setup()
{
#if defined(__AVR_ATtiny85__)
#else
  digitalWrite(ocr1aPin, LOW);
  pinMode(ocr1aPin, OUTPUT);
#endif
}

Thanks again. I am trying to measure the frequency of a square wave on interrupt 3, alter it slightly with the trim pot, and output the altered frequency on pin 20.

Per frequency generated you need an start micros and interval micros.

If you want to adjust one, change the interval but do it where the adjustment won't get made as many times as there are micros in the interval.

JavaMan:
Write your own function like this:

void squareWave(int freq) {

float x = 500/freq;
  digitalWrite(speedoPin, HIGH);
  delay(x);
  digitalWrite(speedoPin, LOW);
  delay(x);
}




This code will give you a square wave with 50% duty cycle

I've been trying this method and I'm not getting a 50% square wave, it seems to be way smaller than 50. Any suggestions? I really need to produce a 50% square wave with a frequency around 10 kHz

sophiastruckman:
I've been trying this method and I'm not getting a 50% square wave, it seems to be way smaller than 50. Any suggestions? I really need to produce a 50% square wave with a frequency around 10 kHz

See reply #16.