步驟1:什么是有限狀態(tài)機(jī)?
一個(gè)有限狀態(tài)機(jī)(簡(jiǎn)稱FSM)是一臺(tái)機(jī)器(以抽象的方式)具有定義的有限數(shù)量的可能狀態(tài),一次只能激活一個(gè)狀態(tài)。狀態(tài)通過(guò)轉(zhuǎn)換連接。這些過(guò)渡有一個(gè)確定的方向,只能朝這個(gè)方向通過(guò)-可以將其視為一條單向街道。此外,過(guò)渡具有某些輸入和輸出。您可以將其視為要使用單向街道之前必須滿足的條件,并且在使用單向街道時(shí)會(huì)向外界發(fā)出信號(hào)–例如,您必須先付費(fèi)才能使用使用街道,然后計(jì)算您的汽車。
在許多房屋中都發(fā)現(xiàn)了一個(gè)非?;镜腇SM示例:按一下按鈕即可激活樓梯間的燈。一定時(shí)間后,指示燈會(huì)自動(dòng)關(guān)閉。您可以將此模型建模為具有兩種狀態(tài)的FSM:“亮”和“滅”。從一種狀態(tài)過(guò)渡到另一種狀態(tài),反之亦然,條件是在一個(gè)方向上按下按鈕,而在另一方向上經(jīng)過(guò)了一定的時(shí)間。我們可以在圖表(即狀態(tài)圖)中對(duì)此行為進(jìn)行建模??匆幌聢D片1。
黑色實(shí)心圓圈標(biāo)記著狀態(tài)機(jī)的入口點(diǎn)(一切都必須從某處開(kāi)始)。因此,如果我們的機(jī)器開(kāi)始運(yùn)行,則指示燈熄滅。一直這樣,直到我們使用電燈開(kāi)關(guān)–燈才亮起,并在30秒后熄滅。當(dāng)指示燈已亮起時(shí)按按鈕不起作用,并且30秒鐘后指示燈熄滅。該FSM沒(méi)有任何輸出。從傳統(tǒng)的數(shù)學(xué)角度來(lái)看,“點(diǎn)亮”狀態(tài)與“打開(kāi)”狀態(tài)等效,但是當(dāng)我們真正開(kāi)始對(duì)系統(tǒng)進(jìn)行編程時(shí),我們當(dāng)然需要添加某種實(shí)際上可以完成某些工作的輸出,例如打開(kāi)
此狀態(tài)機(jī)有效,但這是一個(gè)好的系統(tǒng)嗎? 30秒足夠長(zhǎng)嗎?對(duì)于大多數(shù)人來(lái)說(shuō),可能是的,但是位于10樓的人們可能不喜歡我們的系統(tǒng)。他們可能需要30秒以上的時(shí)間,比方說(shuō)他們需要40秒。但是他們需要等待30秒鐘過(guò)去,然后關(guān)閉燈以再次激活燈,然后他們可能在樓梯中間。因此,我們需要做的是允許在燈亮并且再次按下按鈕時(shí)重置計(jì)時(shí)器。為此,我們將需要對(duì)系統(tǒng)進(jìn)行重新建模,將計(jì)時(shí)器的開(kāi)始建模為轉(zhuǎn)換的輸出,并添加另一個(gè)轉(zhuǎn)換,如圖2所示。
在這里,您可以看到兩件事:
過(guò)渡完全可以進(jìn)入到它來(lái)自的狀態(tài)
過(guò)渡可以有一個(gè)事件作為發(fā)生過(guò)渡的條件,例如以及分配的輸出。斜線左邊的信息是事件,右邊的信息是輸出。事件也被視為機(jī)器的輸入,這被稱為輸入-輸出-自動(dòng)機(jī)。
步驟2:在Arduino上手動(dòng)實(shí)現(xiàn)FSM
當(dāng)我們想在Arduino上實(shí)現(xiàn)此行為時(shí),代碼可能類似于以下要點(diǎn)。代碼沒(méi)有什么特別的,switch-case語(yǔ)句僅針對(duì)每種可能的狀態(tài)包含一個(gè)case,并在其中檢查是否滿足轉(zhuǎn)換條件。如果是這樣,狀態(tài)就會(huì)更改。
如您所見(jiàn),代碼非常簡(jiǎn)單。但是您能想象如果沒(méi)有2個(gè)州,而是10個(gè)或100個(gè)州,會(huì)發(fā)生什么情況?對(duì)于現(xiàn)實(shí)世界的FSM來(lái)說(shuō),這并不罕見(jiàn)。該代碼變得不可讀,并且可以達(dá)到數(shù)千行的長(zhǎng)度。同樣,通常,我們希望以圖形方式計(jì)劃FSM,因?yàn)槲覀冃枰軌虮M快查看其實(shí)際功能。然后,我們?nèi)匀恍枰獙?duì)實(shí)際的狀態(tài)機(jī)進(jìn)行編碼,并且需要確保圖形設(shè)計(jì)和手寫代碼實(shí)際上可以完成相同的工作。這可能是一個(gè)巨大的問(wèn)題。
考慮一下:對(duì)于我們的FSM所具有的每個(gè)狀態(tài),我們的代碼都需要一個(gè)“ case”語(yǔ)句,對(duì)于向其他狀態(tài)的每個(gè)轉(zhuǎn)換,我們都需要在其中包含一個(gè)if或case語(yǔ)句。如果我們有一個(gè)狀態(tài)機(jī),每個(gè)狀態(tài)都可以到達(dá)其他每個(gè)狀態(tài)(最極端的情況),我們的代碼將以 n平方增長(zhǎng),其中 n 是狀態(tài)。因此,對(duì)于3個(gè)狀態(tài),我們將有3種情況,內(nèi)部有3個(gè)ifs,因此代碼長(zhǎng)度將與9成正比。當(dāng)我們有10個(gè)狀態(tài)(不是很多)時(shí),代碼長(zhǎng)度將與100成正比,并且在20個(gè)州中,代碼已長(zhǎng)四倍。該FSM的圖形表示將更容易掌握,并且如果我們不必處理所有這些switch case語(yǔ)句,那將很好。如果您熟悉描述模擬器原理圖的網(wǎng)表–我們也不想使用網(wǎng)表設(shè)計(jì)原理圖。那么,我們?cè)撛趺醋瞿兀?/p>
步驟3:獲取YAKINDU Statechart工具
Yakindu SCT正是為此而設(shè)計(jì)的:對(duì)系統(tǒng)建模并從中生成代碼。建模工具比簡(jiǎn)單的有限狀態(tài)機(jī)先進(jìn)得多,因?yàn)樗鼈兓贖arel的狀態(tài)圖理論。它們通過(guò)一些其他概念擴(kuò)展了常規(guī)自動(dòng)機(jī)理論-例如,歷史狀態(tài),其中離開(kāi)狀態(tài)圖可保存活動(dòng)狀態(tài),因此您可以稍后再返回等等。對(duì)于‘Ible,我們將不需要這些額外的功能。
Yakindu SCT基于Eclipse(最常用的IDE之一)。因此,我們可以使用市場(chǎng)上所有的Eclipse插件,并擁有一個(gè)已知的環(huán)境。它是開(kāi)源的,這意味著它是免費(fèi)的!首先,請(qǐng)?jiān)L問(wèn)statecharts.org,然后選擇“下載SCT”。您將需要輸入姓名,電子郵件地址和職業(yè)。下載該工具后,只需解壓縮它(右鍵單擊-》全部提取,或類似操作)。在里面,您會(huì)發(fā)現(xiàn)“ SCT”。啟動(dòng)它。 (不,不需要真正的安裝。)
在安裝Yakindu SCT之后,您將具有對(duì)FSM進(jìn)行建模的工具,但是我們將希望獲得在Arduino上運(yùn)行的代碼。有一個(gè)出色的Eclipse插件可以做到這一點(diǎn),您可以在http://www.baeyens.it/eclipse/上找到有關(guān)它的更多信息。它為您提供了Eclipse內(nèi)部的完整Arduino工具鏈,因此您可以輕松使用Arduino IDE以及Eclipse的智能代碼管理和編碼助手。在SCT中,轉(zhuǎn)到幫助-》安裝新軟件。安裝向?qū)Т蜷_(kāi)。單擊向?qū)в疑辖歉浇奶砑印?. 按鈕。將打開(kāi)一個(gè)對(duì)話框,要求您指定要從中安裝新軟件的更新存儲(chǔ)庫(kù)。在“名稱”字段中輸入一些文本。該文本原則上是任意的,但是您應(yīng)該選擇一些使其更容易在其他更新存儲(chǔ)庫(kù)中標(biāo)識(shí)此特定更新存儲(chǔ)庫(kù)的內(nèi)容。輸入更新存儲(chǔ)庫(kù)的名稱和位置(http://eclipse.baeyens.it/update/V4/stable)之后,單擊“確定”。 Eclipse建立與更新存儲(chǔ)庫(kù)的網(wǎng)絡(luò)連接,向其詢問(wèn)可用的軟件項(xiàng),并在安裝向?qū)е酗@示它們。在這里,您只需接受“ Arduino”選項(xiàng)。再單擊幾次“下一步”并在以后接受許可協(xié)議,它將要求您重新啟動(dòng)該工具。完成此操作后,插件將下載所有需要的庫(kù),因此您無(wú)需從現(xiàn)有的Arduino項(xiàng)目復(fù)制它們。接下來(lái),在Yakindu SCT安裝中安裝了Arduino工具。現(xiàn)在是時(shí)候結(jié)合兩者的可能性了。
注意:如果您尚未安裝Windows,請(qǐng)同時(shí)安裝官方的Arduino IDE。它帶有必需的驅(qū)動(dòng)程序。我不確定Mac上的情況。 Linux已經(jīng)包含驅(qū)動(dòng)程序,因此不需要安裝Arduino IDE。
步驟4:開(kāi)始創(chuàng)建狀態(tài)圖
我們現(xiàn)在將開(kāi)始一起對(duì)狀態(tài)圖進(jìn)行建模。首先,我們將創(chuàng)建一個(gè)新項(xiàng)目。您應(yīng)該在SCT/Eclipse的歡迎頁(yè)面上。 轉(zhuǎn)到文件-》新建-》項(xiàng)目。.. ,然后在主菜單中選擇 Arduino-》新建Arduino Sketch 。將出現(xiàn)新Eclipse項(xiàng)目的常規(guī)向?qū)?。您必須給您的項(xiàng)目起一個(gè)名字。我們將其命名為ArduinoFSM。在下一個(gè)窗口中,您可以指定arduino連接到的端口。如果您不知道并且不知道如何查找,請(qǐng)忽略此。現(xiàn)在,您可以單擊完成。
如果您改為選擇 New-》 Arduino Sketch ,則不會(huì)詢問(wèn)您arduino的連接位置。然后,使用 Project-》 Properties 進(jìn)行操作。如果您不知道如何確定Arduino的端口,此說(shuō)明的最后一步將為您提供幫助。
如果在創(chuàng)建項(xiàng)目后未關(guān)閉歡迎屏幕,請(qǐng)關(guān)閉它您自己的,使用標(biāo)簽中的X?,F(xiàn)在,您應(yīng)該具有與左側(cè)“項(xiàng)目資源管理器”中的第一張圖片相似的圖片。
我們現(xiàn)在要?jiǎng)?chuàng)建一個(gè)名為“ model”的新文件夾。右鍵單擊您的項(xiàng)目,然后選擇新建-》文件夾。鍵入名稱,然后單擊“完成”。
右鍵單擊該新文件夾,再次轉(zhuǎn)到“新建”。根據(jù)您的安裝,您可能可以直接添加新的Statechart模型,或者可能必須使用Other,選擇Yakindu,然后選擇Statechart模型?,F(xiàn)在,您應(yīng)該看到的是第二張圖片:一個(gè)進(jìn)入狀態(tài)和一個(gè)名為 StateA 的通用第一狀態(tài)。
左側(cè)的文本框允許您聲明相關(guān)的事件和變量狀態(tài)圖,右邊的區(qū)域是圖形狀態(tài)圖編輯器。
我們將需要一個(gè)事件:按鈕。雙擊左側(cè)的文本框,然后在界面下插入文本
in event button
,然后聲明有一個(gè)名為“ button”的傳入事件。另外,在該文本框中雙擊單詞“ default”,并給狀態(tài)圖取一個(gè)更好的名稱-“ LightCtrl”怎么樣?現(xiàn)在,添加另一個(gè)狀態(tài):只需在右側(cè)面板中單擊 State ,然后在圖形狀態(tài)圖編輯器中的某個(gè)位置。雙擊這兩個(gè)州的名稱,并為其命名一個(gè)帶有黑色輸入狀態(tài)的名稱,熄滅,然后將新?tīng)顟B(tài)點(diǎn)亮。現(xiàn)在,我們需要過(guò)渡:從面板中選擇過(guò)渡,單擊一個(gè)狀態(tài),保持并拖動(dòng)到另一狀態(tài)。這應(yīng)該構(gòu)成過(guò)渡。它從您第一次單擊的狀態(tài)變?yōu)榈诙€(gè)狀態(tài)。通過(guò)單擊您現(xiàn)在拖動(dòng)到第一個(gè)的狀態(tài)并拖動(dòng)到另一個(gè)狀態(tài)來(lái)添加第二個(gè)過(guò)渡,這樣您就可以在兩個(gè)方向上進(jìn)行過(guò)渡?,F(xiàn)在,單擊過(guò)渡。將出現(xiàn)一個(gè)文本字段。在這里,您可以輸入要進(jìn)行過(guò)渡的事件和輸出。在從關(guān)閉燈光到打開(kāi)燈光的過(guò)渡上,鍵入按鈕,在另一個(gè)按鈕上,在5秒后鍵入 (比測(cè)試的30秒要快)?,F(xiàn)在,您應(yīng)該擁有看起來(lái)像第三張圖片的東西!
現(xiàn)在就這些了。您有一個(gè)樓梯燈的工作模型!
步驟5:模擬狀態(tài)圖
Yakindu SCT的另一個(gè)不錯(cuò)的功能是您可以模擬狀態(tài)圖而無(wú)需事先編寫任何代碼。您可以嘗試使用狀態(tài)機(jī)來(lái)實(shí)現(xiàn)您想要的狀態(tài)。
模擬狀態(tài)圖非常簡(jiǎn)單。右鍵單擊Eclipse/SCT中的.sct文件,選擇運(yùn)行方式,然后選擇狀態(tài)圖模擬。
將打開(kāi)一個(gè)新透視圖。您應(yīng)該能夠看到第一個(gè)狀態(tài)是紅色,這是活動(dòng)狀態(tài)。 (看圖片)在右邊,應(yīng)該打開(kāi)了Simulation View。您可以通過(guò)在右下方的模擬視圖中單擊單詞 button 來(lái)模擬按鈕按下事件?;顒?dòng)狀態(tài)應(yīng)從“熄滅”更改為“點(diǎn)亮”。五秒鐘后,或單擊時(shí)間事件 Light_On_timer_event_0 后,活動(dòng)狀態(tài)將更改回 Light Off 。太棒了!現(xiàn)在,讓我們檢查一下如何在Arduino上使用它。
步驟6:將系統(tǒng)帶入現(xiàn)實(shí)世界
好吧,我們點(diǎn)擊了一下,使用了圖形編輯器(通常與低級(jí)語(yǔ)言相關(guān)聯(lián)),讓這件事栩栩如生。首先,我們需要一個(gè)代碼生成器,將狀態(tài)圖轉(zhuǎn)換為C代碼。
右鍵單擊您的模型文件夾,然后選擇 New-》 Code Generator Model ,這非常簡(jiǎn)單,盡管看起來(lái)一開(kāi)始就像是黑魔法。在向?qū)е袉螕糇约?,然后將代碼生成器附加到之前創(chuàng)建的狀態(tài)圖。注意:在同一窗口中,頂部有一個(gè)選擇器,可以輕松查看。使用它選擇C代碼生成器而不是Java代碼生成器,然后選中狀態(tài)圖旁邊的復(fù)選框,然后單擊完成。正常情況下,生成器現(xiàn)在應(yīng)該一直一直直接自動(dòng)運(yùn)行。檢查是否創(chuàng)建了兩個(gè)文件夾src和src-gen。如果不是這種情況,請(qǐng)轉(zhuǎn)到主菜單中的“項(xiàng)目”,然后檢查是否激活了“自動(dòng)生成”。如果不是,請(qǐng)這樣做,然后右鍵單擊您的項(xiàng)目,然后選擇“生成項(xiàng)目”。進(jìn)度條以及兩個(gè)提到的文件夾都應(yīng)出現(xiàn)。進(jìn)行任何更改后,還可以右鍵單擊生成器文件,然后選擇 Generate Code Artifacts 。
src-gen文件夾的內(nèi)容非常有趣。文件 LightCtrl.c 包含狀態(tài)圖的實(shí)現(xiàn)。檢查時(shí),您會(huì)發(fā)現(xiàn)一個(gè)函數(shù) LightCtrlIface_raise_button(LightCtrl *句柄)。您可以調(diào)用此函數(shù)來(lái)引發(fā)我們先前聲明的按鈕事件,例如,當(dāng)您檢查硬件按鈕的引腳并看到其具有高電平時(shí)。然后是文件 LightCtrlRequired.h ,您需要在其中查看。它聲明您需要實(shí)現(xiàn)的功能。對(duì)于此狀態(tài)圖,只有兩個(gè)功能: lightCtrl_setTimer 和 lightCtrl_unsetTimer 。我們需要這些功能,因?yàn)闋顟B(tài)圖在5s之后使用了構(gòu)造。這是一個(gè)非常方便的功能,但是我們的狀態(tài)圖代碼生成器不提供計(jì)時(shí)服務(wù),因?yàn)樗叨纫蕾囉谄脚_(tái)–您的計(jì)算機(jī)與微型Arduino的計(jì)時(shí)器處理方式不同,而Mac和Linux上的計(jì)時(shí)器處理方式與Windows上的處理方式不同。
幸運(yùn)的是,我將為您提供計(jì)時(shí)服務(wù),因此您無(wú)需自己實(shí)現(xiàn)。在您的項(xiàng)目中,創(chuàng)建一個(gè)新文件夾,將其命名為 scutils ,用于 s tate c hart 實(shí)用程序功能。您可以隨意命名,也可以選擇不創(chuàng)建該文件夾,這只是組織問(wèn)題。我們將在其中創(chuàng)建兩個(gè)文件,分別是 sc_timer_service.c 和 sc_timer_service.h 。從GitHub中復(fù)制
代碼:
sc_timer_service.h
sc_timer_service.c
使用YAKINDU SCT 2.7.0,在那里是一個(gè)新選項(xiàng),可用于獲得此可指導(dǎo)的項(xiàng)目:
在SCT中,轉(zhuǎn)到“文件”-》“新建”-》“示例。..”,選擇“ YAKINDU Statechart示例”,然后單擊“下一步”。在新的示例向?qū)е?,單擊“下載”以獲取最新的示例集。從arduino類別中選擇“ Arduino的有限狀態(tài)機(jī)”,然后單擊“完成”。該項(xiàng)目將被復(fù)制到您的工作區(qū)中。右鍵單擊它,然后單擊“刷新”-可以肯定。
現(xiàn)在,我們可以開(kāi)始在向?qū)傻? .ino文件中的Arduino代碼上工作。
除了 Arduino.h ,還包括 avr/sleep.h ,當(dāng)然還有我們的狀態(tài)機(jī)和計(jì)時(shí)器服務(wù): LightCtrl.h , LightCtrlRequired.h 和 sc_timer_service.h ?,F(xiàn)在,需要常規(guī)的Arduino東西:我們定義按鈕和LED的引腳,并將它們?cè)O(shè)置在設(shè)置功能內(nèi)(這就是它的用途)。然后,我們需要定義狀態(tài)圖期望我們定義的函數(shù)-如前所述,- lightCtrl_setTimer 和 lightCtrl_unsetTimer 。在這里,我們只使用計(jì)時(shí)器服務(wù),就完成了?,F(xiàn)在,我們應(yīng)該思考一下當(dāng)達(dá)到 Light On 狀態(tài)時(shí)實(shí)際上如何激活LED。基本上,我們有三個(gè)選項(xiàng):
我們可以檢查狀態(tài)機(jī)是否處于Light On狀態(tài),并根據(jù)該信息激活/禁用LED
我們可以進(jìn)入狀態(tài)圖,并在到達(dá)狀態(tài)時(shí)設(shè)置一個(gè)變量,以便我們可以輪詢
我們可以添加一個(gè)操作來(lái)管理狀態(tài)圖在過(guò)渡時(shí)調(diào)用的光。
第一個(gè)解決方案確實(shí)很糟糕。我們將有關(guān)于狀態(tài)圖外部的邏輯。如果我們重命名我們的州,它將停止正常工作;但是這些名稱是平淡無(wú)奇的,與邏輯無(wú)關(guān)??梢允褂米兞浚貏e是在使用桌面應(yīng)用程序時(shí)。我們可以每x毫秒左右與他們同步一次。在這里,我們要使用一個(gè)操作。在狀態(tài)圖的接口聲明中添加以下內(nèi)容:
operation setLight(LightOn: boolean): void
這聲明了一個(gè)函數(shù),該函數(shù)接受布爾值作為參數(shù),但不返回任何值(無(wú)效)。這對(duì)您來(lái)說(shuō)不是新手,只是這里的語(yǔ)法不同。請(qǐng)記住–狀態(tài)圖未綁定到特定語(yǔ)言,因此語(yǔ)法是通用的。此功能自動(dòng)顯示在 LightCtrlRequired.h 中。如果沒(méi)有,請(qǐng)保存狀態(tài)圖,右鍵單擊您的項(xiàng)目并進(jìn)行構(gòu)建。
此處聲明的函數(shù)如下所示:
extern void lightCtrlIface_setLight(const LightCtrl* handle, const sc_boolean lightOn);
輸入?yún)?shù)句柄為類型的LightCtrl,它是狀態(tài)圖的引用者。如果您不熟悉C:星號(hào)表示所謂的指針,那么該變量包含statechart變量的地址。這對(duì)我們有幫助,因?yàn)槲覀兛梢詫?duì)原始對(duì)象進(jìn)行操作,而不必創(chuàng)建其副本。因此,讓我們實(shí)現(xiàn)此功能:
void lightCtrlIface_setLight(const LightCtrl* handle, const sc_boolean lightOn) {
if(lightOn)
digitalWrite(LED_PIN, HIGH);
else
digitalWrite(LED_PIN, LOW);
}
如您所見(jiàn),此功能非常簡(jiǎn)單-我們甚至不使用狀態(tài)圖的句柄,我們只在LED上寫HIGH如果操作的參數(shù)為true,則為pin;否則為L(zhǎng)OW。
我們更改狀態(tài)圖本身,使其看起來(lái)像第一張圖片。
還記得第1步嗎?斜線左邊是過(guò)渡所需要的輸入,右邊是狀態(tài)機(jī)的輸出(如果進(jìn)行了過(guò)渡)。此處的輸出是使用這些參數(shù)調(diào)用指定的操作。
#include “Arduino.h”
#include “avr/sleep.h”
#include “src-gen/LightCtrl.h”
#include “src-gen/LightCtrlRequired.h”
#include “scutil/sc_timer_service.h”
#define BUTTON_PIN 3
#define LED_PIN 6
#define MAX_TIMERS 20 //number of timers our timer service can use
#define CYCLE_PERIOD 10 //number of milliseconds that pass between each statechart cycle
static unsigned long cycle_count = 0L; //number of passed cycles
static unsigned long last_cycle_time = 0L; //timestamp of last cycle
static LightCtrl lightctrl;
static sc_timer_service_t timer_service;
static sc_timer_t timers[MAX_TIMERS];
//! callback implementation for the setting up time events
void lightCtrl_setTimer(LightCtrl* handle, const sc_eventid evid, const sc_integer time_ms, const sc_boolean periodic){
sc_timer_start(&timer_service, (void*) handle, evid, time_ms, periodic);
}
//! callback implementation for canceling time events.
void lightCtrl_unsetTimer(LightCtrl* handle, const sc_eventid evid) {
sc_timer_cancel(&timer_service, evid);
}
void lightCtrlIface_setLight(const LightCtrl* handle, const sc_boolean lightOn) {
if(lightOn)
digitalWrite(LED_PIN, HIGH);
else
digitalWrite(LED_PIN, LOW);
}
//The setup function is called once at startup of the sketch
void setup()
{
pinMode(BUTTON_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
sc_timer_service_init(
&timer_service,
timers,
MAX_TIMERS,
(sc_raise_time_event_fp) &lightCtrl_raiseTimeEvent
);
lightCtrl_init(&lightctrl); //initialize statechart
lightCtrl_enter(&lightctrl); //enter the statechart
}
// The loop function is called in an endless loop
void loop()
{
unsigned long current_millies = millis();
if(digitalRead(BUTTON_PIN))
lightCtrlIface_raise_button(&lightctrl);
if ( cycle_count == 0L || (current_millies 》= last_cycle_time + CYCLE_PERIOD) ) {
sc_timer_service_proceed(&timer_service, current_millies - last_cycle_time);
lightCtrl_runCycle(&lightctrl);
last_cycle_time = current_millies;
cycle_count++;
}
}
此外,請(qǐng)按行號(hào)在本要點(diǎn)中檢查代碼。
第1-6行包含如前所述的包含。
第8行和第9行定義了我們將要用于arduino的硬件引腳。
第11行和第12行定義了狀態(tài)圖可以使用多少個(gè)計(jì)時(shí)器,以及狀態(tài)圖的每個(gè)計(jì)算周期之間應(yīng)經(jīng)過(guò)多少毫秒。
第15和16行聲明了一些變量,我們可以用它們來(lái)計(jì)數(shù)周期并管理最后一個(gè)周期的時(shí)間。
第17、19和21行聲明了使用狀態(tài)圖的重要變量:狀態(tài)圖本身,計(jì)時(shí)器服務(wù)和計(jì)時(shí)器數(shù)組。
第24行和第33行定義了狀態(tài)圖需要計(jì)時(shí)器使用的功能,第33行是設(shè)置前面討論過(guò)的LED的功能。
在第41行中,void setup()是Arduino的標(biāo)準(zhǔn)功能。它在啟動(dòng)時(shí)被調(diào)用一次。我們用它來(lái)初始化東西–我們的LED和按鈕引腳配置了它們的方向(INPUT是標(biāo)準(zhǔn)的,為清楚起見(jiàn),我們這樣做了),計(jì)時(shí)器服務(wù)被初始化,狀態(tài)圖被初始化并輸入。輸入意味著啟動(dòng)狀態(tài)機(jī),因此第一個(gè)狀態(tài)被激活-這是輸入狀態(tài)所指向的狀態(tài)。因此,在啟動(dòng)時(shí),指示燈熄滅。
在第59行中,跟隨著循環(huán)功能,Arduino一直在調(diào)用它。
在第61行中,我們使用millis()函數(shù)捕獲當(dāng)前時(shí)間,該函數(shù)由Arduino庫(kù)定義。
在第63行中,我們檢查按鈕是否被按下,如果按下,則引發(fā)按鈕事件。
在第66行中,我們檢查自上次循環(huán)狀態(tài)圖以來(lái)是否已超過(guò)CYCLE_PERIOD毫秒。
這會(huì)給我們的arduino帶來(lái)一些負(fù)擔(dān),這意味著我們可以可靠地將長(zhǎng)達(dá)10毫秒的時(shí)間用于自己的功能。
在第68行中,我們告訴計(jì)時(shí)器服務(wù)自上次調(diào)用以來(lái)已經(jīng)過(guò)去了多少時(shí)間,在第70行中告訴statechart運(yùn)行一個(gè)周期,在第72行中保存當(dāng)前時(shí)間,并增加周期計(jì)數(shù)在第73行。
使用arduino插件,您現(xiàn)在可以將arduino與LED和連接到計(jì)算機(jī)的按鈕連接起來(lái),并使用頂部工具欄中的按鈕將程序上傳到
電路如圖2和圖3所示。
LED通過(guò)大約200歐姆的電阻連接到數(shù)字引腳(6)。陰極連接到GND。
按鈕有四個(gè)引腳,請(qǐng)?jiān)诎聪掳粹o時(shí)檢查其中哪些始終連接以及哪些連接。然后,將數(shù)字引腳(此處使用3)連接到一側(cè),將下拉電阻連接到GND。這將使引腳停止處于“浮動(dòng)”狀態(tài)(不確定狀態(tài)),并將其保持在0 V電壓。當(dāng)按下按鈕并將另一側(cè)連接到VCC時(shí),該側(cè)“更強(qiáng)”,因?yàn)樗鼪](méi)有電阻,并且電壓高達(dá)5伏–基本上是一個(gè)分壓器,其中一個(gè)電阻為0歐姆。請(qǐng)?jiān)诖耸褂靡粋€(gè)較高的電阻,因?yàn)樗鼤?huì)限制通過(guò)按鈕的電流。最小值為1 kR。
如您所見(jiàn),該程序的邏輯完全獨(dú)立于我們狀態(tài)圖的實(shí)際大小。狀態(tài)圖具有2個(gè)或20個(gè)狀態(tài)都沒(méi)有關(guān)系-當(dāng)然,如果我們想做點(diǎn)什么,我們需要在這里和那里實(shí)現(xiàn)一個(gè)功能。但是void loop()內(nèi)部的主要代碼總是很小,并且允許模塊化程序體系結(jié)構(gòu)。我們只需要在代碼中處理從狀態(tài)圖到Arduino硬件的接口,自動(dòng)生成的狀態(tài)圖將處理其內(nèi)部邏輯。還記得我們討論過(guò)如何在再次按下按鈕時(shí)重置計(jì)時(shí)器嗎?現(xiàn)在,您可以使用“按鈕”作為保護(hù)事件,從“點(diǎn)亮”狀態(tài)添加到其自身的過(guò)渡,而無(wú)需在代碼中更改或添加一行。嘗試一下,然后開(kāi)始對(duì)軟件進(jìn)行建模,而不是編寫它!
步驟7:此外:查找您的Arduino端口
因此,您陷入困境,因?yàn)槟鸁o(wú)法弄清Arduino連接到哪個(gè)串行/USB端口。好的,您會(huì)在下面找到有關(guān)Windows和Linux的說(shuō)明。
Windows
將arduino插入計(jì)算機(jī),然后轉(zhuǎn)到“設(shè)備和打印機(jī)”(從開(kāi)始菜單或系統(tǒng)控制面板)。如圖所示,您的arduino應(yīng)該出現(xiàn)在這里-對(duì)我來(lái)說(shuō),端口為COM12。這可能會(huì)改變,例如,當(dāng)您使用另一個(gè)USB端口時(shí),重新啟動(dòng)系統(tǒng)。..如果仍然無(wú)法解決問(wèn)題,請(qǐng)檢查是否仍然正確。
Linux
使用您的arduino未連接,啟動(dòng)終端。輸入 dmesg 并返回,這將為您提供冗長(zhǎng)的文本輸出。插入您的arduino,然后再次輸入 dmesg 。最后應(yīng)該是一些有關(guān)arduino的消息,包括一個(gè)端口-例如,/dev/USB0,/dev/ttyAMC3-可以理解。如果您插入arduino且LED不亮,并且 dmesg 在插入之前和之后都顯示完全相同的內(nèi)容,則很可能是您的Arduino吐司了。
如果此方法不適合您,也可以在插入Arduino之前和之后嘗試 ls/dev/。這列出了所有可用的設(shè)備,并且在連接Arduino之后應(yīng)該能夠看到一個(gè)新設(shè)備。
-
fsm
+關(guān)注
關(guān)注
0文章
35瀏覽量
12831 -
Arduino
+關(guān)注
關(guān)注
188文章
6473瀏覽量
187418
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論