Programming Outputs As Squarewaves

For starters I'm using an Arduino Atmega328. I have access to Arduino Uno if necessary but I'd rather just use the Atmega328. Now as for the programming; what I'm trying to to do is take a 0.5V -4.5V input and turn it into a 0V - 5V square wave output ranging from 0hz - 750hz (0.5V in = 0hz out, 4.5V in = 750hz out, etc.). I have three problem:

  1. I don't know how to use/program the output pins.
  2. I don't know how to program an output frequency.
  3. Can the Arduino even generate a 0V to 5V square wave at 0hz to 750hz?

Thank you for your help should you so choose to give it 8)

bhay:

  1. I don't know how to use/program the output pins.
  2. I don't know how to program an output frequency.

Read about the Timer/Counter feature of the AVR chip. Each of the three timers has two output pins. You can set up the timer to toggle an output pin every time the timer resets. You can set how fast the timer runs and at what count the timer resets to control the frequency. You might want to look for a timer library to do the basic work for you. Like this one:
http://playground.arduino.cc/Code/Timer1

bhay:
3. Can the Arduino even generate a 0V to 5V square wave at 0hz to 750hz?

Yes.

Can the Arduino even generate a 0V to 5V square wave at 0hz to 750hz?

Have you tried the tone library?

Thanks John, your link is proving very helpful although I'll probably need some more help since I don't completely understand how to do it yet, I'll come back with some specific questions later. And no, I haven't tried the tone libraries, what are those? (just to be clear I'm not using the ardunio to generate a frequency for sound, I'm using it to deliver a the square wave to a speedometer of sorts that will then show the speed)

Okay so "Time per tick = (prescale)*(1/frequency)" so Frequency = (prescale)/(time per tick). And then I'd assume that I'd have to do something like:

    sensorValue = analogRead(analogInPin);            
    // map it to the range of the analog out:
    outputValue = map(sensorValue, 102.4, 921.6, 0, 750);

Where 102.4(0.5V) is the bit equivalent to 0hz and 921.6 (4.5V) is the bit equivalent to 750hz. Other than that though I'm kind of stuck. I'm still not sure how to put these two things together and I have no clue how to make sure the squarewave that is does put out is 0-5V.

*Note: The analog chip has a max input of 5V and holds 1024bits so each volt is equal to 204.8bits.

I have no clue how to make sure the squarewave that is does put out is 0-5V.

The output of the timers that johnwasser talked about will be 0-5V (or close to it, an output pin will never get fully to 0V or +5V).

outputValue = map(sensorValue, 102.4, 921.6, 0, 750);

When you do this mapping, you might want to make the lowest value map to a frequency of 1Hz rather than 0Hz. The arithmetic to work out the timer constants may object to "divide by zero".

Note: The analog chip has a max input of 5V and holds 1024bits so each volt is equal to 204.8bits.

That's really quite nonsensical - the ADC is a ten bit device, so can represent 1024 different counts, 0. . .1023 inclusive, so one volt is represented by 204.8 counts for a Vref of 5V.

The output can only be a square wave from 0-5V, that's all the digital outputs can do.
And there are only digital outputs.
"analogWrite" is a misnomer, its a 0-5V PWM output, 490 Hz square wave with varying duty cycle.
750 Hz = period of 1333uS. Half that is 666uS.
You can easily write code that toggles an output every 666uS, or slower as the frequency drops. Use analogRead like you started, calculate the period needed, and feed that into "blink without delay" code (see the examples) to toggle an output.

Okay I got it for the most part (I think). Here is what I have so far (the other part of the code is for testing a switch, I think it's fine but hey, if you find something wrong with it feel free to let me know!):

// constants won't change. Used here to set pin numbers:
const int digitalOut = x;      // the number of the digital pin used to output the frequency 
const int analogInPin = A0;    // the number of the analog pin recieving the voltage for the frequecy calculations

const int digitalOut2 = x;     // the number of the digital pin used to 
const int analogInPin2 = A1;    // the number of the analog pin recieving the signal for the ignition switch


// Variables will change:
int frequency = 1;             // Set the intial frequency to 1
long previousMillis = 0;        // will store last time LED was updated
int sensorValue = 0; 

// **DO I STILL NEED THIS?!?!?!?!?!** 
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() 
{
  // set the digital pins as outputs:
  pinMode(digitalOut, OUTPUT);
  pinMode(digitalOut2, OUTPUT);
}

void loop()
{
  // read the voltage going into pin A1
  sensorValue2 = analogRead(analogInPin2);
  
  // Sets the output of digitalOut2 low
  digitalWrite(digitalOut2, LOW);
  
  // If there is there is 2.5V or more going into analogInPin2 digitalOut2 will go high 
  if sensorValue2 > 511
  {
    digitalWrite(digitalOut2, HIGH);
  }
  
  // read the voltage going into pin A0
  sensorValue = analogRead(analogInPin); 
  
  // map it to the range of the analog out:
  frequency = map(sensorValue, 102.4, 921.6, 1, 750);  
  
  period = 1/(frequency);
  
  //HOW DO I INTEREGRATE THIS WITH MY CURRENT CODE TO MAKE DIGITALOUT OUTPUT THE RIGHT FREQUENCY?
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > interval) 
  { 
    previousMillis = currentMillis;   
  }
}

The only part I'm having trouble with is getting it to actually output the frequency. Also if you find any mistakes in my code while you're looking over it please let me know :slight_smile:

f sensorValue2 > 511

The compiler won't like that.

Blink without delay manages a square wave output of roughly 0.5Hz, but could go much faster.

And does one please the almighty compiler? (basically what I'm trying to do is make it so that digitalOut2 is pushing out 5V when analogInPin2 is getting more than 2.5Volts and push out 0V when analogInPin2 is receiving less than 2.5V.

Hackscribble:
The output of the timers that johnwasser talked about will be 0-5V (or close to it, an output pin will never get fully to 0V or +5V).

The output voltage depends on the load impedance, output transistors have about
30 ohms of on-resistance so a pin driven HIGH into 10k ohms will be a few millivolts
from 5V.

How fast does the system have to respond - normally a timer/counter module won't
notice a change in a register till the end of the current cycle, which at 0Hz will be a
long time (in fact you can't do 0Hz at all, there is a minimum clocking speed).

The way to handle this problem is, I think:

  1. opamp circuit to level shift the analog voltage to the 0V to 5V range, then analogRead()

  2. DDS to generate the squarewave, using a sampling frequency well above 750Hz
    (lets say 10kHz or more).

DDS is direct digital synthesis and can do arbitrary frequency to arbitrary precision
if you use enough bits.

Basically:

volatile long frequency ;
volatile long phase ;

#define interrupt_Hz .....

void dds_interrupt ()  // typically use timer2 at tens of kHz to drive this
{
  phase += frequency ;
  digitalWrite (output_pin, phase < 0L) ;
}

float scale = pow (2, 32) / interrupt_Hz ;

void set_frequency (float Hz)
{
  long freq_value = (long) (Hz * scale) ;
  noInterrupts () ;
  frequency = freq_value ;
  interrupts () ;
}

void loop ()
{
  float Hz = 750.0 * analogRead () / 1024.0 ;
  set_frequency (Hz) ;
}

So this is my fourth attempt at doing this but for some reason it's still not working. Just to recap I want to take a 0.5-4.5V input and turn it into a 1-750hz square wave(There is also code in there that is setting outPin2 low unless there is 2.5V or more but that's not really what I'm concentrated on right now, in theory it should work fine). I don't know why it's not working, I vary the voltage in but it has little to no effect on the very inconsistent square wave (It is flipping out on the oscilloscope, it looks square like but it looks like it is vibrating rapidly on the screen and ranges from a frequency of 62hz to 129hz). Here is my code:

// constants won't change. Used here to set pin numbers:
#include <TimerOne.h>
#define pwmRegister OCR1A      // the logical pin, can be set to OCR1B

const int outPin = 9;      // the number of the digital pin used to output the frequency 
const int inPin = A0;    // the number of the analog pin recieving the voltage for the frequecy calculations

const int outPin2 = 3;     // the number of the digital pin used to 
const int inPin2 = A1;   // the number of the analog pin recieving the signal for the ignition switch

int sensorValue = 1;
int sensorValue2 = 1;

long period = 1;
long frequency = 1;

//int prescale[] = {0,1,8,64,256,1024};

void setup() 
{
  Serial.begin(9600);
  
  // set the digital pins as outputs:
  pinMode(outPin, OUTPUT);
  pinMode(outPin2, OUTPUT);
  
  Timer1.initialize(period);
}


void loop()
{
  // read the voltage going into pin A0
  sensorValue = analogRead(inPin); 
  
  // map it to the range of the analog out (0.5V to 4.5V represented by their bit equivalents is being converted into 1-750hz) :
  frequency = map(sensorValue, 102.4, 921.6, 1, 750);  
  
  Timer1.setPeriod(1 / frequency);
  
  Timer1.pwm(outPin, 512, period); 
  
  //read the voltage going into pin A1
  sensorValue = analogRead(inPin2);
  
  //Sets the output of digitalOut2 low
  digitalWrite(outPin2, LOW);
  
   //If there is there is 2.5V or more going into analogInPin2 digitalOut2 will go high 
  if (sensorValue2 > 512)
  {
    digitalWrite(outPin2, HIGH);
  }
}

^^ That code is actually right. The only thing that needed to be changed was this line: "Timer1.setPeriod(1 / frequency);" to
"period = (1 / frequency)". My main problem was there were some random really weird issues my ground.