单片机程序构架

2019-04-15 17:15发布

似乎软件架构,只有纯上位机软件才有,其实,嵌入式软件也有架构可言,只有好的架构,才能结构清晰,方便开发和让系统稳定的工作。在有嵌入式操作系统的情况下,可以利用多任务和信号量,事件等设计嵌入式软件。但是在没有操作系统的裸机中,更需要有好的架构。例如利用事件和状态机模拟实现多任务,或者利用定时器和消息队列,信号量等模拟实现多任务,有了多任务就能灵活的设计软件架构。
一种简单的信号量实现: void sem_init( volatile U08 *Sem ) { (*Sem)=0; } void sem_post( volatile U08 *Sem ) { if( 0 == (*Sem) ) (*Sem)++; } U08 sem_wait( volatile U08 *Sem ) { if(0 == *Sem) return 1; (*Sem)--; return 0; }
在一个大的while(1)大循环中,利用信号量实现各个函数(任务)的同步。 void Task_SysTime( void ) { static int TaskInitFlg = 0; U32 Timer1sCount = 0; //时钟计数器个数 U32 disstat = 0; static int tmrid0 = 0, tmrid1 = 0, tmrid2 = 0, tmrid3 = 0; if( 0 == TaskInitFlg ) { OSTimeDlyHMSM( 0, 0, 0, 50 //主要等待任务删除后才创建卡任务 tmrid0 = TimerSet(20); //定时器0(毫秒定时器)用于键盘、寻卡、定时器中断服务程序,20ms tmrid1 = TimerSet(1000);//定时器1(毫秒定时器)用于背显、GPS、定时连接检测、空闲显示 tmrid2 = TimerSet(500); //定时器2(毫秒定时器)用于信号显示,500ms tmrid3 = TimerSet(500); //定时器3(毫秒定时器)用于电池显示,500ms sem_init( &gSem_EVT_CARDFLG_OK ); //初始化为没有卡 APP_DisIdle( 2 ); //显示一次时间 APP_DisVoice(); TaskInitFlg = 1; //任务初始化完成 } else { HW_IWDG_ReloadCounter(); //清看门狗 if( 0 == TimerCheck(tmrid0) ) { tmrid0 = TimerSet(20); //定时器0重新定时, 20ms Timer_ScanKeyboard(); //20MS键盘扫描 Timer_FindCard(); //20MS寻卡处理 TIM20MS_IRQHandler(); //20MS定时器中断服务程序 } } } void Task_Tick( void ) { Task_SysError(); Task_CardProc(); Task_SysTime(); Task_MenuProc(); Task_MtnLink(); Task_CommProc(); } int main( void ) { Sys_Init(); //系统初始化 while( 1 ) { Task_Tick(); //任务轮询 if( 0 == sem_wait( &gSem_EVT_QUIT_APP ) ) break; //应用退出 } }
以上为借助信号量和定时器实现的一种简单的模拟多任务,其实也算不上是多任务,因为如果一个函数执行时间很长,如何打断它?
以下为借住定时器和任务队列实现的一种模拟多任务: #include #include "timTask.h" #include "disp.h" /*===================================================== = 变量定义 =====================================================*/ //任务队列 typedef struct{ char flagState; //运行方式 0: 无任务 // 1: 运行 char flagRun; //完成状态 0: 正在计数 // 1: 计数完成 char flagType; //处理方式 0: 主任务处理 // 1: 中断处理 ulong cntRun; //运行计数器 ulong numCircle; //循环计数器 void (*pTaskFunc)(void); //任务 }TypeTimTask; TypeTimTask timTaskTab[TIM_TASK_NUMBER]; /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void TimTaskInit(void) { int i; for (i=0; i更为巧妙的是,可以借住函数指针实现一种灵活的菜单和按键实时处理结构。类似于windows下win32的消息驱动机制,
通过中断等方式把实时事件封装成消息。以下为定义界面刷新显示和响应按键处理的结构:
#ifndef __PAGE_H_ #define __PAGE_H_ #include "heads.h" /*===================================================== = =====================================================*/ typedef struct{ void (* OnPaint)(void); void (* OnKey)(short); }TypePage; /*===================================================== = =====================================================*/ void WndPageSet(const TypePage *pg, int type = 0); TypePage * WndGetPage(void); void WndPageEsc(void); void WndOnKey(short key); void WndOnPaint(void); void WndMenuInit(const char *pmn, char mline); void WndMenuSelet(int m); char WndMenuGetSelet(void); long WndGetPaseword(int x, int y, char *psw, int len, long qevent);
#include "pageWnd.h" /*===================================================== = =====================================================*/ char flagPaint = 0; void (* pOnPaint)(void) = 0; void (* pOnKey)(short) = 0; const char *pMenuStr; uchar menuSelect = 0; uchar menuLine = 0; uchar menuTop; TypePage *pageCurrent; TypePage *pageTreeTab[10]; uchar pageIndex = 0; /*===================================================== = =====================================================*/ void WndDrawMenu(void); /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndPageSet(const TypePage *pg, int type) { if (pg == &pageMain) //防止出错 { pageIndex = 0; } else if (type == 0) { pageTreeTab[pageIndex++] = pageCurrent; } pageCurrent = (TypePage *)pg; pOnPaint = pg->OnPaint; pOnKey = pg->OnKey; flagPaint = 1; } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ TypePage * WndGetPage(void) { return pageCurrent; } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndPageEsc(void) { TypePage *pg; if (pageIndex != 0) { pageIndex--; pg = pageTreeTab[pageIndex]; } else { pg = (TypePage *)&pageMain; } pageCurrent = pg; pOnPaint = pg->OnPaint; pOnKey = pg->OnKey; flagPaint = 1; } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndOnPaint(void) { if (flagPaint != 0) { flagPaint = 0; (*pOnPaint)(); } } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndOnKey(short key) { if (pOnKey != 0) { (*pOnKey)(key); } } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndMenuInit(const char *pmn, char mline) { menuSelect = 0; pMenuStr = pmn; menuLine = mline; menuTop = 0; WndDrawMenu(); } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndMenuSelet(int m) { //光标滑动 if (m > 0) //下移 { menuSelect++; if (menuSelect == menuLine) menuSelect = 0; if (menuSelect > menuTop + 4) { if (menuLine < menuTop + 4) menuTop = menuLine - 4; else menuTop = menuSelect - 4; } } else if (m < 0) //上移 { if (menuSelect == 0) menuSelect = menuLine - 1; else menuSelect--; } //图框移动 if (menuSelect < menuTop) //上移 { menuTop = menuSelect; } else if (menuSelect >= menuTop + 4) //下移 { menuTop = menuSelect - 3; } WndDrawMenu(); } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ char WndMenuGetSelet(void) { return menuSelect + 1; } /************************************************************************* * 函数原型: * 功能描述: * 入口参数: * 出口参数: * 返 回 值: *************************************************************************/ void WndDrawMenu(void) { int i; char buf[17]; const char *pmn = pMenuStr + menuTop * 16; DispClr(); for (i=0; i<4; i++) { if (menuTop + i == menuLine) break; memcpy(buf, pmn, 16); buf[16] = '