STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

学习板:STM32F103ZET6

强推系列:
STM32F103五分钟入门系列(一)跑马灯(库函数+寄存器)+加编程模板+GPIO总结

STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置

STM32F103五分钟入门系列(三)GPIO的常用库函数使用方法总结+一个网络上的误区

参考:

STM32F103五分钟入门系列(六)时钟框图+相关寄存器总结+系统时钟来源代码(寄存器)

STM32F103五分钟入门系列(七)SystemInit()函数、SetSysClock()函数

51单片机(四)定时器中断(+数码管——24小时制钟表)

SysTick滴答定时器+SysTick中断实现跑马灯

前言

本博客总结一下SysTick定时器的几个相关寄存器和库函数及它们的使用方法;而SysTick定时器是可以产生中断的,为了更好的理解SysTick并为之后中断总结提供方便,本博着重总结了SysTick中断的使用方法。不过SysTick去实现中断一般情况下最好不要用,32有许多定时器都可以产生中断,没必要用这个,本博举例子只是为了更好的去理解SysTick中断。

一、Systick滴答定时器

Systick定时器的参考资料在《Cortex-M3权威指南》第8章NVIC与中断控制中。然后注意一下这个计数器是递减计数器

(一)SysTick相关寄存器

SysTick定义在core_cm3.h头文件中:
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

与SysTick相关寄存器也定义在该文件中:

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

1、SysTick控制和状态寄存器(CTRL)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

该寄存器位2:0、位16为有效位

位0

为该寄存器使能位,置1对该寄存器使能,置0使该寄存器失能。操作方式:

	SysTick->CTRL|=1;//使能CTRL寄存器

位1

该位设置是否产生中断,置1时,倒数到0产生中断请求;置0时,不产生中断请求。该中断有专门的中断服务函数,中断服务函数定义在:stm32f10x_it.h中:

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

这个中断完全可以用在各类实验中,不过最好还是用别的定时器,毕竟32的定时器很多的,没必要用systick这个处理器内部包含的定时器来中断。下一博客会通过这个Systick总结一下延时函数,但是也强调一下,延时函数就是一个坑,不到万不得已不要用!!!

设置方法:

	SysTick->CTRL|=1<<1;//倒计数到0时产生中断
	SysTick->CTRL&=0xfffd;//倒计数到0不产生中断

位2

该位是选择时钟源的,当该位置0时采用外部时钟源,对于32,外部时钟源是AHB总线时钟的1/8。(下图红色箭头所示)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

之前两篇博客也总结过,AHB默认状态下为1分频,所以AHB出来的时钟为系统时钟,默认状态下系统时钟为72MHZ,所以当该寄存器位2软件置0时,时钟为72MHZ/8=9MHZ。

当该寄存器软件置1时采用内核时钟,即HCLK(默认状态下为sysclk时钟的1分频)=72MHZ。(下图红色箭头)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯
所以:
位2为0:

SysTick的时钟为:(SYSCLK/(AHB分频系数))/8,默认状态下为9MHZ

位2为1:

SysTick的时钟为:SYSCLK/(AHB分频系数),默认状态下为72MHZ

位16

该位为状态标志位,如果倒计数到0,则该位硬件置1,如果未计数到0,则该位为0。计数到0后要不产生中断,要不重新装载初值,进入下一次倒计数。可以如下代码判断是否计数到0:

if((SysTick->CTRL&0x10000)!=0)//倒计数到0
		{
		
		}
		
		else//未倒计数到0
		{
		
		}

2、SysTick重装载数值寄存器(LOAD)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

该寄存器也是32位寄存器,不过有效位是24位,高8位为保留位。那么还寄存器最大装载的值为(2^24)-1=16777215,所以该重载值定义为:u32、 uint32_t类型。

重装载值操作:

	SysTick->LOAD=(u32)一个数;

3、SysTick当前数值寄存器(VAL)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

该寄存器用来获取当前倒计数的值(读操作),如果对它进行写操作,会把它清零,并且将CTRL寄存器的位24清零。(这个写操作一般不会用到)

对该寄存器的操作:

	u32 temp=SysTick->VAL;

4、SysTick校准数值寄存器(CALIB)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

该寄存器一般不会用到,了解即可。

通过该寄存器,可以使系统在不同的CM3产品上运行,且可以产生恒定的SysTick中断频率。可以直接把TENMS的值写入重装载寄存器,这样一来,只要没突破系统极限,就能做到每10ms来一次 SysTick异常。

二、SysTick相关库函数

1、时钟源选择函数SysTick_CLKSourceConfig()

该函数定义在misc.h头文件中:

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

打开函数体:
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

代码很简单,我们现看一下这个函数传递的参数:

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

顾名思义,就是我们之前总结CTRL寄存器时说的,时钟源可以是HCLK、HCLK/8。

仔细看看代码:

  if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
  {
    SysTick->CTRL |= SysTick_CLKSource_HCLK;
  }
  else
  {
    SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
  }

当传递过来的参数是SysTick_CLKSource_HCLK、SysTick_CLKSource_HCLK_Div8,则分别执行对应的一行代码。

把标识换为16进制数:

  if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
  {
    SysTick->CTRL |= 0x00000004;
  }
  else
  {
    SysTick->CTRL &= 0xFFFFFFFB;

其中执行SysTick->CTRL |= 0x00000004后,CTRL寄存器第2位为1,即我们之前总结的:使用HCLK内核时钟源,默认状态下为72MHZ(=SYSCLK)(AHB默认1分频)

执行SysTick->CTRL &= 0xFFFFFFFB后,CTRL寄存器第2位为0,即我们之前总结的:使用外部时钟源,即HCLK/8,默认状态下,9MHZ(=SYSCLK/8)(AHB默认1分频)

2、SysTick配置函数SysTick_Config()

(1)函数实现

该函数定义在core_cm3.h中,毕竟是Cortex-M3自带的简单定时器,定义到这个文件夹很合理。
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

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 */
}

该函数是由返回值的,返回0和1,返回0表示设置成功,返回1表示重装载失败(注意不是设置失败

在看一下SysTick_LOAD_RELOAD_Msk标识:

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

而这个值就是我们之前总结的最大的重装载值,即LOAD寄存器23:0全为1.如此的话if (ticks > SysTick_LOAD_RELOAD_Msk) return (1);这行代码就不难理解,因为传递的重装载值超过了最大可装载值,所以返回1表示重装载失败。

虽然能返回1报错,但是程序并没有结束,还会继续往下执行:SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;这行代码就是在设置装载值的,如果ticks小于等于最大可装载值,则当传递的装载值ticks与0xffffff位与运算后,结果还是ticks;如果ticks大于最大可装载值,(eg:0x1000000,结果为0;eg0x1000001,结果为1)经过位与运算,会把重装载值运算到小于最大可装载值的数。

然后是设置中断优先级,这个先不总结了,等总结完中断这一块应该就清楚了。

SysTick->VAL = 0;这行代码是对VAL的写操作,之前总结过,如果是写操作,就将VAL寄存器清零(注意这是对VAL寄存器清零且不产生中断的方法!)同时清除CTRL的位24为0,此时虽然VAL为0,但是CTRL的位24仍为0,系统判断该计数器没有倒计数到0,所以不会产生中断。

接下来的代码:

  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;   

将标识改为16进制数:(ul表示无符号长整型)

  SysTick->CTRL  = 1ul | 1ul |1ul;   

即:

  SysTick->CTRL  = 1;   

即对CTRL寄存器第0位赋1,其它位赋0。

(2)总结

通过该函数设置后,状态如下:

①如果重装载值大于最大可装载值,则返回1,且继续向下执行,最后再返回0

②如果重装载值小于等于最大可装载值,继续向下执行,最后返回0

③如果重装载值大于最大可装载值,会通过位与运算将重装载值减小到小于最大可装载值的一个数。

④清空VAL寄存器

⑤使能SysTick定时器

3、中断服务函数SysTick_Handler()

中断服务函数定义在stm32f10x_it.h中
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

这个函数默认有函数体,有时候不想用这个函数体,就注释掉,再把这个中断服务函数框架复制到自己想要的文件中继续编写,当然如果SysTick计数器不产生中断时(CTRL寄存器位1始终置0),就不用写中断服务函数了。

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

三、利用SysTick写一个LED闪烁实验(0.5s循环)

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

LED配置那一块就不再总结了,可以看我这个系列的第一篇博客。

1、初始化LED

#include "stm32f10x.h"
#include "led.h"
 int main(void)
 {	
	 LED_Init();
 }

2、设置SysTick时钟源

为了让SysTick倒计数器计数慢点,重装载值尽量小一点,采用小的时钟周期,即外部时钟源、AHB总线时钟的1/8。

	 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟源72MHZ/8=9MHZ
	 //SysTick->CTRL &=0xFFFFFFFB

3、设置CTRL寄存器

到计数到0后会产生中断,进入中断服务函数,所以CTRL寄存器的位1置1:

	 SysTick->CTRL|=1<<1;//允许产生中断

4、设置重装载值

SysTick时钟为9MHZ,一个时钟周期计数一次,则:

1s 计数: 9000000次
1ms计数:9000次
1us计数: 9次

如果让LED0每0.5s闪一次,即每0.5s中断一次,在中断服务函数中对LED0取反。

则需要计数0.5×9000000=4500000次

而最大可重装载值为0xffffff=16777215,没有超过最大可装载值,所以把4500000装载到LOAD寄存器中,完成一次计数即可。

	SysTick->LOAD=4500000;//重装载初值

5、清空VAL寄存器

#include "stm32f10x.h"
#include "led.h"
 int main(void)
 {	
	 LED_Init();
	 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟源72MHZ/8=9MHZ
	 //SysTick->CTRL &=0xFFFFFFFB 
	 SysTick->CTRL|=1<<1;//允许产生中断
	 SysTick->LOAD=4500000;//重装载初值
	 SysTick->VAL=0;//清空VAL寄存器
 }

6、使能SysTick时钟,开始计数

	SysTick->CTRL|=1;//使能SysTick时钟,开始计数

7、屏蔽原有的中断服务函数

中断服务函数定义在stm32f10x_it.h中,可以用全局搜索:
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

或者直接Ctrl+F
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯
打开函数体:
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

将它注释掉
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

8、编写中断服务函数

void  SysTick_Handler()
{
	LED0=~LED0;
}

这个代码只能持续一次,因为计数完成后并没有重新装载初值,还得下一步

9、重复装载初值

在中断服务函数中再次装载初值,每中断一次就装载一次初值:

oid  SysTick_Handler(void)
{
	LED0=~LED0;
	SysTick->LOAD=4500000;//重装载初值
	SysTick->VAL=0;//清空VAL寄存器
}

10、完整代码

//led.h
#ifndef __LED_H 
#define __LED_H	 
void LED_Init(void);	
#endif
//led.c
#include "led.h"
#include "stm32f10x.h"
void LED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE , ENABLE);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	 
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
}
//main.c
#include "stm32f10x.h"
#include "led.h"
#include "sys.h"
#define LED0 PBout(5)	// DS0
 int main(void)
 {	
	 LED_Init();
	 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟源72MHZ/8=9MHZ
	 //SysTick->CTRL &=0xFFFFFFFB
	 SysTick->CTRL|=1<<1;//允许产生中断
	 
	 SysTick->LOAD=4500000;//重装载初值
	 SysTick->VAL=0;//清空VAL寄存器	
	 SysTick->CTRL|=1;//使能SysTick时钟,开始计数
	 while(1)
	 {
	 }
 }

void  SysTick_Handler(void)
{
	LED0=~LED0;
	SysTick->LOAD=4500000;//重装载初值
	SysTick->VAL=0;//清空VAL寄存器
}

11、效果动图

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

四、利用SysTick写一个LED闪烁实验(3s循环)

之前的计数没有超过最大可装载值,这次搞一个超过最大可装载值的实验,就让LED3s闪烁

1、思路

SysTick时钟为9MHZ,一个时钟周期计数一次,则:

1s 计数: 9000000次
1ms计数:9000次
1us计数: 9次

如果让LED每3s亮、3s灭,则中断一次需要的计数值:

9000000×3=27000000>(2^24 -1)

所以需要多次重装载

如重装载10次,每次装载值为2700000

程序中需要改变的地方:
STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

2、完整代码

//led.h
#ifndef __LED_H 
#define __LED_H	 
void LED_Init(void);	
#endif
//led.c
#include "led.h"
#include "stm32f10x.h"
void LED_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOE , ENABLE);
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStruct);
		
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;
	GPIO_Init(GPIOE,&GPIO_InitStruct);
	 
	GPIO_SetBits(GPIOB, GPIO_Pin_5);
	GPIO_SetBits(GPIOE, GPIO_Pin_5);
}
//main.c
#include "stm32f10x.h"
#include "led.h"
#include "sys.h"
#define LED0 PBout(5)	// DS0
static u16 a=10;
 int main(void)
 {	
	 LED_Init();
	 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟源72MHZ/8=9MHZ
	 //SysTick->CTRL &=0xFFFFFFFB
	 SysTick->CTRL|=1<<1;//允许产生中断
	 
	 SysTick->LOAD=2700000;//重装载初值
	 SysTick->VAL=0;//清空VAL寄存器	
	 SysTick->CTRL|=1;//使能SysTick时钟,开始计数
	 while(1)
	 {
	 
	 }
 }

void  SysTick_Handler(void)
{
	a--;
	if(!a)
	{
		LED0=~LED0;
		a=10;
	}
	SysTick->LOAD=2700000;//重装载初值
	SysTick->VAL=0;//清空VAL寄存器
}

3、效果动图

STM32F103五分钟入门系列(八)SysTick滴答定时器+SysTick中断实现跑马灯

上一篇:基于51单片机的智能鱼缸温度控制器protues仿真


下一篇:智能仓库物流控制系统(2)