STM32单片机学习笔记(九)-SysTick系统定时器

写在前面:本系列内容均为自学笔记,参考资料为野火指南者开发板资料及芯片参考手册等,使用野火指南者开发板进行学习,该系列内容仅用于记录笔记,不做其他用途,笔记的内容可能会存在不准确或者错误等,如有大佬看到错误内容还望能够评论指正,感谢各位。
本节包括前几节的程序,请参考野火开发板资料,里面由更加清晰的教学,野火B站账号:野火官方B站账号链接

参考资料

《STM32F10x芯片参考手册-中文版》、《STM32F10x Cortex-M3编程手册-英文版》、《CM3权威指南-中文版》、《野火开发板资料》等;

学习目标

1、简单了解SysTick;
2、写一个毫秒级的延时函数,并尝试用其闪烁LED灯;

电路图

本次实验需要闪烁LED灯,所以用到下方电路图:
图9-1:
在这里插入图片描述

一、简单了解SysTick-系统定时器

1、SysTick简介

SysTick–系统定时器,存在于内核,嵌套在NVIC,它是一个24bit的向下递减的计数器,并且每计数一次的时间为1/SYSCLK,通常情况下,STM32F103中SYSCLK使用72M;当重装载数值寄存器倒数计数到0时,系统定时器就会产生一次中断,以此循环往复;
另外提一句,这个系统定时器在《芯片参考手册-中文版》中的内容其实就一句话,就是下面这句:
系统嘀嗒校准值固定为9000,当系统嘀嗒时钟设定为9MHz(HCLK/8的最大值),产生1ms时间基准。
这里的系统滴答说的就是SysTick;

2、SysTick寄存器描述

官方固件库中的关于SysTick寄存器的结构体定义

typedef struct
{
  __IO uint32_t CTRL;                         /*!< Offset: 0x00  SysTick控制及状态寄存器 */
  __IO uint32_t LOAD;                         /*!< Offset: 0x04  SysTick重装载数值寄存器 */
  __IO uint32_t VAL;                          /*!< Offset: 0x08  SysTick当前数值寄存器 */
  __I  uint32_t CALIB;                        /*!< Offset: 0x0C  SysTick校准数值寄存器 */
} SysTick_Type;
①、STK_CTRL:SysTick控制及状态寄存器

该部分内容请参考《STM32F10x Cortex-M3编程手册-英文版》;
表9-1:STK_CTRL(只有4位有效)

位段名称类型复位值描述
16COUNTFLAGRW0如果在上次读取该寄存器后,SysTick已经计数到0,则该位置1,当读取该位后,该位将自动清0
2CLKSOURCERW0选择时钟源:
0,表示选择AHB/8后的时钟作为时钟源
1,表示选择AHB作为时钟源
1TICKINTRW00:SysTick倒数计数到0时,无动作
1:SysTick倒数计数到0时,产生SysTick异常请求(中断)
可以通过读取COUNTFLAG标志位是否置1来确定倒数计数是否到0
0ENABLERW00:不使能SysTick
1:使能SysTick

上述表格中时钟源选择位有两个选项:AHB和AHB/8,这两个选项在时钟树上可以看到,下图是关于这部分的截图,没截全整个时钟树,整个时钟树请看《STM32F10x芯片参考手册-中文版》的第6章RCC的6.2中的图8:
图9-2:(黄框部分)
在这里插入图片描述

②、STK_LOAD:SysTick重装载数值寄存器

表9-2:STK_LOAD(前24位有效)

位段名称类型复位值描述
[23:0]RELOADRW0用于存放当SysTick计数器启用并倒数计数到0时,需要重新装载到STK_VAL的值(预设值),最大2^24
③、STK_VAL:SysTick当前数值寄存器

表9-3:STK_VAL(前24位有效)

位段名称类型复位值描述
[23:0]CURRENTRW0包含SysTick的当前值:
执行读操作时,将返回SysTick的当前值
执行写操作时,将被清0,同时STK_CTRL寄存器中的COUNTFLAG位清0
④、STK_CALIB:SysTick校准数值寄存器

本寄存器描述将英文描述截图也放出来,主要因为描述中并未像中文描述那样一目了然,下表中的中文描述也是笔者根据英文翻译和其他资料得来的,英文原文放出方便各位自己理解;
表9-3:STK_CALIB(前24位及最后两位有效)

位段名称类型复位值描述英文描述
31NOREFR00:时钟频率为AHB/8的时钟源可用
1:时钟频率为AHB/8的时钟源不可用
在这里插入图片描述
30SKEWR00:校准值是准确的1ms
1:校准值不是准确的1ms
在这里插入图片描述
[23:0]TENMSR0(英文翻译):表示SysTick计数器以AHB/8作为时钟源时的校准值,该值与芯片有关,可参考《芯片参考手册》SysTick校准值部分,当AHB以最大频率编程时,SysTick周期为1ms;
如果校准信息未知,则从处理器时钟或外部时钟的频率计算所需的校准值。
在这里插入图片描述

3、部分固件库程序

①、SysTick配置库函数

该程序在官方库文件core_cm3.h中有定义,其中有英文释义,这里将英文释义稍微翻译一下,以便更好的理解:

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* 判断预设值,不能大于2^24,重装载寄存器24位最多2^24 */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* 设置重装载寄存器的初值 */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* 设置中断优先级 */
  SysTick->VAL   = 0;                                          /* 设置当前数值寄存器,清除上次数据 */
	// 设置系统定时器的时钟源为 AHBCLK=72M
	// 使能系统定时器中断
	// 使能定时器
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0);                                                  /* 配置成功返回0 */
}

上述程序使用时只需要直接调用即可,调用时设置好形参数据(预设值),之后系统就会根据预设值开始计数,倒计时到0后产生中断,然后重装载数据寄存器将数据重新装载到计数器中,使其再次计数,往复循环;
另外这个函数中对中断优先级进行了设置,这个优先级实质上是内核的优先级,所以没有像外设那样的抢占优先级和子优先级的说法,不过在进行优先级比较时,可以按照外设那样进行比较,内核的优先级可以设置成0~15,共4bit,按照外设优先级的比较方式,系统预设的优先级是15,这个优先级是最低的,当然这个优先级可以手动修改,本文程序就不修改了;

②、SysTick配置库函数中的优先级配置函数
/*为指定的中断设置优先级。
 *中断号可以为正以指定外部(设备特定)中断,
 *也可以为负以指定内部(核心)中断。*/
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* 设置Cortex-M3系统中断的优先级 */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* 设置外设中断的优先级  */
}

IRQn:中断号,其定义请看库文件stm32f10x.h中的typedef enum IRQn定义,这是一个枚举类型,其中定义了可以使用的中断编号,内容较多这里不再放出,SysTick配置库函数中使用的SysTick_IRQn就在这个枚举中定义了;
priority:优先级,在SysTick配置库函数中使用的变量声明请看下面第③点;

③、SysTick配置库函数中涉及到的变量的声明

core_cm3.h中的部分声明如下

/* SysTick Reload Register Definitions */
#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)        /*!< SysTick LOAD: RELOAD Mask */
/* SysTick Control / Status Register Definitions */
#define SysTick_CTRL_CLKSOURCE_Pos          2                                             /*!< SysTick CTRL: CLKSOURCE Position */
#define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */

#define SysTick_CTRL_TICKINT_Pos            1                                             /*!< SysTick CTRL: TICKINT Position */
#define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */

#define SysTick_CTRL_ENABLE_Pos             0                                             /*!< SysTick CTRL: ENABLE Position */
#define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)               /*!< SysTick CTRL: ENABLE Mask */

stm32f10x.h中的部分声明如下

#define __NVIC_PRIO_BITS          4 /*!< STM32 uses 4 Bits for the Priority Levels    */

4、延时计算公式

根据SysTick的特性以及固件库配置函数我们可以定义延时函数,这时需要用到下面两个函数,以便更加准确的设定延时时间:
  t=RELOAD/CLK,
  t:时间;RELOAD:重装载数值寄存器的值;CLK:系统频率,这里设定系统频率为72M;
所以可以得到以下两个时间计算公式:
  t=72/72M=1us,//设置1微妙
  t=72000/72M=1ms;//设置1毫秒

二、程序

1、程序思路

SysTick配置库函数SysTick_Config()可以直接调用,如果正确调用,则说明SysTick各寄存器已经配置完成,此时系统开始计时,我们只需要等待系统计时完成,然后判断标志位COUNTFLAG即可;
>>使用SysTick_Config()初始化SysTick的寄存器,并设定重装载数值寄存器的值
>>设定for循环,判断标志位,用于判断系统是否延时完成
>>延时完成,关闭SysTick使能,关闭延时

2、延时程序

程序9-1:bsp_systick.c中的程序

#include "bsp_systick.h"

void SysTick_Delay_us(uint32_t us)//微秒定义
{
	uint32_t i;//计时变量
	SysTick_Config(72);//系统时钟为72M,设置72正好是1us
	for(i = 0; i < us; i++)
	{//计时到0以后,CRTL寄存器第16位将被置1
		while(!(SysTick->CTRL & (1<<16)));//判断STK_CTRL第16位是否为1,为1则跳出循环
	}
	SysTick->CTRL  &= ~SysTick_CTRL_ENABLE_Msk;//延时完成,关掉定时器使能
}

void SysTick_Delay_ms(uint32_t ms)//微秒定义
{
	uint32_t i;//计时变量
	SysTick_Config(72000);//系统时钟为72M,设置72000正好是1ms
	for(i = 0; i < ms; i++)
	{//计时到0以后,CRTL寄存器第16位将被置1
		while(!(SysTick->CTRL & (1<<16)));//判断STK_CTRL第16位是否为1,为1则跳出循环
	}
	SysTick->CTRL  &= ~SysTick_CTRL_ENABLE_Msk;//延时完成,关掉定时器使能
}

程序9-2:bsp_systick.h中的程序

#ifndef __BSP_SYSTICK_H
#define __BSP_SYSTICK_H

#include "stm32f10x.h"
#include "core_cm3.h"

void SysTick_Delay_us(uint32_t us);
void SysTick_Delay_ms(uint32_t ms);

#endif /*__BSP_SYSTICK_H*/

上述程序中除了调用SysTick配置库函数以外,还设置了for循环,这个循环是为了判断系统是否已经延时完成,其内部的while循环就是判断条件,当STK_CTRL第16位为1时,说明系统已经计时完成,此时可以将使能关闭,使能关闭则计时结束,如果第16为不为1,则一直循环,直到该位置1;

3、其他配套程序

LED的初始化程序和主程序如下:
程序9-3:bsp_led.c

#include "bsp_led.h"

void LED_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;		//定义变量,方便赋值
	
	RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK,ENABLE);	//打开APB2时钟,GPIO挂载在APB2

	GPIO_InitStruct.GPIO_Pin = (LED_G_GPIO_PIN);	//设置需要用到的管脚,LED_G_GPIO_PIN看.h文件
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;	//设置输出模式为推挽输出
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;	//设置输出速率为50MHz,LED_G_GPIO_CLK看.h文件
	
	GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct);	//加上&,方便取值	//初始化GPIO
}

程序9-4:bsp_led.h

#ifndef _BSP_LED_H
#define _BSP_LED_H

#include "stm32f10x.h"		//要包含固件库的.h文件

#define LED_G_GPIO_PIN						GPIO_Pin_0				//定义绿灯管脚号
#define LED_G_GPIO_PORT						GPIOB					//定义用到的GPIO
#define LED_G_GPIO_CLK						RCC_APB2Periph_GPIOB	//定义RCC时钟寄存器
//下面几个声明可以不管,直接按照main中屏蔽掉的部分写也行
//下面用到的“\”符号为续行符,其后面不能由任何东西,意为这行下面的一行跟这行是一起的,分行写看起来比较清晰
#define ON									1
#define OFF									0		
#define LED(a)								if(a) \
												GPIO_ResetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN); \
											else \
												GPIO_SetBits(LED_G_GPIO_PORT,LED_G_GPIO_PIN);

void LED_GPIO_Config(void);		//.c文件中的函数声明

#endif	/*_BSP_LED_H*/

程序9-4:main.c

#include "stm32f10x.h"   // 相当于51单片机中的  #include <reg51.h>
#include "bsp_led.h"
#include "bsp_systick.h"
                                                                                         
int main(void)
{
	// 来到这里的时候,系统的时钟已经被配置成72M。
	LED_GPIO_Config();	//GPIO初始化
	while(1)
	{
		LED(OFF);		//关灯
		SysTick_Delay_ms(500);
		LED(ON);		//开灯
		SysTick_Delay_ms(500);
	}
}

本文内容还存在很多不足,等再有新的内容或者是修改之处,会直接在本文添加修改,感谢观看。

以上仅为笔记记录,不作教学等用途,感谢观看。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值