单片机程序构架
扫描二维码
随时随地手机看文章
似乎软件架构,只有纯上位机软件才有,其实,嵌入式软件也有架构可言,只有好的架构,才能结构清晰,方便开发和让系统稳定的工作。在有嵌入式操作系统的情况下,可以利用多任务和信号量,事件等设计嵌入式软件。但是在没有操作系统的裸机中,更需要有好的架构。例如利用事件和状态机模拟实现多任务,或者利用定时器和消息队列,信号量等模拟实现多任务,有了多任务就能灵活的设计软件架构。
一种简单的信号量实现:
[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)大循环中,利用信号量实现各个函数(任务)的同步。
[cpp]%20view%20plain%20copy
void%20%20Task_SysTime(%20void%20)
{
static%20int%20TaskInitFlg%20=%200;
U32%20Timer1sCount%20=%200;%20//时钟计数器个数
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//主要等待任务删除后才创建卡任务
tmrid0%20=%20TimerSet(20);%20//定时器0(毫秒定时器)用于键盘、寻卡、定时器中断服务程序,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//任务初始化完成
}
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定时器中断服务程序
}
}
}
void%20Task_Tick(%20void%20)
{
Task_SysError();
Task_CardProc();
Task_SysTime();
Task_MenuProc();
Task_MtnLink();
Task_CommProc();
}
int%20main(%20void%20)
{
Sys_Init();%20//系统初始化
while(%201%20)
{
Task_Tick();%20//任务轮询
if(%200%20==%20sem_wait(%20&gSem_EVT_QUIT_APP%20)%20)
break;%20//应用退出
}
}
以上为借助信号量和定时器实现的一种简单的模拟多任务,其实也算不上是多任务,因为如果一个函数执行时间很长,如何打断它?
以下为借住定时器和任务队列实现的一种模拟多任务:
[cpp]%20view%20plain%20copy
#include%20
#include%20"timTask.h"
#include%20"disp.h"
/*=====================================================
=%20变量定义
=====================================================*/
//任务队列
typedef%20struct{
char%20flagState;%20//运行方式%200:%20无任务
//%201:%20运行
char%20flagRun;%20//完成状态%200:%20正在计数
//%201:%20计数完成
char%20flagType;%20//处理方式%200:%20主任务处理
//%201:%20中断处理
ulong%20cntRun;%20//运行计数器
ulong%20numCircle;%20//循环计数器
void%20(*pTaskFunc)(void);%20//任务
}TypeTimTask;
TypeTimTask%20timTaskTab[TIM_TASK_NUMBER];
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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;
}
}
//任务已满
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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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)();
}
}
}
更为巧妙的是,可以借住函数指针实现一种灵活的菜单和按键实时处理结构。类似于windows下win32的消息驱动机制,
通过中断等方式把实时事件封装成消息。以下为定义界面刷新显示和响应按键处理的结构:
[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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%20返%20回%20值:
*************************************************************************/
TypePage%20*%20WndGetPage(void)
{
return%20pageCurrent;
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%20返%20回%20值:
*************************************************************************/
void%20WndOnPaint(void)
{
if%20(flagPaint%20!=%200)
{
flagPaint%20=%200;
(*pOnPaint)();
}
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%20返%20回%20值:
*************************************************************************/
void%20WndOnKey(short%20key)
{
if%20(pOnKey%20!=%200)
{
(*pOnKey)(key);
}
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%20返%20回%20值:
*************************************************************************/
char%20WndMenuGetSelet(void)
{
return%20menuSelect%20+%201;
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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;
}
在软件设计时,如果添加界面和对应的按键处理,很灵活,只需要新添加一个文件就可以了,文件的内容,只需要实现OnPain和对应的OnKey
[cpp]%20%20view%20plain%20copy%20%20
#include%20"PageMenu.h"
/*=====================================================
=
=====================================================*/
const%20char%20mainMenuTab[]%20=%20/*
1234567890123456*/"\
1.%20现场采集%20\
2.%20数据上传%20\
3.%20存储状态查询%20\
4.%20时间设置%20\
5.%20对比度设置%20\
6.%20恢复出厂设置%20\
7.%20关于%20";
/*=====================================================
=
=====================================================*/
void%20PageMenuOnPain(void);
void%20WndMenuOnKey(short%20key);
const%20TypePage%20pageMenu%20=%20{PageMenuOnPain,%20WndMenuOnKey};
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%20返%20回%20值:
*************************************************************************/
void%20PageMenuOnPain(void)
{
WndMenuInit(mainMenuTab,%207);
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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等文件,他们的结构很类似。都是实现了OnPaint和OnKey函数。
如:pagePclink.c文件内容:
实现了PagePclinkOnPaint和PagePclinOnKey函数.
CommPclink函数是自己想要实现的功能,可以自己定义。
[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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%20返%20回%20值:
*************************************************************************/
void%20PagePclinkOnPaint(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,%206,%20"[连接]%20[返回]");
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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关机%20");
DispString(0,%204,%20"%20[F3%20]%20返回%20");
}
/*************************************************************************
*%20函数原型:
*%20功能描述:
*%20入口参数:
*%20出口参数:
*%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;
}
}
这样的一种结构,很灵活,在主函数中只需要这样调用:
[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();
}
//实时任务
if (smw.bits.time_out)
{
TimTaskProc();
}
}
}