微控制器(MCU)不僅存在于工業(yè)設(shè)備中,它們?yōu)榘ㄍ婢吆陀螒蛟趦?nèi)的許多家庭電子產(chǎn)品 供算力。在這一章中,你將創(chuàng)建一個簡單的反應(yīng)計時游戲,看看你的朋友中誰會在燈熄滅時第一個按下按鈕。
你的反應(yīng)時間,大腦來處理的時間以毫秒計:人類的平均反應(yīng)時間大約是 200 – 250 毫秒。
對于這個項目,你需要:
– 樹莓派 Pico
– 面包板
– 任何顏色的 LED 燈
– 一個 330Ω 電阻
– 兩個按鈕開關(guān)
– 若干公對公跳線
– 一根 microUSB 數(shù)據(jù)線
將 Pico 連接到樹莓派或其他運行 Thonny MicroPython IDE 的計算機(jī)。
單人游戲
如圖所示在面包板上搭建電路。LED 和 330Ω 限流電阻串聯(lián)在 Pico 的 GP15 和 GND 引腳之間。
接下來,添加按鈕開關(guān)。將按鈕一側(cè)的引腳連接到 Pico 的 GP14,另一側(cè)的引腳接到 Pico 的 3V3 引腳上。
為什么要連接 3v3?請記住,開關(guān)和 LED 一樣,需要電阻器才能正確工作,而且
Pico 上 GPIO 是有可編程電阻器的。在這本文的項目中,我們將它們設(shè)置為下拉電阻,這意味著當(dāng)按鈕按下時,引腳電壓必須被拉高。
現(xiàn)在你的電路已經(jīng)具備了作為一個單人游戲所需要的一切,LED 是輸出設(shè)備(類似電視機(jī)的作用),按鈕開關(guān)為控制器,而 Pico 是游戲主機(jī),盡管它比你通常看到的要小得多!
現(xiàn)在你需要真正編寫游戲。和往常一樣,把樹莓派上的 Thonny 打開。創(chuàng)建一個新程序:
import machine import utime
此外,你將需要一個新的庫:urandom,它用來創(chuàng)建隨機(jī)數(shù)并在這個游戲中使用,以防止曾經(jīng)玩過它的玩家簡單地倒數(shù)一個固定的秒數(shù)點擊按鈕而一招制勝。
接下來,設(shè)置好 LED 和按鈕的引腳:
led = machine.Pin(15, machine.Pin.OUT) button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN)
在前面的章節(jié)中,你已經(jīng)了解如何在主程序或單獨的線程中使用按鈕。這一次我們將采 用一種不同的、更靈活的方法:中斷請求(IRQs)來處理按鈕的反饋。
這個名字聽起來很復(fù)雜,但其實很簡單。想象一下,你正在一頁一頁地閱讀一本書,有人走過來問你一個問題。那個人在執(zhí)行一個打斷請求,要求你停止正在做的事情,回答他們的問題,然后讓你繼續(xù)讀你的書。
MicroPython 中斷請求以完全相同的方式工作,它允許某些東西(在這種情況下是按下按鈕 開關(guān))中斷主程序。在某些方面,它和線程很相似,在主程序之外有一段代碼。然而,與線程不 同的是,代碼不是持續(xù)運行的,它只在中斷被觸發(fā)時運行。
首先定義中斷的處理程序。這個被稱為回調(diào)函數(shù)的代碼在中斷被觸發(fā)時運行。
def button_handler(pin): button.irq(handler=None) print(pin)
這兩行代碼首先關(guān)閉中斷,這樣它只觸發(fā)一次,然后打印有關(guān)觸發(fā)中斷的引腳編號。
繼續(xù)下面的程序:
led.value(1) utime.sleep(urandom.uniform(5, 10)) led.value(0)
第一行將 LED 點亮,下一行暫停程序,最后一行再次關(guān)閉 LED 燈。玩家按下按鈕之后,LED 被點亮的時間并不是固定的,而是利用 urandom 庫將程序暫停 5 到 10 秒,換句話說,就是 LED 會亮 5 到 10 秒。
然而,目前還沒有什么東西在等待著按鈕被按下。你需要為此設(shè)置中斷,方法是在程序末尾增加一行:
button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler)
設(shè)置中斷需要兩個東西:觸發(fā)器和處理程序。觸發(fā)器告訴 Pico 它應(yīng)該尋找什么作為中斷它正在做的事情的有效信號;handler 就是中斷被觸發(fā)后運行的函數(shù)名。
在這個程序中,你的觸發(fā)器是 IRQ_RISING,這意味著中斷是由引腳電壓從低電平升到高電平時觸發(fā)。而 IRQ_FALLING 這是引腳電壓從高電平到低電平時觸發(fā)。如果你需要寫一個程序,在一個引腳改變時觸發(fā)一個中斷,而不關(guān)心它是上升還是下降,你可以使用「|」組合這兩個觸發(fā)器:
button.irq(trigger=machine.Pin.IRQ_RISING | machine.Pin.IRQ_FALLING, andler=button_handler)
本項目中,代碼將變成下面這樣:
import machine import utime import urandom led = machine.Pin(15, machine.Pin.OUT) button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN) def button_handler(pin): button.irq(handler=None) print(pin) led.value(1) utime.sleep(urandom.uniform(5, 10)) led.value(0) button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler)
單擊 Run 按鈕,并將程序保存到 Pico 上命名為 Reaction_Game.py。你會看到 LED 燈亮起來,這是信號,用你的手指放在按鈕上。當(dāng) LED 熄滅時,盡可能快地按下按鈕。
當(dāng)你按下按鈕時,它會觸發(fā)你之前編寫的處理程序代碼。查看 Shell 區(qū)域,你將看到 Pico 打印了一條消息,確認(rèn)中斷是由 GP14 引腳觸發(fā)的。你還會看到另一個細(xì)節(jié):mode=IN 告訴你引腳被配置為輸入。不過,這個信息并沒有給游戲造成多大的影響,為此,你需要一種方法來加快玩家的反應(yīng)速度。首先從按鈕處理程序中刪除 print(pin) 這一行,你不再需要它了。
轉(zhuǎn)到程序的底部并添加一條新行,就在你設(shè)置中斷的位置的正上方:
timer_start = utime.ticks_ms()
這里創(chuàng)建了一個名為 timer_start 的新變量,并賦予了 utime.ticks_ms() 函數(shù)的輸出,該函數(shù)計算自 utime 庫開始計數(shù)以來已過的毫秒數(shù)。這給在 LED 熄滅之后和中斷觸發(fā)器準(zhǔn)備好讀取按鈕之前,提供了一個參考的時間點。
接下來,回到按鈕處理程序,添加以下兩行:
timer_reaction = utime.ticks_diff(utime.ticks_ms(), timer_start) print("Your reaction time was " + str(timer_reaction) + "milliseconds!")
第一行創(chuàng)建了另一個變量,這一次是中斷實際觸發(fā)的時刻,換句話說,就是按下按鈕的時 候。但是,它不像以前那樣簡單地從 utime.ticks_ms() 中讀取數(shù)據(jù),而是使用 utime.ticks_diff() 這個函數(shù),它得到了觸發(fā)這行代碼的時間與變量 timer_start 中保存的參考點之間的差異。
第二行代碼打印出計算結(jié)果。
最后的代碼如下:
import machine import utime import urandom led = machine.Pin(15, machine.Pin.OUT) button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN) def button_handler(pin): button.irq(handler=None) timer_reaction = utime.ticks_diff(utime.ticks_ms(), timer_start) print("Your reaction time was " + str(timer_reaction) + " milliseconds!") led.value(1) utime.sleep(urandom.uniform(5, 10)) led.value(0) timer_start = utime.ticks_ms() button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler)
再次點擊 Run 按鈕,等待 LED 熄滅,然后按下按鈕。這一次,你將看到一條消息,告訴你按下按鈕的速度,而不是觸發(fā)中斷的針的報告,這是對你反應(yīng)時間的測量。
再次點擊運行按鈕,看看你是否可以更快的速度按下按鈕,在這個游戲中,你正在嘗試盡可能低的分?jǐn)?shù)!
雙人游戲
單人游戲很有趣,但是讓你的朋友參與進(jìn)來會更好。你可以先邀請他們玩你的游戲,比較你 的高分或低分,看看誰的反應(yīng)最快。然后,你可以修改你的游戲,讓他們和你一起玩。
首先在你的電路中添加第二個按鈕。如圖所示。確保兩個按鈕之間有足夠的距離,以便玩家能夠?qū)⑹种阜旁诎粹o上。
雖然第二個按鈕現(xiàn)在已經(jīng)連接到 Pico,但它還不知道如何使用它。回到你在 Thonny 的程序 中,找到你設(shè)置第一個按鈕的地方。在這一行下面,添加:
right_button = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_DOWN)
你將注意到,名稱現(xiàn)在指定了你正在使用的按鈕(右側(cè)的按鈕)。為了避免混淆,請編輯
上面的一行,這樣你就可以清楚地看到,原來黑板上唯一的按鈕現(xiàn)在變成了左邊的按鈕:
left_button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN)
你還需要在程序的其他地方進(jìn)行相同的更改。轉(zhuǎn)到按鈕處理器功能并更改行:
button.irq(handler=None)
它讀取:
left_button.irq(handler=None)
接下來,為第二個按鈕添加:
right_button.irq(handler=None)
向下滾動到程序的底部,并更改設(shè)置中斷觸發(fā)器的行,以便它進(jìn)行讀取:
left_button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler)
同樣,在它下面添加另一行,以在新按鈕上設(shè)置中斷觸發(fā)器:
right_button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler)
你的程序現(xiàn)在應(yīng)該看起來像這樣:
import machine import utime import urandom led = machine.Pin(15, machine.Pin.OUT) left_button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN)right_button = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_DOWN) def button_handler(pin): left_button.irq(handler=None) right_button.irq(handler=None) timer_reaction = utime.ticks_diff(utime.ticks_ms(), timer_start) print("Your reaction time was " + str(timer_reaction) + " milliseconds!") led.value(1) utime.sleep(urandom.uniform(5, 10)) led.value(0) right_button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler) left_button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler)
點擊 Run 圖標(biāo),等待 LED 熄滅,然后按下左邊的按鈕開關(guān),你會看到游戲和之前一樣,將你的反應(yīng)時間打印到 Shell 區(qū)域。再次點擊運行圖標(biāo),但這一次,當(dāng) LED 熄滅時,按右邊的按鈕也在正常工作,打印你們的反應(yīng)時間。
中斷和中斷處理函數(shù)
你創(chuàng)建的每個中斷都需要一個處理程序,但單個處理程序可以處理任意數(shù)量的中斷。在這個程序中,有兩個中斷都指向同一個處理程序,這意味著無論觸發(fā)哪個中斷,它們都將運行相同的代碼。不同的程序可能有兩個處理程序,讓每個中斷運行不同的代碼,這完全取決于你需要你的程序做什么。
為了讓游戲更精彩一點,你可以讓它報告兩個玩家中哪一個是第一個按下按鈕的。回到程序的 頂部,就在下面,你可以設(shè)置 LED 和兩個按鈕,并添加以下內(nèi)容:
fastest_button = None
這將設(shè)置一個新變量 fastest_button,并將其初始值設(shè)置為 None,因為還沒有按下任何按鈕。
接下來,到按鈕處理程序的底部,刪除處理計時器和打印的兩行,然后用以下代碼替換它們:
global fastest_button fastest_button = pin
這兩行代碼讓 fastest_button 成為變量,并將其設(shè)置為相應(yīng)按鈕的引腳編號。
現(xiàn)在直接轉(zhuǎn)到程序的底部,并添加以下兩行:
while fastest_button is None: utime.sleep(1)
這里創(chuàng)建了一個循環(huán),但它不是一個無限循環(huán)。這里,你告訴 MicroPython 只有在 fastest_button 變量仍然為 None 時才在循環(huán)中運行代碼。實際上,這會暫停程序的主線程,直到中斷處理程序更改了變量的值。
如果兩個玩家都沒有按下按鈕,程序就會暫停。
最后,你需要一種方法來確定哪位選手獲勝,并向他們表示祝賀。在程序的底部輸入以下代碼:
if fastest_button is left_button: print("Left Player wins!") elif fastest_button is right_button: print("Right Player wins!")
第一行設(shè)置了一個 if 條件,用于查看 fastest_button 變量是否為 left_button(這意味著 IRQ 是由左手按鈕觸發(fā)的)。如果是這樣,它將打印一條消息祝賀左邊的玩家(他的按鈕連接到 GP14 引腳)。
如果條件不成立,它將查看 fastest_button 變量是否為 right_button。如果是,則打印一條消息祝賀右邊的玩家,該玩家的按鈕已連接到 GP16。
完成的程序如下:
import machine import utime import urandom led = machine.Pin(15, machine.Pin.OUT) left_button = machine.Pin(14, machine.Pin.IN, machine.Pin.PULL_DOWN) right_button = machine.Pin(16, machine.Pin.IN, machine.Pin.PULL_DOWN) fastest_button = None def button_handler(pin): left_button.irq(handler=None) right_button.irq(handler=None) global fastest_button fastest_button = pin led.value(1) utime.sleep(urandom.uniform(5, 10)) led.value(0) left_button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler) right_button.irq(trigger=machine.Pin.IRQ_RISING, handler=button_handler) while fastest_button is None: utime.sleep(1) if fastest_button is left_button: print("Left Player wins!") elif fastest_button is right_button: print("Right Player wins!")
點擊 Run 按鈕運行程序,等待 LED 熄滅,但不要按下任何一個按鈕開關(guān)。
你將看到 Shell 區(qū)域仍然是空白的,并且不會返回「>>>」提示符。這是因為主線程仍在運行,處于你創(chuàng)建的循環(huán)中。
現(xiàn)在按左手按鈕(GP14)。你將看到一條祝賀你的消息「Left Player wins!」打印到 Shell 上。
再次單擊 Run 運行,并嘗試在 LED 熄滅后按下右手按鈕。你將看到另一條消息「Right Player wins!」打印出來,這一次祝賀你的右手。
再次點擊 Run 運行,這次每個按鈕上都有一個手指,同時按下它們,看看你的右手還是左手更快!
現(xiàn)在你已經(jīng)創(chuàng)造了一個雙人游戲,你可以邀請你的朋友一起玩,看看你們誰的反應(yīng)速度最快!
-
微控制器
+關(guān)注
關(guān)注
48文章
7578瀏覽量
151734 -
mcu
+關(guān)注
關(guān)注
146文章
17205瀏覽量
351936 -
led燈
+關(guān)注
關(guān)注
22文章
1592瀏覽量
108187 -
計算機(jī)
+關(guān)注
關(guān)注
19文章
7525瀏覽量
88318 -
樹莓派
+關(guān)注
關(guān)注
117文章
1710瀏覽量
105755
原文標(biāo)題:用樹莓派 Pico 編一個拼手速游戲
文章出處:【微信號:趣無盡,微信公眾號:趣無盡】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論