Mizar32/Timers

From Wikibooks, open books for an open world
< Mizar32
Jump to: navigation, search

Hardware view[edit]

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.

eLua view[edit]

eLua provides a tmr library to access the real-time counters.

Three types of timer[edit]

Hardware timers[edit]

The first two hardware timers are directly accessible, using timer IDs 0 and 1. eLua 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.

tmr.delay( 0, 100000 )  -- Wait for 1/10th of a second using the first timer

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.

-- Set the highest possible timing precision for timer 1,
-- giving a maximum delay of 7.94ms
freq = tmr.setclock( 1, 10000000 )
print( freq )

prints 8250000

Virtual timers[edit]

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]

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]

Delays[edit]

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]

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.

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...

Timer interrupts[edit]

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]