Announce: Using Sandwich Delays to schedule timed tasks in super loops on X2 chips

hippy

Technical Support
Staff member
I meant to post earlier to congratulate you for that. I am not sure "Sandwich Delays" are an entirely new or novel concept but useful and I don't recall anyone else having used DOZE for synchronising timing. It's not just useful for separate tasks but for overall loop timing -
Code:
#Picaxe 20X2
#No_Data
#No_Table
#Terminal 9600

SetFreq M8
SetTimer 3036 ; Interrupt every 250ms at 8MHz

Do
    Gosub SleepFiveSeconds
    SerTxd( #w0, " " )
    w0 = w0 + 1
Loop

SleepFiveSeconds:
    Gosub SleepOneSecond
    Gosub SleepOneSecond
    Gosub SleepOneSecond
    Gosub SleepOneSecond
    Gosub SleepOneSecond
    Return

SleepOneSecond:
    Doze 0
    Doze 0
    Doze 0
    Doze 0
    Return
 

Flenser

Senior Member
Hippy,

Thanks for the comment. I don't remember ever hearing sandwich timers described. Affer I read about PTTES the idea of finding out how well they could be made to work on the PICAXE chips fascinated me and I am very pleased with the result.

It was taking me a long time to prepare my project post so I keep it short to focus on getting the general idea across clearly.

Looking at the code in your post #2 there are a couple of points I wanted to add about the implications of using the hardware interrupt + the DOZE command to implement the sandwich timers.

1) If someone wanted to schedule a task every 5s using sandwich timer code that generates a hardware interrupt every 250ms, like in your example, then the code for their task must always complete in well under 250ms.

If the task runs longer than the sandwich delay, for example 400ms, then one hardware interrupt will occur while the task code is running. This hardware interrupt will not effect the running of the task code but it will be missed by our sandwich timer code and this would mean that the main loop will execute every 5.250 ms, not ever 5s.

NOTE: When you calculate the time the task takes to execute you must include an allowance for the overhead of the sandwich timer code that runs between the last DOZE command before your task starts and the first DOZE command after your task ends. i.e. this is the RETURN command from the last execution of SleepOneSecond, the RETURN command from SleepFiveSeconds and the jump back to the start of the main do loop.

2) We are using the hardware timer to wake the chip from the DOZE command and when the hardware timer overflows to zero the preload value is loaded back by the timer hardware with no delay. This means that our main loop will occur _exactly_ every 5 sec as counted by the hardware timer. There is some jitter in the time for each loop, which I assume iis the variation in the time the PICAXE firmware takes to execute the BASIC commands in our program, but I don't think that this jitter is cumulative.

i.e. The sandwich timer code allows us to schedule tasks with the accuracy of the hardware interrupt. The second task runs at 5s +/- the jitter, the third task runs at 10s +/- the jitter, 15s +/- the jitter, and so on where 5 sec is 20 x the 250ms hardware interrupt.

When Using the internal resonator as the source for our microcontroller our task scheduling should be as accurate as the the internal resonator and using an external resonator or crystal source for our microcontroller our task scheduling should be as accurate as the external resonator or crystal.
 

hippy

Technical Support
Staff member
I don't remember ever hearing sandwich timers described. Affer I read about PTTES the idea of finding out how well they could be made to work on the PICAXE chips fascinated me and I am very pleased with the result.
I have never heard them described as such but we are doing similar when we use PULSOUT and a PAUSEUS to bit-bang out a Servo Loop. The difference is we don't use timers, just have a wait of 20ms, minus the pulse period, minus whatever overhead we think we have. Same too when we throw in a PAUSE 1000 minus something at the end of a loop to get output about every second.

Starting a timer, doing something, then waiting for that timer to timeout gives far more accurate timing. The neat trick with your solution is that 'wait for timeout' is just DOZE, set-up is a simple SETTIMER. I would have poked and peeked timers or their overflowed flags and this is a lot simpler than that.

Most programs are a single task in a single loop , where one often wants things to have repeatable timing, which I suppose is a sandwich with just one slice of bread. But there are cases where there are more steps and each needs to start at certain time marks and your implementation does make that easy to have.

It also means 'jittery, but best we can get' fixed PAUSE at the end of loops where one cannot be bothered to work out how long a task may actually take or handle variance, can be made to have much better timing accuracy with just two commands.

What impressed me is that it's such a simple solution, and well done in figuring it out - Seems you can teach an old dog new tricks.

If someone wanted to schedule a task every 5s using sandwich timer code that generates a hardware interrupt every 250ms, like in your example, then the code for their task must always complete in well under 250ms.
Yes, exceed that and one won't get the interrupt until 250ms after the last which has been ignored.

I was thinking it might be possible to peek the timer, at its end or while running, to tell when it had missed an interrupt if a task took say 150ms some times, 350ms others. But that makes things more complicated.

The pragmatic approach would be to split any task which might exceed 250ms into smaller parts which won't.
 

hippy

Technical Support
Staff member
Having brought up Servo Loops I wondered what sort of improvement this would bring to those.

Quite a lot it turns out. My thrown together code with no attempt to compensate for code path variations and overhead, has a frame time of around 26ms, 37Hz, with a fair amount of variation on that.

Using Sandwich Delay techniques it instantly becomes 19.969ms, 50.07762Hz, with far less variation. I think that conclusively proves its worth.

My example code can be found at -
https://picaxeforum.co.uk/threads/accurate-servo-frame-timing.32534
 

Flenser

Senior Member
I have never heard them described as such but we are doing similar when we use PULSOUT and a PAUSEUS to bit-bang out a Servo Loop. The difference is we don't use timers
Hippy, it was the pattern in the reference where I first saw it described that was given the name "sandwich timer" and the use of the hardware timer was just the way its implementation was demonstrated in C on that microcontroller so the technique was obviously already known more widely, just not given that particular name.

I was thinking it might be possible to peek the timer, at its end or while running, to tell
The recommendation given in the references is that it is your responsibility as the designer to ensure the the sandwich timer delay is always longer than the worst-case execution time for the task.

The pragmatic approach would be to split any task which might exceed 250ms into smaller parts which won't
What to do about long running tasks, that are too long for any reasonable single sandwich timer duration like you describe, is discussed as a separate issue. The technique given in the PTTES book is to code the long task as a finite-state machine where the task is split into several smaller parts. i.e. the first time through the main loop the first part of the task runs and sets the state to the second part, the second time through the main loop the second part of the task runs and sets the state to the third part, etc, until we execute the last part of the task which sets the state back to the first part.
 
Top