有限狀態(tài)機(jī)在單片機(jī)編程中的應(yīng)用(共16頁)_第1頁
有限狀態(tài)機(jī)在單片機(jī)編程中的應(yīng)用(共16頁)_第2頁
有限狀態(tài)機(jī)在單片機(jī)編程中的應(yīng)用(共16頁)_第3頁
有限狀態(tài)機(jī)在單片機(jī)編程中的應(yīng)用(共16頁)_第4頁
有限狀態(tài)機(jī)在單片機(jī)編程中的應(yīng)用(共16頁)_第5頁
已閱讀5頁,還剩11頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、窗體底端 有限狀態(tài)機(jī)在單片機(jī)編程中的應(yīng)用 在單片機(jī)編程中,如果在不使用操作系統(tǒng)的情況下同時(shí)執(zhí)行多個(gè)任務(wù),可能會(huì)遇到下面這些情況:· 一個(gè)任務(wù)的執(zhí)行時(shí)間過長,導(dǎo)致其他任務(wù)無法及時(shí)執(zhí)行· 在一些任務(wù)中大量使用 delay() 等函數(shù)進(jìn)行軟件延時(shí),這些延時(shí)函數(shù)占用過多時(shí)間,影響其他任務(wù)的執(zhí)行· 一些復(fù)雜任務(wù)的程序邏輯不清晰,不便于以后對(duì)程序進(jìn)行維護(hù),或添加新功能本文介紹的有限狀態(tài)機(jī),可以做到將一個(gè)耗時(shí)較長的復(fù)雜任務(wù)分解為多個(gè)簡單任務(wù),同時(shí)使代碼邏輯更加清晰,從而解決上述問題。目錄:·· ···· ·&

2、#183;·· ····1. 什么是有限狀態(tài)機(jī)根據(jù)維基百科上的定義,有限狀態(tài)機(jī)(finite-state machine, FSM,簡稱狀態(tài)機(jī))是表示有限個(gè)狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動(dòng)作等行為的數(shù)學(xué)模型。為了理解這句話,假設(shè)自己還有三天就要考試,這時(shí)候就要進(jìn)入緊張的備考狀態(tài),將空閑時(shí)間用在復(fù)習(xí)上。但是,為了保證足夠的精力,小睡一會(huì)兒也是十分有必要的。那么,什么時(shí)候復(fù)習(xí),什么時(shí)候睡覺呢?可以這樣描述:在復(fù)習(xí)的時(shí)候:如果 感到瞌睡,則 睡覺如果 沒有感覺到瞌睡,則 繼續(xù)復(fù)習(xí)在小睡的時(shí)候:如果 感覺不再瞌睡,則 開始復(fù)習(xí)如果 感覺依舊瞌

3、睡,則 繼續(xù)睡覺也可通過一幅簡單的示意圖(也叫狀態(tài)轉(zhuǎn)移圖)表示出來:這個(gè)例子其實(shí)就是一個(gè)簡單的有限狀態(tài)機(jī),其中,復(fù)習(xí)和小睡是兩個(gè)狀態(tài),感覺瞌睡和感覺清醒這兩個(gè)條件可以使?fàn)顟B(tài)發(fā)生轉(zhuǎn)換。另外,Programming Basics 網(wǎng)站上也提供了狀態(tài)機(jī)相關(guān)的教程,用形象化的圖片解釋了什么是有限狀態(tài)機(jī),可。在嵌入式程序設(shè)計(jì)中,如果一個(gè)系統(tǒng)需要處理一系列連續(xù)發(fā)生的任務(wù),或在不同的模式下對(duì)輸入進(jìn)行不同的處理,常常使用有限狀態(tài)機(jī)實(shí)現(xiàn)。例如測(cè)量、監(jiān)測(cè)、控制等控制邏輯型應(yīng)用。2. 有限狀態(tài)機(jī)的作用2.1 分解耗時(shí)過長的任務(wù)大家應(yīng)該都知道,CPU 沒有并行執(zhí)行任務(wù)的能力。計(jì)算機(jī)同時(shí)運(yùn)行多個(gè)程序,其實(shí)是多個(gè)程序依次

4、交替執(zhí)行,給人以程序同時(shí)運(yùn)行的錯(cuò)覺。各個(gè)程序在什么時(shí)候開始執(zhí)行,執(zhí)行多長時(shí)間后切換到下一個(gè)程序,由操作系統(tǒng)決定。單片機(jī)執(zhí)行多任務(wù)也是類似的過程,但由于其資源有限,為了節(jié)省對(duì) CPU 和存儲(chǔ)空間的占用,在很多情況下沒有使用操作系統(tǒng)。這時(shí),單片機(jī)中運(yùn)行的各個(gè)任務(wù)必須在一定時(shí)間內(nèi)主動(dòng)執(zhí)行完畢,才能保證下一個(gè)任務(wù)能夠及時(shí)執(zhí)行。對(duì)于一些需要長時(shí)間執(zhí)行的任務(wù),例如按鍵去除抖動(dòng)、讀取和播放 MP3 文件等,采用有限狀態(tài)機(jī)的方式,將任務(wù)劃分為多個(gè)小的步驟(狀態(tài)),每次只執(zhí)行其中的一步。這樣,其他任務(wù)就有機(jī)會(huì)插入到這個(gè)任務(wù)之中,確保了各個(gè)任務(wù)都能按時(shí)執(zhí)行。2.2 避免軟件延時(shí)對(duì) CPU 資源造成浪費(fèi)對(duì)于一些簡單

5、的程序,可通過 delay(), delay_ms() 之類的函數(shù)進(jìn)行軟件延時(shí)。這些延時(shí)函數(shù),一般是通過將某個(gè)變量循環(huán)遞加或遞加,到達(dá)一定值后跳出循環(huán),從而通過消耗 CPU 時(shí)間實(shí)現(xiàn)了延時(shí)。這種方式雖然簡單,但在延時(shí)函數(shù)執(zhí)行的過程中,其他程序無法運(yùn)行,消耗了大量 CPU 資源。而通過狀態(tài)機(jī),有助于減少軟件延時(shí)的使用,提高 CPU 利用率。請(qǐng)參考下文中的 示例一:按鍵去抖動(dòng)程序的優(yōu)化,這一例子展示了如何通過軟件延時(shí)分解耗時(shí)較長的任務(wù),同時(shí)減少軟件延時(shí)的使用。2.3 使程序邏輯更加清晰通過狀態(tài)機(jī),將一個(gè)復(fù)雜任務(wù)劃分為多個(gè)狀態(tài),可以使程序清晰易懂,便于維護(hù)。以后想要添加、刪除程序中的功能,都會(huì)變得非

6、常容易。下文中的 示例二:通過狀態(tài)機(jī)實(shí)現(xiàn)的鬧鐘 展示了如何通過狀態(tài)機(jī)優(yōu)化程序邏輯。3. 有限狀態(tài)機(jī)的實(shí)現(xiàn)3.1 通過 switch - case 語句實(shí)現(xiàn)如果使用 C 語言,switch - case 語句,即可簡單地實(shí)現(xiàn)有限狀態(tài)機(jī)。/* 定義各個(gè)狀態(tài)所對(duì)應(yīng)的數(shù)值 */#define STATUS_A 0#define STATUS_B 1#define STATUS_C 2/* 該變量的值即為當(dāng)前狀態(tài)機(jī)所處的狀態(tài) */uint8_t currentStatus = STATUS_A; /* 通過狀態(tài)機(jī)實(shí)現(xiàn)的某個(gè)任務(wù), * 需要放入 while(1) 等地方循環(huán)執(zhí)行 * /void fsm_a

7、pp(void) switch(currentStatus) /* 根據(jù)現(xiàn)在的狀態(tài)執(zhí)行相應(yīng)的程序 */ case STATUS_A: /* 狀態(tài) A */ doThingsForStatusA(); /* 執(zhí)行狀態(tài) A 中需要執(zhí)行的任務(wù) */ /* 若滿足狀態(tài)轉(zhuǎn)換的條件,則轉(zhuǎn)換到另一個(gè)狀態(tài) */ if(condition_1) currentStatus = STATUE_B; break; case STATUS_B: /* 狀態(tài) B */ doThingsForStatusB(); /* 執(zhí)行狀態(tài) B 中需要執(zhí)行的任務(wù) */ /* 若滿足狀態(tài)轉(zhuǎn)換的條件,則轉(zhuǎn)換到另一個(gè)狀態(tài) */ if(co

8、ndition_2) currentStatus = STATUE_C; if(condition_3) currentStatus = STATUE_A; break; case STATUS_C: /* 狀態(tài) C */ doThingsForStatusB(); /* 執(zhí)行狀態(tài) B 中需要執(zhí)行的任務(wù) */ /* 若滿足狀態(tài)轉(zhuǎn)換的條件,則轉(zhuǎn)換到另一個(gè)狀態(tài) */ if(condition_4) currentStatus = STATUE_A; break; default: currentStatus = STATUE_A; 通過這段程序,即可實(shí)現(xiàn)一個(gè)具有三個(gè)狀態(tài)的狀態(tài)機(jī)。狀態(tài)轉(zhuǎn)移圖如下圖所

9、示:3.2 通過 Arduino 庫實(shí)現(xiàn)對(duì)于 Arduino 用戶,還可以使用 FSM Library 實(shí)現(xiàn)。這一庫將有限狀態(tài)機(jī)進(jìn)行了封裝,可以以更簡潔的方式實(shí)現(xiàn)狀態(tài)機(jī)。下載地址及使用說明:3.3 其他方式對(duì)于一些更復(fù)雜的任務(wù),使用 switch - case 語句,代碼可能會(huì)太簡潔。這時(shí)候,使用其他方式實(shí)現(xiàn)狀態(tài)機(jī),可能會(huì)更好。具體請(qǐng)查閱相關(guān)資料。4. 示例一:按鍵去抖動(dòng)程序的優(yōu)化4.1 傳統(tǒng)的按鍵去抖動(dòng)程序初學(xué)單片機(jī)時(shí),我們接觸的按鍵去抖動(dòng)程序一般是這樣的:void keyscan() if(key1 = 0) / 如果按鍵 1 按下 delayms(10); / 延時(shí) 10ms,消除因干擾

10、產(chǎn)生的抖動(dòng) if(key1 = 0) / 再次檢測(cè)按鍵 1,如果依舊按下 doSomething(); /此時(shí)說明按鍵 1 已按下,執(zhí)行按鍵 1 需要執(zhí)行的任務(wù) while(!key1); / 等待按鍵釋放 對(duì)應(yīng)的流程圖如下:從流程圖中可知,delayms() 延時(shí)函數(shù)和最后的等待按鍵釋放的程序,會(huì)占用過多時(shí)間。4.2 優(yōu)化后的按鍵去抖動(dòng)程序如果使用有限狀態(tài)機(jī)的思路,可以按照下圖方式實(shí)現(xiàn):該狀態(tài)機(jī)有三個(gè)狀態(tài),分別是按鍵未按下,等待,按鍵按下。當(dāng)按鍵按下時(shí),則會(huì)進(jìn)入等待狀態(tài),若在等待狀態(tài)中按鍵一直保持按下,說明按鍵已經(jīng)穩(wěn)定地按下,進(jìn)入按鍵按下的狀態(tài),等待按鍵釋放。程序代碼如下:/* 按鍵去抖動(dòng)狀

11、態(tài)機(jī)中的三個(gè)狀態(tài) */#define KEY_STATE_RELEASE / 按鍵未按下#define KEY_STATE_WAITING / 等待(消抖)#define KEY_STATE_PRESSED / 按鍵按下(等待釋放)/* 等待狀態(tài)持續(xù)時(shí)間 * 需要根據(jù)單片機(jī)速度和按鍵消抖程序被調(diào)用的速度來進(jìn)行調(diào)整 */#define DURIATION_TIME 40/* 按鍵檢測(cè)函數(shù)的返回值,按下為 1,未按下為 0 */#define PRESSED 1#define NOT_PRESSED 0/* 按鍵掃描程序所處的狀態(tài) * 初始狀態(tài)為:按鍵按下(KEY_STATE_RELEASE) *

12、/uint8_t keyState = KEY_STATE_RELEASE;/* 按鍵檢測(cè)函數(shù),通過有限狀態(tài)機(jī)實(shí)現(xiàn) * 函數(shù)在從等待狀態(tài)轉(zhuǎn)換到按鍵按下狀態(tài)時(shí)返回 PRESSED,代表按鍵已被觸發(fā) * 其他情況返回 NOT_PRESSED */uint8_t keyDetect(void) static uint8_t duriation; / 用于在等待狀態(tài)中計(jì)數(shù) switch(keyState) case KEY_STATE_RELEASE: if(readKey() = 1) / 如果按鍵按下 keyState = KEY_STATE_WAITING; / 轉(zhuǎn)換至下一個(gè)狀態(tài) return

13、NOT_PRESSED; / 返回:按鍵未按下 break; case KEY_STATE_WAITING: if(readKey() = 1) / 如果按鍵按下 duriation+; if(duriation >= DURIATION_TIME) / 如果經(jīng)過多次檢測(cè),按鍵仍然按下 / 說明沒有抖動(dòng)了,可以確定按鍵已按下 duriation = 0; keyState = KEY_STATE_PRESSED; / 轉(zhuǎn)換至下一個(gè)狀態(tài) return PRESSED; else / 如果此時(shí)按鍵松開 / 可能存在抖動(dòng)或干擾 duriation = 0; / 清零的目的是便于下次重新計(jì)數(shù) k

14、eyState = KEY_STATE_RELEASE; / 重新返回按鍵松開的狀態(tài) return NOT_PRESSED; break; case KEY_STATE_PRESSED: if(readKey() = 0) / 如果按鍵松開 keyState = KEY_STATE_RELEASE; / 回到按鍵松開的狀態(tài) return NOT_PRESSED; break; default: keyState = KEY_STATE_RELEASE; return NOT_PRESSED; 該程序也可經(jīng)過擴(kuò)展,實(shí)現(xiàn)判斷按鍵雙擊、長按等功能。只需增加相應(yīng)的狀態(tài)和轉(zhuǎn)移條件即可。5. 示例二:通過

15、有限狀態(tài)機(jī)實(shí)現(xiàn)的鬧鐘程序最近正在制作一個(gè)鬧鐘。這個(gè)鬧鐘支持播放 MP3 格式的鬧鐘聲,支持貪睡模式,同時(shí)還有一些功能打算以后再添加上。為了使程序邏輯更加清晰,也為了更方便地添加新功能,我打算采用有限狀態(tài)機(jī)實(shí)現(xiàn)。相關(guān)程序如下:#include "App_Alarm.h"#include "USART1.h"#include <stdio.h>#include "diag/Trace.h"/* 相關(guān)常量定義 */#define ALARM_MUSIC_END 0 / 鬧鐘音樂播放完畢#define FORMAT_OK 0 /

16、格式正確#define FORMAT_ERROR (-1) / 格式錯(cuò)誤/* 輸入信息定義 * 作為函數(shù)的返回值供函數(shù) getInput() 使用 * getInput() 將獲取并返回鍵盤或觸摸屏等設(shè)備中輸入的控制命令或鬧鐘時(shí)間值 */#define INPUT_ERROR (-1) / 輸入格式錯(cuò)誤#define INPUT_CANCEL (-2) / 輸入了取消命令#define INPUT_SNOOZE (-3) / 輸入了小睡命令#define INPUT_ALARM_ON (-4) / 輸入了打開鬧鐘命令#define NO_INPUT (-10) / 沒有輸入/* 輸出信息定義

17、* 作為為函數(shù)的參數(shù)供函數(shù) displayMessege() 使用 * displayMessege() 用于在顯示屏上顯示相關(guān)的提示信息 */#define MESSEGE_SET_ALARM_TIME (0) / 提示:設(shè)置鬧鐘時(shí)間#define MESSEGE_CLEAR (1) / 提示:已取消#define MESSEGE_ALARM_IS_ON (2) / 提示:鬧鐘已打開#define MESSEGE_WAITING (3) / 提示:等待鬧鐘響起#define MESSEGE_SET_SNOOZE_TIME (4) / 提示:設(shè)置小睡時(shí)間#define MESSEGE_GET_

18、UP (5) / 提示:該起床了/* 鬧鐘的狀態(tài) */enum alarmStates ALARM_OFF, / 鬧鐘關(guān)閉 SET_ALARM_TIME, / 設(shè)置鬧鐘時(shí)間 WATING_FOR_ALARM, / 等待鬧鐘響起 PLAY_ALARM_MUSIC, / 播放鬧鐘音樂 SET_SNOOZE_TIME / 設(shè)置貪睡時(shí)間 alarmState = ALARM_OFF; / 默認(rèn)狀態(tài):鬧鐘關(guān)閉/* 相關(guān)函數(shù)的定義 */int16_t getInput(void);void displayMessege(uint8_t);void setAlarm(int16_t);int16_t ala

19、rmTimeDiff(void);int8_t playAlarmMusic(void);void setSnooze(int16_t);uint8_t checkAlarmFormat(int16_t);uint8_t checkSnoozeFormat(int16_t);/* * 鬧鐘主程序,需要放入 while(1) 中循環(huán)調(diào)用 */void alarmApp(void) int16_t input; / 輸入值暫存在這個(gè)變量中 switch (alarmState) / 獲取鬧鐘狀態(tài),下面程序?qū)⒏鶕?jù)鬧鐘的狀態(tài)執(zhí)行相應(yīng)的任務(wù) /* 狀態(tài):鬧鐘關(guān)閉 * 在此狀態(tài)中,將會(huì)不斷檢查是否打開鬧鐘

20、,如果打開了鬧鐘,則會(huì)進(jìn)入下一個(gè)狀態(tài):設(shè)置鬧鐘時(shí)間 */ case ALARM_OFF: if (getInput() = INPUT_ALARM_ON) / 檢查是否打開了鬧鐘 / 如果打開了鬧鐘 displayMessege(MESSEGE_SET_ALARM_TIME); / 在屏幕或串口上提示:請(qǐng)?jiān)O(shè)置鬧鐘時(shí)間 alarmState = SET_ALARM_TIME; / 進(jìn)入下一個(gè)狀態(tài):設(shè)置鬧鐘時(shí)間 break; /* 狀態(tài):設(shè)置鬧鐘時(shí)間 * 在此狀態(tài)中,將會(huì)檢查輸入值, * 如果 * 輸入“取消”命令,則取消鬧鐘設(shè)置,返回到鬧鐘關(guān)閉的狀態(tài) * 輸入鬧鐘時(shí)間格式錯(cuò)誤,則狀態(tài)不變,等待下

21、一次重新輸入 * 輸入了正確的鬧鐘時(shí)間,則設(shè)置鬧鐘,顯示鬧鐘設(shè)置成功,并進(jìn)入下一狀態(tài):等待鬧鐘響起 */ case SET_ALARM_TIME: input = getInput(); / 獲取輸入值 if(input = INPUT_CANCEL) / 如果輸入了“取消” displayMessege(MESSEGE_CLEAR); / 顯示“已取消” alarmState = ALARM_OFF; / 進(jìn)入狀態(tài):關(guān)閉鬧鐘 else if(checkAlarmFormat(input) = FORMAT_OK) / 如果輸入格式正確 displayMessege(MESSEGE_ALARM

22、_IS_ON); / 顯示“成功設(shè)置鬧鐘,鬧鐘已啟動(dòng)” setAlarm(input); / 根據(jù)輸入值設(shè)置鬧鐘 alarmState = WATING_FOR_ALARM; / 進(jìn)入下一狀態(tài):等待鬧鐘響起 break; /* 狀態(tài):等待鬧鐘響起 * 在此狀態(tài)中,將會(huì)檢查是否到達(dá)鬧鐘時(shí)間,如果到達(dá),則進(jìn)入下一狀態(tài):播放鬧鐘音樂 * 同時(shí),在此狀態(tài)中也會(huì)檢查輸入,如果輸入了“取消”的命令,則進(jìn)入鬧鐘關(guān)閉的狀態(tài) */ case WATING_FOR_ALARM: displayMessege(MESSEGE_WAITING); / 顯示等待鬧鐘響起的信息,例如離鬧鐘響起還有多長時(shí)間 if (ala

23、rmTimeDiff() <= 0) / 檢查離鬧鐘響起還有多少時(shí)間,如果時(shí)間小于等于零(到達(dá)鬧鐘時(shí)間) alarmState = PLAY_ALARM_MUSIC; / 進(jìn)入下一個(gè)狀態(tài):播放鬧鐘音樂 if(getInput() = INPUT_CANCEL) / 如果輸入了“取消”命令 displayMessege(MESSEGE_CLEAR); alarmState = ALARM_OFF; / 進(jìn)入鬧鐘關(guān)閉的狀態(tài) break; /* 狀態(tài):播放鬧鐘音樂 * 在此狀態(tài)中,將播放鬧鐘音樂,若播放完畢,進(jìn)入鬧鐘關(guān)閉的狀態(tài) * 同時(shí),在此狀態(tài)中也會(huì)檢查輸入, * 如果輸入了“小睡”的命令,則進(jìn)入狀態(tài):設(shè)置小睡時(shí)間 * 如果輸入了“取消”的命令,則進(jìn)入狀態(tài):鬧鐘關(guān)閉 */ case PLAY_ALARM_MUSIC: displayMessege(MESSEGE_GET_UP); / 顯示消息:“該起床了” if(playAlarmMusic() = ALARM_MUSIC_END) / 播放鬧鐘音樂 / 若音樂播放完畢 displayMessege(MESSEGE_

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論