单片机非阻塞延时实现

  1. 准备工作

1.1 配置定时器

利用单片机外设配置定时器中断 --- 1ms周期(该触发周期决定非阻塞延时精度)

1.2 定义全局变量以及编写中断服务函数

uint32_t delay_tick = 0;
void TIM2_IRQHandler(void){
    if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
        delay_tick++;             //1ms自加一次
        delay_tick&=0xFFFFFFFF;   //防止溢出
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //清除中断标志位
    }
}

uint32_t GetDelayTick(void)
{
    return delay_tick;
}

1.3 定义全局变量以及编写ms精度延时函数

uint32_t tick = 0;
bool IsTimeOut(uint32_t *tick, uint32_t delay_time){
    uint32_t temp = 0;
    /* 获取上一次延时到时时刻到本次调用该函数时间间隔 */
    if(*tick <= GetDelayTick()){
        temp = GetDelayTick() - *tick;
    }
    //delay_tick已经产生溢出
    else{
        temp = 0xFFFFFFFF - *tick + GetDelayTick();
    }
    /* 判断是否已经超过延时时间 */
    if(temp >= delay_time){
        *tick = sysTick;  //记录本次延时到时时刻
         return true;    //延时时间到,返回true
    }
    return false;
}
### 单片机实现非阻塞延时的方法 在单片机开发中,非阻塞延时是一种高效的编程方式,它允许系统在等待某个事件发生的同时还能执行其他任务。这种方法通常通过硬件定时器配合中断机制来实现。 #### 使用硬件定时器和中断的非阻塞延时 一种常见的做法是利用硬件定时器触发周期性中断,在中断服务程序中更新状态变量或标志位。以下是具体实现方法: 1. **初始化定时器** 配置一个硬件定时器(如TIM3),使其按照设定的时间间隔触发中断。例如,设置定时器每10毫秒触发一次中断[^2]。 2. **编写中断服务程序** 在中断服务程序中,可以通过修改全局变量或其他标志位来记录当前的状态变化。以下是一个简单的示例代码: ```c // 定义全局计数器 volatile uint32_t globalCounter = 0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM3) { // 判断是否为TIM3中断 globalCounter++; // 增加计数器值 } } ``` 在此基础上,可以根据`globalCounter`的值判断是否达到所需的延迟时间。例如,如果希望实现500毫秒的延时,则可以检测`globalCounter >= 50`。 --- #### 软件定时器的设计 另一种更灵活的方式是基于软件定时器的设计。这种设计的核心在于维护一组定时任务的数据结构,并定期检查这些任务是否到期。以下是一个典型的例子: 1. **定义数据结构** 创建一个结构体数组,用于存储各个定时任务的信息。例如: ```c typedef struct { uint32_t tickStart; // 记录任务启动时刻 uint32_t durationMs; // 设置的任务持续时间(单位:毫秒) bool isRunning; // 表示任务是否正在运行 } TimerTask; ``` 2. **初始化任务列表** 初始化多个定时任务并将其加入到任务队列中。例如: ```c #define MAX_TASKS 10 TimerTask tasks[MAX_TASKS]; void initTasks() { for (int i = 0; i < MAX_TASKS; ++i) { tasks[i].isRunning = false; } } void startTask(int index, uint32_t durationMs) { if (!tasks[index].isRunning && index < MAX_TASKS) { tasks[index].tickStart = globalCounter; // 当前计数值作为起始点 tasks[index].durationMs = durationMs / 10; // 将毫秒转换为10ms刻度 tasks[index].isRunning = true; } } ``` 3. **轮询任务状态** 在主循环中定期检查每个任务的状态,判断是否有任务已经超时。例如: ```c void checkTasks() { for (int i = 0; i < MAX_TASKS; ++i) { if (tasks[i].isRunning) { if ((globalCounter - tasks[i].tickStart) >= tasks[i].durationMs) { tasks[i].isRunning = false; // 结束该任务 handleTaskCompletion(i); // 处理任务完成后的逻辑 } } } } ``` 4. **处理任务完成的动作** 编写函数`handleTaskCompletion()`,用来指定当某项任务完成后应采取的具体行动。例如点亮LED、发送串口消息等。 --- #### DWT寄存器的应用 除了传统的硬件定时器外,还可以使用ARM Cortex-M系列微控制器内置的DWT(Data Watchpoint and Trace)模块来获取高精度的时间戳。这种方式无需额外配置定时器资源,适合轻量级场景。 1. **启用DWT功能** 启用DWT和CYCCNT寄存器以访问CPU核心时钟计数器: ```c #include "core_cm3.h" void enableDWT() { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 开启跟踪单元 DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // 启用CYCCNT计数器 } ``` 2. **计算延迟时间** 使用`DWT->CYCCNT`读取当前的CPU时钟计数值,并据此推算经过的时间。例如: ```c uint32_t startTime = DWT->CYCCNT; while (((DWT->CYCCNT - startTime) * 1000) / SystemCoreClock < 500) { // 循环直到超过500毫秒 } ``` 注意:此方法依赖于精确的系统时钟频率(SystemCoreClock),因此需确保其值被正确定义。 --- ### 总结 以上介绍了三种实现单片机非阻塞延时的主要方法: - 硬件定时器结合中断; - 软件定时器管理多任务; - ARM Cortex-M平台下DWT寄存器的高效运用。 无论采用哪种方案,都需要合理规划系统架构以及优化资源分配,从而最大化性能表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值