您好,登录后才能下订单哦!
# STM32延时函数的方法有哪些
## 引言
在嵌入式系统开发中,精确的时间控制是至关重要的功能。无论是LED闪烁、传感器数据采集,还是通信协议的时序控制,都需要依赖精准的延时功能。STM32作为广泛应用的ARM Cortex-M系列微控制器,提供了多种实现延时功能的方法。本文将全面剖析STM32开发中常用的延时实现方式,包括原理分析、代码实现以及各自的优缺点比较,帮助开发者根据实际需求选择最合适的方案。
## 一、基础延时方法
### 1.1 空循环延时
#### 实现原理
```c
void Delay(uint32_t count)
{
while(count--);
}
通过让CPU执行无意义的循环消耗时钟周期实现延时,延时精度取决于CPU主频和编译器优化。
// 使用volatile防止优化
void Delay_Volatile(uint32_t count)
{
volatile uint32_t temp = count;
while(temp--);
}
SysTick是ARM内核集成的24位递减计数器,具有以下特性: - 可配置时钟源(HCLK或HCLK/8) - 可编程重装载值 - 计数到零时产生中断
// 初始化SysTick
void SysTick_Init(uint32_t ticks)
{
SysTick->LOAD = ticks - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk;
}
// 微秒级延时
void Delay_us(uint32_t us)
{
uint32_t ticks = (SystemCoreClock / 1000000) * us;
SysTick_Init(ticks);
while(!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk));
}
// 使用HAL库的延时函数
HAL_Delay(100); // 延时100ms
void TIM_Delay_Init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = SystemCoreClock/1000000 - 1; // 1MHz计数频率
TIM2->CR1 |= TIM_CR1_OPM; // 单脉冲模式
}
void TIM_Delay_us(uint16_t us)
{
TIM2->CNT = 0;
TIM2->ARR = us - 1;
TIM2->CR1 |= TIM_CR1_CEN;
while((TIM2->SR & TIM_SR_UIF) == 0);
TIM2->SR &= ~TIM_SR_UIF;
}
void TIM1_Delay_ns(uint16_t ns)
{
// 配置TIM1为最高分辨率
TIM1->PSC = 0;
TIM1->ARR = (SystemCoreClock/1000000) * ns / 1000 - 1;
TIM1->EGR |= TIM_EGR_UG;
TIM1->CR1 |= TIM_CR1_CEN;
while(!(TIM1->SR & TIM_SR_UIF));
TIM1->SR &= ~TIM_SR_UIF;
}
#include "FreeRTOS.h"
#include "task.h"
void vTaskFunction(void *pvParameters)
{
for(;;)
{
vTaskDelay(pdMS_TO_TICKS(100)); // 精确延时100ms
}
}
#include <rtthread.h>
void thread_entry(void *parameter)
{
while(1)
{
rt_thread_mdelay(50); // 毫秒级延时
rt_thread_delay(5); // 系统tick延时
}
}
void Enter_Sleep_Mode(uint32_t ms)
{
HAL_SuspendTick();
HAL_PWR_EnterSLEEPMode(PWR_MNREGULATOR_ON, PWR_SLEEPENTRY_WFI);
HAL_ResumeTick();
}
void Stop_Mode_Delay(uint32_t seconds)
{
RTC->CR |= RTC_CR_ALRE;
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
SystemClock_Config(); // 需要重新配置时钟
}
Cortex-M内核包含Data Watchpoint and Trace单元,其中的CYCCNT寄存器可提供精确的时钟周期计数。
#define DWT_CR *(uint32_t *)0xE0001000
#define DWT_CYCCNT *(uint32_t *)0xE0001004
#define DEM_CR *(uint32_t *)0xE000EDFC
void DWT_Init(void)
{
DEM_CR |= (1 << 24); // 使能TRC
DWT_CR |= (1 << 0); // 使能CYCCNT
}
void DWT_Delay(uint32_t cycles)
{
uint32_t start = DWT_CYCCNT;
while((DWT_CYCCNT - start) < cycles);
}
方法 | 精度 | CPU占用 | 功耗 |
---|---|---|---|
空循环 | ±10% | 100% | 高 |
SysTick | ±1% | 100% | 中 |
硬件定时器 | ±0.1% | 0% | 低 |
DWT | ±0.01% | 100% | 中 |
RTOS延时 | ±5% | 0% | 最低 |
void Precision_Delay(uint32_t us)
{
uint32_t ms = us / 1000;
uint32_t remainder = us % 1000;
if(ms > 0) HAL_Delay(ms);
if(remainder > 0) TIM_Delay_us(remainder);
}
void Dynamic_Delay(uint32_t delay)
{
if(SystemCoreClock > 8000000)
{
DWT_Delay(delay * (SystemCoreClock/1000000));
}
else
{
HAL_Delay(delay / 1000);
}
}
void Auto_Calibrate(void)
{
uint32_t start = DWT_CYCCNT;
HAL_Delay(100);
uint32_t actual = (DWT_CYCCNT - start) / (SystemCoreClock/1000);
g_calibration_factor = 100.0f / actual;
}
__attribute__((section(".ramfunc")))
void ISR_Safe_Delay(uint32_t cycles)
{
uint32_t start = DWT_CYCCNT;
while((DWT_CYCCNT - start) < cycles);
}
void Sync_Cores_Delay(uint32_t us)
{
#ifdef CORE_CM4
HSEM->COMMON[0].R = 0x5A5A;
#else
while(HSEM->COMMON[0].R != 0x5A5A);
#endif
DWT_Delay(us * (SystemCoreClock/1000000));
}
STM32延时实现方式多样,开发者应根据以下因素选择合适方案: 1. 精度要求:从毫秒到纳秒级的不同需求 2. 功耗约束:是否允许CPU全速运行 3. 系统环境:是否在RTOS中运行 4. 资源占用:可用定时器资源情况
建议组合使用多种方法,如主循环中使用RTOS延时,中断中使用DWT延时,低功耗场景使用RTC唤醒等。通过本文介绍的各种方法及其实现细节,开发者可以构建出满足各种复杂场景需求的时间控制系统。
注:本文所有代码示例基于STM32F4系列,其他系列可能需要调整寄存器配置。实际开发中请参考对应型号的参考手册和数据手册。 “`
这篇文章共计约3850字,全面涵盖了STM32开发中的各种延时实现方法,包括: 1. 基础软件延时 2. 硬件定时器延时 3. RTOS专用延时 4. 低功耗模式延时 5. 高精度DWT延时 6. 混合策略和校准方法
每种方法都包含实现原理、代码示例和优缺点分析,并提供了性能对比表格和特殊场景的应用建议。文章采用Markdown格式,包含代码块、表格和分级标题,便于阅读和技术文档的维护。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。