在学习51单片机时一直是使用软件延时,刚开始玩stm32的时候就听别人说stm32要用SysTick做延时才准。但是看了原子哥的例子程,一直很不解,因为在stm32中文参考手册中找不到关于这个的信息,今天翻了《ARM Cortex-M3与Cortex-M4权威指南(中文版)》,终于找到了答案。
先来看看它的原理框图,其中最重要的就是那个向下计数的递减计数器,当我们向重装在值寄存器写入一个值之后,使能计数器,计数器就会从重装载值寄存器中读取数据,然后从我们写入的值开始倒数,一直数到0,当计数到0的时候就会触发标示位。
再往下看,官方给出了一些实例代码,我们就根据这段代码的思想来写。
第一步:关闭SysTick,也就是控制寄存器的第一个bit为0
第二步:给重装载值寄存器赋值,这个值很关键,后面再讲怎么算。
第三步:清空当前值寄存器的值,否则计数器无法从重装载值寄存器中读取数据(当前值为0,计数器自动读取重装载值寄存器的值)
第四部:等待计数完毕,这里要判断标志位的值,计数完毕标志位(控制状态寄存器位16)会清零
第五步:关闭SysTick
那么我们要写入什么样的值才能延时1us呢?
这个跟它的时钟源有关,SysTick属于内核部分,时钟源为系统时钟的 1/8,如下图(看中文参考手册的时钟树)。
因此时钟频率为 SystemCoreClock / 8 ,一般SystemCoreClock 为72MHz。72M / 8 =9M,由此可知计数一次用了 (1/9000000) s ,用了(1/9000) ms, 用了(1/9) us。所以,1us计数9次,所以写入的值应该为SystemCoreClock / 8000000 = 9。毫秒延时以此类推。
我写的最终代码:
/*
* 函 数 名: Delay_us
* 功能说明: 微秒级延时
* 形 参: num :延时num个微秒
* 返 回 值: 无
*/
void Delay_us(__IO uint32_t num)
{
uint32_t temp;
SysTick->CTRL = 0x00; /* 关闭计数器 */
/* 设置重装载值为(72M/8M) * num,也就是num 个 1us, */
SysTick->LOAD = (SystemCoreClock / 8000000) * num;
SysTick->VAL = 0x00; /* 清空当前值器 */
SysTick->CTRL = 0x01; /* 使能SysTick */
/* 等待计数器倒数到0,计数完毕,标志位16会清零 */
do
{
temp = SysTick->CTRL;
}
while ((temp&0x01) && !(temp & (0x1<<16))); /* temp&0x01表示是否使能 防止无意关闭时出现死循环 */
SysTick->CTRL = 0x00;/* 关闭计数器 */
}
/*
* 函 数 名: Delay_ms
* 功能说明: 毫秒级延时
* 形 参: num :延时num个毫秒
* 返 回 值: 无
*/
void Delay_ms(__IO uint32_t num)
{
uint32_t temp;
SysTick->CTRL = 0x00; /* 关闭计数器 */
/* 设置重装载值为(72M/8K) * num,也就是num 个 1ms, */
SysTick->LOAD = (SystemCoreClock / 8000) * num;
SysTick->VAL = 0x00; /* 清空当前值器 */
SysTick->CTRL = 0x01; /* 使能SysTick */
/* 等待计数器倒数到0,计数完毕,标志位16会清零 */
do
{
temp = SysTick->CTRL;
}
while ((temp&0x01) && !(temp & (0x1<<16))); /* temp&0x01表示是否使能 防止无意关闭时出现死循环 */
SysTick->CTRL = 0x00; /* 关闭计数器 */
}