STM32延时函数的实现方法

我们在开发STM32工程时常常都需要使用到延时函数,比如控制LED的闪烁、IIC和SPI总线等都会用到延时函数,笔者刚开始接触STM32时查阅了不少的资料,本文将分享三种方法来实现延时函数。

## 软件延时

软件延时非常的简单粗暴,让单片机循环达到延时的目的:

1
2
3
4
void Delay(__IO u32 nCount)
{
for(; nCount != 0; nCount--);
}

软件延时的优点是简单易懂,实现容易。缺点是不能精准地控制延时时间,在对延时时间要求较高的工程中,软件延时明显不能满足需求。

硬件延时


定时器延时(非中断方式)


STM32内核中包含一个24位计数器SysTick,计数到0后又重新加载计数初始值到寄存器。下面介绍如何配置延时函数。
STM32外部8MHz时钟倍频到72MHz,然后SysTick计数器再8分频,所以SysTick计数器的工作频率是9MHz(一秒钟计数9M次)。SysTick在STM32的固件库core_cm3.h中是这样定义的:

1
2
3
4
5
6
7
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
} SysTick_Type;

CTRL是SysTick控制和状态寄存器,LOAD是SysTick重载值寄存器,VAL是当前值寄存器,CALIB是SysTick校准寄存器(不常用)。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "delay.h"


static uint8_t fac_us=0;//us延时倍数

static uint16_t fac_ms=0;//ms延时倍数

/**
* @brief 计数器初始化函数
* @param None
* @retval None
*/
void delay_init(void)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//选择外部时钟HCLK/8

fac_us=SystemCoreClock/8000000; //72000000/8000000 = 9

fac_ms=(uint16_t)fac_us*1000; //9000
}

/**
* @brief us延时函数
* @param nus 延时us数
* @retval None
*/
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD = nus*fac_us; //加载时间
SysTick->VAL = 0x00; //清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开始计数
do
{
temp = SysTick->CTRL;
}while(temp&0x01&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //停止计数
SysTick->VAL = 0x00; //清空计数器
}

/**
* @brief ms延时函数
* @param mus 延时ms数
* @retval None
*/
void delay_ms(uint16_t nms)
{
uint32_t temp;
SysTick->LOAD = nms*fac_ms; //加载时间
SysTick->VAL = 0x00; //清空计数器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开始计数
do
{
temp = SysTick->CTRL;
}while(temp&0x01&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //停止计数
SysTick->VAL = 0x00; //清空计数器
}

中断延时(中断方式)


使用SysTick计数器的中断方式实现延时,SysTick_Config()配置SysTick计数器,SystemCoreClock/1000实现1ms产生一次中断,在中断函数SysTick_Handler中对time_delay变量减一处理,当time_delay为0时,到达延时时间。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
uint32_t time_delay;

/**
* @brief ms延时函数
* @param mus 延时ms数
* @retval None
*/
void delay_ms(__IO uint32_t nms)
{
if(SysTick_Config(SystemCoreClock/1000)) //配置SysTick计数器
{
while(1);
}
time_delay = nms;
while(time_delay); //time_delay为0时,到达延时时间。
SysTick->CTRL = 0x00;
SysTick->VAL = 0x00;
}

/**
* @brief SysTick中断函数
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
if(time_delay)
{
time_delay--;
}
}

总结


使用软件延时实现方便但不能精确延时。使用中断的方法实现的延时函数可以做到精确延时,但使用中断在中断嵌套中不利与其他中断调用此中断函数。使用非中断的方式可以很好的解决以上两种延时方法的缺点,所以比较推荐使用非中断的硬件延时方法实现延时函数。

作者

Junle

发布于

2020-05-02

更新于

2024-03-22

许可协议

评论