Generating separate square-wave oscillators

Hello,

I would like to generate 4 different square-wave oscillators on separate pins. What would be the best approach? I need to have them as accurate as possible? I am already using one (16bit timer).

Regards,
Jonas

You really need to mention something about the frequency and amplitude you are looking for. Actually, you can forget about amplitude. The Arduino has one amplitude - 5V.

Well indeed, amplitude is defined by the digital outputs of the Atmega. Frequencies, lets say 1, 1.5, 2 and 2.5 Hz.
Ideally without using delays.

For frequencies that low, look at the blink without delay example.

That is not helpful - I don't want to use delays (as mentioned in previous post).

That is not helpful - I don't want to use delays (as mentioned in previous post).

Well, then maybe you should look at the example I referred to. Not a delay in the whole damned code.

PaulS mentioned: blink without delay

That seems to fit your requirements.

For that low frequency you could set up timer 1 to interrupt every half second and then toggle the appropriate pins. The time taken to decide which would be insignificant compared to the 500 mS between ticks.

Timers can run that slow. The timers each have two independent output compare units, so each can be set for different frequencies. Timer 1, being a 16 bit timer, can generate those frequencies directly. Timer 2, being only 8 bits, doesn't have the capacity to divide the system clock down that slow, but it could come close, and the rest could be done in code, which would be interrupt-driven and fairly minimal.

Sorry for the confusion. I would indeed welcome timer-based solution to this. If I can use only one timer (16-bit - because of the speed reasons), how can I easily deliver 4 different frequencies? Lets say they are not related - 1.3, 2.0, 2.7 Hz?

I published a little library named the "run" library that does pretty much what you're asking for (with software resolution, not hardware timers).

Depending on how critical your timing needs really are, you might find it adequate, or at least a starting point for understanding.

I think you'll find the example here would be easy to extend to more pins/timers: run/run.ino at master · billroy/run · GitHub

Library code and doc here: GitHub - billroy/run: Arduino function scheduler library: runs your C function at a specified interval, like the Bitlash run command

-br

how can I easily deliver 4 different frequencies? Lets say they are not related - 1.3, 2.0, 2.7 Hz?

As PaulS Said look at blink without delay. You can use this model to give you as many different frequences in the 05. Meg or less range as you want.

Mark

mrkva:
Sorry for the confusion. I would indeed welcome timer-based solution to this. If I can use only one timer (16-bit - because of the speed reasons), how can I easily deliver 4 different frequencies? Lets say they are not related - 1.3, 2.0, 2.7 Hz?

With only two output compare units, each timer can only generate two independent frequencies. Things could be done in code, though. For example, your requirements seem to be in the range of 0-10 Hz with a resolution of 0.1Hz. A timer could be configured to produce a 100ms interrupt, then code could toggle pins on any multiple of 100ms.

mrkva:
Sorry for the confusion. I would indeed welcome timer-based solution to this. If I can use only one timer (16-bit - because of the speed reasons), how can I easily deliver 4 different frequencies? Lets say they are not related - 1.3, 2.0, 2.7 Hz?

I think you need to define what acceptable accuracy is.

Simply using micros calls, and checking against a "next toggle" time should give you pretty high accuracy.

Just for the heck of it, I did this using Timer 1 and outputting 3 different square waves in the ISR:

const byte timer1Output = 9;

const int PINS = 3;
const byte outputPin [PINS] = { 4, 5, 6};
const int intervalAmount [PINS] = { 13, 20, 27 };
volatile int interval [PINS] = { 0, 0, 0 };

// here when 1/10 second elapsed
ISR (TIMER1_COMPA_vect)
  {
  
  for (byte i = 0; i < PINS; i++)
    {
    if (++interval [i] >= intervalAmount [i])
      {
      digitalWrite (outputPin [i], ! digitalRead (outputPin [i]));  // toggle it 
      interval [i] = 0; 
      }  // end if time up
    }  // end of for each pin
  }  // end of TIMER1_OVF_vect


void setup () 
{
  // stop Timer 0 interrupts from throwing it out
  TCCR0A = 0;    // stop timer 0
  TCCR0B = 0;    

  // output pins
  for (byte i = 0; i < PINS; i++)
    pinMode (outputPin [i], OUTPUT);
  
  // set up Timer 1
  pinMode (timer1Output, OUTPUT); 

  TCCR1A = _BV (COM1A0);            // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS12);  // CTC, prescale 256
  OCR1A =  6249;                    // count to 6250  - that is it will toggle every 1/10 of a second
  TIMSK1 = _BV (OCIE1A);            // interrupt on compare match
} // end of setup

void loop () { }

This isn’t exactly what you want, because I did a period of 1.3, 2.0 and 2.7 not a frequency. Plus, that was between each toggle, so the actual period would be double that.

However it illustrates the idea. You just have to tweak the intervals.

The basic resolution of this will be 1/10 of a second, because that is how often the interrupt is called. If you make OCR1A lower, you get a higher resolution (but have to increase the counts accordingly).

You can see from the analyzer output that the width of the pulse on pin 4 is 1.3 seconds (as expected) with a small error amount:

Frequencies, lets say 1, 1.5, 2 and 2.5 Hz.

Fairly easy.

  1. set up a free running timer (a timer with auto reloading works as well).
  2. In the isr, advance four counters, each corresponding to one of the four frequencies above.
  3. Flip a pin as a result of the frequencies.

Something like this:

ISR:
  static unsigned long counter_1000ms=0, countre_1500ms=0, ...;
  counter_1000ms += 0x10000ul; //for free-running 16-bit timer
  if (counter_1000ms >= F_CPU) {
    counter_1000ms -= F_CPU; //reset the counter
    //flip the pin for 1hz
  }

  counter_1500ms += 0x10000ul; //for free-running 16-bit timer
  if (counter_1000ms >= F_CPU * 2 / 3) {
    counter_1500ms -= F_CPU * 2 / 3;
    //flip the pin for 1.5hz
  }

You can pre-define the constants to make it easier to read.