当前位置:首页 >> 单片机入门 >> 单片机程序构架

单片机程序构架

单片机 程序构架 作者:admin 来源:不详 发布时间:2018-05-19 09:14:59   浏览:9

 似乎软件架构,只有纯上位机软件才有,其实,嵌入式软件也有架构可言,只有好的架构,才能结构清晰,方便开发和让系统稳定的工作。在有嵌入式操作系统的情况下,可以利用多任务和信号量,事件等设计嵌入式软件。但是在没有操作系统的裸机中,更需要有好的架构。例如利用事件和状态机模拟实现多任务,或者利用定时器和消息队列,信号量等模拟实现多任务,有了多任务就能灵活的设计软件架构。

一种简单的信号量实现:

[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();

}

}

}

 

编辑:admin  最后修改时间:2018-05-19