STM32使用Systick实现微秒(us)级延时
1、代码:(引用自立创开发板)
下列代码直接CV进自己的板子,.h文件改个#include "stm32f4xx.h"
然后把 SYSCLK 改为时钟树配的系统主频
SYS.c函数:
#include "sys.h"
#define SYSCLK 168000000
/**
- @brief 用内核的 systick 实现的微妙延时
- @note None
- @param _us:要延时的us数
- @retval None
*/
void delay_us(uint32_t _us)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
// 计算需要的时钟数 = 延迟微秒数 * 每微秒的时钟数
ticks = _us * (SYSCLK / 1000000);
// 获取当前的SysTick值
told = SysTick->VAL;
while (1)
{
// 重复刷新获取当前的SysTick值
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
tcnt += told - tnow;
else
tcnt += SysTick->LOAD - tnow + told;
told = tnow;
// 如果达到了需要的时钟数,就退出循环
if (tcnt >= ticks)
break;
}
}
}
/**
- @brief 调用用内核的 systick 实现的毫秒延时
- @note None
- @param _ms:要延时的ms数
- @retval None
*/
void delay_ms(uint32_t _ms) { delay_us(_ms * 1000); }
void delay_1ms(uint32_t ms) { delay_us(ms * 1000); }
void delay_1us(uint32_t us) { delay_us(us); }
SYS.h函数
#ifndef __SYS_H__
#define __SYS_H__
#include "stm32f4xx.h"
//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS 0 //定义系统文件夹是否支持UCOS
///
//定义一些常用的数据类型短关键字
typedef int32_t s32;
typedef int16_t s16;
typedef int8_t s8;
typedef const int32_t sc32;
typedef const int16_t sc16;
typedef const int8_t sc8;
typedef __IO int32_t vs32;
typedef __IO int16_t vs16;
typedef __IO int8_t vs8;
typedef __I int32_t vsc32;
typedef __I int16_t vsc16;
typedef __I int8_t vsc8;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef const uint32_t uc32;
typedef const uint16_t uc16;
typedef const uint8_t uc8;
typedef __IO uint32_t vu32;
typedef __IO uint16_t vu16;
typedef __IO uint8_t vu8;
typedef __I uint32_t vuc32;
typedef __I uint16_t vuc16;
typedef __I uint8_t vuc8;
void delay_us(uint32_t _us);
void delay_ms(uint32_t _ms);
void delay_1ms(uint32_t ms);
void delay_1us(uint32_t us);
#endif
2.原理:系统滴答定时器(SYSTick)
ARM_M4手册对SYSTick有关的寄存器的描述:
SysTick是属于Cortex-M4内核中的一个外设,内嵌在NVIC中。
系统定时器是一个24位的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK。
系统时钟SYSCLK的值=HAL库时钟树配置的值
由于系统滴答定时器属于Cotex-M4内核的外设,相关寄存器介绍不在《参考手册》,而在《编程手册》
图例1 编程手册上对寄存器的定义
系统滴答定时器只有四个控制寄存器:STK_CTRL,STK_LOAD,STK_VAL和STK_CALIB。
图例2 四个控制寄存器
寄存器 | 中文名 | 描述 |
CTRL | SysTick控制和状态寄存器 | [31:17]保留位、16溢出标志位、[15:3]、2时钟源、1异常、0使能 |
LOAD | SysTick重载寄存器 | [31:24]保留位、[23:0]为重装位 |
VAL | SysTick当前值寄存器 | [31:24]保留位、[23:0]为当前值 |
CALIB | SysTick校准值寄存器 | 没用到就先不管,我补充在后边,想看就记点 |
[31:24]保留位就是没用的位
整理一下流程:(当做定时器)
- 时钟源不分频
- 自动重装载值设置为 [SYSCLK(主频) / 1000 000] * n 这样出来的就是n us的值
- 在while(1)循环计算是否已经达到需要的时钟数、
- 到这里我们就有了us延时,封装一下就可以有毫秒和秒的延时。
如下图
3.分析HAL库的HAL_Delay()函数
如果wait
小于HAL_MAX_DELAY
,则在wait
上加上uwTickFreq
的值,确保延时时间的准确性。
而HAL_MAX_DELAY = 0xFFFFFFFF
只要你的HAL_Delay延时时间不是超过2^32 = 4294967296ms,都会加上这个时间补偿,
既wait = wait + 1
AI对等待时间+1 的解释:
4.补充(寄存器)
4.1 STK_CTRL SysTick控制和状态寄存器
4.2 STK_LOAD SysTick重载寄存器
4.3 STK_VAL SysTick当前值寄存器
4.4 STK_CTRL SysTick校准值寄存器
5.参考资料:
【经验分享】STM32 基础重点—SysTick定时器 – STM32团队 ST意法半导体中文论坛
:16
作者:橘好き