单片机前后台轮询控制框架
在单片机开发中,代码结构往往由“main主函数+一堆if语句+中断服务函数”构成,随着项目复杂化,容易出现混乱、不易维护的代码。为了构建清晰、可扩展、响应及时的系统框架,前后台轮询结构是一种非常实用的设计思路。
本文对前后台架构、如何实现轮询机制,以及如何管理系统任务的执行优先级与效率进行讨论。
一、什么是“前后台轮询”结构?
前后台结构是一种将“实时响应任务”与“非实时轮询任务”分离的单片机编程框架:
前台(中断服务):用于响应“事件驱动”的突发事务,例如:按键中断、串口接收、定时器超时等;
后台(主循环):不断轮询任务标志,根据前台产生的事件决定是否执行相应处理逻辑。
关键思想:中断只设置标志,不做处理;主循环根据标志执行实际任务。
二、最小可行例子:按键处理
volatile uint8_t key_flag = 0;
// 前台:中断服务函数,只置标志
void EXTI0_IRQHandler(void) {
key_flag = 1;
EXTI->PR = 1 << 0;// 清除中断
}
// 后台:主循环中轮询标志并处理
int main(void) {
while (1) {
if (key_flag) {
key_flag = 0;
handle_key_press();// 执行实际业务
}
other_tasks();// 其他周期性任务
}
}
优点:
中断执行极快,避免阻塞;
处理逻辑集中在主循环,方便调试、跟踪;
易于拓展其他任务与调度控制。
三、通用后台轮询任务框架
使用结构体或函数数组,管理多个“轮询任务”,构建简易调度器:
方法1:任务函数数组轮询
typedef void (*TaskFunc)(void);
void task_led(void);
void task_adc(void);
void task_uart(void);
TaskFunc task_list[] = {
task_led,
task_adc,
task_uart,
};
int main(void) {
while (1) {
for (int i = 0; i < sizeof(task_list)/sizeof(TaskFunc); i++) {
task_list();
}
}
}
方法2:任务调度结构体(含时间间隔)
typedef struct {
void (*task_func)(void);
uint16_t interval_ms;
uint32_t last_tick;
} Task_t;
Task_t task_list[] = {
{task_led, 100, 0},
{task_adc, 500, 0},
{task_uart, 0, 0},// 轮询,无延时
};
void scheduler(void) {
uint32_t now = millis();
for (int i = 0; i < sizeof(task_list)/sizeof(Task_t); i++) {
if (task_list.interval_ms == 0 || now - task_list.last_tick >= task_list.interval_ms) {
task_list.last_tick = now;
task_list.task_func();
}
}
}
主循环只需调用 scheduler();,实现多任务周期调度。
四、前后台通信机制:标志、环形缓冲、事件队列
标志变量(适用于单次触发)
volatile uint8_t uart_rx_flag = 0;
void USART_IRQHandler(void) {
uart_rx_flag = 1;
}
环形缓冲区(适用于数据流)
uint8_t rx_buffer;
volatile uint8_t rx_head = 0, rx_tail = 0;
void USART_IRQHandler(void) {
rx_buffer = USART->DR;
rx_head %= 64;
}
事件队列/事件枚举(适用于多任务系统)
typedef enum {
EVENT_NONE,
EVENT_KEY,
EVENT_UART_RX,
EVENT_SENSOR_READY,
} SystemEvent_t;
volatile SystemEvent_t event_queue;
volatile uint8_t evt_head = 0, evt_tail = 0;
void push_event(SystemEvent_t e) {
event_queue = e;
evt_head %= 8;
}
SystemEvent_t pop_event(void) {
if (evt_head == evt_tail) return EVENT_NONE;
SystemEvent_t e = event_queue;
evt_tail %= 8;
return e;
}
主循环中执行任务:
void XXX_IRQHandler(void)
{
push_event(EVENT_XXX);
}
void main(void)
{
while(1){
SystemEvent_t evt = pop_event();
switch (evt) {
case EVENT_KEY: handle_key(); break;
case EVENT_UART_RX: handle_uart(); break;
}
}
}
单片机前后台轮询控制框架
页:
[1]