日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁 > 嵌入式 > 嵌入式大雜燴
[導(dǎo)讀]來源:embed linux?share 作者:亞索老哥 模式動(dòng)機(jī) 狀態(tài)模式(狀態(tài)機(jī))是嵌入式開發(fā)中最重要、最核心的設(shè)計(jì)模式之一,毫不夸張的說,是否熟練掌握狀態(tài)模式,很大程度上直接決定了嵌入式工程師的代碼掌控能力。在嵌入式開發(fā)里面,幾乎80%以上的程序都有狀態(tài)模式(

來源:embed linux share

作者:亞索老哥


模式動(dòng)機(jī)

狀態(tài)模式(狀態(tài)機(jī))是嵌入式開發(fā)中最重要、最核心的設(shè)計(jì)模式之一,毫不夸張的說,是否熟練掌握狀態(tài)模式,很大程度上直接決定了嵌入式工程師的代碼掌控能力。在嵌入式開發(fā)里面,幾乎80%以上的程序都有狀態(tài)模式(狀態(tài)機(jī))的影子。在一個(gè)思路清晰而且高效的程序中,必然有狀態(tài)模式(狀態(tài)機(jī))身影浮現(xiàn)。但是很多嵌入式開發(fā)者只是掌握一些很基礎(chǔ)的狀態(tài)機(jī)編程,對(duì)狀態(tài)機(jī)編程如果提高程序的可維護(hù)性和可拓展性并沒有一個(gè)深刻的理解。

這里我通過一個(gè)簡單易懂的MP3播放器案例,把自己獨(dú)家總結(jié)的狀態(tài)機(jī)六步法分享給大家,幫助大家在啃下狀態(tài)機(jī)這塊硬骨頭。相信你深度掌握狀態(tài)機(jī)編程以后,你優(yōu)雅美觀的代碼會(huì)讓同事朋友們眼前一亮,嘖嘖稱贊。

生活中的狀態(tài)模式(狀態(tài)機(jī))

幾乎在所有的復(fù)雜項(xiàng)目里面,都充斥著各種事物狀態(tài)的變化。這是因?yàn)槲覀兩硖幍奈锢硎澜绫緛砭褪且粋€(gè)動(dòng)態(tài)多變的環(huán)境,自然我們開發(fā)的程序也要根據(jù)事物不同時(shí)刻不同場(chǎng)景的狀態(tài),不斷調(diào)整自身的行為屬性。

比如電影《分裂》里面,詹姆斯·麥卡沃伊飾演的男主Kevin患有精神分裂,有著多重人格疾病,他被精神病醫(yī)生Dr. Fletcher診斷出有23重人格,可以隨時(shí)間或境遇切換,一會(huì)變成精明聰穎的律師,一會(huì)是懦弱的失敗者總是要自殺,一個(gè)境遇觸發(fā)又是憤怒的殺人暴徒,這人格切換速度,喪心病狂到令人發(fā)指。

想象一下,假如我們要在程序中實(shí)現(xiàn)這樣一個(gè)角色,就必須要有一個(gè)良好的狀態(tài)變化設(shè)計(jì),才能保證主人公在快速切換狀態(tài)的情況下,都能擁有與之匹配的精神狀態(tài)和行為舉止。

場(chǎng)景案例

場(chǎng)景:設(shè)計(jì)一個(gè)簡單的MP3播放器,要求兩個(gè)按鍵(播放/暫停、停止)分別控制MP3的播放/停止功能。

如下表所示:

按鍵 功能
[play/pause] 播放/暫停
[stop] 停止

狀態(tài)遷移圖

在狀態(tài)模式的設(shè)計(jì)開發(fā)中,我們通常借助狀態(tài)遷移圖來進(jìn)行多個(gè)狀態(tài)的分析。本案例中的MP3播放器,狀態(tài)遷移圖如下圖所示:

雖然圖示很簡單,但是非常有用,因?yàn)楦靼存I按下后,MP3播放器的狀態(tài)變化一目了然,根據(jù)狀態(tài)遷移圖,我們就可以著手程序的編寫了。

我們先來看一個(gè)狀態(tài)模式(狀態(tài)機(jī))的入門級(jí)別的實(shí)現(xiàn)--簡單狀態(tài)機(jī)。其實(shí)就是通過大量的switch/case和if/else,在很多項(xiàng)目中經(jīng)??梢钥吹筋愃频拇a:

#include <stdio.h>

void stopPlayer();
void pausePlayer();
void resumePlayer();
void startPlayer();
//按鍵的動(dòng)作類型
typedef enum {
EV_STOP,
EV_PLAY_PAUSE
}EventCode;

//MP3的狀態(tài)
enum{
ST_IDLE,
ST_PLAY,
ST_PAUSE
};

//MP3當(dāng)前狀態(tài)
char state;

//MP3狀態(tài)初始化
void init()
{
state = ST_IDLE;
}

//狀態(tài)機(jī)處理MP3的過程變化
void onEvent(EventCode ec)
{
switch (state)
{
case ST_IDLE:
if(EV_PLAY_PAUSE == ec)
startPlayer();
break;
case ST_PLAY:
if(EV_STOP == ec)
stopPlayer();
else if(EV_PLAY_PAUSE == ec)
pausePlayer();
break;
case ST_PAUSE:
if(EV_STOP == ec)
stopPlayer();
else if(EV_PLAY_PAUSE == ec)
resumePlayer();
break;
default:
break;
}
}

void stopPlayer()
{
state = ST_IDLE;
printf("停止播放音樂\n");
}

void pausePlayer()
{
state = ST_PAUSE;
printf("暫停播放音樂\n");
}

void resumePlayer()
{
state = ST_PLAY;
printf("恢復(fù)播放音樂\n");
}

void startPlayer()
{
state = ST_PLAY;
printf("開始播放音樂\n");
}
//主程序?qū)崿F(xiàn)MP3的播放控制
void main()
{
init();
onEvent(EV_PLAY_PAUSE);//播放
onEvent(EV_PLAY_PAUSE);//暫停
onEvent(EV_PLAY_PAUSE);//繼續(xù)播放
onEvent(EV_STOP); //停止
}

代碼已經(jīng)在c在線工具|菜鳥工具中運(yùn)行驗(yàn)證,讀者也可以自行驗(yàn)證。運(yùn)行結(jié)果如下:

開始播放音樂
暫停播放音樂
恢復(fù)播放音樂
停止播放音樂

在上面的代碼實(shí)現(xiàn)中,主要是在onEvent函數(shù)中,以MP3的當(dāng)前狀態(tài)作為判斷條件進(jìn)行相應(yīng)的分支改動(dòng),簡單地按照狀態(tài)遷移圖,實(shí)現(xiàn)了功能。

但是我們觀察onEvent函數(shù),不難發(fā)現(xiàn)其中有大量的swith...case這樣的判斷(if...else也是一樣).對(duì)于MP3播放器這樣簡單的例子,這樣的代碼還是不難閱讀和維護(hù)的。但是當(dāng)狀態(tài)和事件增加后,onEvent函數(shù)就會(huì)變得非常龐大,這是因?yàn)樵摵瘮?shù)的代碼行數(shù)與狀態(tài)和事件數(shù)量的乘積成正比,直接導(dǎo)致代碼行數(shù)爆炸增長,代碼會(huì)越發(fā)變得難以閱讀和維護(hù)。

其次,程序的擴(kuò)展性非常差,無論是我們新增一種狀態(tài),還是新增一種按鍵動(dòng)作,onEvent函數(shù)都要大改特改,極難保障程序的穩(wěn)定性。

解決方案

核心思路:我們可以利用C語言的多態(tài)特性來分解復(fù)雜的條件分支(關(guān)于c語言多態(tài)的實(shí)現(xiàn),請(qǐng)查看c語言面向?qū)ο蠡A(chǔ))。這樣一來可以就避免大量的swith...case和 if...else等條件分支語句,提高程序的可維護(hù)性和可擴(kuò)展性。

下面我將使用獨(dú)家總結(jié)的六步法,幫助大家輕松掌握狀態(tài)模式(狀態(tài)機(jī))的編程訣竅。

#include <stdio.h>

/***********************************************
1、定義狀態(tài)接口,以MP3的狀態(tài)接口為例,每種狀態(tài)下都可能發(fā)生
兩種按鍵動(dòng)作。
************************************************/

typedef struct State{
void (* stop)();
void (* palyOrPause)();
}State;


/***********************************************
2、定義系統(tǒng)當(dāng)前狀態(tài)指針,保存系統(tǒng)的當(dāng)前狀態(tài)
************************************************/

State * pCurrentState;


/***********************************************
3、定義具體狀態(tài),根據(jù)狀態(tài)遷移圖來實(shí)現(xiàn)具體功能和狀態(tài)切換。
************************************************/

void ignore();
void startPlay();
void stopPlay();
void pausePlay();
void resumePlay();

//空閑狀態(tài)時(shí),stop鍵操作無效,play/pause會(huì)開始播放音樂
State IDLE = {
ignore,
startPlay
};

//播放狀態(tài)時(shí),stop鍵會(huì)停止播放音樂,play/pause會(huì)暫停播放音樂
State PLAY = {
stopPlay,
pausePlay
};

//暫停狀態(tài)時(shí),stop鍵會(huì)停止播放音樂,play/pause會(huì)恢復(fù)播放音樂
State PAUSE = {
stopPlay,
resumePlay
};

void ignore()
{
//空函數(shù),不進(jìn)行操作
}

void startPlay()
{
//實(shí)現(xiàn)具體功能
printf("開始播放音樂\n");
//進(jìn)入播放狀態(tài)
pCurrentState = &PLAY;
}
void stopPlay()
{
//實(shí)現(xiàn)具體功能
printf("停止播放音樂\n");
//進(jìn)入空閑狀態(tài)
pCurrentState = &IDLE;
}

void pausePlay()
{
//實(shí)現(xiàn)具體功能
printf("暫停播放音樂\n");
//進(jìn)入暫停狀態(tài)
pCurrentState = &PAUSE;
}

void resumePlay()
{
//實(shí)現(xiàn)具體功能
printf("恢復(fù)播放音樂\n");
//進(jìn)入播放狀態(tài)
pCurrentState = &PLAY;
}


/***********************************************
4、定義主程序上下文操作接口,主程序只關(guān)心當(dāng)前狀態(tài),不關(guān)心狀態(tài)之間
是怎么變化的。
************************************************/

void onStop();
void onPlayOrPause();

State context = {
onStop,
onPlayOrPause
};

void onStop(State *pThis)
{
pCurrentState->stop(pThis);
}

void onPlayOrPause(State *pThis)
{
pCurrentState->palyOrPause(pThis);
}


/***********************************************
5、初始化系統(tǒng)當(dāng)前狀態(tài)指針,其實(shí)就是指定系統(tǒng)的起始狀態(tài)
************************************************/

void init()
{
pCurrentState = &IDLE;
}

/***********************************************
6、主程序通過上下文操作接口來控制系統(tǒng)當(dāng)前狀態(tài)的變化
************************************************/
void main()
{
init();
context.palyOrPause();//播放
context.palyOrPause();//暫停
context.palyOrPause();//播放
context.stop();//停止
}

代碼已經(jīng)在c在線工具|菜鳥工具中運(yùn)行驗(yàn)證,讀者也可以自行驗(yàn)證。運(yùn)行結(jié)果如下:

開始播放音樂
暫停播放音樂
恢復(fù)播放音樂
停止播放音樂

對(duì)比前后兩份代碼,六步法實(shí)現(xiàn)的狀態(tài)機(jī)比簡單狀態(tài)機(jī)明顯有以下幾方面的優(yōu)點(diǎn):

  • 代碼結(jié)構(gòu)要更加清晰,避免了過多的switch...case或者if...else語句   的使用。

  • 很好地體現(xiàn)了開閉原則和單一職責(zé)原則,每個(gè)狀態(tài)都是一個(gè)子結(jié)構(gòu)體,你要增加狀態(tài)就要增加子結(jié)構(gòu)體,你要修改狀態(tài),你只修改一個(gè)子結(jié)構(gòu)體就可以了。

  • 封裝性非常好,狀態(tài)變換放置到子結(jié)構(gòu)體的內(nèi)部來實(shí)現(xiàn),外部的調(diào)用不用知道子結(jié)構(gòu)體的內(nèi)部如何實(shí)現(xiàn)狀態(tài)和行為的變換。

最后跟大家總計(jì)一下狀態(tài)機(jī)六步法:

(1)、定義狀態(tài)接口。
(2)、定義系統(tǒng)當(dāng)前狀態(tài)指針。
(3)、定義具體狀態(tài),根據(jù)狀態(tài)遷移圖來實(shí)現(xiàn)具體功能和狀態(tài)切換。
(4)、定義主程序上下文操作接口。
(5)、初始化系統(tǒng)當(dāng)前狀態(tài)指針。
(6)、主程序通過上下文操作接口來控制系統(tǒng)當(dāng)前狀態(tài)的變化。

一般來說,熟練使用狀態(tài)機(jī)六步法的嵌入式開發(fā)者,大都是兩年軟件開發(fā)經(jīng)驗(yàn)以上的老鳥了。所以,如果你還是個(gè)嵌入式新手,請(qǐng)?jiān)趯?shí)際開發(fā)中多多運(yùn)用它,以后你的代碼才能越來越優(yōu)雅美觀。而且掌握狀態(tài)機(jī)編程對(duì)理解其他更復(fù)雜的設(shè)計(jì)模式也是大有裨益的。

    如果你認(rèn)為文章不錯(cuò),歡迎分享給身邊的同事好友


猜你喜歡

C語言設(shè)計(jì)模式--簡單工廠模式

C語言、嵌入式中幾個(gè)非常實(shí)用的宏技巧

C語言、嵌入式應(yīng)用:TCP通信實(shí)例分析

C語言、嵌入式重點(diǎn)知識(shí):回調(diào)函數(shù)

C語言、嵌入式位操作精華技巧大匯總

最后

若覺得文章不錯(cuò),轉(zhuǎn)發(fā)分享、在看,也是我們繼續(xù)更新的動(dòng)力。

在公眾號(hào)內(nèi)回復(fù)更多資源,可免費(fèi)獲取嵌入式資料。期待你的關(guān)注~


加好友,回暗號(hào)【嵌入式大雜燴】,進(jìn)微信群


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動(dòng)電源

在工業(yè)自動(dòng)化蓬勃發(fā)展的當(dāng)下,工業(yè)電機(jī)作為核心動(dòng)力設(shè)備,其驅(qū)動(dòng)電源的性能直接關(guān)系到整個(gè)系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動(dòng)勢(shì)抑制與過流保護(hù)是驅(qū)動(dòng)電源設(shè)計(jì)中至關(guān)重要的兩個(gè)環(huán)節(jié),集成化方案的設(shè)計(jì)成為提升電機(jī)驅(qū)動(dòng)性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機(jī) 驅(qū)動(dòng)電源

LED 驅(qū)動(dòng)電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個(gè)照明設(shè)備的使用壽命。然而,在實(shí)際應(yīng)用中,LED 驅(qū)動(dòng)電源易損壞的問題卻十分常見,不僅增加了維護(hù)成本,還影響了用戶體驗(yàn)。要解決這一問題,需從設(shè)計(jì)、生...

關(guān)鍵字: 驅(qū)動(dòng)電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動(dòng)電源的公式,電感內(nèi)電流波動(dòng)大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計(jì) 驅(qū)動(dòng)電源

電動(dòng)汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動(dòng)汽車的核心技術(shù)之一是電機(jī)驅(qū)動(dòng)控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機(jī)驅(qū)動(dòng)系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動(dòng)汽車的動(dòng)力性能和...

關(guān)鍵字: 電動(dòng)汽車 新能源 驅(qū)動(dòng)電源

在現(xiàn)代城市建設(shè)中,街道及停車場(chǎng)照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進(jìn)步,高亮度白光發(fā)光二極管(LED)因其獨(dú)特的優(yōu)勢(shì)逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動(dòng)電源 LED

LED通用照明設(shè)計(jì)工程師會(huì)遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動(dòng)電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動(dòng)電源的電磁干擾(EMI)問題成為了一個(gè)不可忽視的挑戰(zhàn)。電磁干擾不僅會(huì)影響LED燈具的正常工作,還可能對(duì)周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動(dòng)電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機(jī)重量也有所下降,所以,現(xiàn)在的LED驅(qū)動(dòng)電源

關(guān)鍵字: LED 驅(qū)動(dòng)電源 開關(guān)電源

LED驅(qū)動(dòng)電源是把電源供應(yīng)轉(zhuǎn)換為特定的電壓電流以驅(qū)動(dòng)LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動(dòng)電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動(dòng)電源
關(guān)閉