我们在开发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; __IO uint32_t LOAD; __IO uint32_t VAL; __I uint32_t CALIB; } 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;
static uint16_t fac_ms=0;
void delay_init(void) { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SystemCoreClock/8000000;
fac_ms=(uint16_t)fac_us*1000; }
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; }
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;
void delay_ms(__IO uint32_t nms) { if(SysTick_Config(SystemCoreClock/1000)) { while(1); } time_delay = nms; while(time_delay); SysTick->CTRL = 0x00; SysTick->VAL = 0x00; }
void SysTick_Handler(void) { if(time_delay) { time_delay--; } }
|
总结
使用软件延时实现方便但不能精确延时。使用中断的方法实现的延时函数可以做到精确延时,但使用中断在中断嵌套中不利与其他中断调用此中断函数。使用非中断的方式可以很好的解决以上两种延时方法的缺点,所以比较推荐使用非中断的硬件延时方法实现延时函数。