跳转到内容

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 01。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.VIRT0tmr.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.VIRT0tmr.VIRT3);系统定时器无法生成中断。选择使用哪种定时器取决于您所需的计时精度以及您需要处理的时间长度。硬件定时器具有半秒的最大周期,但精确到十万分之一秒,而虚拟定时器仅精确到十分之一秒,但可以处理整数 eLua 中长达 35 分钟的周期,或浮点 eLua 中长达 142 年的周期。

进一步阅读

[编辑 | 编辑源代码]
华夏公益教科书