Mizar32/定时器
AT32UC3A 具有三个 16 位倒计时定时器,它们可以独立地以三种不同的频率运行。当它们设置为以高频率运行时,计时精度更高,但最长可能的延迟更短。
只有一小部分时钟频率可用,这些频率从 16.5MHz 的 PBA 总线频率降级。
除数 | 时钟频率 | 最长延迟 |
---|---|---|
PBA/2 | 8.25 MHz | 7.94 毫秒 |
PBA/8 | 2.0625 MHz | 31.77 毫秒 |
PBA/32 | 515.625 kHz | 0.127 秒 |
PBA/128 | 128.906 kHz | 0.508 秒 |
除了这四种选择外,该芯片还提供了一种从外部 32768Hz 实时精密晶体运行任何定时器的机制。1.3.2 版本的 Mizar32 板在可以安装该组件 (X2) 的地方留出了空间。
Alcor6L 提供了一个 timer
库来访问实时计数器。
前两个硬件定时器可以直接访问,使用定时器 ID 0
和 1
。Alcor6L 使用 16.5MHz 的 PBA 频率,默认情况下,时钟速率设置为最低可用频率 128906Hz,提供大约百万分之一秒的计时精度,最大延迟略超过半秒。
以下是如何使用第一个定时器创建等待十分之一秒的代码:
语言 | 代码 |
---|---|
eLua | tmr.delay(0, 100000)
|
PicoLisp | (tmr-delay 0 100000)
|
这些代码可用于实现高精度短延迟。
对于前两个定时器中的任何一个,您可以设置比默认值 129kHz 更高的时钟速率。但是,如上表所示,仅支持四个值,其中 PBA 频率 = 16.5MHz。其他值将设置算术上最接近的可用频率。
以下是一个设置定时器 1 的最高计时精度的示例,提供 7.94ms 的最大延迟:
语言 | 代码 |
---|---|
eLua | freq = tmr.setclock(1, 10000000); print(freq);
|
PicoLisp | (prinl (tmr-setclock 1 10000000) )
|
以上代码(对于所有语言)将打印 8250000
第三个硬件定时器无法直接访问,而是用于生成四个“虚拟定时器”,它们的定时器 ID 为 tmr.VIRT0
到 tmr.VIRT3
。这些定时器的滴答频率和精度较低,每秒十次,但可用于在整数 eLua 中创建长达 35 分钟的延迟,或在浮点 eLua 中创建长达 142 年的延迟。
tmr.delay( tmr.VIRT0, 5000000 ) -- Wait for five seconds
这些代码用于实现精度较低的长延迟,但虚拟定时器的时钟速率无法更改。
从 20120123 固件版本开始,存在第三种定时器机制,系统定时器 tmr.SYS_TIMER
,其精度为百万分之一秒,可用于提供高精度延迟和计时,在整数 Lua 中长达 35 分钟,在浮点 Lua 中长达 142 年,但您无法更改系统定时器的时钟频率,它也不能用于生成中断(见下文)。
所有三种定时器都可用于使您的程序等待指定的时间,如上面的示例所示。延迟的精度和可用的最大延迟取决于使用的定时器类型。一般来说,系统定时器最适合所有类型的延迟,因为它具有高精度,可以执行长延迟。
有时了解自某个之前时刻起经过了多少时间会很有用,例如测量代码的速度,或者当您需要在一定时间过去后做出某种决定,但在等待的同时还需要做其他事情时。
此示例通过在控制台中打印“Go!”来测量人们的反应时间,然后观察他们按下键的响应时间。我们将为此使用系统定时器。
在 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"
当然,如果您在它说“Go!”之前按下了 Enter,它会说您的反应非常快。
请注意:您也可以从我们位于 GitHub 上的示例存储库下载上述代码 reaction.lua
。
在 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) )
请注意:您也可以从我们位于 GitHub 上的示例存储库下载上述代码 reaction.l
。
请注意:PicoLisp 目前不支持中断处理。请参阅 问题 #12。但是,您可以在 eLua 中使用中断。
您可以安排定期或在一定时间过去后调用 Lua 函数 - 您可以在此过程中执行其他操作。以下示例展示了如何使用硬件定时器 0 每半秒生成一次中断。每次定时器导致中断时,都会调用我们的一个 Lua 函数(此处为 irq_handler
),以使板载 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 )
要改为在经过一定时间后生成单个中断,请使用
tmr.set_match_int( timer, period, tmr.INT_ONESHOT )
而不是
tmr.set_match_int( timer, period, tmr.INT_CYCLIC )
请注意,定时器中断仅适用于硬件定时器(0 和 1)和虚拟定时器(tmr.VIRT0
到 tmr.VIRT3
);系统定时器无法生成中断。选择使用哪种定时器取决于您所需的计时精度以及您需要处理的时间长度。硬件定时器具有半秒的最大周期,但精确到十万分之一秒,而虚拟定时器仅精确到十分之一秒,但可以处理整数 eLua 中长达 35 分钟的周期,或浮点 eLua 中长达 142 年的周期。
- Atmel AT32UC3A 数据手册,第 31 章:定时器/计数器 (TC) eLua 参考手册中的 tmr 模块
- eLua 参考手册中的虚拟定时器.