做一件事情,先有全局视角,拆分出系统的模块,然后根据不同的模块确认出待办顺序,并从全局的角度考虑多个模块的整合,是否需要公共模块,模块之间的依赖关系是什么样的,然后整合后,逐条去解决!
做软件,其实这两年的成长是最大的,因为转到架构岗,确实是少了一部分之前在开发岗的一些不好的习惯,比方一上来就是细节,一上来就是某个功能点如何实现,现在接触一个事物,或者一个项目,首先考虑的是项目的整体框架,先搭骨架,后填充血肉,把基础打好了,后面细节的问题总是能解决的;
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