STM32系统定时器Systick详解

STM32系统定时器SysTick是一个内置于Cortex-M系列微控制器内核中的简单而有效的系统定时器,它为操作系统提供时间基准,或用于生成周期性的中断。以下是对STM32系统定时器SysTick的详细讲解:

一、基本概念

SysTick,全称System Tick Timer,是一个24位(在某些STM32型号中可能为32位,但常用于计数的有效位数为24位)的自动重载递减计数器。当SysTick计数到0时,如果使能了中断,则会产生一个SysTick中断请求,同时计数器会根据预设的值自动重载,并继续递减计数,从而实现周期性的定时功能。

二、工作原理

  1. 时钟源选择:SysTick定时器的时钟源通常是系统时钟(SYSCLK),也可以是系统时钟的1/8(具体取决于配置)。这使得SysTick定时器能够提供与系统时钟相关的精确定时功能。

  2. 计数器配置:SysTick定时器使用一个24位的自动递减计数器进行计时。在初始化时,需要设置计数器的初始值(重装载值),这决定了定时器开始计数的起点。

  3. 计数器递减:SysTick定时器的计数器从初始值开始递减,每个时钟周期减少一个计数值,直到计数器减至零。

  4. 中断或事件触发:当计数器减至零时,可以选择触发一个中断或一个事件。中断触发会生成一个中断请求,CPU会相应地跳转到SysTick中断处理程序执行特定的操作。事件触发则可以用于与其他外设或模块进行同步操作。

  5. 重载计数器:当计数器减至零后,如果使能了自动重载功能,则计数器的值会自动被重装载为预设的初始值,从而允许定时器继续下一个周期的计数。

三、寄存器介绍

SysTick定时器涉及的主要寄存器包括:

  1. SysTick 控制和状态寄存器(CTRL):用于配置SysTick定时器的工作模式、使能定时器和中断,并提供定时器当前状态的查询功能。

  2. SysTick 重装载值寄存器(LOAD):用于设置SysTick定时器的重装载值,即定时器计数器的初始值。

  3. SysTick 当前值寄存器(VAL):用于读取或写入当前SysTick定时器的计数器值。在读取时返回当前计数器的剩余值,在写入时清零计数器的值。

  4. SysTick 校准值寄存器(CALIB):用于读取SysTick定时器的校准值,包括重装载值(TENMS)和时钟源的准确度(SKEW、NOREF),用于校准定时器的溢出时间。

四、使用场景

SysTick定时器因其简单性和内置性,在STM32微控制器中得到了广泛应用,主要包括:

  1. 实现延时函数:通过配置SysTick定时器的重装载值和使能中断,可以实现毫秒级甚至微秒级的延时功能。

  2. 系统时钟节拍:在实时操作系统(RTOS)中,SysTick定时器通常用作系统的节拍定时器,用于调度任务的执行和维持操作系统的心跳。

  3. 定时任务调度:通过SysTick定时器中断,可以在特定时间间隔内执行一些周期性的任务或操作。

  4. 时间测量:SysTick定时器还可以用于程序执行时间的测量,帮助开发者优化代码。

五、配置步骤

要配置STM32的SysTick定时器,通常需要执行以下步骤:

  1. 选择时钟源:通过SysTick控制和状态寄存器(CTRL)选择SysTick定时器的时钟源。

  2. 设置重装载值:将所需的定时时间转换为对应的重装载值,并写入SysTick重装载值寄存器(LOAD)。

  3. 使能SysTick定时器:通过SysTick控制和状态寄存器(CTRL)使能SysTick定时器。

  4. (可选)使能中断:如果需要SysTick定时器在计数到0时产生中断,则通过SysTick控制和状态寄存器(CTRL)使能中断。

  5. 编写中断服务程序:如果使能了中断,则需要编写SysTick中断服务程序,以处理定时器中断触发时需要执行的任务。

STM32的SysTick定时器与通用定时器(General Purpose Timer)在功能和应用上存在一些异同点。以下是它们的详细比较:

相同点

  1. 定时功能:两者都具备定时功能,能够按照预设的时间间隔执行特定的操作或任务。

  2. 中断支持:SysTick定时器和通用定时器都支持中断功能,当定时时间到达时,可以产生中断请求,CPU会跳转到相应的中断服务程序执行操作。

  3. 时钟源选择:它们都可以选择不同的时钟源进行计时,尽管SysTick定时器的时钟源选择相对有限,通常为系统时钟或系统时钟的1/8,而通用定时器则更为灵活。

不同点

  1. 内置与外设
  2. SysTick定时器:是Cortex-M系列微控制器内核中的一个内置系统定时器,不需要额外的硬件资源,使用简单方便。
  3. 通用定时器:是STM32微控制器中的一个外设,具有更多的硬件资源和功能,如外部IO接口、输入捕获、输出比较、PWM生成等。
  4. 精度与范围
  5. SysTick定时器:通常是24位(或32位,但常用为24位)递减计数器,适用于较短时间间隔的定时需求。
  6. 通用定时器:通常为16位自动重装载计数器,支持向上、向下或向上/向下计数模式,适用于更广泛的定时范围和控制需求。
  7. 功能丰富性
  8. SysTick定时器:功能相对简单,主要用于系统节拍、延时函数和时间测量等。
  9. 通用定时器:功能丰富,除了基本的定时功能外,还支持输入捕获、输出比较、PWM生成等多种功能,适用于电机控制、测量系统、定时任务等多种应用场景。
  10. 配置与灵活性
  11. SysTick定时器:配置相对简单,主要通过控制和状态寄存器进行配置,适合用于系统级别的定时需求。
  12. 通用定时器:配置更为复杂和灵活,需要配置多个寄存器以实现不同的功能,如预分频器、计数器、自动重装载寄存器等,适用于需要精确控制和多种功能的场景。
  13. 应用场景
  14. SysTick定时器:更适用于系统层面的定时需求,如操作系统的心跳、任务调度等。
  15. 通用定时器:则更适用于需要精确控制和多种功能的场景,如电机控制、PWM信号生成、传感器数据采集等。

综上所述,STM32的SysTick定时器和通用定时器在功能和应用上各有侧重。SysTick定时器作为内核内置的系统定时器,具有简单、方便的特点,适用于系统层面的定时需求;

而通用定时器则以其丰富的功能和灵活性,在多种应用场景中发挥着重要作用。在选择使用哪种定时器时,需要根据具体的应用场景和需求来进行选择。

在STM32(以及许多基于ARM Cortex-M核心的微控制器)的编程环境中,SysTick_Type 结构体通常用于表示SysTick定时器的硬件寄存器映射。这个结构体并不是STM32 HAL库或标准外设库直接提供的标准部分,但它反映了SysTick定时器在硬件层面的寄存器布局,使得开发者可以通过访问这些寄存器来配置和控制SysTick定时器。

下面是对 SysTick_Type 结构体中各个成员的详细讲解:

成员变量

  • __IO uint32_t CTRL;
  • 描述:SysTick 控制和状态寄存器(Control and Status Register)。
  • 访问权限__IO 表示这个寄存器既可以被读取(Input)也可以被写入(Output)。
  • 用途:用于配置SysTick定时器的工作模式(如使能定时器、选择时钟源、使能中断等)以及查询定时器的状态(如计数是否到达0)。
  • 偏移地址0x00 表示这个寄存器在SysTick定时器内存映射中的起始地址。
  • __IO uint32_t LOAD;
  • 描述:SysTick 重装载值寄存器(Reload Value Register)。
  • 访问权限:同样为 __IO,即可读可写。
  • 用途:用于设置SysTick定时器计数器的初始值(重装载值)。当计数器从0递减到0时,如果使能了自动重载功能,则计数器的值会自动被设置为这个寄存器中的值,从而允许定时器继续下一个周期的计数。
  • 偏移地址0x04 表示这个寄存器在SysTick定时器内存映射中的地址。
  • __IO uint32_t VAL;
  • 描述:SysTick 当前值寄存器(Current Value Register)。
  • 访问权限__IO,即可读可写(尽管在实际使用中,写入此寄存器主要用于清零计数器)。
  • 用途:用于读取当前SysTick定时器的计数器值,或者通过写入0来清零计数器(注意,直接写入其他值的行为是未定义的)。
  • 偏移地址0x08
  • __I uint32_t CALIB;
  • 描述:SysTick 校准寄存器(Calibration Register)。
  • 访问权限__I 表示这个寄存器是只读的。
  • 用途:提供了SysTick定时器的校准值,包括重装载值(TENMS)和时钟源的准确度(SKEW、NOREF),用于校准定时器的溢出时间。这些值可以帮助开发者更准确地配置SysTick定时器,以适应不同的系统时钟频率和精度要求。
  • 偏移地址0x0C
  • 注意事项

  • 在实际编程中,SysTick_Type 结构体可能不是直接使用的,因为STM32的HAL库或标准外设库通常会提供更高层次的抽象来封装SysTick定时器的配置和控制。然而,了解这个结构体的成员和它们对应的硬件寄存器对于深入理解SysTick定时器的工作原理和进行底层编程是非常有帮助的。
  • 访问这些寄存器时,需要确保使用了正确的内存映射地址。在STM32中,这些地址通常在微控制器的参考手册或数据手册中给出。
  • 这个函数 SysTick_Config 是一个用于配置SysTick定时器的静态内联(inline)函数。内联函数通常用于优化代码,通过直接在调用点展开函数体来减少函数调用的开销。这个函数接受一个参数 ticks,表示SysTick定时器溢出前需要计数的周期数(或称为重装载值),并返回配置是否成功的状态。下面是对这个函数各部分的详细讲解:

    参数

  • uint32_t ticks:这是SysTick定时器溢出前需要计数的周期数。它决定了定时器的溢出时间,具体取决于系统时钟的频率。
  • 函数体

    1. 检查重装载值是否可能

      if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */

      这行代码检查传入的 ticks 值是否超过了SysTick重装载寄存器能够表示的最大值。SysTick_LOAD_RELOAD_Msk 是一个宏定义,表示重装载寄存器中有效位的掩码。如果 ticks 超过了这个值,函数返回 1 表示配置失败。

    2. 设置重装载寄存器

      SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */

      这行代码首先将 ticksSysTick_LOAD_RELOAD_Msk 进行按位与操作,以确保重装载值不会超出寄存器的有效位范围。然后,将结果减 1 并写入SysTick的重装载寄存器(SysTick->LOAD)。这是因为SysTick是一个递减计数器,当计数器从 1 递减到 0 时,会发生溢出(或称为更新事件),此时如果使能了中断,则会产生SysTick中断。

    3. 设置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型号和库版本而有所不同。

    4. 清零当前值寄存器

      SysTick->VAL = 0; /* Load the SysTick Counter Value */

      这行代码将SysTick的当前值寄存器(SysTick->VAL)清零。这通常是在配置完重装载寄存器之后进行的,以确保定时器从 0 开始计数。

    5. 使能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定时器。

    6. 返回成功状态

      return (0); /* Function successful */

      如果函数成功执行到这一步,说明SysTick定时器已经成功配置,函数返回 0 表示成功。

    注意事项

  • 这个函数假设了SysTick的时钟源已经配置好,并且没有在这个函数内部进行配置。
  • 不同的STM32型号和库版本可能会有不同的宏定义和函数实现,因此在具体使用时需要参考相应的文档。
  • 在某些情况下,可能需要禁用全局中断(使用 __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_delayus_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定时器禁用。让我们分解这行代码以更好地理解它的工作原理:

    1. SysTick_CTRL_ENABLE_Msk:这是一个宏定义,其值通常是SysTick控制寄存器中启用/禁用位(ENABLE位)的位掩码。在STM32中,这个位通常是最低位(位0),因此SysTick_CTRL_ENABLE_Msk可能被定义为1UL(或0x01,表示一个无符号长整型值,其中最低位被设置为1)。这个宏的确切值取决于具体的库和微控制器型号,但大多数ARM Cortex-M的SysTick定时器都遵循类似的布局。

    2. ~SysTick_CTRL_ENABLE_Msk:这是位取反操作。由于SysTick_CTRL_ENABLE_Msk是一个只有最低位被设置为1的掩码,对它进行取反操作会创建一个除了最低位外所有位都被设置为1的掩码。这个掩码在后续的操作中用于清除(即禁用)SysTick控制寄存器中的ENABLE位。

    3. 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定时器进行时间测量或产生延时后,将其禁用以避免不必要的中断或系统资源占用的常见做法。

    作者:翻斗花园第一后卫胡图图

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32系统定时器Systick详解

    发表评论