第六章:RTOS 任务 —— 任务逻辑与并发的 TDD 路径

这一章我们聊聊嵌入式开发的"高级阶段":RTOS(实时操作系统)。

很多开发者认为 RTOS 的代码没法测,因为涉及任务调度、抢占和阻塞。但在 TDD 的世界里,我们有一个黄金法则:测试任务的"逻辑主体",而不是测试调度器本身。

6.1 核心思想:剥离 OS 依赖

如果你的代码里写满了 xQueueSend 或 osDelay,它在 PC 上依然跑不起来。 策略: 将 RTOS 的 API 视为一种特殊的"底层硬件",通过 Mock 掉信号量、队列和延时函数,我们可以验证任务在各种同步场景下的行为。

6.2 接口抽象:OS_Interface.h

不要直接在业务代码里包含 FreeRTOS.h。我们定义一个中间层:

// OS_Interface.h

#ifndef OS_INTERFACE_H

#define OS_INTERFACE_H

#include <stdint.h>

#include <stdbool.h>

bool OS_QueueReceive(void* msg, uint32_t timeout);

void OS_SignalSemaphore(void);

void OS_Delay(uint32_t ms);

#endif

6.3 实战:测试一个"数据处理任务"

需求: 任务平时处于阻塞状态,等待队列消息。收到数据后,进行计算并根据结果判断是否触发紧急信号。

被测代码 (Data_Task.c):

void Data_Processing_Task_Loop(void) {

uint16_t sensor_data;

// 阻塞等待队列

if (OS_QueueReceive(&sensor_data, 100)) {

if (sensor_data > 900) {

OS_SignalSemaphore(); // 触发紧急信号

}

}

}

编写测试 (test_Data_Task.c): 我们要模拟队列接收成功和失败两种情况。

#include "unity.h"

#include "mock_OS_Interface.h"

#include "Data_Task.h"

// 测试场景 1:收到正常数据

void test_Task_ShouldDoNothing_WhenDataIsNormal(void) {

uint16_t fake_data = 500;

// 模拟队列收到数据 500

OS_QueueReceive_ExpectAnyArgsAndReturn(true);

OS_QueueReceive_ReturnArrayThruPtr_msg(&fake_data, 1);

// 执行一次任务循环

Data_Processing_Task_Loop();

// 结果:没有任何信号量被触发(隐含验证)

}

// 测试场景 2:收到异常高数据

void test_Task_ShouldSignalEmergency_WhenDataExceedsThreshold(void) {

uint16_t alarm_data = 950;

// 模拟队列收到 950

OS_QueueReceive_ExpectAnyArgsAndReturn(true);

OS_QueueReceive_ReturnArrayThruPtr_msg(&alarm_data, 1);

// 预期:信号量会被释放

OS_SignalSemaphore_Expect();

Data_Processing_Task_Loop();

}

6.4 进阶:如何测试"并发"和"竞态"?

在单机单元测试中,我们其实无法模拟真正的多线程抢占,但我们可以模拟"顺序交织"。

  • 测试重入性:连续调用同一个函数,利用 Mock 改变第二次调用的返回状态(比如模拟信号量已被占用)。

  • 测试超时逻辑:

void test_Task_ShouldRetry_WhenQueueTimeout(void) {

// 第一次模拟超时

OS_QueueReceive_ExpectAnyArgsAndReturn(false);

// 第二次模拟成功

OS_QueueReceive_ExpectAnyArgsAndReturn(true);

Data_Processing_Task_Loop();

Data_Processing_Task_Loop();

}

6.5 本章核心:任务即函数

注意:

  1. 打破无限循环 :为了测试,不要把 while(1) 写死在任务逻辑函数里。可以写成 void Task_Step(void),由外部循环调用。这样测试用例才能控制执行步数。

  2. 测试粒度:不要试图测试 RTOS 的调度算法,我们要测的是"我的业务逻辑在拿到/拿不到资源时的反应"。

本章小结

现在,你已经掌握了如何将 RTOS 任务降维打击,转变为可控的单线程逻辑。这种方式能帮你规避 80% 的死锁和同步 Bug。

相关推荐
不悔哥3 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
XiHongShi20163 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
虎头金猫3 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
优信电子3 天前
AIT1001 非接触式红外测温模块 STM32 驱动开发实战:从引脚到代码全解析
stm32·嵌入式·温度传感器·红外测温·非接触式测温·ait1001
深圳老胡3 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
wuyk5553 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网
女神下凡3 天前
嵌入式设计的各种存储芯片的硬件/软件设计规范,非常全面。
arm开发·单片机·嵌入式硬件
梅梅9663 天前
一款mipi转lvds的lcd调试(LT8912B)
单片机·计算机外设·电脑·显示器·pd芯片
开发笔记-阿牛3 天前
蓝牙音乐灯拆解,又一次拆到蓝牙音乐灯芯片CK6865L
人工智能·单片机·嵌入式硬件·音视频·音频
从零开始的嵌入式之旅3 天前
day47
arm开发·经验分享·笔记·嵌入式硬件