(1)实验平台:
普中STM32精灵开发板https://item.taobao.com/item.htm?id=739076227953(2)资料下载:普中科技-各型号产品资料下载链接
在前面章节中我们使用到的延时是通过 CPU 循环等待产生的,这个延时是不精确的。本章就给大家介绍 STM32F1 内部 SysTick 系统定时器,通过一个简单的LED 流水灯程序来讲述如何配置 SysTick 系统定时器实现精确延时。学习本章可以参考《Cortex M3 权威指南(中文)》 chpt13 Cortex-M3 的其它特性章节或者参考库函数中 core_cm3.h 文件。若结合视频学习效果更佳。本章分为如下几部分内容:
11.1 SysTick 定时器介绍
SysTick 定时器也叫 SysTick 滴答定时器,它是 Cortex-M3 内核的一个外设,被嵌入在 NVIC 中。它是一个 24 位向下递减的定时器,每计数一次所需时间为1/SYSTICK,SYSTICK 是系统定时器时钟,它可以直接取自系统时钟,还可以通过系统时钟 8 分频后获取,本套程序中我们采用后者,即每计数一次所需时间为1/(72/8)us,换句话说在 1us 的时间内会计数 9 次。当定时器计数到0 时,将从LOAD 寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启 SysTick 中断的话,当定时器计数到 0,将产生一个中断信号。因此只要知道计数的次数就可以准确得到它的延时时间。
SysTick 定时器通常应用在操作系统中,为其提供时钟周期。
11.2 SysTick 定时器操作
在 STM32F1 库函数中,并没有提供相应的 SysTick 定时器配置函数,我们要操作 SysTick 定时器就需要了解它的寄存器功能。其实SysTick 定时器寄存器很简单,只有 4 个,分别是 CTRL、LOAD、VAL、CALIB。这些寄存器都可以在《CortexM3 权威指南(中文)》 apdx D NVIC 寄存器小结章节或者库函数core_cm3.h文件中找到,里面有详细的介绍。
11.2.1 SysTick 定时器寄存器
(1)CTRL 寄存器
CTRL 是 SysTick 定时器的控制及状态寄存器。其相应位功能如下:
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
16 | COUNTFLAG | R | 0 | 如果在上次读取本寄存器后,Systick已经数到了0,则该位为1。如果读取该位,该位将自动清零 |
2 | CLKSOURCE | R/W | 0 | 0=外部时钟源(STCLK) 1=内核时钟(FCLK) |
1 | TICKINT | R/W | 0 | 1=Systick 倒数到0时产生Systick异常请求 0=数到0时无动作 |
0 | ENABLE | R/W | 0 | Systick定时器的使能位 |
注:CLKSOUTCE 位是用于选择 SysTick 定时器时钟来源,如果该位为1,表示其时钟是由系统时钟直接提供即 72M。如果为 0,表示其时钟是由系统时钟八分频后提供即 72/8=9M。
(2)LOAD 寄存器
LOAD 是 SysTick 定时器的重装载数值寄存器。其相应位功能如下:
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:0 | RELOAD | R/W | 0 | 当倒数至0时,将被重装载的值 |
因为 STM32F1 的 SysTick 定时器是一个 24 位递减计数器,因此重装载寄存器中只使用到了低 24 位,即 bit0-bit23。当系统复位时,其值为0。
(3)VAL 寄存器
VAL 是 SysTick 定时器的当前数值寄存器。其相应位功能如下:
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
23:0 | CURRENT | R/Wc | 0 | 读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 Systick 控制及状态寄存器中的COUNTFLAG标志 |
同样只有 bit0-bit24 有效,复位时值为 0。
(4)CALIB 寄存器
CALIB 是 SysTick 定时器的校准数值寄存器。其相应位功能如下:
位段 | 名称 | 类型 | 复位值 | 描述 |
---|---|---|---|---|
31 | NOREF | R | - | 1=没有外部参考时钟(STCLK不可用) 0=外部参考时钟可用 |
30 | SKEW | R | - | 1=校准值不是准确的 10ms 0=校准值是准确的 10ms |
23:0 | TENMS | R/W | 0 | 10ms 的时间内倒计数的格数。芯片设计者应该通过 Cortex-M3 的输入信号提供该数值。若该值读回零,则表示无法使用校准功能 |
此寄存器在定时实验中不需要使用,可以不用了解。
11.2.2 SysTick 定时器操作步骤
SysTick 定时器的操作可以分为 4 步:
(1)设置 SysTick 定时器的时钟源。
(2)设置 SysTick 定时器的重装初始值(如果要使用中断的话,就将中断使能打开)。
(3)清零 SysTick 定时器当前计数器的值。
(4)打开 SysTick 定时器。
11.3 软件设计
从这章开始,软件设计部分我们就不再讲述怎么复制工程、新建文件以及添加对应的头文件路径,我们直接打开实验例程内的工程进行讲解。同样SysTick定时器延时函数在任何一个 STM32F1 应用程序中都用得上,因此将其驱动文件放在 Public 文件夹内。这里我们打开“\4--实验程序\1--基础实验\5-SysTick系统定时器实验”工程,从工程 Public 组中可以看到,引进了一个新的SysTick.c源文件,此文件内的函数就是按照操作步骤完成,有对us 的延时和ms 的延时。将其打开,这里我们主要介绍关键代码。
11.3.1 SysTick_Init()函数
SysTick_Init 函数代码如下:
//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void SysTick_Init(u8 SYSCLK)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SYSCLK/8;
fac_ms=(u16)fac_us*1000;
}
SysTick_Init 函数形参 SYSCLK 表示的系统时钟大小,默认配置我们使用的系统时钟是 72M,所以调用这个函数时,形参值即为 72。函数内部调用了一个库函数 SysTick_CLKSourceConfig,此函数用来对 SysTick 定时器时钟的选择,我们使用的 SysTick 定时器时钟是系统时钟的 8 分频,所以参数是SysTick_CLKSource_HCLK_Div8。如果使用系统时钟作为SysTick 定时器时钟,那么参数即为 SysTick_CLKSource_HCLK。这个函数在misc.c 库文件内,如何查找我们前面介绍过方法。
下面的一条语句是用来求取 SysTick 定时器在 1us 时间内和1ms 时间内的计数次数。
11.3.2 delay_us()函数
delay_us 函数代码如下:
/*******************************************************************************
* 函 数 名 : delay_us
* 函数功能 : us延时,
* 输 入 : nus:要延时的us数
注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
* 输 出 : 无
*******************************************************************************/
void delay_us(u32 nus)
{
u32 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; //清空计数器
}
①将需要延时多少 us 的计数值加载到 SysTick 的LOAD 寄存器中,fac_us值是延时 1us 所需的计数值。
②清空当前计数值寄存器 VAL。
③打开 SysTick 定时器,定时器开始向下递减计数。
④CTRL 寄存器的第 16 位是 SysTick 递减到 0 的标志位,如果递减到0,此为置 1,通过读取该位来判断延时是否完成,从而退出while 循环。
⑤关闭 SysTick 定时器。
⑥清空当前计数值寄存器 VAL。
11.3.3 delay_ms()函数
delay_ms 函数代码如下:
/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时,
* 输 入 : nms:要延时的ms数
注意:nms的值,SysTick->LOAD为24位寄存器,
不要大于0xffffff*8*1000/SYSCLK
对72M条件下,nms<=1864ms
* 输 出 : 无
*******************************************************************************/
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
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; //清空计数器
}
此函数功能与 delay_us 基本一样,只不过这里是延时ms。要注意的是,SysTick 定时器是 24 位的,其计数最大值为 0xffffff,时间为nms<=0xffffff*8*1000/SYSCLK,SYSCLK 是系统时钟为72M,所以最大延时为1864ms。如果需要延时大于 1.864S,可以调用多个 delay_ms 函数即可。
11.3.4 主函数
在 main.c 文件中前面引入了工程中所需的头文件,可以打开工程查看,这里我们主要看下 main 函数,代码如下:
#include "system.h"
#include "SysTick.h"
#include "led.h"
int main()
{
SysTick_Init(72);
LED_Init();
while(1)
{
LED0=!LED0;
delay_ms(500); //精确延时500ms
}
}
主函数实现的功能比较简单,首先对 SysTick 定时器进行初始化配置,选择系统时钟作为 SysTick 的时钟,然后初始化 LED,这个初始化过程前面已经介绍过,大家也可以进入这个函数内查看。最后进入 while 循环语句,对LED 管脚进行位操作,里面也调用了 delay_ms 延时函数,这时候的延时是非常精确的。
11.4 实验现象
将工程程序编译下载到开发板内,可以看到核心板上的LED 指示灯闪烁。