单片机程序构架

2019-04-15 17:16发布

似乎软件架构,只有纯上位机软件才有,其实,嵌入式软件也有架构可言,只有好的架构,才能结构清晰,方便开发和让系统稳定的工作。在有嵌入式操作系统的情况下,可以利用多任务和信号量,事件等设计嵌入式软件。但是在没有操作系统的裸机中,更需要有好的架构。例如利用事件和状态机模拟实现多任务,或者利用定时器和消息队列,信号量等模拟实现多任务,有了多任务就能灵活的设计软件架构。
一种简单的信号量实现: [cpp] view plain copy
  1. void sem_init( volatile U08 *Sem )  
  2. {  
  3.     (*Sem)=0;  
  4. }  
  5.   
  6. void sem_post( volatile U08 *Sem )  
  7. {  
  8.     if( 0 == (*Sem) )  
  9.         (*Sem)++;  
  10. }  
  11.   
  12. U08  sem_wait( volatile U08 *Sem )   
  13. {  
  14.     if(0 == *Sem)  
  15.         return 1;  
  16.   
  17.     (*Sem)--;  
  18.   
  19.     return 0;  


在一个大的while(1)大循环中,利用信号量实现各个函数(任务)的同步。 [cpp] view plain copy
  1. void Task_SysTime( void )  
  2. {     
  3.     static int TaskInitFlg = 0;  
  4.     U32 Timer1sCount = 0;           //时钟计数器个数   
  5.     U32 disstat = 0;  
  6.     static int tmrid0 = 0, tmrid1 = 0, tmrid2 = 0, tmrid3 = 0;  
  7.   
  8.     if( 0 == TaskInitFlg )  
  9.     {  
  10.         OSTimeDlyHMSM( 0, 0, 0, 50 //主要等待任务删除后才创建卡任务  
  11.           
  12.         tmrid0 = TimerSet(20);  //定时器0(毫秒定时器)用于键盘、寻卡、定时器中断服务程序,20ms  
  13.         tmrid1 = TimerSet(1000);//定时器1(毫秒定时器)用于背显、GPS、定时连接检测、空闲显示  
  14.         tmrid2 = TimerSet(500); //定时器2(毫秒定时器)用于信号显示,500ms  
  15.         tmrid3 = TimerSet(500); //定时器3(毫秒定时器)用于电池显示,500ms  
  16.   
  17.         sem_init( &gSem_EVT_CARDFLG_OK );                   //初始化为没有卡  
  18.           
  19.         APP_DisIdle( 2 );                               //显示一次时间  
  20.         APP_DisVoice();  
  21.           
  22.         TaskInitFlg = 1;                //任务初始化完成  
  23.     }  
  24.     else  
  25.         {  
  26.         HW_IWDG_ReloadCounter();                //清看门狗  
  27.   
  28.         if( 0 == TimerCheck(tmrid0) )  
  29.         {  
  30.             tmrid0 = TimerSet(20);          //定时器0重新定时, 20ms      
  31.   
  32.             Timer_ScanKeyboard();             //20MS键盘扫描   
  33.             Timer_FindCard();           //20MS寻卡处理  
  34.             TIM20MS_IRQHandler();       //20MS定时器中断服务程序  
  35.            }  
  36.       }  
  37. }  
  38.   
  39. void Task_Tick( void )  
  40. {  
  41.     Task_SysError();  
  42.   
  43.     Task_CardProc();  
  44.   
  45.     Task_SysTime();  
  46.   
  47.     Task_MenuProc();  
  48.   
  49.     Task_MtnLink();  
  50.       
  51.     Task_CommProc();  
  52. }  
  53. int main( void )  
  54. {  
  55.     Sys_Init(); //系统初始化  
  56.   
  57.     while( 1 )                                  
  58.     {   
  59.         Task_Tick();    //任务轮询  
  60.   
  61.         if( 0 == sem_wait( &gSem_EVT_QUIT_APP ) )   
  62.            break;     //应用退出  
  63.     }  
  64. }  

以上为借助信号量和定时器实现的一种简单的模拟多任务,其实也算不上是多任务,因为如果一个函数执行时间很长,如何打断它?
以下为借住定时器和任务队列实现的一种模拟多任务: [cpp] view plain copy
  1. #include   
  2. #include "timTask.h"  
  3. #include "disp.h"  
  4.   
  5. /*===================================================== 
  6. =   变量定义 
  7. =====================================================*/  
  8. //任务队列  
  9. typedef struct{  
  10.     char    flagState;  //运行方式  0: 无任务  
  11.                         //          1: 运行   
  12.     char    flagRun;    //完成状态  0: 正在计数  
  13.                         //          1: 计数完成  
  14.     char    flagType;   //处理方式  0: 主任务处理  
  15.                         //          1: 中断处理  
  16.     ulong   cntRun;     //运行计数器  
  17.     ulong   numCircle;  //循环计数器  
  18.     void (*pTaskFunc)(void);    //任务  
  19. }TypeTimTask;  
  20.   
  21. TypeTimTask timTaskTab[TIM_TASK_NUMBER];  
  22.   
  23. /************************************************************************* 
  24. * 函数原型: 
  25. * 功能描述: 
  26. * 入口参数: 
  27. * 出口参数: 
  28. * 返 回 值: 
  29. *************************************************************************/  
  30. void TimTaskInit(void)  
  31. {  
  32.     int i;  
  33.       
  34.     for (i=0; i
  35.     {  
  36.         timTaskTab[i].pTaskFunc = 0;  
  37.         timTaskTab[i].cntRun = 0;  
  38.         timTaskTab[i].numCircle = 0;  
  39.         timTaskTab[i].flagRun = 0;  
  40.         timTaskTab[i].flagState = 0;  
  41.     }  
  42.     SPT_register_call_back(TimTaskUpdate);  
  43.     SPT_set(TIM_TASK_PERIOD *64 / 1000);  
  44. }  
  45.   
  46. /************************************************************************* 
  47. * 函数原型: 
  48. * 功能描述: 
  49. * 入口参数: 
  50. * 出口参数: 
  51. * 返 回 值: 
  52. *************************************************************************/  
  53. short TimTaskAdd(ulong fsttim, ulong cirtim, void (*pTaskFunc)(void), uchar type)  
  54. {  
  55.     int i;  
  56.     int pos = -1;  
  57.     //查找位置  
  58.     for (i=0; i
  59.     {  
  60.         if (timTaskTab[i].pTaskFunc == pTaskFunc)  
  61.         {  
  62.             pos = i;  
  63.             break;  
  64.         }  
  65.         if ((pos == -1) && (timTaskTab[i].flagState == 0))  
  66.         {  
  67.             pos = i;  
  68.         }  
  69.     }  
  70.     //任务已满  
  71.     if (pos == -1)  
  72.     {  
  73.         return -1;  
  74.     }  
  75.     //  
  76.     timTaskTab[pos].pTaskFunc = pTaskFunc;  
  77.     timTaskTab[pos].cntRun = fsttim / TIM_TASK_PERIOD;  
  78.     timTaskTab[pos].numCircle = cirtim / TIM_TASK_PERIOD;  
  79.     timTaskTab[pos].flagRun = 0;  
  80.     timTaskTab[pos].flagType = type;  
  81.     timTaskTab[pos].flagState = 1;  
  82.       
  83.     return 0;  
  84. }  
  85.   
  86. /************************************************************************* 
  87. * 函数原型: 
  88. * 功能描述: 
  89. * 入口参数: 
  90. * 出口参数: 
  91. * 返 回 值: 
  92. *************************************************************************/  
  93. void TimTaskDel(void (*pTaskFunc)(void))  
  94. {  
  95.     int i;  
  96.       
  97.     for (i=0; i
  98.     {  
  99.         if (timTaskTab[i].pTaskFunc == pTaskFunc)  
  100.         {  
  101.             timTaskTab[i].flagState = 0;  
  102.             timTaskTab[i].flagRun = 0;  
  103.             return;  
  104.         }  
  105.     }  
  106. }  
  107.   
  108. /************************************************************************* 
  109. * 函数原型: 
  110. * 功能描述: 
  111. * 入口参数: 
  112. * 出口参数: 
  113. * 返 回 值: 
  114. *************************************************************************/  
  115. void TimTaskUpdate(void)  
  116. {  
  117.     int i;  
  118.       
  119.     SPT_set(TIM_TASK_PERIOD *64 / 1000);  
  120.       
  121.     for (i=0; i
  122.     {  
  123.         if (timTaskTab[i].flagState != 0)  
  124.         {  
  125.             if (timTaskTab[i].cntRun != 0)  
  126.             {  
  127.                 timTaskTab[i].cntRun--;  
  128.             }  
  129.             else  
  130.             {  
  131.                 //判断处理位置  
  132.                 if (timTaskTab[i].flagType != 0)  
  133.                     (*timTaskTab[i].pTaskFunc)();  
  134.                 else  
  135.                     timTaskTab[i].flagRun = 1;  
  136.                 //判断重载  
  137.                 if (timTaskTab[i].numCircle)  
  138.                     timTaskTab[i].cntRun = timTaskTab[i].numCircle;  
  139.                 else  
  140.                     timTaskTab[i].flagState = 0;  
  141.             }  
  142.         }  
  143.     }  
  144. }  
  145.   
  146. /************************************************************************* 
  147. * 函数原型: 
  148. * 功能描述: 
  149. * 入口参数: 
  150. * 出口参数: 
  151. * 返 回 值: 
  152. *************************************************************************/  
  153. void TimTaskProc(void)  
  154. {  
  155.     int i;  
  156.       
  157.     for (i=0; i
  158.     {  
  159.         if (timTaskTab[i].flagRun != 0)  
  160.         {  
  161.             timTaskTab[i].flagRun = 0;  
  162.             (*timTaskTab[i].pTaskFunc)();  
  163.         }  
  164.     }  
  165. }  
更为巧妙的是,可以借住函数指针实现一种灵活的菜单和按键实时处理结构。类似于windows下win32的消息驱动机制,
通过中断等方式把实时事件封装成消息。以下为定义界面刷新显示和响应按键处理的结构:
[cpp] view plain copy
  1. #ifndef __PAGE_H_  
  2. #define __PAGE_H_  
  3.   
  4. #include "heads.h"  
  5.   
  6. /*===================================================== 
  7. =    
  8. =====================================================*/  
  9. typedef struct{  
  10.     void (* OnPaint)(void);  
  11.     void (* OnKey)(short);  
  12. }TypePage;  
  13.   
  14. /*===================================================== 
  15. =    
  16. =====================================================*/  
  17. void WndPageSet(const TypePage *pg, int type = 0);  
  18. TypePage * WndGetPage(void);  
  19. void WndPageEsc(void);  
  20. void WndOnKey(short key);  
  21. void WndOnPaint(void);  
  22. void WndMenuInit(const char *pmn, char mline);  
  23. void WndMenuSelet(int m);  
  24. char WndMenuGetSelet(void);  
  25. long WndGetPaseword(int x, int y, char *psw, int len, long qevent);  

[cpp] view plain copy
  1. #include "pageWnd.h"  
  2.   
  3. /*===================================================== 
  4. =    
  5. =====================================================*/  
  6. char  flagPaint = 0;  
  7. void (* pOnPaint)(void) = 0;  
  8. void (* pOnKey)(short) = 0;  
  9.   
  10. const char *pMenuStr;  
  11. uchar menuSelect = 0;  
  12. uchar menuLine = 0;  
  13. uchar menuTop;  
  14.   
  15.   
  16. TypePage *pageCurrent;  
  17. TypePage *pageTreeTab[10];  
  18. uchar pageIndex = 0;  
  19.   
  20. /*===================================================== 
  21. =    
  22. =====================================================*/  
  23. void WndDrawMenu(void);  
  24.   
  25. /************************************************************************* 
  26. * 函数原型: 
  27. * 功能描述: 
  28. * 入口参数: 
  29. * 出口参数: 
  30. * 返 回 值: 
  31. *************************************************************************/  
  32. void WndPageSet(const TypePage *pg, int type)  
  33. {  
  34.     if (pg == &pageMain)        //防止出错  
  35.     {  
  36.         pageIndex = 0;  
  37.     }  
  38.     else if (type == 0)  
  39.     {  
  40.         pageTreeTab[pageIndex++] = pageCurrent;  
  41.     }  
  42.     pageCurrent = (TypePage *)pg;  
  43.     pOnPaint = pg->OnPaint;  
  44.     pOnKey = pg->OnKey;  
  45.     flagPaint = 1;  
  46. }  
  47.   
  48. /************************************************************************* 
  49. * 函数原型: 
  50. * 功能描述: 
  51. * 入口参数: 
  52. * 出口参数: 
  53. * 返 回 值: 
  54. *************************************************************************/  
  55. TypePage * WndGetPage(void)  
  56. {  
  57.     return pageCurrent;  
  58. }  
  59.   
  60. /************************************************************************* 
  61. * 函数原型: 
  62. * 功能描述: 
  63. * 入口参数: 
  64. * 出口参数: 
  65. * 返 回 值: 
  66. *************************************************************************/  
  67. void WndPageEsc(void)  
  68. {  
  69.     TypePage *pg;  
  70.       
  71.     if (pageIndex != 0)  
  72.     {  
  73.         pageIndex--;  
  74.         pg = pageTreeTab[pageIndex];  
  75.     }  
  76.     else  
  77.     {  
  78.         pg = (TypePage *)&pageMain;  
  79.     }  
  80.     pageCurrent = pg;  
  81.     pOnPaint = pg->OnPaint;  
  82.     pOnKey = pg->OnKey;  
  83.     flagPaint = 1;  
  84. }  
  85.   
  86. /************************************************************************* 
  87. * 函数原型: 
  88. * 功能描述: 
  89. * 入口参数: 
  90. * 出口参数: 
  91. * 返 回 值: 
  92. *************************************************************************/  
  93. void WndOnPaint(void)  
  94. {  
  95.     if (flagPaint != 0)  
  96.     {  
  97.         flagPaint = 0;  
  98.         (*pOnPaint)();  
  99.     }  
  100. }  
  101.   
  102. /************************************************************************* 
  103. * 函数原型: 
  104. * 功能描述: 
  105. * 入口参数: 
  106. * 出口参数: 
  107. * 返 回 值: 
  108. *************************************************************************/  
  109. void WndOnKey(short key)  
  110. {  
  111.     if (pOnKey != 0)  
  112.     {  
  113.         (*pOnKey)(key);  
  114.     }  
  115. }  
  116.   
  117. /************************************************************************* 
  118. * 函数原型: 
  119. * 功能描述: 
  120. * 入口参数: 
  121. * 出口参数: 
  122. * 返 回 值: 
  123. *************************************************************************/  
  124. void WndMenuInit(const char *pmn, char mline)  
  125. {  
  126.     menuSelect = 0;  
  127.     pMenuStr = pmn;  
  128.     menuLine = mline;  
  129.     menuTop = 0;  
  130.     WndDrawMenu();  
  131. }  
  132.   
  133. /************************************************************************* 
  134. * 函数原型: 
  135. * 功能描述: 
  136. * 入口参数: 
  137. * 出口参数: 
  138. * 返 回 值: 
  139. *************************************************************************/  
  140. void WndMenuSelet(int m)  
  141. {  
  142.     //光标滑动  
  143.     if (m > 0)           //下移  
  144.     {  
  145.         menuSelect++;  
  146.         if (menuSelect == menuLine)  
  147.             menuSelect = 0;  
  148.   
  149.         if (menuSelect > menuTop + 4)  
  150.         {  
  151.             if (menuLine < menuTop + 4)  
  152.                 menuTop = menuLine - 4;  
  153.             else  
  154.                 menuTop = menuSelect - 4;  
  155.         }  
  156.     }  
  157.     else if (m < 0)      //上移  
  158.     {  
  159.         if (menuSelect == 0)  
  160.             menuSelect = menuLine - 1;  
  161.         else  
  162.             menuSelect--;  
  163.     }  
  164.     //图框移动  
  165.     if (menuSelect < menuTop)                //上移  
  166.     {  
  167.         menuTop = menuSelect;  
  168.     }  
  169.     else if (menuSelect >= menuTop + 4)      //下移  
  170.     {  
  171.         menuTop = menuSelect - 3;  
  172.     }  
  173.       
  174.     WndDrawMenu();  
  175. }  
  176.   
  177. /************************************************************************* 
  178. * 函数原型: 
  179. * 功能描述: 
  180. * 入口参数: 
  181. * 出口参数: 
  182. * 返 回 值: 
  183. *************************************************************************/  
  184. char WndMenuGetSelet(void)  
  185. {  
  186.     return menuSelect + 1;  
  187. }  
  188.   
  189. /************************************************************************* 
  190. * 函数原型: 
  191. * 功能描述: 
  192. * 入口参数: 
  193. * 出口参数: 
  194. * 返 回 值: 
  195. *************************************************************************/  
  196. void WndDrawMenu(void)  
  197. {  
  198.     int i;  
  199.   
  200.     char buf[17];  
  201.     const char *pmn = pMenuStr + menuTop * 16;  
  202.   
  203.     DispClr();  
  204.     for (i=0; i<4; i++)  
  205.     {  
  206.         if (menuTop + i == menuLine)  
  207.             break;  
  208.         memcpy(buf, pmn, 16);  
  209.         buf[16] = '