似乎软件架构,只有纯上位机软件才有,其实,嵌入式软件也有架构可言,只有好的架构,才能结构清晰,方便开发和让系统稳定的工作。在有嵌入式操作系统的情况下,可以利用多任务和信号量,事件等设计嵌入式软件。但是在没有操作系统的裸机中,更需要有好的架构。例如利用事件和状态机模拟实现多任务,或者利用定时器和消息队列,信号量等模拟实现多任务,有了多任务就能灵活的设计软件架构。
一种简单的信号量实现:
代码语言:javascript复制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)大循环中,利用信号量实现各个函数(任务)的同步。
代码语言:javascript复制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; //应用退出
}
}
以上为借助信号量和定时器实现的一种简单的模拟多任务,其实也算不上是多任务,因为如果一个函数执行时间很长,如何打断它?
以下为借住定时器和任务队列实现的一种模拟多任务:
代码语言:javascript复制#include <stdio.h>
#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<TIM_TASK_NUMBER; i )
{
timTaskTab[i].pTaskFunc = 0;
timTaskTab[i].cntRun = 0;
timTaskTab[i].numCircle = 0;
timTaskTab[i].flagRun = 0;
timTaskTab[i].flagState = 0;
}
SPT_register_call_back(TimTaskUpdate);
SPT_set(TIM_TASK_PERIOD *64 / 1000);
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
short TimTaskAdd(ulong fsttim, ulong cirtim, void (*pTaskFunc)(void), uchar type)
{
int i;
int pos = -1;
//查找位置
for (i=0; i<TIM_TASK_NUMBER; i )
{
if (timTaskTab[i].pTaskFunc == pTaskFunc)
{
pos = i;
break;
}
if ((pos == -1) && (timTaskTab[i].flagState == 0))
{
pos = i;
}
}
//任务已满
if (pos == -1)
{
return -1;
}
//
timTaskTab[pos].pTaskFunc = pTaskFunc;
timTaskTab[pos].cntRun = fsttim / TIM_TASK_PERIOD;
timTaskTab[pos].numCircle = cirtim / TIM_TASK_PERIOD;
timTaskTab[pos].flagRun = 0;
timTaskTab[pos].flagType = type;
timTaskTab[pos].flagState = 1;
return 0;
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskDel(void (*pTaskFunc)(void))
{
int i;
for (i=0; i<TIM_TASK_NUMBER; i )
{
if (timTaskTab[i].pTaskFunc == pTaskFunc)
{
timTaskTab[i].flagState = 0;
timTaskTab[i].flagRun = 0;
return;
}
}
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskUpdate(void)
{
int i;
SPT_set(TIM_TASK_PERIOD *64 / 1000);
for (i=0; i<TIM_TASK_NUMBER; i )
{
if (timTaskTab[i].flagState != 0)
{
if (timTaskTab[i].cntRun != 0)
{
timTaskTab[i].cntRun--;
}
else
{
//判断处理位置
if (timTaskTab[i].flagType != 0)
(*timTaskTab[i].pTaskFunc)();
else
timTaskTab[i].flagRun = 1;
//判断重载
if (timTaskTab[i].numCircle)
timTaskTab[i].cntRun = timTaskTab[i].numCircle;
else
timTaskTab[i].flagState = 0;
}
}
}
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void TimTaskProc(void)
{
int i;
for (i=0; i<TIM_TASK_NUMBER; i )
{
if (timTaskTab[i].flagRun != 0)
{
timTaskTab[i].flagRun = 0;
(*timTaskTab[i].pTaskFunc)();
}
}
}
更为巧妙的是,可以借住函数指针实现一种灵活的菜单和按键实时处理结构。类似于windows下win32的消息驱动机制, 通过中断等方式把实时事件封装成消息。以下为定义界面刷新显示和响应按键处理的结构:
代码语言:javascript复制#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);
代码语言:javascript复制#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] = '