Bootstrap

第7课【SysTick定时器】中断 系统定时器 寄存器

基本知识框架

在这里插入图片描述

课堂笔记

SysTick定时器简介

什么是SysTick定时器

SysTick:系统定时器,和普通定时器类似,它也可以定时,但是却是有特殊用途。它有24bits,定时器最大数值是2^24,并且计数时是向下递减计数,每次递减1,递减一次的时间是1/SYSCLK,一般来说SYSCLK为72MHz。当寄存器中的值减少到0时,会产生一个中断信号,进而执行相应的中断服务函数

SysTick定时器的作用

基于CM3内核的单片机,都会有SysTick定时器。这使得使用SysTick的软件都很容易移植。SysTick除了可以做正常的定时器使用以外,还有一个最重要的特殊作用:给操作系统提供时基,为操作系统提供必要的时钟节拍,从而为操作系统的任务调度提供一个稳定的心跳

SysTick寄存器

与SysTick相关的寄存器有4个:

  • CTRL控制及状态寄存器
  • LOAD重装载数值寄存器
  • VALUE当前数值寄存器
  • CALIB校准数值寄存器

但是一般使用情况下,只使用到前三个寄存器。最后一个校准数值寄存器不经常使用

CTRL控制及状态寄存器

在这里插入图片描述

LOAD重装载数值寄存器

在这里插入图片描述

VALUE当前数值寄存器

在这里插入图片描述

CALIB校准数值寄存器

在这里插入图片描述

SysTick实战

需求

使用SysTick产生1s的定时,并设计程序让LED每1s闪烁一次

硬件设计和软件设计

硬件上只需要LED以及自带的SysTick定时器

软件需要依赖于标准库函数中的core_m3.c和core_m3.h,这两个文件中有用于设置SysTick的函数。
同时需要建立两个文件bsp_systick.c和bsp_systick.h,用于编写函数

要点

使用SysTick定时器的简要流程:

  1. 设置RELOAD重载数值寄存器
  2. 设置VALUE当前数值寄存器
  3. 设置CTRL控制及状态寄存器

函数实战

SysTick配置函数

库文件 core_cm3.h 中的SysTick配置库函数代码主体部分

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
	// 判断ticks有没有超过定时器的2^24最大值
	if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
	{
		return (1UL);
	}

	// 设置重装载寄存器
	SysTick->LOAD = (uint32_t)(ticks - 1UL);

	// 设置中断优先级
	NVIC_Setriority(SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
	
	// 设置当前数值寄存器
	SysTick->VAL = 0UL;

	// 设置SysTick定时器的时钟源为AHBCLK 72MHz
	// 使能SysTick定时器中断
	// 使能SysTick定时器
	SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
					SysTick_CTRL_TICKINT_Msk   |
					SysTick_CTRL_ENABLE_Msk;

	return (0UL);
}
SysTick的优先级设置

其中有一个用于设置优先级的函数NVIC_Setriority(),这个函数也是在core_m3.h中定义的
NVIC_Setriority()代码主体部分

__STATIC_INLINE void NVIC_SetPririty(IRQn_Type IRQn, uint32_t priority)
{
	if ((int32_t)IRQn < 0)
	{
		SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = 
		(uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uiny32_t)0xFFUL);
	}
	else
	{
		NVIC->IP[((uint32_t)(int32_t)IRQn)] = 
		(uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
	}
}

函数解析
判断IRQn的大小,如果小于0,则此中断为系统中断,大于0,则此中断为外设中断。系统中断的优先级是由内核外设SCB的寄存器SHPRx(x=1~3)控制,而外设中断是由内核外设NVIC中的寄存器IPx控制


系统中断的优先级和外设中断的优先级比较
对于SysTick定时器,由于它是系统级的外设,那么在优先级设置方面会有特殊优待吗?
没有的
对于SysTick定时器产生的中断,它的优先级由内核外设SCB中的寄存器SHPRx控制,SHPRx是32位的寄存器,并且只能按字节进行访问,同时在STM32F103中只使用到寄存器单个字节的前4位,这就意味着SysTick定时器的中断优先级仅能设置为0~15,转换为二进制也就0000~1111
无论是系统中断还是外设中断,统一都是由NVIC进行管理,所以说,在进行优先级比较时,可以将寄存器SHPRx和寄存器IPRx中前四位进行比较,数值越小的中断优先级越高,也遵循之前讨论的优先级比较逻辑

SysTick的初始化

在了解完 SysTick_Config()函数之后,可以通过调用它来配置定时器达到想要的效果

但在此之前要了解SysTick时间的相关计算:
AHBCLK时钟频率为72MHz
SysTick定时器减1所用的时间Tsys = 1 / SYSCLK = 1 / 72 000 000 (s)
所以当重装载数值为720时,产生一次中断信号的时间就为720 * Tsys = 720 / 72 000 000 = 1 / 100 000 (s) = 10us

所以首先,要先给SysTick定时器,赋上重装载数值SystemCoreClock/100000,SystemCoreClock的值通过宏定义定义为72M,即72 000 000
代码实现如下

// SysTick定时器初始化
void SysTick_Init(void)
{
	if (SysTick_Config(SystemCoreClock/100000))
	{
		// ERROR
		while (1);
	}
}
定时函数的实现

编写定时函数,输入参数为定时的时间,只有指定时间过后,代码才会继续执行
代码实现如下

// 定时函数
void Delay_nTime_10us(__IO u32 nTime)
{
	if (nTime < 0)
	{
		return;
	}

	DelayTime = nTime; // DelayTime是一个全局变量,并且和SysTick中断服务函数相关联

	while (DelayTime > 0);
}
SysTick中断服务函数的实现

编写SysTick中断服务函数,用于联系SysTick中断信号和定时函数
代码实现如下

// 中断服务函数
void SysTick_Handler(void)
{
	DelayTime_Decrement(); // 用于计算中断产生次数的,每产生一次中断,DelayTime递减1
}
// DelayTime_Decrement()函数
void DelayTime_Decrement(void)
{
	if (DelayTime > 0)
	{
		DeylayTime--;
	}
}
总结SysTick定时器使用流程
  • 通过SysTick_Config()函数,配置产生中断的时间Tsys
  • 通过定时函数,输入参数nTime,使得代码在nTime * Tsys后执行
  • 通过中断服务函数中的DelayTime_Decrement函数,联系输入参数nTime和中断产生时间Tsys

最后通过调用Delay_nTime_10us()函数可以实现定时指定时间后执行代码,例如Delay_nTime_10us(100000)),那么100000* 10us (1s)后,代码会继续执行

主函数实现

main.c中的代码实现如下

int main (void)
{
	// 配置LED引脚
	LED_GPIO_Config();
	
	// SysTick定时器初始化
	SysTick_Init();

	// 业务代码
	while (1)
	{
		// 红绿蓝三色灯,间隔1s换颜色
		LED_RED;
		Delay_nTime_10us(100000));
		LED_GREEN;
		Delay_nTime_10us(100000));
		LED_BLUE;
		Delay_nTime_10us(100000));
	}
}

基本知识框架Xmind文件下载

链接:资源下载

;