問(wèn)題描述 在使用定時(shí)器的過(guò)程中最令人苦惱的就是,定義flag,holdtime,每用一次定義就會(huì)導(dǎo)致中斷函數(shù)中標(biāo)志位滿天飛,時(shí)間變量在程序中隨處可見(jiàn)。在想要移植,又不敢隨便刪除。程序處于高耦合狀態(tài),失去了一個(gè).c 一個(gè) .h的意義。
如何解決這種問(wèn)題
引入注冊(cè)機(jī)制。為了方便說(shuō)明注冊(cè)機(jī)制,舉一個(gè)例子:手機(jī)在使用相機(jī)這個(gè)功能時(shí),有一個(gè)操作:將拍攝的照片發(fā)送出去。以程序來(lái)實(shí)現(xiàn)這一過(guò)程,最容易想到的方法如下:
在相機(jī)的發(fā)送模塊添加以下代碼:
if (選擇發(fā)送) { if(選擇微信發(fā)送) { 獲取發(fā)送人; 選擇發(fā)送人; } else if(選擇qq發(fā)送) { 獲取發(fā)送人; 選擇發(fā)送人; } else if(選擇微博發(fā)送) { 獲取發(fā)送人; 選擇發(fā)送人; } . .此處省略一萬(wàn)行 . }
這是最容易想到的實(shí)現(xiàn)方式,就如上面定時(shí)器的實(shí)現(xiàn)方式,哪里要用了,再定義一系列變量就是了?;氐较鄼C(jī)例子,假設(shè)某一天一個(gè)比微信還火的聊天軟件出現(xiàn)了,用戶安裝了,想要發(fā)送圖片,這時(shí)該怎么做 ?當(dāng)然,只能在上面相機(jī)的發(fā)送發(fā)送模塊中添加else if(。。。。)和它的實(shí)現(xiàn)方式了,也就意味著,每更新一個(gè)需要使用圖片功能的軟件,就必須去修改相機(jī)模塊,是不是覺(jué)得和我們的定時(shí)器很像? 注冊(cè)的精髓: 解耦各個(gè)模塊。程序講究高內(nèi)聚,低耦合。我目前對(duì)這句話的理解是:高內(nèi)聚:每一個(gè)功能模塊(c文件,h文件),內(nèi)部不和其他模塊相互調(diào)用,比如障礙物函數(shù)里面不應(yīng)該有狀態(tài)這一個(gè)變量存在,更不應(yīng)該擁有零地標(biāo)恢復(fù)運(yùn)行這一操作。它只做一件事,處理IO口信息,產(chǎn)生相應(yīng)的障礙物狀態(tài)。低耦合:障礙物函數(shù)與其他模塊的耦合,僅僅為產(chǎn)生的障礙物狀態(tài)。下面深入探討注冊(cè)機(jī)制。 何謂注冊(cè):
我目前這樣理解的,相機(jī)要發(fā)送圖片,面臨著多種發(fā)送方式,每一種發(fā)送方式肯定會(huì)調(diào)用不同的函數(shù)。反過(guò)來(lái)想,就是我有很多的應(yīng)用,要使用相機(jī)這個(gè)模塊(此處對(duì)比定時(shí)器)。既然這樣,相機(jī)模塊定義一個(gè)注冊(cè)函數(shù),供其他模塊調(diào)用,以告訴相機(jī),允許使用對(duì)應(yīng)的發(fā)送方式。
#define num_max 20 //最大設(shè)備數(shù) typedef struct { u8 num;//當(dāng)前注冊(cè)設(shè)備數(shù) u8 list_ name[num _max]; //用于保存注冊(cè)設(shè)備列表 void (*click[num _max])(u8 * temp); //存放不同模塊(微信qq)的發(fā)送函數(shù)地址 }Equiment; Equiment COM; /**************************注冊(cè)函數(shù)****************************************/ void Photo_Register ( void(*a)(u8 * temp),u8 list ) //提供給外部的接口 { if(COM.num < num.max) { COM. click[COM. num]=a; //保存函數(shù)地址 COM. List _name [ COM. num ]=list; //保存設(shè)備名至列表 COM. num++; } else { /****超過(guò)最大設(shè)備數(shù)報(bào)錯(cuò)******/ } } /*相機(jī)中的發(fā)送函數(shù)*/ void Click(u8 temp) // 最終實(shí)現(xiàn)圖片發(fā)送調(diào)用此函數(shù)即可 { u8 i,NUM; for(i=0; i<= COM.num ; i++) { printf(“打印列表,顯示已經(jīng)注冊(cè)的設(shè)備”) } NUM =Get(選擇的發(fā)送方式); if(!NUM) COM.click[NUM](temp); } /*******************以上在相機(jī)中實(shí)現(xiàn)************************************/
微信中若要使用,在安裝過(guò)程中,提示打開(kāi)相機(jī)權(quán)限,便是調(diào)用上述注冊(cè)函數(shù)。將微信本身自集成的發(fā)送函數(shù)地址傳給相機(jī),相機(jī)每次發(fā)送只需判斷哪些設(shè)備注冊(cè)了,選擇對(duì)應(yīng)的方式即可。如此一來(lái),出現(xiàn)再多的新應(yīng)用要使用相機(jī),只需注冊(cè)一次即可。相機(jī)與微信QQ微博等模塊之間完美解耦!類似的,定時(shí)器的解耦也能這樣處理。
定時(shí)器運(yùn)用注冊(cè)機(jī)制
首先,要想解耦,必須去掉胡亂定義的標(biāo)志位與時(shí)間變量,只允許一個(gè)時(shí)間變量。因此定義一個(gè)32位的時(shí)間變量,不要任何條件限制,讓他一直自加。 參考arduino 中定時(shí)處理的方法:定義一個(gè)函數(shù)獲取當(dāng)前時(shí)間,保存下當(dāng)前時(shí)間,運(yùn)行一段時(shí)間后,再次查詢當(dāng)前時(shí)間,兩次做差,便得出運(yùn)行的時(shí)間。從以上不難看出,關(guān)鍵點(diǎn)在于:獲取當(dāng)前時(shí)間的函數(shù),當(dāng)前時(shí)間的存放,做差后的時(shí)間。以下是實(shí)現(xiàn)方法:
time.h :
#include "stm32f10x.h" #ifndef __TIME_H #define __TIME_H #define TimerID_max 20 //最大注冊(cè)設(shè)備數(shù) #define RunOutOf_time(ID , ms) ( systime.no w-systime.last[ID -1]< ms ) typedef struct { u8 ID; //設(shè)備ID u32 now; //當(dāng)前時(shí)間 u32 last[TimerID_max]; //存放抓取到的時(shí)間 void (*timer_init)(u16 countdata,u16 freqData); //指向初始化函數(shù) u8 (*get_id)(void); //指向獲取ID函數(shù) void (*refresh)(u8 ID); //指向更新時(shí)間函數(shù) }SYSTIME; extern SYSTIME systime; #endif time.c #include "time.h" /*********提供給外部的API*******************/ void Timer_Init(u16 CountData,u16 FreqData); unsigned char systime_get(void); void Refresh(u8 ID); /***********************************************/ SYSTIME systime = 定義SYSTIME類型變量,并初始化函數(shù)指針 { .get_id=systime_get, .refresh=Refresh, .timer_init=Timer_Init }; /****************************************************/ //函數(shù)名:Timer_init //描述:初始化定時(shí)器 //輸入:中斷時(shí)間相關(guān) //輸出:null /****************************************************/ void Timer_Init(u16 CountData,u16 FreqData) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_DeInit(TIM4); TIM_TimeBaseStructure.TIM_Prescaler = FreqData; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); TIM_ClearFlag(TIM4, TIM_FLAG_Update); TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); TIM_Cmd(TIM4, ENABLE); } /****************************************************/ //函數(shù)名:systime_get //描述:獲取當(dāng)前時(shí)間,并產(chǎn)生一個(gè)注冊(cè) //輸入:null //輸出:null /****************************************************/ unsigned char systime_get() { if(systime.ID
在.c與.h實(shí)現(xiàn)了以上后,面向外部的只有三個(gè)函數(shù):
/*********提供給外部的API*******************/ void Timer_Init(u16 CountData,u16 FreqData); unsigned char systime_get(void); void Refresh(u8 ID); /***********************************************/
定時(shí)器的使用方法
1
/*初始化*/2/**********任務(wù)1實(shí)現(xiàn)運(yùn)行等閃爍,頻率1s **********/ void task1() { static u8 Task1_ID; if(!Task1_ID) Task1_ID=systime.get_id(); if(RunOutOf_time(Task1_ID,1000)) RUN_LED()=1; else if(RunOutOf_time(Task1_ID,2000)) RUN_LED()=0; else if(RunOutOf_time(Task1_ID,3000)) RUN_LED()=1; else if(RunOutOf_time(Task1_ID,4000)) RUN_LED()=0; else if(RunOutOf_time(Task1_ID,5000) RUN_LED()=1; else systime.refresh(Task1_ID); } /*******************任務(wù)2實(shí)現(xiàn)運(yùn)行等閃爍,頻率100ms**********************/ void task2() { static u8 Task1_ID; if(!Task1_ID) Task1_ID=systime.get_id(); if(RunOutOf_time(Task1_ID,100)) RUN_LED()=1; else if(RunOutOf_time(Task1_ID,200)) RUN_LED()=0; else if(RunOutOf_time(Task1_ID,300)) RUN_LED()=1; else if(RunOutOf_time(Task1_ID,400)) RUN_LED()=0; else if(RunOutOf_time(Task1_ID,500)) RUN_LED()=1; else systime.refresh(Task1_ID); } /***************main函數(shù)實(shí)現(xiàn)任務(wù)1運(yùn)行10s,任務(wù)2運(yùn)行10s****************/ int main(void) { static u8 main_ID; System_Init(); while(1) { if(!main_ID) main_ID=systime.get_id(); if(RunOutOf_time(main_ID,10000)) task1(); else if(RunOutOf_time(main_ID,20000)) task2(); else systime.refresh(main_ID); } }以上,任何函數(shù)想要使用定時(shí)器,只需要按要求,設(shè)立一個(gè)ID存儲(chǔ)變量,以存儲(chǔ)注冊(cè)時(shí)分配的ID,便可調(diào)用定時(shí)器,且在任何平臺(tái)上均可方便的移植,只需修改硬件初始化。 此程序無(wú)法實(shí)現(xiàn)時(shí)刻任務(wù)執(zhí)行,例如某任務(wù)要100ms執(zhí)行一次,只能用作時(shí)間段內(nèi)執(zhí)行。原因在于程序主循環(huán)會(huì)耗時(shí),導(dǎo)致輪詢時(shí)無(wú)法精準(zhǔn)捕捉到100ms時(shí)刻,想要達(dá)到此效果,還需改進(jìn)或者完全換一種思路來(lái)寫(xiě),例如捕捉放到中斷中,主循環(huán)來(lái)查詢100ms使能位。
責(zé)任編輯:彭菁
-
相機(jī)
+關(guān)注
關(guān)注
4文章
1351瀏覽量
53620 -
定時(shí)器
+關(guān)注
關(guān)注
23文章
3248瀏覽量
114832 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62629 -
代碼
+關(guān)注
關(guān)注
30文章
4788瀏覽量
68625 -
注冊(cè)
+關(guān)注
關(guān)注
2文章
7瀏覽量
11248
原文標(biāo)題:簡(jiǎn)單介紹一下嵌入式軟件開(kāi)發(fā)中的注冊(cè)機(jī)制!
文章出處:【微信號(hào):c-stm32,微信公眾號(hào):STM32嵌入式開(kāi)發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論