在【STM32F103笔记】2、单片机中的HelloWorld——流水灯中我们曾写过一个简单的延时函数,利用空操作函数__nop()并大致计算延时时间,但这个函数并不精确,有兴趣的朋友可以再把那一篇中的程序运行结果和标准时钟比较一下。
这一篇中将使用Cortex-M3内核自带的系统时钟 (System Time)设计精确的延时函数。
Cortex-M3内核自带一个24位的降序计数器,也就是SysTick,通常Systick是用于给实时操作系统提供准确的滴答时钟。
在这里将SysTick用于精确的计时,先介绍一下2个SysTick相关的寄存器:
由于SysTick的计数器自减操作不受其它干扰和影响,完全由其时钟决定,若时钟源选择AHB 72MHz,那么当计数器从72计数到0,相当于时间1us,因此可以使用SysTick来精确计算时间。
程序设计思路是,首先初始化SysTick,设置固定的计数(比如720),并且每次计数完成后触发中断,这样每次触发中断就是固定的事件(10us)。
同样,官方库中提供了用于SysTick配置初始化的函数:
/**
* @brief Initialize and start the SysTick counter and its interrupt.
*
* @param ticks number of ticks between two interrupts
* @return 1 = failed, 0 = successful
*
* Initialise the system tick timer and its interrupt and start the
* system tick timer / counter in free running mode to generate
* periodical interrupts.
*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
SysTick->VAL = 0; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
#endif
在工程的USER文件夹下新建delay.c、delay.h两个文件用于存放延时函数,并且方便以后使用。
这里直接先给出delay.h文件的内容:
/**
******************************************************************************
* @file delay.h
* @author TTZZWWW
* @version
* @date
* @brief Delay functions.
******************************************************************************
* @attention
* Delay functions should be consistent with initialized SysTick clock.
******************************************************************************
*/
#ifndef __DELAY_H
#define __DELAY_H
/* Includes ------------------------------------------------------------------*/
#include
/* Defines -------------------------------------------------------------------*/
#define DELAYCLOCK_1us SystemCoreClock/1000000
#define DELAYCLOCK_10us SystemCoreClock/100000
#define DELAYCLOCK_100us SystemCoreClock/10000
#define DELAYCLOCK_1ms SystemCoreClock/1000
/* External variables --------------------------------------------------------*/
extern uint32_t DelayTimeCount;
/**
* @brief Initialize SysTick for delay functions
* @param Delay clock, already defined in delay.h
* @return None
*/
void DelayInitSysTick(uint32_t delayclock);
/**
* @brief delay us
* @param us
* @return None
*/
void delay_us(uint32_t us);
/**
* @brief delay ms
* @param ms
* @return None
*/
void delay_ms(uint32_t ms);
#endif /* __DELAY_H */
#ifndef __DELAY_H
#define __DELAY_H
...
#endif /* __DELAY_H */
#define DELAYCLOCK_1us SystemCoreClock/1000000
#define DELAYCLOCK_10us SystemCoreClock/100000
#define DELAYCLOCK_100us SystemCoreClock/10000
#define DELAYCLOCK_1ms SystemCoreClock/1000
void DelayInitSysTick(uint32_t delayclock);
void delay_us(uint32_t us);
void delay_ms(uint32_t ms);
DelayInitSysTick()函数用于初始化SysTick:
/**
* @brief Initialize SysTick for delay functions
* @param None
* @retval None
*/
void DelayInitSysTick(uint32_t delayclock)
{
if (SysTick_Config(delayclock))
while(1);
DelayClock = delayclock;
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
delay_us()函数用于进行微秒级的延时:
/**
* @brief delay us
* @param us
* @return None
*/
void delay_us(uint32_t us)
{
DelayTimeCount = us * 72 / DelayClock;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(DelayTimeCount);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
和delay_us相似:
/**
* @brief delay ms
* @param ms
* @return None
*/
void delay_ms(uint32_t ms)
{
DelayTimeCount = ms * 72000 / DelayClock;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(DelayTimeCount);
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
在中断处理函数SysTick_Handler中先判断DelayTimeCount是否已经为0,若否则将其减1(注意stm32f10x_it.c文件中要include “delay.h”):
#include "delay.h"
...
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
void SysTick_Handler(void)
{
if(DelayTimeCount != 0)
DelayTimeCount--;
}
这样就完成了延时函数的设计,在main函数中我们通过LED亮灭来验证。
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "delay.h"
/* Private functions ---------------------------------------------------------*/
void LEDPB8Config(void);
int main(void)
{
LEDPB8Config();
DelayInitSysTick(DELAYCLOCK_1ms);
while(1)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_8)));
delay_ms(1000);
}
}
void LEDPB8Config(void)
{
GPIO_InitTypeDef GPIOInitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIOInitStruct.GPIO_Pin = GPIO_Pin_8;
GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIOInitStruct);
}
可以看到LED以1s的延时进行闪烁,将其与标准时钟比较,可以发现延时还是很准的: