STM32系统定时器Systick详解
STM32系统定时器SysTick是一个内置于Cortex-M系列微控制器内核中的简单而有效的系统定时器,它为操作系统提供时间基准,或用于生成周期性的中断。以下是对STM32系统定时器SysTick的详细讲解:
一、基本概念
SysTick,全称System Tick Timer,是一个24位(在某些STM32型号中可能为32位,但常用于计数的有效位数为24位)的自动重载递减计数器。当SysTick计数到0时,如果使能了中断,则会产生一个SysTick中断请求,同时计数器会根据预设的值自动重载,并继续递减计数,从而实现周期性的定时功能。
二、工作原理
-
时钟源选择:SysTick定时器的时钟源通常是系统时钟(SYSCLK),也可以是系统时钟的1/8(具体取决于配置)。这使得SysTick定时器能够提供与系统时钟相关的精确定时功能。
-
计数器配置:SysTick定时器使用一个24位的自动递减计数器进行计时。在初始化时,需要设置计数器的初始值(重装载值),这决定了定时器开始计数的起点。
-
计数器递减:SysTick定时器的计数器从初始值开始递减,每个时钟周期减少一个计数值,直到计数器减至零。
-
中断或事件触发:当计数器减至零时,可以选择触发一个中断或一个事件。中断触发会生成一个中断请求,CPU会相应地跳转到SysTick中断处理程序执行特定的操作。事件触发则可以用于与其他外设或模块进行同步操作。
-
重载计数器:当计数器减至零后,如果使能了自动重载功能,则计数器的值会自动被重装载为预设的初始值,从而允许定时器继续下一个周期的计数。
三、寄存器介绍
SysTick定时器涉及的主要寄存器包括:
-
SysTick 控制和状态寄存器(CTRL):用于配置SysTick定时器的工作模式、使能定时器和中断,并提供定时器当前状态的查询功能。
-
SysTick 重装载值寄存器(LOAD):用于设置SysTick定时器的重装载值,即定时器计数器的初始值。
-
SysTick 当前值寄存器(VAL):用于读取或写入当前SysTick定时器的计数器值。在读取时返回当前计数器的剩余值,在写入时清零计数器的值。
-
SysTick 校准值寄存器(CALIB):用于读取SysTick定时器的校准值,包括重装载值(TENMS)和时钟源的准确度(SKEW、NOREF),用于校准定时器的溢出时间。
四、使用场景
SysTick定时器因其简单性和内置性,在STM32微控制器中得到了广泛应用,主要包括:
-
实现延时函数:通过配置SysTick定时器的重装载值和使能中断,可以实现毫秒级甚至微秒级的延时功能。
-
系统时钟节拍:在实时操作系统(RTOS)中,SysTick定时器通常用作系统的节拍定时器,用于调度任务的执行和维持操作系统的心跳。
-
定时任务调度:通过SysTick定时器中断,可以在特定时间间隔内执行一些周期性的任务或操作。
-
时间测量:SysTick定时器还可以用于程序执行时间的测量,帮助开发者优化代码。
五、配置步骤
要配置STM32的SysTick定时器,通常需要执行以下步骤:
-
选择时钟源:通过SysTick控制和状态寄存器(CTRL)选择SysTick定时器的时钟源。
-
设置重装载值:将所需的定时时间转换为对应的重装载值,并写入SysTick重装载值寄存器(LOAD)。
-
使能SysTick定时器:通过SysTick控制和状态寄存器(CTRL)使能SysTick定时器。
-
(可选)使能中断:如果需要SysTick定时器在计数到0时产生中断,则通过SysTick控制和状态寄存器(CTRL)使能中断。
-
编写中断服务程序:如果使能了中断,则需要编写SysTick中断服务程序,以处理定时器中断触发时需要执行的任务。
STM32的SysTick定时器与通用定时器(General Purpose Timer)在功能和应用上存在一些异同点。以下是它们的详细比较:
相同点
-
定时功能:两者都具备定时功能,能够按照预设的时间间隔执行特定的操作或任务。
-
中断支持:SysTick定时器和通用定时器都支持中断功能,当定时时间到达时,可以产生中断请求,CPU会跳转到相应的中断服务程序执行操作。
-
时钟源选择:它们都可以选择不同的时钟源进行计时,尽管SysTick定时器的时钟源选择相对有限,通常为系统时钟或系统时钟的1/8,而通用定时器则更为灵活。
不同点
- 内置与外设:
- SysTick定时器:是Cortex-M系列微控制器内核中的一个内置系统定时器,不需要额外的硬件资源,使用简单方便。
- 通用定时器:是STM32微控制器中的一个外设,具有更多的硬件资源和功能,如外部IO接口、输入捕获、输出比较、PWM生成等。
- 精度与范围:
- SysTick定时器:通常是24位(或32位,但常用为24位)递减计数器,适用于较短时间间隔的定时需求。
- 通用定时器:通常为16位自动重装载计数器,支持向上、向下或向上/向下计数模式,适用于更广泛的定时范围和控制需求。
- 功能丰富性:
- SysTick定时器:功能相对简单,主要用于系统节拍、延时函数和时间测量等。
- 通用定时器:功能丰富,除了基本的定时功能外,还支持输入捕获、输出比较、PWM生成等多种功能,适用于电机控制、测量系统、定时任务等多种应用场景。
- 配置与灵活性:
- SysTick定时器:配置相对简单,主要通过控制和状态寄存器进行配置,适合用于系统级别的定时需求。
- 通用定时器:配置更为复杂和灵活,需要配置多个寄存器以实现不同的功能,如预分频器、计数器、自动重装载寄存器等,适用于需要精确控制和多种功能的场景。
- 应用场景:
- SysTick定时器:更适用于系统层面的定时需求,如操作系统的心跳、任务调度等。
- 通用定时器:则更适用于需要精确控制和多种功能的场景,如电机控制、PWM信号生成、传感器数据采集等。
综上所述,STM32的SysTick定时器和通用定时器在功能和应用上各有侧重。SysTick定时器作为内核内置的系统定时器,具有简单、方便的特点,适用于系统层面的定时需求;
而通用定时器则以其丰富的功能和灵活性,在多种应用场景中发挥着重要作用。在选择使用哪种定时器时,需要根据具体的应用场景和需求来进行选择。
在STM32(以及许多基于ARM Cortex-M核心的微控制器)的编程环境中,SysTick_Type
结构体通常用于表示SysTick定时器的硬件寄存器映射。这个结构体并不是STM32 HAL库或标准外设库直接提供的标准部分,但它反映了SysTick定时器在硬件层面的寄存器布局,使得开发者可以通过访问这些寄存器来配置和控制SysTick定时器。
下面是对 SysTick_Type
结构体中各个成员的详细讲解:
成员变量
__IO uint32_t CTRL;
__IO
表示这个寄存器既可以被读取(Input)也可以被写入(Output)。0x00
表示这个寄存器在SysTick定时器内存映射中的起始地址。__IO uint32_t LOAD;
__IO
,即可读可写。0x04
表示这个寄存器在SysTick定时器内存映射中的地址。__IO uint32_t VAL;
__IO
,即可读可写(尽管在实际使用中,写入此寄存器主要用于清零计数器)。0x08
。__I uint32_t CALIB;
__I
表示这个寄存器是只读的。0x0C
。注意事项
SysTick_Type
结构体可能不是直接使用的,因为STM32的HAL库或标准外设库通常会提供更高层次的抽象来封装SysTick定时器的配置和控制。然而,了解这个结构体的成员和它们对应的硬件寄存器对于深入理解SysTick定时器的工作原理和进行底层编程是非常有帮助的。这个函数 SysTick_Config
是一个用于配置SysTick定时器的静态内联(inline)函数。内联函数通常用于优化代码,通过直接在调用点展开函数体来减少函数调用的开销。这个函数接受一个参数 ticks
,表示SysTick定时器溢出前需要计数的周期数(或称为重装载值),并返回配置是否成功的状态。下面是对这个函数各部分的详细讲解:
参数
uint32_t ticks
:这是SysTick定时器溢出前需要计数的周期数。它决定了定时器的溢出时间,具体取决于系统时钟的频率。函数体
-
检查重装载值是否可能:
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
这行代码检查传入的
ticks
值是否超过了SysTick重装载寄存器能够表示的最大值。SysTick_LOAD_RELOAD_Msk
是一个宏定义,表示重装载寄存器中有效位的掩码。如果ticks
超过了这个值,函数返回1
表示配置失败。 -
设置重装载寄存器:
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
这行代码首先将
ticks
与SysTick_LOAD_RELOAD_Msk
进行按位与操作,以确保重装载值不会超出寄存器的有效位范围。然后,将结果减1
并写入SysTick的重装载寄存器(SysTick->LOAD
)。这是因为SysTick是一个递减计数器,当计数器从1
递减到0
时,会发生溢出(或称为更新事件),此时如果使能了中断,则会产生SysTick中断。 -
设置SysTick中断优先级:
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
这行代码使用
NVIC_SetPriority
函数设置SysTick中断的优先级。SysTick_IRQn
是SysTick中断的编号,(1<<__NVIC_PRIO_BITS) - 1
通常用于将SysTick中断的优先级设置为最低(在Cortex-M系列微控制器中,优先级数值越大,优先级越低)。注意,这里的实现可能因不同的STM32型号和库版本而有所不同。 -
清零当前值寄存器:
SysTick->VAL = 0; /* Load the SysTick Counter Value */
这行代码将SysTick的当前值寄存器(
SysTick->VAL
)清零。这通常是在配置完重装载寄存器之后进行的,以确保定时器从0
开始计数。 -
使能SysTick定时器和中断:
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
这行代码通过写入SysTick的控制和状态寄存器(
SysTick->CTRL
)来使能SysTick定时器和中断。SysTick_CTRL_CLKSOURCE_Msk
用于选择SysTick的时钟源(通常是系统时钟或系统时钟的1/8),SysTick_CTRL_TICKINT_Msk
用于使能SysTick中断,SysTick_CTRL_ENABLE_Msk
用于使能SysTick定时器。 -
返回成功状态:
return (0); /* Function successful */
如果函数成功执行到这一步,说明SysTick定时器已经成功配置,函数返回
0
表示成功。
注意事项
__disable_irq()
)来保护SysTick的配置过程,以防止在配置过程中发生中断。然而,在这个特定的函数实现中并没有这样做。实现使用系统定时器延迟的功能
主函数与被调用函数
#include "stm32f10x.h" // 包含STM32F10x系列微控制器的标准外设库头文件
#include "systick.h" // SysTick定时器的头文件(假设此文件包含SysTick_Config等函数的声明)
#include "led.h" // LED操作的头文件
int main(){
// 初始化LED相关的GPIO端口
LED_Init();
// 初始时将LED对应的GPIO端口设置为高电平,即LED熄灭(假设低电平点亮)
GPIO_SetBits(GPIOA, GPIO_Pin_1);
while(1)
{
// 将LED对应的GPIO端口设置为低电平,点亮LED
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
// 使用ms_delay函数实现500毫秒延时
ms_delay(500);
// 将LED对应的GPIO端口设置为高电平,熄灭LED
GPIO_SetBits(GPIOA,GPIO_Pin_1);
// 再次使用ms_delay函数实现500毫秒延时
ms_delay(500);
}
}
// ms_delay和us_delay函数的实现,用于实现毫秒和微秒级别的延时
// 注意:这些函数通过配置SysTick定时器来实现精确的延时
// ms_delay函数,使用SysTick定时器实现毫秒级延时
void ms_delay(uint32_t ms)
{
uint32_t i;
// 配置SysTick定时器,以72MHz的时钟频率,重装载值为72000-1,实现大约1ms的定时器中断
// 注意:这里直接调用SysTick_Config可能会影响之前SysTick的配置(如果有的话)
SysTick_Config(72000);
// 等待指定的毫秒数
for(i=0;i<ms;i++)
{
// 等待SysTick计数器计满(即SysTick中断发生),此时SysTick->CTRL的BIT16会被置位
while(!((SysTick->CTRL)&(1<<16)));
}
// 禁用SysTick定时器
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
}
// us_delay函数,使用SysTick定时器实现微秒级延时
// 注意:由于SysTick的分辨率和精度限制,微秒级延时可能不够精确
void us_delay(uint32_t us)
{
uint32_t i;
// 配置SysTick定时器,以72MHz的时钟频率,重装载值为72-1,理论上实现大约1微秒的定时器中断
// 但由于中断处理和其他系统延迟,实际延时可能会更长
SysTick_Config(72);
// 等待指定的微秒数
for(i=0;i<us;i++)
{
// 等待SysTick计数器计满
while(!((SysTick->CTRL)&(1<<16)));
}
// 禁用SysTick定时器
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
}
}
当然可以,让我们详细讲解一下在STM32F10x系列微控制器中,如何使用系统定时器(SysTick)来实现延时功能,以及所调用的函数(如SysTick_Config
和自定义的ms_delay
、us_delay
)是如何工作的,同时解释各种参数的含义。
SysTick定时器
SysTick是Cortex-M内核提供的一个24位的系统定时器,它可以从任何系统时钟源(通常是核心时钟)获取时钟信号,并在计数值达到某个预设的重装载值(RELOAD value)时产生一个中断。SysTick定时器非常适合用于实现操作系统的时间片、软件延时等功能。
SysTick_Config函数
SysTick_Config
函数是STM32标准外设库(或HAL库)中用于配置SysTick定时器的函数。它的原型通常如下(具体参数可能因库版本而异):
uint32_t SysTick_Config(uint32_t ticks); |
ticks
是SysTick定时器的重装载值。这个值决定了定时器溢出的时间间隔。由于SysTick是一个递减的定时器,它从某个值开始递减到0,然后产生中断(如果使能了中断)并可选地自动重载这个值继续计数。重装载值的计算
要计算ticks
参数的值,你需要知道SysTick定时器所使用的时钟源频率。在STM32F10x中,SysTick通常从系统时钟(SYSCLK)获取时钟信号,其频率可以通过PLL、HSI等时钟源进行配置。假设系统时钟频率为72MHz,则SysTick定时器的时钟频率也是72MHz。
要获得1毫秒的延时,你需要将SysTick的时钟频率(72,000,000 Hz)除以1,000(毫秒到秒的转换),然后取整(因为SysTick是整数计数器)。但是,由于SysTick是递减的,并且我们在它到达0时计数完成,所以实际上你需要计算的是从某个大于0的值递减到0所需的时间。因此,对于1毫秒的延时,重装载值通常是72000000 / 1000 - 1 = 71999
,但为了简化计算(因为SysTick是24位的,所以最大值接近65536),并且考虑到精度要求,通常会选择一个接近但稍小一点的数,如72000(这会导致延时稍长一点,因为计数器会多减1次才到达0)。
不容易理解的地方:
在STM32或其他基于ARM Cortex-M的微控制器中,SysTick->CTRL
是一个指向SysTick控制寄存器的指针,它用于配置和控制SysTick定时器的行为。SysTick_CTRL_ENABLE_Msk
通常是一个宏定义,它代表了SysTick控制寄存器中用于启用或禁用SysTick定时器的位掩码(Mask)。
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; |
这行代码的作用是将SysTick定时器禁用。让我们分解这行代码以更好地理解它的工作原理:
-
SysTick_CTRL_ENABLE_Msk
:这是一个宏定义,其值通常是SysTick
控制寄存器中启用/禁用位(ENABLE位)的位掩码。在STM32中,这个位通常是最低位(位0),因此SysTick_CTRL_ENABLE_Msk
可能被定义为1UL
(或0x01
,表示一个无符号长整型值,其中最低位被设置为1)。这个宏的确切值取决于具体的库和微控制器型号,但大多数ARM Cortex-M的SysTick定时器都遵循类似的布局。 -
~SysTick_CTRL_ENABLE_Msk
:这是位取反操作。由于SysTick_CTRL_ENABLE_Msk
是一个只有最低位被设置为1的掩码,对它进行取反操作会创建一个除了最低位外所有位都被设置为1的掩码。这个掩码在后续的操作中用于清除(即禁用)SysTick控制寄存器中的ENABLE位。 -
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
:这行代码首先将SysTick->CTRL
(即SysTick控制寄存器的当前值)与~SysTick_CTRL_ENABLE_Msk
(一个除了ENABLE位外所有位都为1的掩码)进行按位与(AND)操作。由于按位与操作会保留两个操作数中对应位都为1的位,而将任何与0进行按位与的位清零,因此这个操作实际上会清除(即将值设置为0)SysTick控制寄存器中的ENABLE位,而保留其他所有位的当前值。这样做的结果就是禁用了SysTick定时器。
简而言之,这行代码通过将SysTick控制寄存器中的ENABLE位清零来禁用SysTick定时器。这是在使用SysTick定时器进行时间测量或产生延时后,将其禁用以避免不必要的中断或系统资源占用的常见做法。
作者:翻斗花园第一后卫胡图图