单片机裸机中的非阻塞延时:任务分离计数器法实战

发布于:2025-06-25 ⋅ 阅读:(21) ⋅ 点赞:(0)

        在裸机编程中,我们常常需要定时执行一些任务,比如让LED以固定频率闪烁、周期性检测按键输入等。这类需求看似简单,但处理方式不当(例如使用阻塞延时)就会导致系统“卡顿”或响应迟钝。

        本文将介绍一种推荐的裸机延时结构:任务分离计数器法(非阻塞延时),并通过LED闪烁与按键响应为例,展示其实际应用效果。


一、阻塞延时与非阻塞延时的区别

阻塞延时

led_on();
delay_ms(500);  // 阻塞CPU
led_off();
delay_ms(500);

缺点:

  • delay_ms期间CPU无法做任何其他事情

  • 无法响应其他任务,比如按键、串口等

  • 系统实时性差,功能耦合


非阻塞延时

        非阻塞延时通过计时判断是否到了任务的执行时间,而不会让CPU等待。这样可以在主循环中“轮询执行”多个任务,各任务之间互不干扰。

两种实现方式:

方法 描述
对比全局时间差 比如 if(sys_tick - last_tick >= 500)
分离计数器法(推荐) 每个任务维护独立计数变量,如 count_led++,周期到了就触发

本文使用任务分离计数器法,逻辑更清晰,易于扩展和维护。


二、设计思路概览

  • 使用SysTick定时器或硬件定时器生成1ms节拍;

  • 为每个任务分配独立周期变量计数器变量

  • 在中断中对各自的计数器进行自增;

  • 在主循环中判断是否达到设定周期。


三、变量与定时器配置

全局变量定义:

#include <stdint.h>

volatile uint16_t count_led = 0;  // LED计数器
volatile uint16_t count_key = 0;  // 按键计数器

uint16_t time_led = 500;  // LED周期(ms)
uint16_t time_key = 20;   // 按键扫描周期(ms)

 SysTick中断配置(以STM32为例):

void SysTick_Handler(void)
{
    count_led++;
    count_key++;
}

void systick_init(void)
{
    SysTick_Config(SystemCoreClock / 1000); // 配置1ms节拍
}

如果使用的是其他平台,可替换为普通定时器中断,每1ms中断一次即可。


四、LED任务(非阻塞控制)

void led_task(void)
{
    if (count_led >= time_led)
    {
        count_led = 0;
        led_toggle();  // 用户自定义函数:翻转LED状态
    }
}

五、按键任务(非阻塞扫描 + 消抖)

void key_task(void)
{
    if (count_key >= time_key)
    {
        count_key = 0;

        static uint8_t last_key = 1;
        uint8_t key_now = read_key();  // 用户自定义函数,读取按键引脚

        if (last_key == 1 && key_now == 0)
        {
            // 按键按下动作
            led_toggle();  // 示例:按键触发LED翻转
        }

        last_key = key_now;
    }
}

六、主函数结构

int main(void)
{
    system_init();     // 初始化系统时钟、IO、LED、按键等
    systick_init();    // 配置SysTick为1ms节拍

    while (1)
    {
        led_task();     // LED非阻塞控制
        key_task();     // 按键非阻塞扫描
        // 可继续添加其他任务...
    }
}

七、方法优势总结

特性 表现
多任务并行 各任务独立运行
CPU效率 无阻塞等待
可扩展性 添加任务只需新增变量和判断
逻辑清晰 每个任务结构简单直观

每个任务都有自己的计数器和周期变量 在SysTick中断(定时器中断)中统一更新 在主循环中轮询判断执行 高效、灵活、适合各种裸机场景


可扩展应用

  • 增加多个任务(如蜂鸣器、OLED刷新、传感器采集);

  • 周期变量可动态调整,实现可调频率

  • 可封装成任务结构体,实现裸机“轻量任务调度器”。


总结一句话:

用非阻塞延时 + 任务分离计数器,就能在裸机系统中实现“并行任务”的控制结构,轻松应对多个定时控制需求。