|
|
Protothreads 是一种专为资源受限单片机(如 3KB RAM 的 PY32F002B 或 C51单片机)设计的无栈协程(Stackless Coroutine),让你以无阻塞的方式,写出看似阻塞的代码。
其本质是 [size=0.92em]switch-case,在阻塞的地方用__LINE__标识[size=0.92em]记录位置然后返回,第二次进入时,根据记录的位置跳转到中断的行。
[size=0.92em]
- 内存开销极低:不分配独立任务堆栈,每个任务仅需 2~6 字节 RAM 记录断点行号与时间戳。
- 解决痛点:将全屏刷屏、AT 指令握手等长耗时或包含多步延时的阻塞任务切片化,防止主循环卡死导致串口通信丢包。
- 混合架构最佳实践:
- 硬件中断 + RingBuffer:负责串口/SPI 字节接收,保证硬实时零丢包。
- 普通函数:负责微秒级瞬时任务(如喂狗、读 ADC、帧解析),直接调用无额外开销。
- PT 协程:负责需要分步执行或非阻塞延时的流程任务。
几个宏定义和1个小结构体,就实现了全部的功能
/**
* @brief 协程控制块结构体(每个任务仅占用 6 字节 RAM)
*/
typedef struct
{
uint16_t lc; // 记录上次让出时的代码行号 (Local Continuation)
uint32_t timer; // 记录非阻塞延时的起始时间戳
} pt_t;
// 外部全局 1ms 系统节拍(需在 SysTick_Handler 中每 1ms 自增 1)
extern volatile uint32_t sys_tick_ms;
// ==================== Protothreads 核心调度宏 ====================
// 1. 初始化协程控制块
#define PT_INIT(pt) do { (pt)->lc = 0; (pt)->timer = 0; } while(0)
// 2. 协程函数体开始与结束标记
#define PT_BEGIN(pt) switch((pt)->lc) { case 0:
#define PT_END(pt) } (pt)->lc = 0; return 1
// 3. 主动让出 CPU(用于长耗时任务切片,下次调度从下一行继续)
#define PT_YIELD(pt) do { (pt)->lc = __LINE__; return 0; case __LINE__:; } while(0)
// 4. 条件等待(条件不满足时让出 CPU,满足时继续往下执行)
#define PT_WAIT_UNTIL(pt, c) do { (pt)->lc = __LINE__; case __LINE__: if(!(c)) return 0; } while(0)
// 5. 非阻塞延时(单位:ms,基于 uint32_t 无符号减法,完美支持 49.7 天溢出回绕)
#define PT_DELAY(pt, ms) do { \
(pt)->timer = sys_tick_ms; \
PT_WAIT_UNTIL((pt), (sys_tick_ms - (pt)->timer) >= (ms)); \
} while(0)
使用方法如下:
// ==================== 全局变量与协程句柄 ====================
volatile uint32_t sys_tick_ms = 0; // 1ms 系统节拍
static volatile uint8_t need_refresh_flag = 0; // 刷屏触发标志
static pt_t pt_lcd; // 协程1:LCD 分步刷屏
static pt_t pt_led; // 协程2:LED 闪烁
// 协程任务A:LCD 全屏分步刷屏
uint8_t Task_LCD_Refresh(pt_t *pt)
{
// 【关键规范】跨越 PT_YIELD 的循环变量必须加 static,禁止使用普通局部变量!
static uint16_t current_row = 0;
PT_BEGIN(pt);
while (1)
{
// 等待刷屏触发信号
PT_WAIT_UNTIL(pt, need_refresh_flag == 1);
// 将 240 行刷屏拆分为 240 个微切片
for (current_row = 0; current_row < 240; current_row++)
{
// 局部瞬时计算不跨越让出点,可安全使用普通局部变量
{
uint16_t line_bytes = 320 * 2;
(void)line_bytes;
LCD_Draw_One_Line(current_row);
}
// 每刷完 1 行主动让出 CPU,确保通信与看门狗任务即时响应
PT_YIELD(pt);
}
need_refresh_flag = 0;
}
PT_END(pt);
}
// 协程任务B:LED 周期闪烁(亮 200ms,灭 800ms)
uint8_t Task_LED_Blink(pt_t *pt)
{
PT_BEGIN(pt);
while (1)
{
LED_ON();
PT_DELAY(pt, 200); // 非阻塞延时 200ms
LED_OFF();
PT_DELAY(pt, 800); // 非阻塞延时 800ms
}
PT_END(pt);
}
int main(void)
{
Hardware_Init();
PT_INIT(&pt_lcd);
PT_INIT(&pt_led);
while (1)
{
// 混合调度:普通瞬时函数 + 无栈协程
Normal_Task_Watchdog();
Normal_Task_Comm();
Task_LCD_Refresh(&pt_lcd);
Task_LED_Blink(&pt_led);
}
}
Protothreads协程.rar
(4.62 KB, 下载次数: 0)
|
|