接上前面兩篇文章:
基于事件型表驅動法菜單框架之小熊派簡易氣體探測器實戰項目開發(上)
基于事件型表驅動法菜單框架之小熊派簡易氣體探測器實戰項目開發(中)
今天這篇文章不作為氣體探測器實戰項目的感器最后一節,因為很多功能還在編寫中,驅動前兩天在世偉兄的檢測開源群里提到了傳感器檢測框架,群友反應說:楊工有空你要多搞點這種框架出來分享分享,框架感覺很有用啊!表驅
今天分享的動狀這個傳感器驅動檢測框架也是我在副業里給客戶做的那些項目里用得最多的騷技能(但主業上的產品我幾乎就沒用過,還是態機我說的那句話:沒有明確需求的產品,別提什么復用性和高逼格),感器所以今天就拿出來說一說。驅動
看下之前這個項目里寫的檢測這個氣體傳感器MQ-2的檢測流程:
void?Test_CallBack(void)
{
????static?uint8_t?Count_AMI?=?0;
????static?uint8_t?Refresh_flag?=?0?;
????int?smoke_value?=?0?;
????static?uint8_t?display_result_flag?=?0?;
????if(Flow_Cursor.flow_cursor?==?TEST_PAGE?&&?detect_logic.Start_Detect?==?1)
????{
????????switch(detect_logic.Detect_Step)
????????{
????????????case?BASE_LINE:
????????????????Count_AMI++?;
????????????????if(Count_AMI?>?2)
????????????????????Count_AMI?=?0?;
????????????????icon_reflash(Count_AMI);
????????????????smoke_value?=?mq2_sensor_interface.get_smoke_value(&mq2_sensor_interface)?;
????????????????if(smoke_value?????????????????{
????????????????????display_smoke_value(smoke_value,?GREEN,?1);
????????????????????++detect_logic.Count_Base?;
????????????????}
????????????????else
????????????????{
????????????????????display_smoke_value(smoke_value,?RED,?1);
????????????????}
????????????????if(detect_logic.Count_Base?>?10)
????????????????{
????????????????????detect_logic.Count_Base?=?0?;
????????????????????display_result_flag?=?0?;
????????????????????/*隱藏基準*/
????????????????????display_base(0);
????????????????????/*顯示檢測*/
????????????????????display_detect(1);
????????????????????/*顯示進度條框*/
????????????????????Display_Process_Bar_Frame(1);
????????????????????/*切換到檢測中*/
????????????????????detect_logic.Detect_Step?=?DETECTING?;
????????????????????break?;
????????????????}
????????????????break?;
????????????case?DETECTING:
????????????????Count_AMI++?;
????????????????if(Count_AMI?>?2)
????????????????????Count_AMI?=?0?;
????????????????icon_reflash(Count_AMI);
????????????????++detect_logic.Test_Process?;
????????????????/*測試安全*/
????????????????if(detect_logic.Test_Process?==?100?&&?mq2_sensor_interface.Smoke_Value?????????????????{
????????????????????detect_logic.Detect_Step?=?DETECT_SAFETY?;
????????????????????Display_Process_Bar(0,?0);
????????????????????display_smoke_value(smoke_value,?BLACK,?0);
????????????????????/*隱藏檢測*/
????????????????????display_detect(0);
????????????????????/*顯示安全*/
????????????????????display_safety(1);
????????????????????break?;
????????????????}
????????????????else
????????????????{
????????????????????smoke_value?=?mq2_sensor_interface.get_smoke_value(&mq2_sensor_interface)?;
????????????????????if(mq2_sensor_interface.Smoke_Value?????????????????????{
????????????????????????display_smoke_value(smoke_value,?GREEN,?1);
????????????????????}
????????????????????else
????????????????????{
????????????????????????display_smoke_value(smoke_value,?RED,?1);
????????????????????????detect_logic.Count_Alarm++?;
????????????????????????if(detect_logic.Count_Alarm?>?5)
????????????????????????{
????????????????????????????detect_logic.Detect_Step?=?DETECT_DANGER?;
????????????????????????????detect_logic.Count_Alarm?=?0?;
????????????????????????????display_smoke_value(smoke_value,?BLACK,?0);
????????????????????????????Display_Process_Bar(0,?0);
????????????????????????????/*隱藏檢測*/
????????????????????????????display_detect(0);
????????????????????????????/*顯示危險*/
????????????????????????????display_danger(1);
????????????????????????????break?;
????????????????????????}
????????????????????}
????????????????????Display_Process_Bar(detect_logic.Test_Process,?1);
????????????????}
????????????????break?;
????????????case?DETECT_SAFETY:
????????????????detect_logic.Start_Detect?=?0?;
????????????????if(display_result_flag?==?0)
????????????????{
????????????????????display_result_flag?=?1?;
????????????????????smoke_value?=?mq2_sensor_interface.get_smoke_value(&mq2_sensor_interface)?;
????????????????????display_smoke_value(smoke_value,?GREEN,?1);
????????????????}
????????????????break?;
????????????case?DETECT_DANGER:
????????????????/*危險閃爍*/
????????????????Refresh_flag?=?!Refresh_flag?;
????????????????display_danger(Refresh_flag);
????????????????mq2_sensor_interface.led_control(&mq2_sensor_interface,?Refresh_flag);
????????????????mq2_sensor_interface.buzzer_control(&mq2_sensor_interface,?Refresh_flag);
????????????????if(display_result_flag?==?0)
????????????????{
????????????????????display_result_flag?=?1?;
????????????????????smoke_value?=?mq2_sensor_interface.get_smoke_value(&mq2_sensor_interface)?;
????????????????????display_smoke_value(smoke_value,?RED,?1);
????????????????}
????????????????break?;
????????????default:
????????????????break?;
????????}
????}
}
寫完后看了一下,邏輯上沒有什么毛病,框架運行以后最終測試的表驅結果也是我想要的結果就直接提交到Github和碼云倉庫上去了。所謂士別三日,非吳下阿蒙,過幾天再看這代碼,表示我已經看不下去了,居然一個函數能寫這么長!看著不累不亂嗎?于是吐槽了下自己:fuck me!,臥槽!這寫的什么垃圾代碼?不像我的個人風格,不應該更高逼格一點嗎?
于是就有了表驅動+狀態機法傳感器驅動檢測框架的誕生。
1、核心傳感器檢測框架
上面那個寫得很長的傳感器檢測流程,其實說白了就是兩部分:
1、當前到底是對應哪個傳感器檢測流程(狀態機)? 2、當前對應傳感器檢測流程的處理
于是我們就可以把這個過程抽象化成一個數據結構sensor_frame,將這兩個部分相應的共同點提煉出來,這里就包括傳感器檢測流程sensor_detect_step,這里主要有基準、檢測中、安全、危險;傳感器檢測流程處理函數handler_func是一個帶形參的函數指針,這個參數在這里表示傳感器數值,當然這個值可以是浮點型,也可以是其它類型,具體根據傳感器的數據類型去定義,這里我把它定義成int型。
接下來我們還需要一個傳感器的檢測業務結構,用于實現檢測流程切換(狀態機)以及一些其它的邏輯操作,這里提供了一個Sensor_Cursor
的數據結構。
/*檢測流程*/
enum
{
????BASE_LINE_STEP?=?0,
????DETECTING_STEP,
????DETECT_SAFETY_STEP,
????DETECT_DANGER_STEP
};
typedef?void?(*sensor_handler)(int);
typedef?struct
{
????/*傳感器檢測流程*/
????uint8_t?sensor_detect_step?;
????/*傳感器檢測流程處理*/
????sensor_handler?handler_func?;
}?sensor_frame;
/*傳感器狀態機+流程處理集*/
typedef?struct
{
????uint8_t?Detect_Step?;?/*檢測流程*/
????uint8_t?Count_AMI??;?/*動畫計數器*/
????uint8_t?Start_Detect;?/*開始測試標志*/
????uint8_t?Count_Base??;?/*統計基準次數*/
????uint8_t?Count_Alarm?;?/*統計報警次數*/
????uint8_t?Test_Process;?/*測試進度*/
}?Sensor_Cursor?;
extern?Sensor_Cursor?Sensor_Flow_Cursor?;
2、傳感器驅動檢測框架應用
有了這個最基本的框架結構,接下來照葫蘆畫瓢,把前面兩篇文章介紹的菜單表驅動框架的代碼復制粘貼然后稍微騷操作
一下,于是我們就看到了以下的形態:
/*基準流程*/
void?sensor_base_line_step(int?adc);
/*檢測中流程*/
void?sensor_detecting_step(int?adc);
/*檢測安全*/
void?sensor_detect_safety(int?adc);
/*檢測危險*/
void?sensor_detect_danger(int?adc);
/*傳感器驅動表定義*/
static?sensor_frame?sensor_opStruct[]?=
{
????{ BASE_LINE_STEP,????sensor_base_line_step},??/*基準*/
????{ DETECTING_STEP,????sensor_detecting_step},??/*檢測中*/
????{ DETECT_SAFETY_STEP,?sensor_detect_safety},???/*安全*/
????{ DETECT_DANGER_STEP,?sensor_detect_danger},???/*危險*/
};
/*傳感器流程處理*/
int?Sensor_Handler(int8_t?op,?int?adc_value)
{
????assert(op?>=?sizeof(sensor_opStruct)?/?sizeof(sensor_opStruct[0]));
????assert(op?0);
????sensor_opStruct[op].handler_func(adc_value);
????return?0?;
}
這樣看起來舒服多了有木有??在程序后期調試中,如果想增加傳感器檢測流程,或者說發現傳感器檢測哪個流程有BUG,那這不就直接就可以找到了嗎?整個框架組成清晰明了,我們只需要分別去實現如上的基準、檢測中、安全、危險四個流程對應的處理函數就可以了。
在進入檢測頁面時還需要實現并調用傳感器檢測初始化函數,這個初始化函數主要是對一些原始數據(比如檢測流程中用到的一些計數變量)進行清0操作,然后將檢測流程設置為最開始的基準流程。
/*傳感器檢測初始化*/
void?Sensor_Detect_Init(void)
{
????Sensor_Flow_Cursor.Count_AMI?=?0?;
????Sensor_Flow_Cursor.Count_Base?=?0?;
????Sensor_Flow_Cursor.Count_Alarm?=?0?;
????Sensor_Flow_Cursor.Test_Process?=?0?;
????Sensor_Flow_Cursor.Start_Detect?=?1?;
????Sensor_Flow_Cursor.Detect_Step?=?BASE_LINE_STEP?;
}
我們可以來看下其中有關基準流程的處理:
/*基準流程*/
void?sensor_base_line_step(int?adc)
{
????Sensor_Flow_Cursor.Count_AMI++?;
????if(Sensor_Flow_Cursor.Count_AMI?>?2)
????????Sensor_Flow_Cursor.Count_AMI?=?0?;
????/*刷新動畫*/
????icon_reflash(Sensor_Flow_Cursor.Count_AMI);
????/*判斷是否滿足基準條件*/
????if(adc?????{
????????display_smoke_value(adc,?GREEN,?1);
????????++Sensor_Flow_Cursor.Count_Base?;
????}
????else
????{
????????display_smoke_value(adc,?RED,?1);
????}
????if(Sensor_Flow_Cursor.Count_Base?>?10)
????{
????????Sensor_Flow_Cursor.Count_Base?=?0?;
????????/*隱藏基準*/
????????display_base(0);
????????/*顯示檢測*/
????????display_detect(1);
????????/*顯示進度條框*/
????????Display_Process_Bar_Frame(1);
????????/*切換到檢測中*/
????????Sensor_Flow_Cursor.Detect_Step?=?DETECTING_STEP?;
????}
}
滿足通過基準的條件,此時在該函數里寫了這么一句代碼:
Sensor_Flow_Cursor.Detect_Step?=?DETECTING_STEP?;
這一句代碼就是檢測流程的切換(狀態機),后面的幾個流程也是類似的,滿足條件則切換到下一個檢測流程。
最后我們只需要在原來傳感器定時后調函數Test_CallBack
里這么寫就可以了:
/*測試回調*/
void?Test_CallBack(void)
{
??int?smoke_value?=?0?;
??/*如果當前在測試頁面?&&?開始檢測標志為1,則進入傳感器數據處理*/
??if(Flow_Cursor.flow_cursor?==?TEST_PAGE?&&?Sensor_Flow_Cursor.Start_Detect?==?1)
??{
???smoke_value?=?mq2_sensor_interface.get_smoke_value(&mq2_sensor_interface)?;
???Sensor_Handler(Sensor_Flow_Cursor.Detect_Step,smoke_value);
??}
}
這才是我們想要的高逼格嘛!嘿嘿嘿!
表驅動其實還有很多更騷的操作,今晚就分享到這里了,期待楊工下期精彩分享!
其余功能:后續還可以做報警記錄存儲、數據上傳到OneNet或者華為云等平臺、參數設置等等,總之這個項目可拓展性非常強,這些功能將在本項目開發的下一章節持續進行拓展并分享,歡迎及時關注我的碼云倉庫與微信公眾號文章更新。
本節代碼已同步到碼云的代碼倉庫中:
獲取方法如下:
1、新建一個文件夾
2、使用git clone遠程獲取小熊派所有案例代碼
我還將之前做的一些項目以及練習例程在近期內全部上傳完畢,與大家一起分享交流:
公眾號粉絲福利時刻
這里我給大家申請到了福利,本公眾號讀者購買小熊派開發板可享受9折優惠,有需要購買小熊派以及騰訊物聯網開發板的朋友,淘寶搜索即可,跟客服說你是公眾號:嵌入式云IOT技術圈?的粉絲,立享9折優惠!
往期精彩
STM32系統bootloader應用
GitHub上最勵志的計算機自學教程
"結構體"和"共用體"在單片機中的妙用
STM32硬核DIY機械鍵盤|藍牙USB雙模|燈控
覺得本次分享的文章對您有幫助,隨手點[在看]
并轉發分享,也是對我的支持。
免責聲明:本文內容由21ic獲得授權后發布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯系我們,謝謝!