跳轉到內容

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 年的週期。

進一步閱讀

[編輯 | 編輯原始碼]
華夏公益教科書