Mizar32/Timers
Hardware view
[edit | edit source]The AT32UC3A has three 16-bit countdown timers which can run at three different frequencies, independent of each other. When they are set to run at a high frequency, the timing accuracy is higher but the longest possible delay is shorter.
Only a small set of clock frequencies is available, which are divided down from the PBA bus frequency of 16.5MHz:
Divisor | Clock frequency | Longest delay |
---|---|---|
PBA/2 | 8.25 MHz | 7.94 ms |
PBA/8 | 2.0625 MHz | 31.77 ms |
PBA/32 | 515.625 kHz | 0.127 second |
PBA/128 | 128.906 kHz | 0.508 second |
Apart from these four selections, the chip also has provision for running any of the timers from an external 32768Hz real-time precision crystal. The Mizar32 board from version 1.3.2 has a space for this component (X2) where it may be mounted.
Software view
[edit | edit source]Alcor6L provides a timer
library to access the real-time counters.
Three types of timer
[edit | edit source]Hardware timers
[edit | edit source]The first two hardware timers are directly accessible, using timer IDs 0
and 1
. Alcor6L uses a PBA frequency of 16.5MHz and by default the clock rates are set to lowest available frequency of 128906Hz, giving a timing precision of about a hundred thousandth of a second and a maximum delay of just over half a second.
This is how one can create a wait for 1/10th of a second using the first timer:
Language | Code |
---|---|
eLua | tmr.delay(0, 100000)
|
PicoLisp | (tmr-delay 0 100000)
|
These can be used to achieve short delays of high accuracy.
For either of the first two timers, you can set a higher clock rate than the default of 129kHz. However, only four values are supported as shown in the table above with PBA frequency = 16.5MHz. Other values will set the arithmetically nearest available frequency.
Below, is an example to set the highest possible timing precision for timer 1, giving a maximum delay of 7.94ms:
Language | Code |
---|---|
eLua | freq = tmr.setclock(1, 10000000); print(freq);
|
PicoLisp | (prinl (tmr-setclock 1 10000000) )
|
The above code (for all languages) prints 8250000
Virtual timers
[edit | edit source]The third hardware timer cannot be accessed directly, but instead is used to generate four "virtual timers" whose timer ids are tmr.VIRT0
to tmr.VIRT3
. These have a lower tick frequency and accuracy - ten times per second - but can be used to create delays of up to 35 minutes in integer eLua or 142 years in floating point eLua.
tmr.delay( tmr.VIRT0, 5000000 ) -- Wait for five seconds
These are used to achieve longer delays of lower accuracy but the clock rate of the virtual timers cannot be changed.
System timer
[edit | edit source]From the 20120123 firmware release, there is a third timer mechanism, the system timer tmr.SYS_TIMER
which has an accuracy of one millionth of a second and can be used to give high-precision delays and timings up to 35 minutes with integer Lua and up to 142 years in floating point Lua, but you cannot change the clock frequency of the system timer, and it cannot be used to generate interrupts (see below).
Timer operations
[edit | edit source]Delays
[edit | edit source]All three types of timer can be used to make your program wait for a specified length of time, as shown in the examples above. The precision of the delay and the maximum delay available depend on the type of timer used. In general, the system timer is the best for all types of delay, as it has high precision and can perform long delays.
Measuring time
[edit | edit source]Sometimes it can be useful to know how much time has elapsed since some previous moment, for example to measure the speed of your code or when you need to take some decision after a certain amount of time has passed but also need to do something else while you are waiting.
This example measures people's reaction time by printing "Go!" on the console and then seeing how long it takes them to press a key in response. We will use the system timer for this.
In eLua:
print "Welcome to the reaction timer. When I say Go!, press [Enter]." print "Press q [Enter] to quit." repeat timer = tmr.SYS_TIMER print( "Ready?" ) -- Wait for a random time from 2 to 5 seconds tmr.delay( tmr.SYS_TIMER, 2000000 + math.random( 3000000 ) ) print( "Go!" ) start_time = tmr.read( timer ) answer = io.read() -- wait for them to press Enter end_time = tmr.read( timer ) print( "You reacted in " .. tmr.gettimediff( timer, start_time, end_time ) .. " microseconds" ) until answer == "q"
Of course, if you press Enter before it says Go!, it will say you reacted incredibly quickly.
Please note: You may also download the above code reaction.lua
from our examples repository on github.
In PicoLisp:
# A reaction timer in picolisp (de reaction-timer () (prinl "Welcome to the reaction timer. When I say Go!, enter a character.") (prinl "Press q [Enter] to quit.") (setq timer *tmr-sys-timer* answer "" ) (until (=T answer) (println "Ready?") # Wait for a random time from 2 to 5 seconds (tmr-delay timer (+ 2000000 (rand 1 3000000))) (println "Go!") (setq start-time (tmr-read timer) answer (read) # wait for them to enter any character end-time (tmr-read timer) )
Please note: You may also download the above code reaction.l
from our examples repository on github.
Timer interrupts
[edit | edit source]Please note: There is currently no support for interrupt handling in PicoLisp. See issue #12. You can however use interrupts in eLua.
You can arrange that a Lua function be called either regularly or after a certain time has elapsed - you can then go and do other things while this happens. The following example shows how to generate an interrupt once every half second using hardware timer 0. Each time the timer causes an interrupt, a Lua function of ours, called irq_handler
here, is called to flash the on-board LED.
-- Test timer interrupts handled in Lua. -- Flash Mizar32's onboard LED twice a second under Lua interrupt control. led = pio.PB_29 -- Which PIO pin is the LED connected to? timer = 0 -- which timer to use to generate the interrupts? period = 500000 -- how often, in microseconds, should it make an interrupt? function int_handler( resnum ) -- flash the onboard LED pio.pin.setlow( led ) tmr.delay( nil, 10000 ) -- on for 1/100th of a second pio.pin.sethigh( led ) end pio.pin.sethigh( led ) -- prepare the LED as starting "off" pio.pin.setdir( pio.OUTPUT, led ) -- Make the LED pin an output -- tell eLua which function it should call every time the timer times out cpu.set_int_handler( cpu.INT_TMR_MATCH, int_handler ) -- enable that Lua interrupt cpu.sei( cpu.INT_TMR_MATCH, 0 ) -- and start the timer to cause an interrupt once every half second tmr.set_match_int( timer, period, tmr.INT_CYCLIC ) -- Busy-wait for about ten seconds while the test runs for i=1,10000000 do end -- disable the interrupt-generating timer tmr.set_match_int( timer, 0, tmr.INT_CYCLIC ) -- disable the Lua interrupt cpu.cli( cpu.INT_TMR_MATCH, timer ) -- and remove our interrupt handler function cpu.set_int_handler( cpu.INT_TMR_MATCH, nil )
To generate a single interrupt after a certain time has elapsed instead, you use
tmr.set_match_int( timer, period, tmr.INT_ONESHOT )
instead of
tmr.set_match_int( timer, period, tmr.INT_CYCLIC )
Note that timer interrupts only work with hardware timers (0 and 1) and virtual timers (tmr.VIRT0
to tmr.VIRT3
); the system timer cannot generate interrupts.
The choice of which kind of timer to use depends on the time-precision that you require and the length of time you need to deal with. The hardware timers have a maximum period of half a second but are precise to 100,000th of a second, while the virtual timers are only precise to 1/10th of a second but can deal with periods up to 35 minutes in integer eLua or 142 years in floating point eLua.
Further reading
[edit | edit source]- The Atmel AT32UC3A datasheet, Chapter 31: Timer/Counter (TC) The tmr module in the eLua reference manual
- Virtual timers in the eLua reference manual.