文章目录
- 一、延时基础概念
- 二、裸机环境延时实现
- 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延时 | 取决于tick | 0% | ms级以上 | 不浪费CPU,可多任务 |
RTC | 秒级 | 0% | 长延时 | 省电,精度一般 |
二、裸机环境延时实现
2.1 微秒级延时实现
void delay_us_software(uint32_t us) {uint32_t ticks = us * (SystemCoreClock / 1000000);while(ticks--) {__NOP(); }
}
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; while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));SysTick->CTRL = 0;
}
void delay_us_timer(uint32_t us) {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(); }
}
2.3 非阻塞延时方案
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 寄存器位宽限制
定时器类型 位宽 ARR最大值 最大计数
TIM1 16位 65,535 65,535
TIM2 32位 4,294,967,295 约43亿
TIM3,TIM4 16位 65,535 65,535
延时时间 = (PSC + 1) × (ARR + 1) / TIMxCLK
PSC: 预分频器(16位: 0-65535)
ARR: 自动重装载值
TIMxCLK: 定时器输入时钟(最高72MHz)
3.2 最大延时时间计算
最大延时 = 65536 × 65536 / 72MHz = 59.65秒
最大延时 = 65536 × 4294967296 / 72MHz = 45.25天
void config_timer_for_1ms(void) {TIM3->PSC = 72 - 1; TIM3->ARR = 1000 - 1;
}
3.3 超长延时解决方案
void adaptive_delay_us(uint32_t us) {uint32_t prescaler, arr;if(us <= 65535) {prescaler = 72; arr = us;} else if(us <= 655350) {prescaler = 720; arr = us / 10;} else if(us <= 6553500) {prescaler = 7200; 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;
}
void long_delay_ms(uint32_t ms) {const uint32_t MAX_DELAY = 60000; while(ms > 0) {uint32_t current = (ms > MAX_DELAY) ? MAX_DELAY : ms;delay_ms_timer(current);ms -= current;}
}
void rtc_delay_hours(uint32_t hours) {uint32_t target = RTC_GetCounter() + (hours * 3600);while(RTC_GetCounter() < target) {PWR_EnterSTOPMode(); }
}
四、RTOS环境下的延时
4.1 RTOS延时机制原理
void vTaskDelay(TickType_t xTicksToDelay) {
}
4.2 RTOS中的us级延时
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 {vTaskDelay(pdMS_TO_TICKS(us / 1000));}
}
五、多任务环境的线程安全问题
5.1 全局变量竞争问题
volatile uint32_t delay_counter; void Task1() { delay_counter = 100; }
void Task2() { delay_counter = 200; }
SemaphoreHandle_t timer_mutex;void safe_delay(uint32_t ms) {xSemaphoreTake(timer_mutex, portMAX_DELAY);xSemaphoreGive(timer_mutex);
}
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;
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 {vTaskDelay(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 性能优化建议
❌ while(delay--);
✅ vTaskDelay() 或 定时器中断
短延时(<100us): 可以使用临界区
长延时(>100us): 避免关中断太久
优先级高的任务: 分配独立定时器
普通任务: 使用共享定时器或软件定时器
后台任务: 使用vTaskDelay
短延时: 忙等待可接受
长延时: 使用WFI或低功耗模式
超长延时: 使用RTC+深度睡眠
6.3 常见应用场景示例
void i2c_delay(void) {delay_us_timer(5);
}
void led_task(void *param) {while(1) {LED_Toggle();vTaskDelay(500); }
}
void sensor_task(void *param) {TickType_t xLastWake = xTaskGetTickCount();while(1) {read_sensor();vTaskDelayUntil(&xLastWake, pdMS_TO_TICKS(1000));}
}
void low_power_delay(uint32_t minutes) {RTC_SetAlarm(minutes * 60);PWR_EnterSTANDBYMode();
}
6.4 调试技巧
GPIO_SetHigh();
delay_us_timer(100);
GPIO_SetLow();
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;
}
uint32_t start = DWT->CYCCNT;
delay_us_timer(1000);
uint32_t cycles = DWT->CYCCNT - start;
float actual_us = cycles / (SystemCoreClock / 1000000.0);
七、核心要点回顾
- 延时方法选择:根据精度需求、是否在RTOS中、延时长度选择合适方法
- 性能考虑:裸机delay浪费CPU,RTOS delay释放CPU
- 硬件限制:16位定时器最大约60秒,32位约45天
- 线程安全:多任务环境必须考虑资源竞争和互斥
- 最佳实践:优先使用RTOS机制,避免全局变量,合理分配资源