Monday, December 29, 2008

non-blocking delays.

Waiting...
There are probably no JAL users that have not used delay.jal - the jallib library that let your program wait for a specified amount of time. Very usefull in most situations, but has one limitation: it blocks your program so can't do anything else while you are waiting.


Non-blocking delay
Some applications need to take care of multiple tasks at the same time. Imagine a small robot, which has just decided it wants to move forward for 3 seconds. But while doing so, it has to guard it's front whiskers in case it runs into an object. A call like:

delay_100ms(30)

would not allow this. This case requires a non-blocking delay, like the one provided by timer0_isr_interval.jal that generates an interrupt at a regular interval and has an ISR (Interrupt Service Routine) that deals with the delay you require.

Software setup
The setup of this library is straight-forward. First you define the interval of the interrupt you want. With a higher value, more interrupts are generated. This gives a higher resolution of your delay, but also puts a high background load on your PIC. Be carefull if you go beyond 1000 (1kHz). The lowest possible rate depends on the clock frequency you use and is 77 with an 16f877 on 20 MHz.

const timer0_isr_rate = 1000 -- 1 kHz isr rate

Next, you need to specify the number of slots. A slot is used to store the end-time of a delay-period so you need one slot for each concurrent delay.

const DELAY_SLOTS = 2 -- support 2 delays at the same time

Now, include the library and call it's init function:

include timer0_isr_interval
timer0_isr_init() -- init timer0 isr

Blink!
Now we are ready to use the delay functions. To demonstrate it's use, we take two LEDs and let them blink at their own interval:


forever loop
if (check_delay(0)) then
set_delay(0, 409) -- 409 ticks on delay-slot 0
led = !led
end if
if (check_delay(1)) then
set_delay(1, 619) -- 619 ticks on delay-slot 1
led2 = !led2
end if
end loop

It's that simpe! check_delay() takes a slot number as a param and returns true if the delay has expired and false if it has not. When it has expired, the next delay is set with set_delay(). Note that the delay-parameter of this procedure is in 'ticks' - the number of times an interrupt occurs. In this example, we set the interrupt interval at 1000, so one tick is 1 milisecond.

And then the only task left is to invert the led.

In the above example, there are two LEDs blinking at a different (prime number) interval and it takes over 250.000 ticks for the pattern to repeat itself.

Before I get to the conclusion of this article, there is one usefull feature I'd like to show you. The ISR also has (word) counter that is incremented at the tick rate. This is usefull if you want to execute tasks at isr interval, like scanning a keyboard:

if (isr_counter != prev_isr_counter) then
prev_isr_counter = isr_counter
-- put here code that you want
-- to execute each tick.
end if

And if you want an other rate than every tick, use a delay slot!

Epilogue
We've seen an easy way to keep track of multiple delays. It enables you to create applications with complex timing. It also enables you to guard responses and act if there is none within the time set. And you can stop checking a delay or set it again (based on the time it is set again) when you feel like it.
But make sure you go trough you main loop at a fast rate and don't use the classic delay() functions. A blinking led in the main loop helps to detect blocking code that prevents execution of the main loop at a proper rate.

The timer0_interval.jal samples of jallib provides a working example for the code shown.

Joep Suijs

4 comments:

  1. Where is timer0_isr_interval file? I cannot find it in lib folder.

    ReplyDelete
  2. It looks like it missed the release. You can get it from:
    http://code.google.com/p/jallib/source/browse/branches/jallib-0.1/unvalidated/include/peripheral/timer/timer0_isr_interval.jal

    Joep

    ReplyDelete
  3. do you have an example of how to use this for scanning buttons, how would I for example wait a single debounce period then wait for the button to be released?

    ReplyDelete
  4. Hi Tom,

    I'm afraid I don't. But if you take your current debounce-code and take a global variable to hold the last input you read, it should be pretty straight-forward.
    And when you have it running, we'd be happy to add this to jallib.

    Joep

    ReplyDelete