Arduino Due timing problem

I have need to generate two square waves of differing frequencies on different GPIOs. For reasons related to the project, these frequencies may change every 500 µs.

To accomplish this, I’m utilizing the DueTimer library. I have Timer1 that triggers every 500 µs, and calls Timer2. Timer2 will execute every 50 µs. Every time Timer2 executes it toggles a GPIO.

Test code:

#include <DueTimer.h>

boolean state = true;
boolean state2 = true;

void setup()
{
    pinMode(23, OUTPUT);
    pinMode(25, OUTPUT);
    Timer1.attachInterrupt(primaryTimer).start(500);
}

void loop()
{
// put your main code here, to run repeatedly:
}

void primaryTimer()
{
    Timer2.attachInterrupt(secondaryTimer).start(50);
    digitalWrite(23, state);
    digitalWrite(25, state2);
    state2 = !state2;
}

void secondaryTimer()
{
    state = !state;
    digitalWrite(23, state);
}

I would expect pin 23 (Timer2, top trace) to toggle every 50 µs, and pin 25 (Timer1, bottom trace) to toggle every 500 µs. Checking the output with an oscilloscope, this isn’t what happens.

Everything is almost right, but pin 23 (Timer2) does not toggle the last time.

I’ve tried different timers, and a mechanism to ensure Timer2 ran exactly the right number of times, but no matter what Timer2 does not toggle the last time. I’m stumped. Anyone know what’s going on?

Try this:

volatile boolean state = true;
volatile boolean state2 = true;

Pete

That was a good idea; I thought it would work for sure.

Sadly, I still don't get the last toggle.

Try this, just to see what it does:

#include <DueTimer.h>

volatile boolean state = false;
volatile boolean state2 = false;

void setup()
{
    pinMode(23, OUTPUT);
    digitalWrite(23,0);
    pinMode(25, OUTPUT);
    digitalWrite(25,0);
    Timer1.attachInterrupt(primaryTimer).start(500);
    Timer2.attachInterrupt(secondaryTimer).start(50);
}

void loop()
{
// put your main code here, to run repeatedly:
}

void primaryTimer()
{
    state2 = !state2;
    digitalWrite(25, state2);
}

void secondaryTimer()
{
    state = !state;
    digitalWrite(23, state);
}

I haven’t got a Due so I can’t try this myself.

Pete

Now we're getting somewhere. That code produces this, which is what I'd like to see.

However, I still need one timer to trigger another, I think.

I suppose a bit of background is in order. The project is to store data on an audio cassette, like the old Vic-20 days. A high frequency square wave is a 1, low frequency wave is a 0 (FSK).

Every 500 µs starts a new bit. The bit timing needs to be as close to perfect as possible, so I have a master 500 µs timer that calls a sub timer with the appropriate period that actually makes the signal. The sub timer should only run a certain number of times; it should stop before the primary timer triggers again.

I'm worried if I just set two timers to their appropriate frequencies and cycle counts, and let them run free, they won't time exactly 500 µs. As more bits are written, the error will accumulate until the bits are no longer in sync with the 500 µs-per-bit rate.

In other words, the signal timers shouldn't start the next bit, because the signal timers may not take exactly 500 µs, which will offset the next bit, and the next one even more, and so on. I think having a master clock will ensure each sub clock starts at exactly the right time. As long as the sub clock ends before the next master clock, it doesn't matter if the sub clocks have a little error.

At least I now know it's possible to have two clocks running simultaneously. I'll do some reading and coding and see if the issue lies with cascading timers.

Good.
Now give this a try:

#include <DueTimer.h>

volatile boolean state = false;
volatile boolean state2 = false;

volatile unsigned char toggle_count;
void setup()
{
    pinMode(23, OUTPUT);
    digitalWrite(23,0);
    pinMode(25, OUTPUT);
    digitalWrite(25,0);
    Timer1.attachInterrupt(primaryTimer).start(500);
}

void loop()
{
// put your main code here, to run repeatedly:
}

void primaryTimer()
{
    state2 = !state2;
    digitalWrite(25, state2);
    toggle_count = 6;
    Timer2.attachInterrupt(secondaryTimer).start(50);
}

void secondaryTimer()
{
    if(toggle_count) {
    	state = !state;
    	digitalWrite(23, state);
    	toggle_count--;
    	if(toggle_count == 0)Timer2.detachInterrupt(secondaryTimer);
    }
}

Pete

Curiouser and curiouser.

I had to edit your code slightly…

#include <DueTimer.h>

volatile boolean state = false;
volatile boolean state2 = false;

volatile byte toggle_count;
void setup()
{
    pinMode(23, OUTPUT);
    digitalWrite(23,0);
    pinMode(25, OUTPUT);
    digitalWrite(25,0);
    Timer1.attachInterrupt(primaryTimer).start(500);
}

void loop()
{
// put your main code here, to run repeatedly:
}

void primaryTimer()
{
    state2 = !state2;
    digitalWrite(25, state2);
    toggle_count = 8;
    Timer2.attachInterrupt(secondaryTimer).start(50);
}

void secondaryTimer()
{
    if(toggle_count) {
      state = !state;
      digitalWrite(23, state);
      toggle_count--;
      if(toggle_count == 0)Timer2.detachInterrupt();
        
      
    }
}

…as unsigned chars should be bytes, and detachInterrupt() accepts no arguments. Nevertheless the results are strange. Everything works as expected when toggle_count is set to 7 or less. Setting toggle_count to 8 or more results in this output. The first cycle toggles 8 times, the next cycle toggles only 7, then 8, and so on.

The change in behavior from 7 to 8 is interesting, since at 7 toggle_count is using 3 bits, and at 8 it’s using 4. Probably a red herring, since byte is 8 bits wide anyway. I tried toggle_count as int and unsigned char just to be sure, with the same result.

I’m even more confused than before.

The bit that mystifies me with the latest one is that the 50us pulses don't start in sync with the 500us pulses. I'll have to ponder it.

Pete

I believe I’ve figured it out.

When the primaryTimer() function is called, Timer1 begins counting for the next interrupt immediately. Sometime later, Timer2 is invoked. Since Timer1 has a head start on Timer2, Timer2 will never be able to execute the last toggle before Timer1 calls it again. Once Timer2 is called anew, it waits an additional 50 µs before toggling again, resulting in a long period of no signal change.

One solution is to include a toggle and write in the primary timer, essentially replacing the interrupted toggle.

#include <DueTimer.h>

volatile boolean ch1State = false;
volatile boolean ch2State = false;
volatile boolean tempSwitch = false;

void setup() {
  pinMode(28, OUTPUT);
  pinMode(30, OUTPUT);

  Timer0.attachInterrupt(primary).start(5000);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void primary()
{
  tempSwitch = !tempSwitch;
  
  ch1State = !ch1State;
  digitalWrite(28, ch1State);
  ch2State = !ch2State;
  digitalWrite(30, ch2State);
  
  if (tempSwitch)
  {
    Timer1.attachInterrupt(ch1Control).start(500);
    Timer2.attachInterrupt(ch2Control).start(833);
  }
  else
  {
    Timer1.attachInterrupt(ch1Control).start(833);
    Timer2.attachInterrupt(ch2Control).start(500);
  }
}

void ch1Control()
{
  ch1State = !ch1State;
  digitalWrite(28, ch1State);
}

void ch2Control()
{
  ch2State = !ch2State;
  digitalWrite(30, ch2State);
}

This produces exactly the sort of output I’m looking for. The only issue is that those timings were 10 times slower than I need. Going back to 500 µs, I get this, which is the initial problem all over again.

Another solution is to forgo the secondary timer altogether, and simply call a delay loop every 500 µs.

#include <DueTimer.h>

volatile boolean ch1State = false;
volatile boolean ch2State = false;
volatile boolean tempSwitch = false;

void setup() {
  pinMode(28, OUTPUT);
  pinMode(30, OUTPUT);

  Timer0.attachInterrupt(primary).start(500);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void primary()
{
  tempSwitch = !tempSwitch;
  
  if (tempSwitch)
  {
    makeFreq(50, 10);
  }
  else
  {
    makeFreq(83, 6);
  }
}

void makeFreq(byte period, byte cycles)
{
  while (cycles > 0)
  {
    ch1State = !ch1State;
    digitalWrite(28, ch1State);
    delayMicroseconds(period);
    cycles--;
  }
  
}

The result is just what I need. Obviously this will only work for one channel (I need two; both left and right), but with a bit of calculation I can make a lookup table that contains the timing differences between channels for a given pair of bits. The primary timer can call some function that will iterate through the proper sequence of delays. Something like a one-armed drummer.

Anyhow, I appreciate the input; you’ve been most helpful.

False alarm. This method will not work either. Since the program is delayed nearly the entire time it’s running, only the 500 µs interrupt will continue properly.

Various housekeeping things: topping off the buffer, iterating to the next bit, checking if done, etc. will never complete; or only run in fits and starts.

For future readers with a similar problem: I’ve come to the conclusion the Due simply can’t generate a FSK modulated signal very easily; or perhaps any sort of accurate, variable timing. It can be done somewhat for very low bit-rates (200 baud or so), but anything much faster will be a pain.

I’m sure there are some other measures I could have taken. I could have poked around the registers, or played around with timing library some more; it may have even worked. But at the end of the day, I’m getting into some rather complicated programming gymnastics to accomplish a fairly simple task. At times like these, and at least with the Due, I think a software solution is not the best tool for the job.

To that end, I made up a super simple circuit in about an hour that seems to have solved the problem entirely. Start with two 555 timers set to the appropriate frequencies for high and low; in this case 10 kHz and 6 kHz. Add two multiplexers. Connect the 555 timer outputs to inputs A and B on the multiplexers. The outputs of the multiplexers are sent to a scaling circuit, and then the left and right channels on the tape deck.

Now, all the Arduino needs to do is evaluate every 500 µs, and switch the appropriate multiplexers. It has the entire 500 µs period to do all the housekeeping, and there are no issues with conflicting timers. The output isn’t a nice neat wave; and since the timers run free, the multiplexer may switch in while it is in the middle of a waveform. However, none of this should matter (theoretically), since only the number of transitions per 500 µs window is needed. One transition more or less should be tolerable.

The new code is also much simpler

#include <DueTimer.h>

volatile boolean ch1State = false;
volatile boolean ch2State = false;
volatile boolean tempSwitch = false;

void setup() {
  pinMode(50, OUTPUT);
  pinMode(52, OUTPUT);

  Timer0.attachInterrupt(primary).start(500);
}

void loop() {
  // put your main code here, to run repeatedly:

}

void primary()
{
  tempSwitch = !tempSwitch;
  
  if (tempSwitch)
  {
    digitalWrite(50, 0);
    digitalWrite(52, 1);
  }
  else
  {
    digitalWrite(50, 1);
    digitalWrite(52, 0);
  }
}