一、引言

在嵌入式系统中,定时器具有着广泛的使用,通过设置定时器的时钟频率、计数器Counter,便可得到相应的时间,时间截止后执行相应动作,比如中断等。

SysTick是Cortex-M内核处理器所集成的一个小型的定时器,是一个24位倒计数定时器(递减计数),计数到0时自动重装RELOAD寄存器中的值,睡眠模式也可以工作,可产生中断,中断号15,可以对其设置中断优先级。

该定时器在芯片手册上没有介绍,相关介绍在Contex-M3第8章NVIC与中断控制的最后一小节SysTick定时器

二、SysTick定时器寄存器介绍

1. 控制和状态寄存器CTRL(0xE000E010)

bit2的CLKSOURCE,参考前文时钟系统介绍基于STM32的嵌入式软件开发(3)——STM32F407ZGT6芯片时钟系统介绍 https://blog.csdn.net/m0_55824059/article/details/149296794?spm=1001.2014.3001.5502

bit2=0时,SysTick使用外部时钟STCLK,这里STCLK即为AHB总线时钟HCLK/8

bit2=1时,SysTickFCLK,也即是HCLK

2. 装载值寄存器LOAD(0xE000E014)

3. 当前值寄存器VAL(0xE000E018)

4. 校准寄存器CALIB(0xE000E01C)

三、基于标准固件库配置SysTick

1. 库函数介绍

SysTick_Config函数,该函数原型在core_cm4.h中,会将形参的Ticks配置到LOAD寄存器中,还会使能滴答定时器,开启中断,并设置时钟源为1:内核时钟

SysTick_CLKSourceConfig()用来选择时钟源,该函数原型位于misc.c文件中

注意:SysTick_Config函数中会使能滴答定时器,开启中断,设置默认中断优先级,并设置时钟源为1:内核时钟,所以SysTick_CLKSourceConfig必须在SysTick_Config后执行

2. 配置过程

我想要实现1ms计时,时间到执行中断,参考如下初始化代码

  • main函数中,首先将中断优先级分组设为2,表示两位抢占优先级,两位响应优先级
  • 采用外部时钟STCLK,即为AHB总线时钟HCLK/8,按照前文所述,本工程的HCLK=168MHz,所以STCLK=168/8=21MHz,那么按照1ms延时的需求,Load寄存器中需要设定的值为21MHz*(1ms/1s) = 21K Ticks,即21000
  • 注意:先设置Ticks,再设置时钟源
  • 而后通过NVIC_Init函数配置中断优先级,抢占优先级和响应优先级均为0
  • 最后,编写中断处理函数SysTick_Handler();按照上一篇文章所述,由于SysTick中断为内核级中断,标准固件库中已经有了空的中断处理函数在stm32f4xx_it.c中,可直接在该位置编写,我是将该位置的空函数注掉,在别的位置编写
#define MS_TICKS 21000

NVIC_InitTypeDef SysTick_IRQ_Init =
{
    SysTick_IRQn,
    0,
    0,
    ENABLE
};

void SysTick_Init(uint16_t in_ms)
{
    SysTick_Config(in_ms*MS_TICKS);
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

    NVIC_Init(&SysTick_IRQ_Init);
}

int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    SysTick_Init((uint16_t)1); //SysTick_IRQ_1ms 
}

uint8_t Task_10ms_TBD = 0;
void SysTick_Handler(void)
{
  static uint8_t ms_index = 0;
  ms_index++;
  
  if (ms_index == 10)
  {
    Task_10ms_TBD = 1;//10msTaskFlag
    ms_index = 0;
  }
}

四、delay文件夹

1. delay文件夹介绍

在GPIO点亮LED时,提到了正点原子的SYSTEM文件夹,里面有usart\delay\sys,在点亮LED的实验中使用到了sys文件夹中的位带操作,这里我们介绍delay文件夹,将该文件夹的.c/.h文件路径添加进工程

delay.h文件中为相应的函数声明

函数实体delay.c文件中,OS相关内容暂且不看,目前尚未用到

delay_init函数,形参为SYSCLK,也即是HCLK=168MHz,默认将定时器时钟设为STCLK,即为AHB总线时钟HCLK/8

delay_ms(),该函数实现ms级盲等延时,使用查询延时,即设置LOAD寄存器的值后,不断查询CTRL寄存器的COUNTFLAG位,如果该位置1,则表示时间到

delay_us函数同理

2. delay文件夹使用

正点原子提供的delay文件夹非常好用,比如,在LED点亮的实验中,当时由于没有介绍定时器,使用了for循环来进行延时。现在,我们可以使用delay文件夹所提供的函数

main.c代码如下,很简单便实现了LED每隔500ms亮灭一次,Beep每隔500ms响/停一次

#include "LED.h"
#include "Beep.h"
#include "Key.h"

int main(void)
{
    {
        // Initinal Functions
        delay_init(168);
        LED_init();
        BEEP_init();
       
    }

    while (1)
    {
        {
            // LED & BEEP Test
            LED0 = !LED0;
            LED1 = !LED1;
            BEEP = !BEEP; //too noisy
            delay_ms(500);
        }
    }
}

五、基于CubeMx使用SysTick

基于CubeMx软件生成HAL库,同样可以使用SysTick。但没有什么需要配置的,CubeMx不允许配置SysTick的相关寄存器,仅能在NVIC配置其中断优先级,默认中断优先级为:抢占优先级0,响应优先级0

生成代码后,HAL库函数中自带HAL_Delay()函数,使用SYSTICK实现毫秒盲等延时,比如在LED点亮实验中的代码如下

六、结语

本文介绍了STM32系统嘀嗒定时器(SysTick),介绍了其寄存器,固件库中断配置过程和正点原子官方提供的delay文件夹。另外,在HAL库中基本无需配置,直接使用HAL_Delay()函数即可。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐