Mizar32/PIO
PIO 代表可程式設計輸入/輸出,這是控制和測量連線到匯流排聯結器的 AVR32 處理器引腳上的數字電壓電平的最簡單方法。
要將 GPIO 引腳用作 PIO,您必須首先將引腳設定為輸入或輸出。如果將其設定為輸入,則可以檢查輸入電壓以檢視其是否具有進入它的低或高值,例如檢查開關的位置。如果將其設定為輸出,則可以對其進行程式設計以輸出低電壓或高電壓來控制燈、電機或其他電路。
對於作為輸入的 PIO 引腳,您還可以要求當該引腳上的電壓從 0 變化到 1 或從 1 變化到 0 時,這將產生一箇中斷。發生這種情況時,處理器將停止其正在執行的操作,執行一段稱為中斷例程的特殊程式碼,並在完成該操作後,它將返回並繼續在中斷髮生時正在執行的操作。
最後,每個引腳都有一個可選的上拉電阻,可以啟用它,以便如果沒有任何東西連線到作為輸入的引腳,它將漂浮到邏輯“1”,而不是隨機上下波動。這是連線開關或按鈕的常用方法:您在引腳上程式設計一個上拉電阻,然後將開關連線到引腳和零伏之間,以便當觸點閉合時,您將讀取 0 值,而當觸點斷開時,您將讀取 1 值。
AT32UC3A 晶片的任何外設引腳都可以作為數字輸入讀取或設定為輸出,並程式設計為 0 或 1 邏輯電平。
當引腳設定為輸出時,0 邏輯輸出將引腳連線到 0 伏,而邏輯 1(“高”)值則在其上施加 3.3 伏,兩種狀態的最大電流供應或汲取均為 4 毫安。
當它們設定為輸入時,0.0 到 0.8 伏的電壓電平讀取為“0”(低)輸入,而 2.0 伏到 5.0 伏的電壓電平讀取為“1”(高)輸入。0.8 到 2.0 伏之間的值可能讀取為高或低,並且不確定。
Mizar32 的某些引腳只能用作可程式設計 I/O 引腳,因為它們不用於其他任何用途。其他引腳將訊號傳輸到各種外設,但如果未使用這些裝置,則可以將引腳用作 PIO 引腳。
其他引腳對於處理器的正常執行至關重要,例如用於訪問 SDRAM、振盪器和其他板載電路的引腳;如果您將這些用作 PIO 引腳,則電路板可能會崩潰並需要按其重置按鈕。
| 引腳 | 名稱 | 匯流排引腳 | eLua名稱 | PicoLisp |
|---|---|---|---|---|
| PA2 | GPIO2 | BUS5 引腳 11 | pio.PA_2 |
'PA_2
|
| PA7 | GPIO7 | BUS5 引腳 12 | pio.PA_7 |
'PA_7
|
| PB17 | GPIO49 | BUS5 引腳 8 | pio.PB_17 |
'PB_17
|
| PB18 | GPIO50 | BUS5 引腳 9 | pio.PB_18 |
'PB_18
|
| PB29 | GPIO61 | 板載 LED | pio.PB_29 |
'PB_29
|
| PB30 | GPIO62 | BUS6 引腳 9 | pio.PB_30 |
'PB_30
|
| PB31 | GPIO63 | BUS6 引腳 10 | pio.PB_31 |
'PB_31
|
| PX16 | GPIO88 | 使用者按鈕 | pio.PX_16 |
'PX_16
|
| PX19 | GPIO85 | BUS6 引腳 12 | pio.PX_19 |
'PX_19
|
| PX22 | GPIO82 | BUS6 引腳 11 | pio.PX_22 |
'PX_22
|
| PX33 | GPIO71 | BUS5 引腳 10 | pio.PX_33 |
'PX_33
|
未使用的 ADC、PWM、SPI 或 UART 引腳也可以用作 PIO 引腳。請參閱這些部分以獲取相關的引腳名稱。這將可用 PIO 的總數增加到 66 個。
PIO Alcor6L 模組允許您將任何引腳設定為邏輯輸入或將其設定為輸出並在其上放置 0v 或 3.3V,並且您可以在任何引腳上啟用上拉電阻。
此示例點亮板載 LED。
在 eLua 中
led = pio.PB_29 pio.pin.setdir( pio.OUTPUT, led ) pio.pin.setlow( led )
在 PicoLisp 中
(setq led 'PB_29) (pio-pin-setdir *pio-output* led) (pio-pin-setlow led)
請注意,在復位後立即,LED 在第 2 行亮起,因為復位狀態是所有引腳都為低電平,並且當訊號為低電平時,板載 LED 會亮起。要設定一個初始值為高的輸出引腳,您需要在呼叫 pio.pin.setdir() 之前呼叫 pio.pin.sethigh()。
這是一個讀取一個 PIO 引腳並根據它驅動另一個引腳的示例。我們將讀取連線到板載使用者按鈕的引腳,並且只要按下它,我們就將使板載 LED 閃爍。
-- Make the Mizar32 on-board LED flicker as long as the user button is held
led = pio.PB_29
button = pio.PX_16
-- A function to give a short delay of 1/10th of a second
function delay()
tmr.delay( 0, 100000 )
end
-- Main program
-- First, make sure the LED starts in the "off" position
pio.pin.sethigh( led )
-- Now enable input/output pins
pio.pin.setdir( pio.OUTPUT, led )
pio.pin.setdir( pio.INPUT, button )
while true do
-- If the button is pressed down...
if pio.pin.getval( button ) == 0 then
-- ... turn the on-board LED on and off again
pio.pin.setlow( led )
delay()
pio.pin.sethigh( led )
delay()
end
end
在 PicoLisp 中,以下是如何執行此操作。
# A simple program which demonstrates
# the usage of user-buttons.
# declare pins
(setq led 'PB_29 button 'PX_16)
# a simple delay function
(de delay (t)
(tmr-delay 0 t) )
# make sure the LED starts in
# the "off" position and enable
# input/output pins
(de init-pins ()
(pio-pin-sethigh led)
(pio-pin-setdir *pio-output* led)
(pio-pin-setdir *pio-input* button) )
# And now, the main loop
(de prog-loop ()
(init-pins)
(loop
(if (= 0 (pio-pin-getval button))
(pio-pin-setlow led)
(delay 100000)
(pio-pin-sethigh led)
(delay 100000) ) ) )
(prog-loop)
使用者按鈕的電路非常簡單:當按下按鈕時,它將其 PIO 引腳連線到零伏,提供低輸入值,並且在 PIO 引腳和 3.3 伏之間連線了一個電阻,因此如果未按下按鈕,PIO 引腳將被輕輕拉高到 3.3V 並讀取為高值。
如果一些PIO引腳沒有連線任何東西,並且您將它們程式設計為輸入,它們會拾取周圍環境的隨機噪聲,並給出時高時低的值。可程式設計上拉電阻可以確保,如果沒有訊號連線到輸入引腳,它會輕輕地被拉高到高電平,而不是隨機漂浮。
此示例將一個未使用的GPIO引腳轉換為PIO輸入,但確保如果沒有任何物理連線到它,它將始終返回高電平1。
如果您從以下程式碼中刪除pio.pin.setpull()行(至少在我的測試板上),它會打印出大部分為零的值,但是如果您觸控Mizar32板的底部,則值會在0和1之間閃爍。包含pio.pin.setpull()行後,輸入值始終為1,除非您使用一段電線將引腳連線到GND引腳(例如BUS5引腳14)。
在 eLua 中
pin = pio.PA_2 -- Stabilise GPIO2 (connector BUS5 pin 11) pio.pin.setdir( pio.INPUT, pin ) pio.pin.setpull( pio.PULLUP, pin ) while true do io.write( pio.pin.getval( pin ) ) end
在 PicoLisp 中
(setq pin 'PA_2) # Stabilize GPIO2 (connector BUS5 pin 11) (pio-pin-setdir *pio-input* pin) (pio-pin-setpull *pio-pullup* pin) (loop (prinl (pio-pin-getval pin)) )
雖然Alcor6L也有類似的原始PULLDOWN,但Mizar32中使用的AVR32晶片在其硬體中沒有可程式設計下拉電阻,因此使用它會引發錯誤訊息。
要再次停用上拉電阻,您可以使用
| 語言 | 示例 |
|---|---|
| eLua | pio.pin.setpull(pin, pio.NOPULL)
|
| PicoLisp | (pio-pin-setpull pin *pio-nopull*)
|
可程式設計上拉電阻的另一個用途是實現“開漏輸出”。在這種方案中,引腳可以處於兩種狀態之一:要麼被驅動為0輸出,要麼被讀取為輸入。上拉電阻確保,如果沒有人將其驅動為輸出,則每個人都會將其讀取為高電平。當多臺計算機需要透過一根訊號線進行通訊時,可以使用這種方式,這樣任何一臺計算機都可以與任何其他計算機通訊,而無需主從關係或協商誰控制匯流排。使用此係統,任何計算機都可以讀取線路以檢視其值是高還是低,並且任何計算機都可以將線路驅動到低電平,供所有其他計算機讀取。這與將線路驅動為高或低輸出不同,因為如果一臺計算機將其驅動為高電平,而另一臺計算機同時將其驅動為低電平,則可能會損壞相關的計算機,並且肯定會導致通訊混亂。
一個簡單的例子是,以一種可以由多個開關單元中的任何一個啟用的方式來開啟和關閉房屋照明。相反,I2C匯流排是一個高階示例,它在其訊號線上使用開漏輸出,以便I2C總線上的任何一臺計算機都可以與任何其他計算機通訊,而不會在匯流排線上導致衝突訊號。
以下程式碼在PIO引腳上實現了開漏輸出,提供一個函式將其配置為OC引腳,一個函式將其驅動為低輸出,以及一個函式將其設定為輸入並告訴您此時線路上的值。
在 eLua 中
-- Turn a PIO pin into an open-collector output. -- Call this function once before using the other two to drive and read the pin. function oc_setup( pin ) -- OC output starts as an input, not driving the bus wire pio.pin.setdir( pio.INPUT, pin ) -- arrange that, when it is an input and no one is driving it, it will float high pio.pin.setpull( pio.PULLUP, pin ) -- and that when we set it as an output, it will drive a low value pio.pin.setlow( pin ) end -- Drive a low output value onto the pin. -- The low output value is already programmed during setup() -- so we only need to enable it as an output. function oc_drive_low( pin ) pio.pin.setdir( pio.OUTPUT, pin ) end -- Make the pin an input and return the value on the bus wire function oc_read( pin ) pio.pin.setdir( pio.INPUT, pin ) return pio.pin.getval( pin ) end
在 PicoLisp 中
# Turn a PIO pin into an open-collector output. # Call this function once before using the other # two to drive and read the pin. (de oc-setup (pin) # OC output starts as an input, not driving # the bus wire (pio-pin-setdir *pio-input* pin) # Arrange that, when it is an input and no # one is driving it, it will float high (pio-pin-setpull *pio-pullup* pin) # and that when we set it as an output, it will drive a low value (pio-pin-setlow pin) ) # Drive a low output value onto the pin. # The low output value is already programmed during setup() # so we only need to enable it as an output. (de oc-drive-low (pin) (pio-pin-setdir *pio-output* pin) ) # Make the pin an input and return the value on the bus wire (de oc-read (pin) (pio-pin-setdir *pio-input* pin) (pio-pin-getval pin) )
請注意:PicoLisp目前不支援中斷處理。請參閱問題 #12。但是,您可以在eLua中使用中斷。
您可以安排在GPIO引腳的邏輯電平發生變化時呼叫您自己的Lua函式,而無需一直檢查引腳。
您可以要求在GPIO引腳的邏輯電平從0變為1時呼叫您的中斷函式,從1變為0時呼叫,或者兩者都呼叫。
以下示例建立一箇中斷函式,並安排在每次按下使用者按鈕時呼叫它。使用者按鈕的電路設計使得當按鈕未按下時其GPIO引腳為高電平,按下時為低電平,因此我們要求在引腳的邏輯電平從高電平變為低電平時呼叫我們的中斷例程。
-- Example program to show the use of Lua interrupts on PIO edges. -- This flashes the on-board LED every time the user button is depressed. -- Which PIO pins are the button and the LED connected to? button = pio.PX_16 led = pio.PB_29 function when_pressed( resnum ) -- flash the onboard LED pio.pin.setlow(led) for i=1,10000 do end -- for about 1/100th of a second pio.pin.sethigh(led) end -- Enable the LED as an output, starting in the "off" state. -- and the button as an input. pio.pin.sethigh( led ) -- off pio.pin.setdir( pio.OUTPUT, led ) pio.pin.setdir( pio.INPUT, button ) -- Set our interrupt handing function and make it sensitive to a -- change from high to low of the PIO pin connected to the user button. cpu.set_int_handler( cpu.INT_GPIO_NEGEDGE, when_pressed ) cpu.sei( cpu.INT_GPIO_NEGEDGE, button ) -- Do something for about ten seconds while the test runs for i=1,10000000 do end -- disable the interrupt cpu.cli( cpu.INT_GPIO_NEGEDGE, button ) -- and remove our interrupt handler function. cpu.set_int_handler( cpu.INT_GPIO_NEGEDGE, nil )
任何GPIO引腳都可以設定為對基於邊沿的中斷做出反應,無論它們是輸入、輸出還是具有完全不同的功能。
您可以為引腳啟用正邊沿中斷功能、負邊沿中斷功能或兩者。如果啟用兩者,則可以使兩種中斷都由同一個函式處理,也可以由兩個不同的函式處理。
諸如pio.PB_29之類的函式返回在eLua內部用於識別GPIO引腳的數字。除了在一種情況下,您不需要了解這些數字的值:當您同時在不同的GPIO引腳上啟用了多個邊沿觸發的中斷時。
您只能告訴cpu.set_int_handler()為所有正邊沿中斷呼叫單個函式,以及為所有負邊沿中斷呼叫另一個函式(或同一個函式),但是您可以透過檢查傳遞給您的中斷處理函式的引數來找出哪個GPIO引腳導致了中斷,在上面的程式碼示例中,我們將其稱為resnum。
這是“資源編號”,eLua用於表示引腳的內部編號,它與pio.PX_16等返回的值相同。
因此,您可以在中斷函式中說:
if resnum == button ...
或者
if resnum == pio.PA_3 ...
但是,如果您啟用了許多引腳中斷並希望將其解碼為埠號和引腳號,則可以使用
port, pin = pio.decode( resnum )
對於埠A上的引腳,port將為0,pin將從0到31。
對於埠B上的引腳,port將為1,pin將從0到31。
對於埠C上的引腳,port將為2,pin將從0到5。
對於埠X上的引腳,port將為23,pin將從0到39。
- 維基百科上的開漏文章
- Atmel AT32UC3A 資料手冊第22章:通用輸入/輸出控制器(GPIO)
- 有關所有eLua
pio.*()函式的詳細資訊,請參閱eLua 手冊的PIO部分