backlugin 发表于 2025-7-19 08:02

单片机前后台轮询控制框架

      在单片机开发中,代码结构往往由“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;

      }

    }

}


szt1993 发表于 2025-7-31 22:21

单片机前后台轮询控制框架
页: [1]
查看完整版本: 单片机前后台轮询控制框架