10 . 定时器介绍和应用

在前面我们使用到的延时是通过CPU 循环等待产生的,这个延时是不精确的。现在给大家介绍STM32F1 内部SysTick 系统定时器,通过一个简单的LED 流水灯程序来讲述如何配置SysTick 系统定时器实现精确延时。分为如下几部分内容:
1. SysTick 定时器介绍
2. SysTick 定时器操作
3. 软件设计
4. 实验现象

SysTick 定时器介绍

SysTick 定时器也叫SysTick 滴答定时器,它是Cortex-M3 内核的一个外设,被嵌入在NVIC 中。它是一个24 位向下递减的定时器,每计数一次所需时间为1/SYSTICK,SYSTICK 是系统定时器时钟,它可以直接取自系统时钟,还可以通过系统时钟8 分频后获取,本套程序中我们采用后者,即每计数一次所需时间为1/(72/8)us,换句话说在1us 的时间内会计数9 次。当定时器计数到0 时,将从LOAD 寄存器中自动重装定时器初值,重新向下递减计数,如此循环往复。如果开启SysTick 中断的话,当定时器计数到0,将产生一个中断信号。因此只要知
道计数的次数就可以准确得到它的延时时间。SysTick 定时器通常应用在操作系统中,为其提供时钟周期。

SysTick 定时器操作

在STM32F1 库函数中,并没有提供相应的SysTick 定时器配置函数,我们要操作SysTick 定时器就需要了解它的寄存器功能。其实SysTick 定时器寄存器很简单,只有4 个,分别是CTRL、LOAD、VAL、CALIB。

(1)CTRL 寄存器
CTRL 是SysTick 定时器的控制及状态寄存器。其相应位功能如下:
在这里插入图片描述
注:CLKSOUTCE 位是用于选择SysTick 定时器时钟来源,如果该位为1,表示其时钟是由系统时钟直接提供即72M。如果为0,表示其时钟是由系统时钟八分频后提供即72/8=9M。
(2)LOAD 寄存器
LOAD 是SysTick 定时器的重装载数值寄存器。其相应位功能如下:
在这里插入图片描述
因为STM32F1 的SysTick 定时器是一个24 位递减计数器,因此重装载寄存器中只使用到了低24 位,即bit0-bit23。
当系统复位时,其值为0。

(3)VAL 寄存器
VAL 是SysTick 定时器的当前数值寄存器。其相应位功能如下:
在这里插入图片描述
同样只有bit0-bit23 有效,复位时值为0。
(4)CALIB 寄存器
CALIB 是SysTick 定时器的校准数值寄存器。其相应位功能如下:
在这里插入图片描述
SysTick 定时器操作步骤
SysTick 定时器的操作可以分为4 步:
(1)设置SysTick 定时器的时钟源。
(2)设置SysTick 定时器的重装初始值(如果要使用中断的话,就将中断使能打开)。
(3)清零SysTick 定时器当前计数器的值。
(4)打开SysTick 定时器。

软件设计

这里我们主要介绍关键代码:

SysTick_Init 函数代码如下:

/**************************************************************************
*****
* 函数名: SysTick_Init
* 函数功能: SysTick 初始化,SYSTICK 的时钟固定为AHB 时钟的1/8
* 输入: SYSCLK:系统时钟频率
* 输出: 无
***************************************************************************
****/
void SysTick_Init(u8 SYSCLK)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SYSCLK/8; //SYSCLK 的8 分频保存1us 所需的计数次数
fac_ms=(u16)fac_us*1000; //每个ms 需要的systick 时钟数
}

SysTick_Init 函数形参SYSCLK 表示的系统时钟大小,默认配置我们使用的系统时钟是72M,所以调用这个函数时,形参值即为72。函数内部调用了一个库函数SysTick_CLKSourceConfig,此函数用来对SysTick 定时器时钟的选择,我们使用的SysTick 定时器时钟是系统时钟的8 分频, 所以参数是SysTick_CLKSource_HCLK_Div8。如果使用系统时钟作为SysTick 定时器时钟,那么参数即为SysTick_CLKSource_HCLK。这个函数在misc.c 库文件内,如何查找我们前面介绍过方法。下面的两条语句是用来求取SysTick 定时器在1us 时间内和1ms 时间内的计数次数。

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|=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~0x01; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}

①将需要延时多少us 的计数值加载到SysTick 的LOAD 寄存器中,fac_us值是延时1us 所需的计数值。
②清空当前计数值寄存器VAL。
③打开SysTick 定时器,定时器开始向下递减计数。
④CTRL 寄存器的第16 位是SysTick 递减到0 的标志位,如果递减到0,此为置1,通过读取该位来判断延时是否完成,从而退出while 循环。
⑤关闭SysTick 定时器。
⑥清空当前计数值寄存器VAL。

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|=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~0x01; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}

此函数功能与delay_us 基本一样,只不过这里是延时ms。要注意的是,SysTick 定时器是24 位的, 其计数最大值为0xffffff , 时间为nms<=0xffffff81000/SYSCLK,SYSCLK 是系统时钟为72M,所以最大延时为1864ms。如果需要延时大于1.864S,可以调用多个delay_ms 函数即可。

主函数
这里我们主要看下main 函数,代码如下:

/****************************************************************
***************
* 函数名: main
* 函数功能: 主函数
* 输入: 无
* 输出: 无
*****************************************************************
**************/
int main()
{
SysTick_Init(72);
LED_Init();
while(1)
{
led1=0;
led2=1;
delay_ms(500); //精确延时500ms
led1=1;
led2=0;
delay_ms(500); //精确延时500ms
}
}

主函数实现的功能比较简单,首先对SysTick 定时器进行初始化配置,选择系统时钟8 分频作为SysTick 的时钟,然后初始化LED,这个初始化过程前面已经介绍过,大家也可以进入这个函数内查看。最后进入while 循环语句,对PC0和PC1 管脚进行位操作,里面也调用了delay_ms 延时函数,这时候的延时是非常精确的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值