單片機程序構(gòu)架
似乎軟件架構(gòu),只有純上位機軟件才有,其實,嵌入式軟件也有架構(gòu)可言,只有好的架構(gòu),才能結(jié)構(gòu)清晰,方便開發(fā)和讓系統(tǒng)穩(wěn)定的工作。在有嵌入式操作系統(tǒng)的情況下,可以利用多任務(wù)和信號量,事件等設(shè)計嵌入式軟件。但是在沒有操作系統(tǒng)的裸機中,更需要有好的架構(gòu)。例如利用事件和狀態(tài)機模擬實現(xiàn)多任務(wù),或者利用定時器和消息隊列,信號量等模擬實現(xiàn)多任務(wù),有了多任務(wù)就能靈活的設(shè)計軟件架構(gòu)。
一種簡單的信號量實現(xiàn):
[cpp] view plain copy
void%20%20sem_init(%20volatile%20U08%20*Sem%20)
{
(*Sem)=0;
}
void%20sem_post(%20volatile%20U08%20*Sem%20)
{
if(%200%20==%20(*Sem)%20)
(*Sem)++;
}
U08%20sem_wait(%20volatile%20U08%20*Sem%20)
{
if(0%20==%20*Sem)
return%201;
(*Sem)--;
return%200;
}
在一個大的while(1)大循環(huán)中,利用信號量實現(xiàn)各個函數(shù)(任務(wù))的同步。
[cpp]%20view%20plain%20copy
void%20%20Task_SysTime(%20void%20)
{
static%20int%20TaskInitFlg%20=%200;
U32%20Timer1sCount%20=%200;%20//時鐘計數(shù)器個數(shù)
U32%20disstat%20=%200;
static%20int%20tmrid0%20=%200,%20tmrid1%20=%200,%20tmrid2%20=%200,%20tmrid3%20=%200;
if(%200%20==%20TaskInitFlg%20)
{
OSTimeDlyHMSM(%200,%200,%200,%2050%20//主要等待任務(wù)刪除后才創(chuàng)建卡任務(wù)
tmrid0%20=%20TimerSet(20);%20//定時器0(毫秒定時器)用于鍵盤、尋卡、定時器中斷服務(wù)程序,20ms
tmrid1%20=%20TimerSet(1000);//定時器1(毫秒定時器)用于背顯、GPS、定時連接檢測、空閑顯示
tmrid2%20=%20TimerSet(500);%20//定時器2(毫秒定時器)用于信號顯示,500ms
tmrid3%20=%20TimerSet(500);%20//定時器3(毫秒定時器)用于電池顯示,500ms
sem_init(%20&gSem_EVT_CARDFLG_OK%20);%20//初始化為沒有卡
APP_DisIdle(%202%20);%20//顯示一次時間
APP_DisVoice();
TaskInitFlg%20=%201;%20//任務(wù)初始化完成
}
else
{
HW_IWDG_ReloadCounter();%20//清看門狗
if(%200%20==%20TimerCheck(tmrid0)%20)
{
tmrid0%20=%20TimerSet(20);%20//定時器0重新定時,%2020ms
Timer_ScanKeyboard();%20//20MS鍵盤掃描
Timer_FindCard();%20//20MS尋卡處理
TIM20MS_IRQHandler();%20//20MS定時器中斷服務(wù)程序
}
}
}
void%20Task_Tick(%20void%20)
{
Task_SysError();
Task_CardProc();
Task_SysTime();
Task_MenuProc();
Task_MtnLink();
Task_CommProc();
}
int%20main(%20void%20)
{
Sys_Init();%20//系統(tǒng)初始化
while(%201%20)
{
Task_Tick();%20//任務(wù)輪詢
if(%200%20==%20sem_wait(%20&gSem_EVT_QUIT_APP%20)%20)
break;%20//應(yīng)用退出
}
}
以上為借助信號量和定時器實現(xiàn)的一種簡單的模擬多任務(wù),其實也算不上是多任務(wù),因為如果一個函數(shù)執(zhí)行時間很長,如何打斷它?
以下為借住定時器和任務(wù)隊列實現(xiàn)的一種模擬多任務(wù):
[cpp]%20view%20plain%20copy
#include%20
#include%20"timTask.h"
#include%20"disp.h"
/*=====================================================
=%20變量定義
=====================================================*/
//任務(wù)隊列
typedef%20struct{
char%20flagState;%20//運行方式%200:%20無任務(wù)
//%201:%20運行
char%20flagRun;%20//完成狀態(tài)%200:%20正在計數(shù)
//%201:%20計數(shù)完成
char%20flagType;%20//處理方式%200:%20主任務(wù)處理
//%201:%20中斷處理
ulong%20cntRun;%20//運行計數(shù)器
ulong%20numCircle;%20//循環(huán)計數(shù)器
void%20(*pTaskFunc)(void);%20//任務(wù)
}TypeTimTask;
TypeTimTask%20timTaskTab[TIM_TASK_NUMBER];
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20TimTaskInit(void)
{
int%20i;
for%20(i=0;%20i
{
timTaskTab[i].pTaskFunc%20=%200;
timTaskTab[i].cntRun%20=%200;
timTaskTab[i].numCircle%20=%200;
timTaskTab[i].flagRun%20=%200;
timTaskTab[i].flagState%20=%200;
}
SPT_register_call_back(TimTaskUpdate);
SPT_set(TIM_TASK_PERIOD%20*64%20/%201000);
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
short%20TimTaskAdd(ulong%20fsttim,%20ulong%20cirtim,%20void%20(*pTaskFunc)(void),%20uchar%20%20type)
{
int%20i;
int%20pos%20=%20-1;
//查找位置
for%20(i=0;%20i
{
if%20(timTaskTab[i].pTaskFunc%20==%20pTaskFunc)
{
pos%20=%20i;
break;
}
if%20((pos%20==%20-1)%20&&%20(timTaskTab[i].flagState%20==%200))
{
pos%20=%20i;
}
}
//任務(wù)已滿
if%20(pos%20==%20-1)
{
return%20-1;
}
//
timTaskTab[pos].pTaskFunc%20=%20pTaskFunc;
timTaskTab[pos].cntRun%20=%20fsttim%20/%20TIM_TASK_PERIOD;
timTaskTab[pos].numCircle%20=%20cirtim%20/%20TIM_TASK_PERIOD;
timTaskTab[pos].flagRun%20=%200;
timTaskTab[pos].flagType%20=%20type;
timTaskTab[pos].flagState%20=%201;
return%200;
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20TimTaskDel(void%20(*pTaskFunc)(void))
{
int%20i;
for%20(i=0;%20i
{
if%20(timTaskTab[i].pTaskFunc%20==%20pTaskFunc)
{
timTaskTab[i].flagState%20=%200;
timTaskTab[i].flagRun%20=%200;
return;
}
}
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20TimTaskUpdate(void)
{
int%20i;
SPT_set(TIM_TASK_PERIOD%20*64%20/%201000);
for%20(i=0;%20i
{
if%20(timTaskTab[i].flagState%20!=%200)
{
if%20(timTaskTab[i].cntRun%20!=%200)
{
timTaskTab[i].cntRun--;
}
else
{
//判斷處理位置
if%20(timTaskTab[i].flagType%20!=%200)
(*timTaskTab[i].pTaskFunc)();
else
timTaskTab[i].flagRun%20=%201;
//判斷重載
if%20(timTaskTab[i].numCircle)
timTaskTab[i].cntRun%20=%20timTaskTab[i].numCircle;
else
timTaskTab[i].flagState%20=%200;
}
}
}
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20TimTaskProc(void)
{
int%20i;
for%20(i=0;%20i
{
if%20(timTaskTab[i].flagRun%20!=%200)
{
timTaskTab[i].flagRun%20=%200;
(*timTaskTab[i].pTaskFunc)();
}
}
}
更為巧妙的是,可以借住函數(shù)指針實現(xiàn)一種靈活的菜單和按鍵實時處理結(jié)構(gòu)。類似于windows下win32的消息驅(qū)動機制,
通過中斷等方式把實時事件封裝成消息。以下為定義界面刷新顯示和響應(yīng)按鍵處理的結(jié)構(gòu):
[cpp]%20view%20plain%20copy%20%20
#ifndef%20__PAGE_H_
#define%20__PAGE_H_
#include%20"heads.h"
/*=====================================================
=
=====================================================*/
typedef%20struct{
void%20(*%20OnPaint)(void);
void%20(*%20OnKey)(short);
}TypePage;
/*=====================================================
=
=====================================================*/
void%20WndPageSet(const%20TypePage%20*pg,%20int%20type%20=%200);
TypePage%20*%20WndGetPage(void);
void%20WndPageEsc(void);
void%20WndOnKey(short%20key);
void%20WndOnPaint(void);
void%20WndMenuInit(const%20char%20*pmn,%20char%20mline);
void%20WndMenuSelet(int%20m);
char%20WndMenuGetSelet(void);
long%20WndGetPaseword(int%20x,%20int%20y,%20char%20*psw,%20int%20len,%20long%20qevent);
[cpp]%20%20view%20plain%20copy%20%20
#include%20"pageWnd.h"
/*=====================================================
=
=====================================================*/
char%20flagPaint%20=%200;
void%20(*%20pOnPaint)(void)%20=%200;
void%20(*%20pOnKey)(short)%20=%200;
const%20char%20*pMenuStr;
uchar%20menuSelect%20=%200;
uchar%20menuLine%20=%200;
uchar%20menuTop;
TypePage%20*pageCurrent;
TypePage%20*pageTreeTab[10];
uchar%20pageIndex%20=%200;
/*=====================================================
=
=====================================================*/
void%20WndDrawMenu(void);
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndPageSet(const%20TypePage%20*pg,%20int%20type)
{
if%20(pg%20==%20&pageMain)%20//防止出錯
{
pageIndex%20=%200;
}
else%20if%20(type%20==%200)
{
pageTreeTab[pageIndex++]%20=%20pageCurrent;
}
pageCurrent%20=%20(TypePage%20*)pg;
pOnPaint%20=%20pg->OnPaint;
pOnKey%20=%20pg->OnKey;
flagPaint%20=%201;
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
TypePage%20*%20WndGetPage(void)
{
return%20pageCurrent;
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndPageEsc(void)
{
TypePage%20*pg;
if%20(pageIndex%20!=%200)
{
pageIndex--;
pg%20=%20pageTreeTab[pageIndex];
}
else
{
pg%20=%20(TypePage%20*)&pageMain;
}
pageCurrent%20=%20pg;
pOnPaint%20=%20pg->OnPaint;
pOnKey%20=%20pg->OnKey;
flagPaint%20=%201;
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndOnPaint(void)
{
if%20(flagPaint%20!=%200)
{
flagPaint%20=%200;
(*pOnPaint)();
}
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndOnKey(short%20key)
{
if%20(pOnKey%20!=%200)
{
(*pOnKey)(key);
}
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndMenuInit(const%20char%20*pmn,%20char%20mline)
{
menuSelect%20=%200;
pMenuStr%20=%20pmn;
menuLine%20=%20mline;
menuTop%20=%200;
WndDrawMenu();
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndMenuSelet(int%20m)
{
//光標滑動
if%20(m%20>%200)%20//下移
{
menuSelect++;
if%20(menuSelect%20==%20menuLine)
menuSelect%20=%200;
if%20(menuSelect%20>%20menuTop%20+%204)
{
if%20(menuLine%20<%20menuTop%20+%204)
menuTop%20=%20menuLine%20-%204;
else
menuTop%20=%20menuSelect%20-%204;
}
}
else%20if%20(m%20<%200)%20//上移
{
if%20(menuSelect%20==%200)
menuSelect%20=%20menuLine%20-%201;
else
menuSelect--;
}
//圖框移動
if%20(menuSelect%20<%20menuTop)%20//上移
{
menuTop%20=%20menuSelect;
}
else%20if%20(menuSelect%20>=%20menuTop%20+%204)%20//下移
{
menuTop%20=%20menuSelect%20-%203;
}
WndDrawMenu();
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
char%20WndMenuGetSelet(void)
{
return%20menuSelect%20+%201;
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndDrawMenu(void)
{
int%20i;
char%20buf[17];
const%20char%20*pmn%20=%20pMenuStr%20+%20menuTop%20*%2016;
DispClr();
for%20(i=0;%20i<4;%20i++)
{
if%20(menuTop%20+%20i%20==%20menuLine)
break;
memcpy(buf,%20pmn,%2016);
buf[16]%20=%20'\0';
if%20(menuSelect%20==%20menuTop%20+%20i)
DispSetStyle(DISP_POSITION%20|%20DISP_REVERSE%20|%20DISP_7x9);
else
DispSetStyle(DISP_POSITION%20|%20DISP_NORMAL%20|%20DISP_7x9);
DispString(0,%20i%20*%202,%20buf);
pmn%20+=%2016;
}
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
long%20WndGetPaseword(int%20x,%20int%20y,%20char%20*psw,%20int%20len,%20long%20qevent)
{
int%20pin%20=%200;
long%20keyevt;
char%20key;
char%20buf[20];
memset(buf,%20'_',%20len);
buf[len]%20=%20'\0';
PSW_INPUT_LOOP:
DispString(x,%20y,%20buf);
keyevt%20=%20delay_and_wait_key(0,%20EXIT_KEY_ALL,%200);
if%20(keyevt%20==%20qevent)
{
psw[0]%20=%20'\0';
return%20keyevt;
}
switch%20(keyevt)
{
case%20EXIT_KEY_0:
key%20=%20'0';
break;
case%20EXIT_KEY_1:
key%20=%20'1';
break;
case%20EXIT_KEY_2:
key%20=%20'2';
break;
case%20EXIT_KEY_3:
key%20=%20'3';
break;
case%20EXIT_KEY_4:
key%20=%20'4';
break;
case%20EXIT_KEY_5:
key%20=%20'5';
break;
case%20EXIT_KEY_6:
key%20=%20'6';
break;
case%20EXIT_KEY_7:
key%20=%20'7';
break;
case%20EXIT_KEY_8:
key%20=%20'8';
break;
case%20EXIT_KEY_9:
key%20=%20'9';
break;
case%20EXIT_KEY_COMM:
if%20(pin%20!=%200)
{
buf[--pin]%20=%20'_';
}
goto%20PSW_INPUT_LOOP;
break;
case%20EXIT_KEY_ENTER:
psw[pin]%20=%200;
return%200;
default:
goto%20PSW_INPUT_LOOP;
}
if%20(pin%20!=%20len)
{
psw[pin]%20=%20key;
buf[pin]%20=%20'*';
pin++;
}
goto%20PSW_INPUT_LOOP;
}
在軟件設(shè)計時,如果添加界面和對應(yīng)的按鍵處理,很靈活,只需要新添加一個文件就可以了,文件的內(nèi)容,只需要實現(xiàn)OnPain和對應(yīng)的OnKey
[cpp]%20%20view%20plain%20copy%20%20
#include%20"PageMenu.h"
/*=====================================================
=
=====================================================*/
const%20char%20mainMenuTab[]%20=%20/*
1234567890123456*/"\
1.%20現(xiàn)場采集%20\
2.%20數(shù)據(jù)上傳%20\
3.%20存儲狀態(tài)查詢%20\
4.%20時間設(shè)置%20\
5.%20對比度設(shè)置%20\
6.%20恢復(fù)出廠設(shè)置%20\
7.%20關(guān)于%20";
/*=====================================================
=
=====================================================*/
void%20PageMenuOnPain(void);
void%20WndMenuOnKey(short%20key);
const%20TypePage%20pageMenu%20=%20{PageMenuOnPain,%20WndMenuOnKey};
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20PageMenuOnPain(void)
{
WndMenuInit(mainMenuTab,%207);
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20WndMenuOnKey(short%20key)
{
int%20res;
switch%20(key)
{
case%20KEY_F1:
case%20KEY_ENTER:
res%20=%20WndMenuGetSelet();
switch%20(res)
{
case%201:
WndPageSet(&pageSimp);
break;
case%202:
WndPageSet(&pagePclink);
break;
case%203:
WndPageSet(&pageInquire);
break;
case%204:
WndPageSet(&pageRtc);
break;
case%205:
WndPageSet(&pageGray);
break;
case%206:
SPageInit();
WndPageSet(&pageMenu,%201);
break;
case%207:
WndPageSet(&pageAbout);
break;
}
break;
case%20KEY_F2:
case%20KEY_F3:
WndPageSet(&pageMain);
break;
case%20KEY_1:
WndPageSet(&pageSimp);
break;
case%20KEY_2:
WndPageSet(&pagePclink);
break;
case%20KEY_3:
WndPageSet(&pageInquire);
break;
case%20KEY_4:
WndPageSet(&pageRtc);
break;
case%20KEY_5:
WndPageSet(&pageGray);
break;
case%20KEY_6:
SPageInit();
WndPageSet(&pageMenu,%201);
break;
case%20KEY_7:
WndPageSet(&pageAbout);
break;
case%20KEY_UP:
WndMenuSelet(-1);
break;
case%20KEY_DOWN:
WndMenuSelet(1);
break;
case%20KEY_POWER:
WndPageSet(&pagePower);
break;
}
}
pageMain,pageAbout,pageRtc,pagePclink等文件,他們的結(jié)構(gòu)很類似。都是實現(xiàn)了OnPaint和OnKey函數(shù)。
如:pagePclink.c文件內(nèi)容:
實現(xiàn)了PagePclinkOnPaint和PagePclinOnKey函數(shù).
CommPclink函數(shù)是自己想要實現(xiàn)的功能,可以自己定義。
[cpp]%20view%20plain%20copy%20%20
#include%20"pagePclink.h"
/*=====================================================
=
=====================================================*/
void%20PagePclinkOnPaint(void);
void%20PagePclinOnKey(short%20key);
const%20TypePage%20pagePclink%20=%20{PagePclinkOnPaint,%20PagePclinOnKey};
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20PagePclinkOnPaint(void)
{
DispClr();
DispSetStyle(DISP_CENTER%20|%20DISP_REVERSE%20|%20DISP_7x9);
DispString(0,%200,%20"%20數(shù)據(jù)上傳%20");
DispSetStyle(DISP_POSITION|DISP_NORMAL|DISP_7x9);
DispString(0,%206,%20"[連接]%20[返回]");
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20PagePclinOnKey(short%20key)
{
switch%20(key)
{
case%20KEY_F1:
CommPclink();
break;
case%20KEY_F3:
WndPageEsc();
break;
}
}
[cpp]%20view%20plain%20copy%20%20
#ifndef%20__PAGE_POWER_H_
#define%20__PAGE_POWER_H_
#include%20"pageWnd.h"
/*=====================================================
=
=====================================================*/
extern%20const%20TypePage%20pagePower;
#endif
#include%20"PagePower.h"
#include%20"disp.h"
/*=====================================================
=
=====================================================*/
void%20PagePowerOnPaint(void);
void%20PagePowerOnKey(short%20key);
const%20TypePage%20pagePower%20=%20{PagePowerOnPaint,%20PagePowerOnKey};
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20PagePowerOnPaint(void)
{
DispClr();
DispSetStyle(DISP_CENTER%20|%20DISP_REVERSE%20|%20DISP_7x9);
DispString(0,%200,%20"%20電源管理%20");
DispSetStyle(DISP_POSITION|DISP_NORMAL|DISP_7x9);
DispString(0,%202,%20"%20[Enter]%20關(guān)機%20");
DispString(0,%204,%20"%20[F3%20]%20返回%20");
}
/*************************************************************************
*%20函數(shù)原型:
*%20功能描述:
*%20入口參數(shù):
*%20出口參數(shù):
*%20返%20回%20值:
*************************************************************************/
void%20PagePowerOnKey(short%20key)
{
switch%20(key)
{
case%20KEY_ENTER:
case%20KEY_POWER:
Halt_EH0218(4);
SysInit();
break;
case%20KEY_F3:
WndPageEsc();
break;
}
}
這樣的一種結(jié)構(gòu),很靈活,在主函數(shù)中只需要這樣調(diào)用:
[cpp]%20view%20plain%20copy%20%20
int main(void)
{
short key;
typ_msg_word smw;
SysInit();
for ( ; ; )
{
/*
界面刷新
*/
WndOnPaint();
/*
消息處理
*/
smw.s_word = sys_msg(SM_STAY_AWAKE); //用SM_GOTO_SLEEP串口就不能用
//按鍵處理
if (smw.bits.key_available)
{
LcdOffDelay(LCD_OFF_DELAY);
key = KEY_read();
if (key != -1)
{
WndOnKey(key);
}
}
//插入充電電源
if (smw.bits.charger_on)
{
LcdOffDelay(LCD_OFF_DELAY);
}
//斷開充電電源
if (smw.bits.charger_off)
{
LcdOffDelay(LCD_OFF_DELAY);
RefreshBattery();
}
//串口
if (smw.bits.comm_data)
{
CommReceive();
}
//實時任務(wù)
if (smw.bits.time_out)
{
TimTaskProc();
}
}
}
編輯:admin 最后修改時間:2018-05-19