嵌入式软件开发的框架思维

做一件事情,先有全局视角,拆分出系统的模块,然后根据不同的模块确认出待办顺序,并从全局的角度考虑多个模块的整合,是否需要公共模块,模块之间的依赖关系是什么样的,然后整合后,逐条去解决!


做软件,其实这两年的成长是最大的,因为转到架构岗,确实是少了一部分之前在开发岗的一些不好的习惯,比方一上来就是细节,一上来就是某个功能点如何实现,现在接触一个事物,或者一个项目,首先考虑的是项目的整体框架,先搭骨架,后填充血肉,把基础打好了,后面细节的问题总是能解决的;



UWB标签和锚点的嵌入式系统,大家觉得嵌入式系统的程序大概长什么样,其实不复杂,一个 while(1)的死循环,然后套上很多逻辑,通过中断来接收事件,处理业务,类似下面链接中的前后台顺序执行法,是不是感觉一朝回到了解放前;


做过应用系统的同学多少都知道,主流的应用开发框架都是基于事件驱动的,都有基于IOT反向依赖倒置开发模式的开发框架,只需要在固定的模板方法中填写业务逻辑代码,一个APP的雏形就出来了,那么嵌入式系统为什么没有这样的框架呢?


通过搜索资料,其实这些探索还是有的:比方 CSDN博客:嵌入式软件开发之程序架构(一),提供了一个相对来说不复杂的框架,基于这个思路,实现了一个简单的Uart串口命令接收和处理业务。



Uart串口消息处理框架:

接收串口消息 -> 入命令队列

串口处理task定时运行 -> 出命令队列的命令 -> 处理命令


主要涉及:任务管理(定时器)、主循环、命令队列、串口中断处理


代码在nrf52832芯片上运行,其他嵌入式芯片可以参考!


任务管理:

//taskmanager.h

/**
  * @brief 任务函数相关信息结构体定义.
  */
typedef struct{   
    uint8_t  m_runFlag;                 /*!< 程序运行标记:0-不运行,1运行 */
    uint16_t m_timer;                  /*!< 计时器 */
    uint16_t m_itvTime;                /*!< 任务运行间隔时间 */
    uint16_t (*m_pTaskHook)(void *taskPtr);       /*!< 要运行的任务函数 */
    void *taskPtr;                //任务运行方法的入口参数
} TaskInfo; 
#define TASKS_MAX     5              // 定义任务数目
 
void initTaskTimer(int ms);
void startTaskTimer();
void stopTaskTimer();
int      registerTask(TaskInfo *tasklist, int taskNumber); 
void     remarksTask(void);
uint16_t processTask();


//taskmanager.c
#include "taskmanager.h"
#include "app_timer.h"
static int volatile gTaskNumber = 0; 
static volatile TaskInfo *gTaskList;  
static bool volatile initTimer = false;
APP_TIMER_DEF(taskManagerTimer);
/**
 @brief 测试定时器的回调函数
 @param 无
 @return 无
*/
static void taskManagerTimerCallback(void *arg)
{
	UNUSED_PARAMETER(arg);
	// 在这里加入自己的应用处理
	remarksTask();
}
//ms
void initTaskTimer(int ms){
	if (!initTimer){
		ret_code_t err_code = app_timer_init();
		/* 
			APP_TIMER_MODE_SINGLE_SHOT,                 
			APP_TIMER_MODE_REPEATED                     
		*/
		err_code=app_timer_create(&taskManagerTimer, APP_TIMER_MODE_REPEATED, taskManagerTimerCallback);
	}
	initTimer = true;
  ret_code_t err_code=app_timer_start(taskManagerTimer, APP_TIMER_TICKS(ms) ,NULL);
}
void stopTaskTimer(){ 
  ret_code_t err_code=app_timer_stop(taskManagerTimer);    
}
void startTaskTimer(int ms){ 
	ret_code_t err_code=app_timer_start(taskManagerTimer, APP_TIMER_TICKS(ms) ,NULL);
}
 
/**
  * @brief      任务函数运行标志处理.
  * @note       该函数由1ms定时器中断调用
  * @param      None.
  * @return     None.
  */
void remarksTask(void)
{
	if (gTaskList == NULL){
		return;
	}
  uint8_t i;
  for (i = 0; i < gTaskNumber; i++)
  {
      if (gTaskList[i].m_timer)
      {
          gTaskList[i].m_timer--;
          if (0 == gTaskList[i].m_timer)
          {
               gTaskList[i].m_timer = gTaskList[i].m_itvTime;
               gTaskList[i].m_runFlag = 1;
          }
      }
  }
}
/**
  * @brief      任务函数运行处理.
  * @note       该函数由主循环调用
  * @param      None.
  * @return     下次执行的时间.
  */
uint16_t processTask()
{
	if (gTaskList == NULL){
		return 1000;
	}
	
	uint16_t delay = gTaskList[0].m_timer;
  uint8_t i;
  for (i = 0; i < gTaskNumber; i++)
  {
      if (gTaskList[i].m_runFlag)
      {
           uint16_t nextDelay = gTaskList[i].m_pTaskHook(gTaskList[i].taskPtr);         // 运行任务
           if (nextDelay < gTaskList[i].m_timer){
           	gTaskList[i].m_timer  = nextDelay;//重置下次延时时间
           }
           gTaskList[i].m_runFlag = 0;         // 标志清0
      }
      if (delay > gTaskList[0].m_timer){
      	delay = gTaskList[0].m_timer;
      }
  }   
  return delay;
}
 
int registerTask(TaskInfo *tasklist, int taskNumber){ 
	if (tasklist == NULL){
		return 1000;
	}
	
	gTaskList = tasklist; 
	gTaskNumber = taskNumber;
	return 0;
}


主循环:

//标签的基准定时器定义
#define BASE_TIMER 100 
//如下定义均是对比基准定时器的值 
#define TAG_DELAY_TASK    5
#define UART_HANDLER_DELAY_TIME 1
#define BLE_HANDLER_DELAY_TIME  1
#define LED_DELAY_TASK    1
#define POWER_HANDLER_DELAY_TIME 30 //3秒上报一次
//基站的基准定时器,1s钟处理100张标签,所以每10ms就需要完成一次完整的测距
#define ARCHOR_BASE_TIMER 10
//如下定义均是对比基准定时器的值
#define ARCHOR_DELAY_TASK 1 
#define ARCHOR_UART_HANDLER_DELAY_TIME 10
#define ARCHOR_BLE_HANDLER_DELAY_TIME  10
#define ARCHOR_LED_DELAY_TASK    10 
static uint16_t uwbTask(void *args){ 
 
		uint8_t mode = getCurrentMode();
		if (mode == MODE_TAG){
			//sender tick timeout event
			uwbTimeOut(ARCHOR_DELAY_TASK); 
		}
} 
static uint16_t archorTask(void *args);
static uint16_t uartTask(void *args);
static uint16_t bleTask(void *args);
static uint16_t powerTask(void *args);
static uint16_t ledTask(void *args);
/** TAG任务函数相关信息 */
static TaskInfo gTagTaskList[5] = {
    {0, UART_HANDLER_DELAY_TIME, UART_HANDLER_DELAY_TIME, uartTask},         // uart通信任务 
    {0, BLE_HANDLER_DELAY_TIME, BLE_HANDLER_DELAY_TIME, bleTask},         // 蓝牙通信任务 
    {0, TAG_DELAY_TASK, TAG_DELAY_TASK, uwbTask},   // dw逻辑处理任务,一个标签1s发两次位置定位,则配置为500ms
    {0, LED_DELAY_TASK, LED_DELAY_TASK, ledTask},     // 指示灯控制任务
    {0, POWER_HANDLER_DELAY_TIME, POWER_HANDLER_DELAY_TIME, powerTask},      // 定时器获取电源任务
};
/** ARCHOR任务函数相关信息 */
static TaskInfo gArchorTaskList[4] = {
    {0, ARCHOR_UART_HANDLER_DELAY_TIME, ARCHOR_UART_HANDLER_DELAY_TIME, uartTask},         // uart通信任务 
    {0, ARCHOR_BLE_HANDLER_DELAY_TIME, ARCHOR_BLE_HANDLER_DELAY_TIME, bleTask},         // uart通信任务 
    {0, ARCHOR_DELAY_TASK, ARCHOR_DELAY_TASK, uwbTask},   // dw逻辑处理任务
    {0, ARCHOR_LED_DELAY_TASK, ARCHOR_LED_DELAY_TASK, ledTask},     // 指示灯控制任务 
};
static void initTaskTimerByMode(){ 
    /*
    标签和基站的处理逻辑不同,标签要省电,基站要高性能,基站的任务和标签的任务处理不同
    1、串口命令处理
    2、蓝牙命令处理
    3、测距逻辑
    4、电压上报
    5、LED控制
    */
    uint8_t mode = getCurrentMode();
    if (mode == MODE_ANCHOR){ 
        registerTask(gArchorTaskList, sizeof(gArchorTaskList)/sizeof(TaskInfo)); 
        //启动基准定时器
        initTaskTimer(ARCHOR_BASE_TIMER); 
    }else {
        registerTask(gTagTaskList, sizeof(gTagTaskList)/sizeof(TaskInfo)); 
        //启动基准定时器
        initTaskTimer(BASE_TIMER); 
    }  
}

void main(){
    //初始化任务定时器
    initTaskTimerByMode();
    for (;;)
    {
        //中断处理
        dal_gpio_in_evt_handler(); 
        //任务处理  
        int delay = processTask();
 
        //输出jlink日志
        NRF_LOG_FLUSH();
        
				//等待事件处理
        sd_app_evt_wait();  
    }
   }


串口任务:

static uint16_t uartTask(void *args){  
    uint16_t nextDelayTime = UART_HANDLER_DELAY_TIME;
	
    int commandIndex = getHandlerCommandIndex();
    //logD("Debug logging uartTask started.");
    if (commandIndex == NO_COMMAND){
        return nextDelayTime;
    }
    if (gUartCommand[commandIndex].commandHandleFlg){
			//be handled return 
        return nextDelayTime;
    }
    gUartCommand[commandIndex].commandHandleFlg = true;
    logD("handler type:%d, command:%d, value;%d", gUartCommand[commandIndex].type, gUartCommand[commandIndex].code, gUartCommand[commandIndex].value);
    if (gUartCommand[commandIndex].type == UART_COMMAND_TYPE_UWB) {
        switch(gUartCommand[commandIndex].code){
             case    UWBCODE_SET_MODE:{
                changeMode(gUartCommand[commandIndex].value);
  
								logD("uart handler end.");
             }
             break;
             case    UWBCODE_SET_ADDRESS:{ 
                changeUWBAddress(gUartCommand[commandIndex].value);
             }
             break;
             default:
             break;
        }
    } 
    return nextDelayTime;
}

串口中断:

/**@brief   Function for handling app_uart events.
 *
 * @details This function will receive a single character from the app_uart module and append it to
 *          a string. The string will be be sent over BLE when the last character received was a
 *          'new line' '\n' (hex 0x0A) or if the string has reached the maximum data length.
 */
/**@snippet [Handling the data received over UART] */
void uart_event_handle(app_uart_evt_t * p_event)
{
    static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
    static uint8_t index = 0; 
    uint32_t       err_code = 0;
	NRF_LOG_DEBUG("---uart_event_handle %d", p_event->evt_type);
    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
            index++;
			NRF_LOG_DEBUG("APP_UART_DATA_READY----------");
			NRF_LOG_HEXDUMP_DEBUG(data_array, index);
            if ((data_array[index - 1] == 0x0A) ||
                (data_array[index - 1] == 0x0D) ||
                (index >= m_ble_nus_max_data_len))
            {
                if (data_array[1] == '#' && data_array[3]== '#')
                {
                    //处理一条命令
                    uint8_t type = (data_array[0] - 48);//asscii to int
                    uint8_t code = (data_array[2] - 48);//asscii to int
                    uint8_t  value = getCommandValue(4, data_array, index); 
                    //将命令塞到队列中
                    putCommand(type, code, value); 
#if 0
                    NRF_LOG_DEBUG("Ready to send data over BLE NUS");
                    NRF_LOG_HEXDUMP_DEBUG(data_array, index);
                    do
                    {
                        uint16_t length = (uint16_t)index;
                        err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                        if ((err_code != NRF_ERROR_INVALID_STATE) &&
                            (err_code != NRF_ERROR_RESOURCES) &&
                            (err_code != NRF_ERROR_NOT_FOUND))
                        {
                            APP_ERROR_CHECK(err_code);
                        }
                    } while (err_code == NRF_ERROR_RESOURCES);
#endif
                }
                index = 0;
            }
            break;
        case APP_UART_COMMUNICATION_ERROR:
            //APP_ERROR_HANDLER(p_event->data.error_communication);
            break;
        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;
        default:
            break;
    }
}
/**@snippet [Handling the data received over UART] */
/**@brief  Function for initializing the UART module.
 */
/**@snippet [UART Initialization] */
static void uart_init(void)
{
    uint32_t                     err_code;
    //初始化接收队列
    initCommandQueue();
    app_uart_comm_params_t const comm_params =
    {
        .rx_pin_no    = UART_RXD,
        .tx_pin_no    = UART_TXD,
        .rts_pin_no   = RTS_PIN_NUMBER,
        .cts_pin_no   = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
#if defined (UART_PRESENT)
        .baud_rate    = NRF_UART_BAUDRATE_115200
#else
        .baud_rate    = NRF_UARTE_BAUDRATE_115200
#endif
    };
    APP_UART_FIFO_INIT(&comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_handle,
                       APP_IRQ_PRIORITY_LOWEST,
                       err_code);
    APP_ERROR_CHECK(err_code);
}
/**@snippet [UART Initialization] */

命令队列:

//串口命令处理 
typedef enum{
    UART_COMMAND_TYPE_UWB = 0,
    UART_COMMAND_TYPE_BLE,
}TYPE;
typedef enum{
    UWBCODE_SET_MODE = 0,
    UWBCODE_SET_ADDRESS,
}UWBCODE;
#define COMMNAD_QUEUE_LENGTH 128
#define VALUE_MAX_LENGTH 128
/*
type#code#value的值#
0#0#1#
*/
//串口命令
typedef struct uartCommand {
  bool  volatile commandHandleFlg;//0:没有处理,1:已经处理
  uint8_t  type;    //命令类型
  uint8_t  code;    //命令码
  uint8_t   ext1;
  uint32_t value;   //命令值
} __attribute__((packed)) uartCommand;
static uint8_t volatile putIndex = 0;
static uint8_t volatile getIndex = 0;
static uartCommand gUartCommand[COMMNAD_QUEUE_LENGTH];
#define NO_COMMAND -1
static void initCommandQueue(){
    for (int i=0; i<COMMNAD_QUEUE_LENGTH; i++){ 
        gUartCommand[i].commandHandleFlg = true;
            gUartCommand[i].type = 0;
            gUartCommand[i].code = 0;
            gUartCommand[i].value = 0; 
    }
}
static void putCommand(uint8_t  type, uint8_t  code, uint32_t  value){
    int index = putIndex++;
    if (!gUartCommand[index].commandHandleFlg){
        //命令没有处理,日志提示下
        logD("putCommand err,index:%d last type:%d, code:%d", index, gUartCommand[index].type, gUartCommand[index].code);
    }
    gUartCommand[index].commandHandleFlg = false,
        gUartCommand[index].type = type,
        gUartCommand[index].code = code,
        gUartCommand[index].value = value;
		
    if (putIndex >= COMMNAD_QUEUE_LENGTH){
        putIndex = 0;
    }
}
static int getHandlerCommandIndex(){ 
    uint8_t retValue = getIndex;
    if (getIndex >= putIndex){
        return NO_COMMAND;
    }
    getIndex++;
    if (getIndex >= COMMNAD_QUEUE_LENGTH){
        getIndex = 0;
    }
    return retValue;
}
/*
type#code#value的值#
0#0#1#
*/
static int getCommandValue(int startPos, uint8_t *cmdArray, uint8_t cmdLength){
    static uint8_t tempCode[VALUE_MAX_LENGTH] =  {0x00};
    if (cmdArray == NULL || cmdLength < 0 || startPos > cmdLength){
        return 0;
    }
    int i = startPos;
    int index = 0;
    while(cmdArray[i] !='#' && index < cmdLength){
        tempCode[index] = cmdArray[i];
        i++;
        index++;
    }
    if (index > 0 && index < 32){
        //转换成无符号整数
        return convertStrToU32(tempCode, index);
    }
    return 0;
}


>> 2021-0204补充简单架构图

呱牛笔记

业务包括:

基于定时器和中断的简单的事件驱动任务框架

定时器实现

串口消息收发

LED控制

LCD控制

SPI收发 



呱牛笔记



-------------------广告线---------------
项目、合作,欢迎勾搭,邮箱:promall@qq.com


本文为呱牛笔记原创文章,转载无需和我联系,但请注明来自呱牛笔记 ,it3q.com

请先登录后发表评论
  • 最新评论
  • 总共0条评论