单片机程序构架

简介: 单片机程序构架

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


一种简单的信号量实现:


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 <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的消息驱动机制,


通过中断等方式把实时事件封装成消息。以下为定义界面刷新显示和响应按键处理的结构:


#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] = '\0';
    if (menuSelect == menuTop + i)
      DispSetStyle(DISP_POSITION | DISP_REVERSE | DISP_7x9);
    else
      DispSetStyle(DISP_POSITION | DISP_NORMAL | DISP_7x9);
    DispString(0, i * 2, buf);
    pmn += 16;
  }
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
long WndGetPaseword(int x, int y, char *psw, int len, long qevent)
{
  int pin = 0;
  long keyevt;
  char key;
  char buf[20];
  memset(buf, '_', len);
  buf[len] = '\0';
PSW_INPUT_LOOP:
  DispString(x, y, buf);
  keyevt = delay_and_wait_key(0, EXIT_KEY_ALL, 0);
  if (keyevt == qevent)
  {
    psw[0] = '\0';
    return keyevt;
  }
  switch (keyevt)
  {
    case EXIT_KEY_0:
      key = '0';
      break;
    case EXIT_KEY_1:
      key = '1';
      break;
    case EXIT_KEY_2:
      key = '2';
      break;
    case EXIT_KEY_3:
      key = '3';
      break;
    case EXIT_KEY_4:
      key = '4';
      break;
    case EXIT_KEY_5:
      key = '5';
      break;
    case EXIT_KEY_6:
      key = '6';
      break;
    case EXIT_KEY_7:
      key = '7';
      break;
    case EXIT_KEY_8:
      key = '8';
      break;
    case EXIT_KEY_9:
      key = '9';
      break;
    case EXIT_KEY_COMM:
      if (pin != 0)
      {
        buf[--pin] = '_';
      }
      goto PSW_INPUT_LOOP;
      break;
    case EXIT_KEY_ENTER:
      psw[pin] = 0;
      return 0;
    default:
      goto PSW_INPUT_LOOP;
  }
  if (pin != len)
  {
    psw[pin] = key;
    buf[pin] = '*';
    pin++;
  }
  goto PSW_INPUT_LOOP;
}


在软件设计时,如果添加界面和对应的按键处理,很灵活,只需要新添加一个文件就可以了,文件的内容,只需要实现OnPain和对应的OnKey


#include "PageMenu.h"
/*=====================================================
= 
=====================================================*/
const char mainMenuTab[] = /*
1234567890123456*/"\
1. 现场采集     \
2. 数据上传     \
3. 存储状态查询 \
4. 时间设置     \
5. 对比度设置   \
6. 恢复出厂设置 \
7. 关于         ";
/*=====================================================
= 
=====================================================*/
void PageMenuOnPain(void);
void WndMenuOnKey(short key);
const TypePage pageMenu = {PageMenuOnPain, WndMenuOnKey};
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PageMenuOnPain(void)
{
  WndMenuInit(mainMenuTab, 7);
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void WndMenuOnKey(short key)
{
  int res;
  switch (key)
  {
    case KEY_F1:
    case KEY_ENTER:
      res = WndMenuGetSelet();
      switch (res)
      {
        case 1:
          WndPageSet(&pageSimp);
          break;
        case 2:
          WndPageSet(&pagePclink);
          break;
        case 3:
          WndPageSet(&pageInquire);
          break;
        case 4:
          WndPageSet(&pageRtc);
          break;
        case 5:
          WndPageSet(&pageGray);
          break;
        case 6:
          SPageInit();
          WndPageSet(&pageMenu, 1);
          break;
        case 7:
          WndPageSet(&pageAbout);
          break;
      }
      break;
    case KEY_F2:
    case KEY_F3:
      WndPageSet(&pageMain);
      break;
    case KEY_1:
      WndPageSet(&pageSimp);
      break;
    case KEY_2:
      WndPageSet(&pagePclink);
      break;
    case KEY_3:
      WndPageSet(&pageInquire);
      break;
    case KEY_4:
      WndPageSet(&pageRtc);
      break;
    case KEY_5:
      WndPageSet(&pageGray);
      break;
    case KEY_6:
      SPageInit();
      WndPageSet(&pageMenu, 1);
      break;
    case KEY_7:
      WndPageSet(&pageAbout);
      break;
    case KEY_UP:
      WndMenuSelet(-1);
      break;
    case KEY_DOWN:
      WndMenuSelet(1);
      break;
    case KEY_POWER:
      WndPageSet(&pagePower);
      break;
  }
}


pageMain,pageAbout,pageRtc,pagePclink等文件,他们的结构很类似。都是实现了OnPaint和OnKey函数。


如:pagePclink.c文件内容:


实现了PagePclinkOnPaint和PagePclinOnKey函数.


CommPclink函数是自己想要实现的功能,可以自己定义。


#include "pagePclink.h"
/*=====================================================
= 
=====================================================*/
void PagePclinkOnPaint(void);
void PagePclinOnKey(short key);
const TypePage pagePclink = {PagePclinkOnPaint, PagePclinOnKey};
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePclinkOnPaint(void)
{
  DispClr();
  DispSetStyle(DISP_CENTER | DISP_REVERSE | DISP_7x9);
  DispString(0, 0, "    数据上传    ");
  DispSetStyle(DISP_POSITION|DISP_NORMAL|DISP_7x9);
  DispString(0, 6, "[连接]    [返回]");
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePclinOnKey(short key)
{
  switch (key)
  {
    case KEY_F1:
      CommPclink();
      break;
    case KEY_F3:
      WndPageEsc();
      break;
  }
}


#ifndef __PAGE_POWER_H_
#define __PAGE_POWER_H_
#include "pageWnd.h"
/*=====================================================
= 
=====================================================*/
extern const TypePage pagePower;
#endif
#include "PagePower.h"
#include "disp.h"
/*=====================================================
= 
=====================================================*/
void PagePowerOnPaint(void);
void PagePowerOnKey(short key);
const TypePage pagePower = {PagePowerOnPaint, PagePowerOnKey};
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePowerOnPaint(void)
{
  DispClr();
  DispSetStyle(DISP_CENTER | DISP_REVERSE | DISP_7x9);
  DispString(0, 0, "    电源管理    ");
  DispSetStyle(DISP_POSITION|DISP_NORMAL|DISP_7x9);
  DispString(0, 2, "  [Enter] 关机  ");
  DispString(0, 4, "  [F3   ] 返回  ");
}
/*************************************************************************
* 函数原型:
* 功能描述:
* 入口参数:
* 出口参数:
* 返 回 值:
*************************************************************************/
void PagePowerOnKey(short key)
{
  switch (key)
  {
    case KEY_ENTER:
    case KEY_POWER:
      Halt_EH0218(4);
      SysInit();
      break;
    case KEY_F3:
      WndPageEsc();
      break;
  }
}


这样的一种结构,很灵活,在主函数中只需要这样调用:


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();
    }
  }
}
相关文章
|
2月前
|
存储 算法 编译器
如何优化单片机程序里面的C代码方法
如何优化单片机程序里面的C代码方法
22 0
|
8月前
|
数据处理 C语言
侃侃单片机的裸奔程序的框架
侃侃单片机的裸奔程序的框架
81 0
|
28天前
基于51单片机的简单交通灯程序
基于51单片机的简单交通灯程序
9 2
|
4月前
|
缓存 编译器 程序员
嵌入式开发环境Vscode开发STM32单片机程序
嵌入式开发环境Vscode开发STM32单片机程序
55 0
|
9月前
【单片机期中测试】9.定时器实现简单的秒表程序
【单片机期中测试】9.定时器实现简单的秒表程序
123 0
|
9月前
【单片机期中测试】1.简单的流水灯程序
【单片机期中测试】1.简单的流水灯程序
62 0
|
12月前
|
芯片
【STC15单片机】初始化程序原理
【STC15单片机】初始化程序原理
190 0
|
传感器 数据采集 监控
资料转发分享【毕业设计】单片机和stm32设计选题,proteues仿真、程序完整资料
资料转发分享【毕业设计】单片机和stm32设计选题,proteues仿真、程序完整资料 基于单片机寻迹巡线避障智能小车系统设计 基于单片机体温心率脉搏检测仪系统设计 基于单片机温湿度光照自动窗帘系统设计 基于单片机环境监测温湿度PM2.5系统设计 基于51单片机的波形发生器(四种波形) 基于单片机SO2 NO2 PM温湿度空气质量检测仪 基于51单片机冰箱温度控制器设计
1203 1
资料转发分享【毕业设计】单片机和stm32设计选题,proteues仿真、程序完整资料
蓝桥杯之单片机学习(二十三)——对于官方驱动的函数调用,包装程序
蓝桥杯之单片机学习(二十三)——对于官方驱动的函数调用,包装程序
蓝桥杯之单片机学习(二十三)——对于官方驱动的函数调用,包装程序
|
存储 编译器
『单片机原理』程序存储器的结构
相信学习单片机的小伙伴们一定听说过一门课程,那就是单片机原理。如果你也是玩单片机的却不怎么懂单片机原理的。那么这单片机原理还是非常有必要学习一下的。如果你问我学了有什么好处,那么应该就是你可能会对单片机(内部)的理解更"深"吧&同时也对你学习单片机编程也是有极大的很多好处的👋
503 0
『单片机原理』程序存储器的结构