当前位置: 首页 > news >正文

STM32定时器与延时系统完整笔记

文章目录

    • 一、延时基础概念
      • 1.1 延时精度分级
      • 1.2 延时实现方法对比
    • 二、裸机环境延时实现
      • 2.1 微秒级延时实现
      • 2.2 性能问题分析
      • 2.3 非阻塞延时方案
    • 三、硬件定时器限制
      • 3.1 寄存器位宽限制
      • 3.2 最大延时时间计算
      • 3.3 超长延时解决方案
    • 四、RTOS环境下的延时
      • 4.1 RTOS延时机制原理
      • 4.2 RTOS中的us级延时
    • 五、多任务环境的线程安全问题
      • 5.1 全局变量竞争问题
      • 5.2 多任务延时管理方案
    • 六、最佳实践总结
      • 6.1 延时方法选择决策树
      • 6.2 性能优化建议
      • 6.3 常见应用场景示例
      • 6.4 调试技巧
    • 七、核心要点回顾

一、延时基础概念

1.1 延时精度分级

纳秒级(ns):  硬件层面,软件难以实现
微秒级(us):  1-999us,需要硬件定时器或精确的软件延时
毫秒级(ms):  1-999ms,系统tick或定时器实现
秒级(s):     RTOS延时或RTC实现

1.2 延时实现方法对比

方法精度CPU占用适用场景优缺点
软件空循环差(±20%)100%极短延时<10us简单但浪费CPU
SysTick好(±1%)100%裸机ms级延时占用系统定时器
硬件定时器最好(<1%)100%/0%us到s级延时精确但占用定时器资源
RTOS延时取决于tick0%ms级以上不浪费CPU,可多任务
RTC秒级0%长延时省电,精度一般

二、裸机环境延时实现

2.1 微秒级延时实现

// 方法1:软件延时(精度差,受编译优化影响)
void delay_us_software(uint32_t us) {uint32_t ticks = us * (SystemCoreClock / 1000000);while(ticks--) {__NOP();  // 空操作,防止编译器优化}
}// 方法2:SysTick定时器(精度好,但占用系统资源)
void delay_us_systick(uint32_t us) {uint32_t ticks = us * (SystemCoreClock / 1000000);SysTick->LOAD = ticks - 1;       // 设置重装载值SysTick->VAL = 0;                 // 清除当前值SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;  // 启动// 等待计数到0while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));SysTick->CTRL = 0;                // 关闭SysTick
}// 方法3:硬件定时器(最精确,推荐)
void delay_us_timer(uint32_t us) {// 假设TIM2已配置为1MHz (1us per tick)TIM2->CNT = 0;        // 清零计数器TIM2->ARR = us - 1;   // 设置自动重装载值TIM2->CR1 |= TIM_CR1_CEN;  // 启动定时器// 等待更新事件while(!(TIM2->SR & TIM_SR_UIF));TIM2->SR &= ~TIM_SR_UIF;    // 清除标志TIM2->CR1 &= ~TIM_CR1_CEN;  // 停止定时器
}

2.2 性能问题分析

// 阻塞式延时的问题
void blocking_delay_ms(uint32_t ms) {for(uint32_t i = 0; i < ms * 8000; i++) {__NOP();  // CPU利用率100%,但有效工作0%}
}// 性能损失:
// - CPU占用率:100%
// - 有效工作:0%
// - 功耗:最高
// - 响应性:除中断外无法响应其他事件

2.3 非阻塞延时方案

// 使用系统tick计数器
volatile uint32_t sys_tick_ms = 0;void SysTick_Handler(void) {sys_tick_ms++;
}// 非阻塞延时检查
typedef struct {uint32_t start_time;uint32_t delay_ms;
} soft_timer_t;uint8_t is_timeout(soft_timer_t *timer) {return (sys_tick_ms - timer->start_time) >= timer->delay_ms;
}// 使用示例
void main_loop(void) {soft_timer_t led_timer = {sys_tick_ms, 500};while(1) {if(is_timeout(&led_timer)) {LED_Toggle();led_timer.start_time = sys_tick_ms;}// 可以执行其他任务Process_UART();Process_ADC();}
}

三、硬件定时器限制

3.1 寄存器位宽限制

// STM32F103定时器类型
定时器类型    位宽    ARR最大值         最大计数
TIM1         1665,535           65,535
TIM2         324,294,967,29543亿
TIM3,TIM4    1665,535           65,535// 定时器延时计算公式
延时时间 = (PSC + 1) × (ARR + 1) / TIMxCLK// 其中:
PSC: 预分频器(16: 0-65535)
ARR: 自动重装载值
TIMxCLK: 定时器输入时钟(最高72MHz)

3.2 最大延时时间计算

// 16位定时器最大延时
// 条件:72MHz系统时钟,最大预分频
最大延时 = 65536 × 65536 / 72MHz = 59.65// 32位定时器最大延时  
// 条件:72MHz系统时钟,最大预分频
最大延时 = 65536 × 4294967296 / 72MHz = 45.25// 实际配置示例
void config_timer_for_1ms(void) {// 72MHz / 72 = 1MHz (1us per tick)TIM3->PSC = 72 - 1;     // 1000us = 1msTIM3->ARR = 1000 - 1;   
}

3.3 超长延时解决方案

// 方案1:自动调整预分频器
void adaptive_delay_us(uint32_t us) {uint32_t prescaler, arr;if(us <= 65535) {prescaler = 72;      // 1us分辨率arr = us;} else if(us <= 655350) {prescaler = 720;     // 10us分辨率arr = us / 10;} else if(us <= 6553500) {prescaler = 7200;    // 100us分辨率arr = us / 100;} else {return;  // 超出范围}TIM3->PSC = prescaler - 1;TIM3->ARR = arr - 1;TIM3->CNT = 0;TIM3->CR1 |= TIM_CR1_CEN;while(!(TIM3->SR & TIM_SR_UIF));TIM3->SR &= ~TIM_SR_UIF;
}// 方案2:分段延时
void long_delay_ms(uint32_t ms) {const uint32_t MAX_DELAY = 60000;  // 60秒while(ms > 0) {uint32_t current = (ms > MAX_DELAY) ? MAX_DELAY : ms;delay_ms_timer(current);ms -= current;}
}// 方案3:使用RTC(适合超长延时)
void rtc_delay_hours(uint32_t hours) {uint32_t target = RTC_GetCounter() + (hours * 3600);while(RTC_GetCounter() < target) {PWR_EnterSTOPMode();  // 进入低功耗}
}

四、RTOS环境下的延时

4.1 RTOS延时机制原理

// RTOS延时不是空转,而是任务切换
void vTaskDelay(TickType_t xTicksToDelay) {// 1. 将当前任务移出就绪列表// 2. 加入延时列表// 3. 触发任务调度,CPU切换到其他任务// 4. 延时到期后,任务重新就绪
}// 对比:
// 裸机delay: CPU利用率0%,阻塞
// RTOS delay: CPU利用率90%+,非阻塞

4.2 RTOS中的us级延时

// RTOS不适合us级延时的原因:
// 1. 系统tick通常是1ms
// 2. 任务切换开销约100-200个时钟周期(1.4-2.8us @72MHz)// 解决方案:混合延时
void rtos_delay_us(uint32_t us) {if(us < 100) {// 短延时直接阻塞taskENTER_CRITICAL();delay_us_timer(us);taskEXIT_CRITICAL();} else if(us < 1000) {// 中等延时用硬件定时器delay_us_timer(us);} else {// 长延时用RTOSvTaskDelay(pdMS_TO_TICKS(us / 1000));}
}

五、多任务环境的线程安全问题

5.1 全局变量竞争问题

// ❌ 错误示例:全局变量被多任务共享
volatile uint32_t delay_counter;  // 危险!void Task1() { delay_counter = 100; }  // 任务1设置
void Task2() { delay_counter = 200; }  // 任务2覆盖
// 结果:混乱// ✅ 正确方案1:互斥锁保护
SemaphoreHandle_t timer_mutex;void safe_delay(uint32_t ms) {xSemaphoreTake(timer_mutex, portMAX_DELAY);// 使用定时器xSemaphoreGive(timer_mutex);
}// ✅ 正确方案2:任务本地存储
typedef struct {uint32_t delay_count;TaskHandle_t task;
} task_delay_t;task_delay_t delays[MAX_TASKS];  // 每个任务独立

5.2 多任务延时管理方案

// 完整的多任务延时管理器
typedef struct {TaskHandle_t task;uint32_t remaining_ms;uint8_t active;
} delay_entry_t;#define MAX_DELAYS 10
delay_entry_t delay_list[MAX_DELAYS];
SemaphoreHandle_t list_mutex;// 定时器中断(每1ms)
void TIM3_IRQHandler(void) {if(TIM3->SR & TIM_SR_UIF) {TIM3->SR &= ~TIM_SR_UIF;BaseType_t xHigherPriorityTaskWoken = pdFALSE;for(int i = 0; i < MAX_DELAYS; i++) {if(delay_list[i].active) {if(--delay_list[i].remaining_ms == 0) {// 通知任务vTaskNotifyGiveFromISR(delay_list[i].task,&xHigherPriorityTaskWoken);delay_list[i].active = 0;}}}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);}
}// 任务安全的延时函数
void multi_task_delay(uint32_t ms) {TaskHandle_t self = xTaskGetCurrentTaskHandle();int slot = -1;// 分配延时槽xSemaphoreTake(list_mutex, portMAX_DELAY);for(int i = 0; i < MAX_DELAYS; i++) {if(!delay_list[i].active) {delay_list[i].task = self;delay_list[i].remaining_ms = ms;delay_list[i].active = 1;slot = i;break;}}xSemaphoreGive(list_mutex);if(slot >= 0) {// 等待通知ulTaskNotifyTake(pdTRUE, portMAX_DELAY);} else {// 回退到vTaskDelayvTaskDelay(pdMS_TO_TICKS(ms));}
}

六、最佳实践总结

6.1 延时方法选择决策树

if (需要延时) {if (延时 < 10us) {使用NOP循环或硬件定时器;} else if (延时 < 100us) {if (在RTOS中) {考虑是否真的需要如此精确的延时;如必须:使用临界区+硬件定时器;} else {使用硬件定时器;}} else if (延时 < 1ms) {if (在RTOS中) {评估使用vTaskDelay(1)是否足够;} else {使用定时器中断;}} else if (延时 < 1s) {if (在RTOS中) {使用vTaskDelay();} else {使用定时器中断+计数器;}} else {使用RTC或软件定时器;}
}

6.2 性能优化建议

// 1. 避免阻塞延时while(delay--);vTaskDelay() 或 定时器中断// 2. 合理使用临界区
短延时(<100us): 可以使用临界区
长延时(>100us): 避免关中断太久// 3. 资源分配策略
优先级高的任务: 分配独立定时器
普通任务: 使用共享定时器或软件定时器
后台任务: 使用vTaskDelay// 4. 功耗优化
短延时: 忙等待可接受
长延时: 使用WFI或低功耗模式
超长延时: 使用RTC+深度睡眠

6.3 常见应用场景示例

// 场景1:I2C/SPI时序(us级)
void i2c_delay(void) {delay_us_timer(5);  // 硬件定时器保证精度
}// 场景2:LED闪烁(ms级)
void led_task(void *param) {while(1) {LED_Toggle();vTaskDelay(500);  // RTOS延时}
}// 场景3:传感器采样(秒级)
void sensor_task(void *param) {TickType_t xLastWake = xTaskGetTickCount();while(1) {read_sensor();vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(1000));}
}// 场景4:低功耗定时唤醒(分钟/小时级)
void low_power_delay(uint32_t minutes) {RTC_SetAlarm(minutes * 60);PWR_EnterSTANDBYMode();  // 深度睡眠
}

6.4 调试技巧

// 1. 测量实际延时时间
GPIO_SetHigh();
delay_us_timer(100);
GPIO_SetLow();
// 用示波器测量脉冲宽度// 2. 检测任务延时冲突
void check_delay_collision(void) {static TaskHandle_t last_user = NULL;TaskHandle_t current = xTaskGetCurrentTaskHandle();if(last_user != NULL && last_user != current) {// 检测到不同任务使用printf("Warning: Timer conflict!\n");}last_user = current;
}// 3. 监控延时精度
uint32_t start = DWT->CYCCNT;
delay_us_timer(1000);
uint32_t cycles = DWT->CYCCNT - start;
float actual_us = cycles / (SystemCoreClock / 1000000.0);

七、核心要点回顾

  1. 延时方法选择:根据精度需求、是否在RTOS中、延时长度选择合适方法
  2. 性能考虑:裸机delay浪费CPU,RTOS delay释放CPU
  3. 硬件限制:16位定时器最大约60秒,32位约45天
  4. 线程安全:多任务环境必须考虑资源竞争和互斥
  5. 最佳实践:优先使用RTOS机制,避免全局变量,合理分配资源
http://www.dtcms.com/a/324926.html

相关文章:

  • 【C#补全计划】万类之父中的方法
  • 使用单调栈解决力扣第42题--接雨水
  • 亚麻云之静态资源管家——S3存储服务实战
  • SSH远程连接TRAE时显示权限被拒绝检查方案
  • 游泳学习 — 蛙泳
  • 变量详解:创建初始化与内存管理
  • go加速配置(下载第三方库)
  • go语言运算符
  • Java变量的声明规则与Scanner的应用
  • 算法训练营day44 动态规划⑪ 1143.最长公共子序列、1035.不相交的线、53. 最大子序和、392.判断子序列
  • BGP实验
  • (三)全栈(部署)
  • 数学建模——回归分析
  • 解决 Linux 下 “E: 仓库xxx没有数字签名” 问题
  • C++高频知识点(十九)
  • CentOS7.9 离线安装mysql数据库
  • Python vs MATLAB:智能体开发实战对比
  • 安卓录音方法
  • Python描述符进阶:自定义文档与属性删除的艺术
  • 可视化程序设计(4) - 第一个图形窗口程序
  • 从 GPT‑2 到 gpt‑oss:解析架构的迭代
  • BandiView:高效多功能的图像查看和管理工具
  • 系统调用sigaction的工作流程
  • 算法训练之队列和优先级队列
  • Ubuntu 24.04 适配联发科 mt7902 pcie wifi 网卡驱动实践
  • MySQL的存储引擎:
  • C/C++内存管理函数模板
  • Flutter开发 页面间的值传递示例
  • 基于C语言(兼容C++17编译器)的记账系统实现
  • 虚拟机安装 爱快ikuai 软路由 浏览器无法访问/拒绝连接