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