本人也工作三个月了,感觉到了状态机的重要,想要学习状态机的设计,目前的想法如下:如果大家有好思路,或者好的教程,可以推荐给我。以下作为分析,有问题大家提出了:
第一版本
举例子, 整体的框架为:
TempTask 协调器 NetTask BatteryTask SafetyTask Worker
1.模块状态定义
每个模块有自己的枚举状态:
c
typedef enum {
TEMP_NORMAL,
TEMP_HIGH
} TempState;
typedef enum {
NET_CONNECTED,
NET_DISCONNECTED
} NetState;
typedef enum {
BATTERY_NORMAL,
BATTERY_LOW,
BATTERY_CHARGING
} BatteryState;
typedef enum {
SAFETY_OK,
SAFETY_ALERT
} SafetyState;
typedef enum {
GLOBAL_RUN,
GLOBAL_COOLING,
GLOBAL_WAIT_NETWORK,
GLOBAL_LOW_POWER,
GLOBAL_SAFETY_ALERT
} GlobalState;
2.每个模块的任务和状态机
温度任务 TempTask
c
void TempTask(void *pvParameters) {
TempState tempState = TEMP_NORMAL;
for (;;) {
int temp = getTemperatureSensor();
if (temp > 80) tempState = TEMP_HIGH;
else tempState = TEMP_NORMAL;
xQueueOverwrite(tempQueue, &tempState);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
网络任务 NetTask
c
void NetTask(void *pvParameters) {
NetState netState = NET_CONNECTED;
for (;;) {
if (!isNetworkConnected()) netState = NET_DISCONNECTED;
else netState = NET_CONNECTED;
xQueueOverwrite(netQueue, &netState);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
电池任务 BatteryTask
c
void BatteryTask(void *pvParameters) {
BatteryState batteryState = BATTERY_NORMAL;
for (;;) {
int level = getBatteryLevel();
if (level < 20) batteryState = BATTERY_LOW;
else if (isCharging()) batteryState = BATTERY_CHARGING;
else batteryState = BATTERY_NORMAL;
xQueueOverwrite(batteryQueue, &batteryState);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
安全任务 SafetyTask
c
void SafetyTask(void *pvParameters) {
SafetyState safetyState = SAFETY_OK;
for (;;) {
if (!isSafetyCoverClosed()) safetyState = SAFETY_ALERT;
else safetyState = SAFETY_OK;
xQueueOverwrite(safetyQueue, &safetyState);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
3.中央协调器任务 CoordinatorTask
逻辑:
- 收集所有模块状态
- 基于优先级做全局状态判断(先安全 → 再电量 → 再温度 → 再网络)
- 将全局状态发送给 WorkerTask
c
void CoordinatorTask(void *pvParameters) {
GlobalState globalState = GLOBAL_RUN;
TempState tempState;
NetState netState;
BatteryState batteryState;
SafetyState safetyState;
for (;;) {
xQueuePeek(tempQueue, &tempState, 0);
xQueuePeek(netQueue, &netState, 0);
xQueuePeek(batteryQueue, &batteryState, 0);
xQueuePeek(safetyQueue, &safetyState, 0);
if (safetyState == SAFETY_ALERT) {
globalState = GLOBAL_SAFETY_ALERT;
}
else if (batteryState == BATTERY_LOW) {
globalState = GLOBAL_LOW_POWER;
}
else if (tempState == TEMP_HIGH) {
globalState = GLOBAL_COOLING;
}
else if (netState == NET_DISCONNECTED) {
globalState = GLOBAL_WAIT_NETWORK;
}
else {
globalState = GLOBAL_RUN;
}
xQueueOverwrite(globalQueue, &globalState);
vTaskDelay(pdMS_TO_TICKS(200));
}
}
4.主业务任务 WorkerTask
c
void WorkerTask(void *pvParameters) {
GlobalState globalState;
for (;;) {
xQueueReceive(globalQueue, &globalState, portMAX_DELAY);
switch (globalState) {
case GLOBAL_RUN:
printf("[Worker] 系统运行正常\n");
break;
case GLOBAL_COOLING:
printf("[Worker] 冷却模式开启\n");
break;
case GLOBAL_WAIT_NETWORK:
printf("[Worker] 等待网络连接...\n");
break;
case GLOBAL_LOW_POWER:
printf("[Worker] 电量低,进入节能模式\n");
break;
case GLOBAL_SAFETY_ALERT:
printf("[Worker] 安全警告!停止所有运行\n");
break;
}
}
}
5.系统初始化 main()
c
int main(void) {
tempQueue = xQueueCreate(1, sizeof(TempState));
netQueue = xQueueCreate(1, sizeof(NetState));
batteryQueue = xQueueCreate(1, sizeof(BatteryState));
safetyQueue = xQueueCreate(1, sizeof(SafetyState));
globalQueue = xQueueCreate(1, sizeof(GlobalState));
xTaskCreate(TempTask, "Temp", 512, NULL, 2, NULL);
xTaskCreate(NetTask, "Net", 512, NULL, 2, NULL);
xTaskCreate(BatteryTask, "Battery", 512, NULL, 2, NULL);
xTaskCreate(SafetyTask, "Safety", 512, NULL, 2, NULL);
xTaskCreate(CoordinatorTask, "Coordinator", 512, NULL, 2, NULL);
xTaskCreate(WorkerTask, "Worker", 512, NULL, 2, NULL);
vTaskStartScheduler();
for (;;);
}
发现的缺点:
- 在中央协调器任务中,globalState只能响应一次,且会覆盖上一个的值。如果多个任务都要切换,那么该怎么办呢?
- 新增模块需要改中央协调器代码,不够解耦,可以用事件发布订阅(Publish/Subscribe),中央协调器不用硬改
第二版本
1.Pub/Sub设计思路
原理:
- 发布者(Publisher):某模块检测到状态变化后,发布一个事件到公共事件队列(EventQueue)
- 订阅者(Subscriber):任何任务都可以从公共事件队列"订阅"事件并处理
好处:
- 减少队列数量:所有事件走一个总队列
- 模块解耦:发布者不关心谁会处理事件
- 易扩展:新增模块只要发布事件;新增订阅者只要监听公共队列
- 类似RTOS Event Bus:在大型系统里是常见模式
2.事件结构定义
我们先定义一个结构体来表示事件:
c
typedef enum {
MODULE_TEMP,
MODULE_NET,
MODULE_BATTERY,
MODULE_SAFETY
} ModuleID;
typedef enum {
EVENT_TEMP_NORMAL,
EVENT_TEMP_HIGH,
EVENT_NET_CONNECTED,
EVENT_NET_DISCONNECTED,
EVENT_BATTERY_NORMAL,
EVENT_BATTERY_LOW,
EVENT_BATTERY_CHARGING,
EVENT_SAFETY_OK,
EVENT_SAFETY_ALERT
} EventType;
typedef struct {
ModuleID module; // 事件来自哪个模块
EventType event; // 事件类型
uint32_t timestamp; // 时间戳(可选)
int value; // 额外数据(例如温度值、电量等)
} EventMessage;
3.公共事件总线(队列)
我们用一个全局队列 eventQueue:
c
QueueHandle_t eventQueue;
因为是公共队列,所有模块都把事件扔进同一个地方。
4.1 发布者示例(温度任务)
c
void TempTask(void *pvParameters) {
for (;;) {
EventMessage msg;
msg.module = MODULE_TEMP;
msg.value = getTemperatureSensor();
msg.timestamp = xTaskGetTickCount();
if (msg.value > 80)
msg.event = EVENT_TEMP_HIGH;
else
msg.event = EVENT_TEMP_NORMAL;
xQueueSend(eventQueue, &msg, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
- 读取传感器数据
- 判断事件类型(高温或正常)
- 发布事件到总队列
4.2 发布者示例(网络任务)
c
void NetTask(void *pvParameters) {
for (;;) {
EventMessage msg;
msg.module = MODULE_NET;
msg.timestamp = xTaskGetTickCount();
if (isNetworkConnected())
msg.event = EVENT_NET_CONNECTED;
else
msg.event = EVENT_NET_DISCONNECTED;
msg.value = 0; // 网络状态用不到value
xQueueSend(eventQueue, &msg, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
5. 订阅者(中央协调器)
c
void CoordinatorTask(void *pvParameters) {
GlobalState globalState = GLOBAL_RUN;
for (;;) {
EventMessage msg;
if (xQueueReceive(eventQueue, &msg, portMAX_DELAY)) {
// 根据事件决定全局状态
switch (msg.module) {
case MODULE_TEMP:
if (msg.event == EVENT_TEMP_HIGH)
globalState = GLOBAL_COOLING;
else
globalState = GLOBAL_RUN;
break;
case MODULE_NET:
if (msg.event == EVENT_NET_DISCONNECTED)
globalState = GLOBAL_WAIT_NETWORK;
break;
case MODULE_BATTERY:
if (msg.event == EVENT_BATTERY_LOW)
globalState = GLOBAL_LOW_POWER;
break;
case MODULE_SAFETY:
if (msg.event == EVENT_SAFETY_ALERT)
globalState = GLOBAL_SAFETY_ALERT;
break;
}
// 发布全局状态到另一个管理队列(或直接调用 Worker)
xQueueOverwrite(globalQueue, &globalState);
}
}
}
6.Worker任务(业务执行)
c
void WorkerTask(void *pvParameters) {
GlobalState globalState;
for (;;) {
xQueueReceive(globalQueue, &globalState, portMAX_DELAY);
switch (globalState) {
case GLOBAL_RUN:
printf("[Worker] 正常运行\n");
break;
case GLOBAL_COOLING:
printf("[Worker] 冷却模式开启\n");
break;
case GLOBAL_WAIT_NETWORK:
printf("[Worker] 等待网络连接...\n");
break;
case GLOBAL_LOW_POWER:
printf("[Worker] 电量低,进入节能模式\n");
break;
case GLOBAL_SAFETY_ALERT:
printf("[Worker] 安全警告!停止运行\n");
break;
}
}
}
7. 系统初始化 main()
c
int main(void) {
eventQueue = xQueueCreate(16, sizeof(EventMessage));
globalQueue = xQueueCreate(1, sizeof(GlobalState));
xTaskCreate(TempTask, "Temp", 512, NULL, 2, NULL);
xTaskCreate(NetTask, "Net", 512, NULL, 2, NULL);
xTaskCreate(BatteryTask, "Battery", 512, NULL, 2, NULL);
xTaskCreate(SafetyTask, "Safety", 512, NULL, 2, NULL);
xTaskCreate(CoordinatorTask, "Coordinator", 512, NULL, 2, NULL);
xTaskCreate(WorkerTask, "Worker", 512, NULL, 2, NULL);
vTaskStartScheduler();
for (;;);
}
缺点
- 单个状态机是没有什么问题,但是如果多个任务内部也有相应的状态机呢?
第二版plus
这里主要是增加一个优先级的情况。主要是为了根据不同的优先级,允许事件插队。整体的还是和上版一样的,主要是
1. 数据结构
EventMessage 结构中加上优先级字段:
c
typedef enum {
EVENT_PRI_LOW = 0, //这里
EVENT_PRI_NORMAL,
EVENT_PRI_HIGH
} EventPriority;
typedef enum {
MODULE_TEMP,
MODULE_NET,
MODULE_BATTERY,
MODULE_SAFETY
} ModuleID;
typedef enum {
EVENT_TEMP_NORMAL,
EVENT_TEMP_HIGH,
EVENT_NET_CONNECTED,
EVENT_NET_DISCONNECTED,
EVENT_BATTERY_NORMAL,
EVENT_BATTERY_LOW,
EVENT_BATTERY_CHARGING,
EVENT_SAFETY_OK,
EVENT_SAFETY_ALERT
} EventType;
typedef struct {
ModuleID module; // 来源模块
EventType event; // 事件类型
EventPriority priority; // 事件优先级 这里
uint32_t timestamp; // 时间戳
int value; // 额外数据
} EventMessage;
2. 全局队列定义
c
QueueHandle_t eventQueueHigh;
QueueHandle_t eventQueueNormal;
QueueHandle_t eventQueueLow;
QueueHandle_t globalQueue; // 协调器->Worker的状态队列
3. 发布者统一接口
任何模块在发布事件时只需调用这个函数,选择优先级即可:
c
void EventBus_Publish(EventMessage *msg) {
switch (msg->priority) {
case EVENT_PRI_HIGH:
xQueueSend(eventQueueHigh, msg, portMAX_DELAY);
break;
case EVENT_PRI_NORMAL:
xQueueSend(eventQueueNormal, msg, portMAX_DELAY);
break;
case EVENT_PRI_LOW:
default:
xQueueSend(eventQueueLow, msg, portMAX_DELAY);
break;
}
}
4. 发布者任务示例
基于3来使用,给出示例:
温度任务
高温事件 → 高优,高温正常 → 低优
c
void TempTask(void *pvParameters) {
for (;;) {
EventMessage msg;
msg.module = MODULE_TEMP;
msg.value = getTemperatureSensor();
msg.timestamp = xTaskGetTickCount();
if (msg.value > 80) {
msg.event = EVENT_TEMP_HIGH;
msg.priority = EVENT_PRI_HIGH;
} else {
msg.event = EVENT_TEMP_NORMAL;
msg.priority = EVENT_PRI_LOW;
}
EventBus_Publish(&msg);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
网络任务
断网 → 中优,正常连接 → 低优
c
void NetTask(void *pvParameters) {
for (;;) {
EventMessage msg;
msg.module = MODULE_NET;
msg.timestamp = xTaskGetTickCount();
if (isNetworkConnected()) {
msg.event = EVENT_NET_CONNECTED;
msg.priority = EVENT_PRI_LOW;
} else {
msg.event = EVENT_NET_DISCONNECTED;
msg.priority = EVENT_PRI_NORMAL;
}
EventBus_Publish(&msg);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
5. 协调器订阅逻辑(优先取高优队列)
c
void CoordinatorTask(void *pvParameters) {
GlobalState globalState = GLOBAL_RUN;
EventMessage msg;
for (;;) {
// 按优先级顺序取事件
if (xQueueReceive(eventQueueHigh, &msg, 0) == pdPASS ||
xQueueReceive(eventQueueNormal, &msg, 0) == pdPASS ||
xQueueReceive(eventQueueLow, &msg, 0) == pdPASS) {
switch (msg.module) {
case MODULE_TEMP:
if (msg.event == EVENT_TEMP_HIGH)
globalState = GLOBAL_COOLING;
else if (globalState == GLOBAL_COOLING)
globalState = GLOBAL_RUN;
break;
case MODULE_NET:
if (msg.event == EVENT_NET_DISCONNECTED)
globalState = GLOBAL_WAIT_NETWORK;
else if (globalState == GLOBAL_WAIT_NETWORK)
globalState = GLOBAL_RUN;
break;
case MODULE_BATTERY:
if (msg.event == EVENT_BATTERY_LOW)
globalState = GLOBAL_LOW_POWER;
else if (globalState == GLOBAL_LOW_POWER)
globalState = GLOBAL_RUN;
break;
case MODULE_SAFETY:
if (msg.event == EVENT_SAFETY_ALERT)
globalState = GLOBAL_SAFETY_ALERT;
break;
}
// 发布状态给 Worker
xQueueOverwrite(globalQueue, &globalState);
}
vTaskDelay(pdMS_TO_TICKS(50)); // 加快响应速度
}
}
6. Worker任务保持原有逻辑
c
void WorkerTask(void *pvParameters) {
GlobalState globalState;
for (;;) {
xQueueReceive(globalQueue, &globalState, portMAX_DELAY);
switch (globalState) {
case GLOBAL_RUN:
printf("[Worker] 正常运行");
break;
case GLOBAL_COOLING:
printf("[Worker] 冷却模式开启");
break;
case GLOBAL_WAIT_NETWORK:
printf("[Worker] 等待网络连接...\n");
break;
case GLOBAL_LOW_POWER:
printf("[Worker] 电量低,进入节能模式");
break;
case GLOBAL_SAFETY_ALERT:
printf("[Worker] 安全警告!停止运行");
break;
}
}
}
7.系统初始化
c
int main(void) {
eventQueueHigh = xQueueCreate(10, sizeof(EventMessage));
eventQueueNormal = xQueueCreate(10, sizeof(EventMessage));
eventQueueLow = xQueueCreate(10, sizeof(EventMessage));
globalQueue = xQueueCreate(1, sizeof(GlobalState));
xTaskCreate(TempTask, "Temp", 512, NULL, 2, NULL);
xTaskCreate(NetTask, "Net", 512, NULL, 2, NULL);
xTaskCreate(BatteryTask, "Battery", 512, NULL, 2, NULL);
xTaskCreate(SafetyTask, "Safety", 512, NULL, 2, NULL);
xTaskCreate(CoordinatorTask, "Coordinator", 512, NULL, 3, NULL);
xTaskCreate(WorkerTask, "Worker", 512, NULL, 2, NULL);
vTaskStartScheduler();
for (;;);
}
改造结果
- 三个队列:eventQueueHigh、eventQueueNormal、eventQueueLow,按优先级明确分流
- 发布接口简单:只需决定 priority 即会丢到对应队列
- 消费顺序明确:Coordinator 始终先取高优队列 → 中优 → 低优
-
性能可控:不需排序,也不会阻塞高优事件
到这里了,以后再做想法优化,同时如果大家有好的思路可以分享