zoukankan      html  css  js  c++  java
  • 第18章 SysTick—系统定时器

    本章参考资料《Cortex®-M7内核编程手册》-4.4 章节SysTick Timer(STK)4.38章节SHPRx,其中STK这个章节有SysTick的简介和寄存器的详细描述。因为SysTick是属于CM7内核的外设,有关寄存器的定义和部分库函数都在core_cm7.h这个头文件中实现。所以学习SysTick的时候可以参考这两个资料,一个是文档,一个是源码。

    18.1  SysTick简介

    SysTick—系统定时器是属于CM7内核中的一个外设,内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器,计数器每计数一次的时间为1/SYSCLK,一般我们设置系统时钟SYSCLK等于216MHz。当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。

    因为SysTick是属于CM7内核的外设,所以所有基于CM7内核的单片机都具有这个系统定时器,使得软件在CM7单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

    18.2  SysTick寄存器介绍

    SysTick—系统定时有4个寄存器,简要介绍如下。在使用SysTick产生定时的时候,只需要配置前三个寄存器,最后一个校准寄存器不需要使用。

    18-1 SysTick寄存器汇总

    寄存器名称

    寄存器描述

    CTRL

    SysTick控制及状态寄存器

    LOAD

    SysTick重装载数值寄存器

    VAL

    SysTick当前数值寄存器

    CALIB

    SysTick校准数值寄存器

    18-2 SysTick控制及状态寄存器

    位段

    名称

    类型

    复位值

    描述

    16

    COUNTFLAG

    R/W

    0

    如果在上次读取本寄存器后, SysTick 已经计到
    了 0,则该位为 1。

    2

    CLKSOURCE

    R/W

    0

    时钟源选择位,0=外部时钟,1=处理器时钟AHB

    1

    TICKINT

    R/W

    0

    1=SysTick倒数计数到 0时产生 SysTick异常请
    求,0=数到 0 时无动作。也可以通过读取COUNTFLAG标志位来确定计数器是否递减到0

    0

    ENABLE

    R/W

    0

    SysTick 定时器的使能位

    18-3 SysTick 重装载数值寄存器

    位段

    名称

    类型

    复位值

    描述

    23:0

    RELOAD

    R/W

    0

    当倒数计数至零时,将被重装载的值

    18-4 SysTick当前数值寄存器

    位段

    名称

    类型

    复位值

    描述

    23:0

    CURRENT

    R/W

    0

    读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick控制及状态寄存器中的COUNTFLAG 标志

    18-5 SysTick校准数值寄存器

    位段

    名称

    类型

    复位值

    描述

    31

    NOREF

    R

    0

    指示是否有参考时钟提供给处理器

    0:提供参考时钟

    1:不提供参考时钟

    如果器件不提供参考时钟,SYST_CSR.CLKSOURCE标志位为1,不可改写。

    30

    SKEW

    R

    1

    S指示TENMS的值是否精确

    0TENMS是精确值

    1TENMS不是精确值或者不提供

    不精确的TENMS值可以影响作为软件实时时钟节拍器的适用性。

    23:0

    TENMS

    R

    0

    重新加载 10ms (100Hz) 计时的值, 受系统时钟偏差的错误。如果值读取为零, 校准值未知。

    系统定时器的校准数值寄存器在定时实验中不需要用到。有研究过的朋友可以交流,起个抛砖引玉的作用。

    18.3  SysTick定时实验

    利用SysTick产生1s的时基,LED1s的频率闪烁。

    18.3.1  硬件设计

    SysTick属于单片机内部的外设,不需要额外的硬件电路,剩下的只需一个LED灯即可。

    18.3.2  软件设计

    这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。我们创建了两个文件:bsp_SysTick.cbsp_ SysTick.h文件用来存放SysTick驱动程序及相关宏定义,中断服务函数放在stm32f7xx_it.c文件中。

    1. 编程要点

    1、设置重装载寄存器的值

    2、清除当前数值寄存器的值

    3、配置控制与状态寄存器

    2. 代码分析

    SysTick 属于内核的外设,有关的寄存器定义和库函数都在内核相关的库文件core_cm7.h中。

    SysTick配置库函数

    代码 181SysTick配置库函数

    1 __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)

     2 {

     3     // 不可能的重装载值,超出范围

     4     if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) {

     5         return (1UL);

     6     }

     7

     8     // 设置重装载寄存器

     9     SysTick->LOAD  = (uint32_t)(ticks - 1UL);

    10

    11     // 设置中断优先级

    12     NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);

    13

    14     // 设置当前数值寄存器

    15     SysTick->VAL   = 0UL;

    16

    17     // 设置系统定时器的时钟源为AHBCLK=180M

    18     // 使能系统定时器中断

    19     // 使能定时器

    20     SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |

    21                      SysTick_CTRL_TICKINT_Msk   |

    22                      SysTick_CTRL_ENABLE_Msk;

    23     return (0UL);

    24 }

    用固件库编程的时候我们只需要调用将SysTick_Config函数封装好的库函数HAL_SYSTICK_Config ()即可,形参ticks用来设置重装载寄存器的值,最大不能超过重装载寄存器的值224,当重装载寄存器的值递减到0的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随其后设置好中断优先级,最后配置系统定时器的时钟为216MHz,使能定时器和定时器中断,这样系统定时器就配置好了,一个库函数搞定。

    SysTick_Config()库函数主要配置了SysTick中的三个寄存器:LOADVALCTRL,有关具体的部分看代码注释即可。其中还调用了固件库函数NVIC_SetPriority()来配置系统定时器的中断优先级,该库函数也在core_m7.h中定义,原型如下:

    1 __STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

     2 {

     3    if ((int32_t)IRQn < 0) {

     4    SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] =

     5    (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);

     6    } else {

     7    NVIC->IP[((uint32_t)(int32_t)IRQn)] =

     8    (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);

     9    }

    10 }

    因为SysTick属于内核外设,跟普通外设的中断优先级有些区别,并没有抢占优先级和子优先级的说法。在STM32F767中,内核外设的中断优先级由内核SCB这个外设的寄存器:SHPRxx=1.2.3)来配置。有关SHPRx寄存器的详细描述可参考《Cortex-M7内核编程手册》4.3.8章节。下面我们简单介绍下这个寄存器。

    SPRH1-SPRH3是一个32位的寄存器,但是只能通过字节访问,每8个字段控制着一个内核外设的中断优先级的配置。在STM32F767中,只有位7:3这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为:0~15,只有16个可编程优先级,数值越小,优先级越高。如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。

    18-6 系统异常优先级字段

    异常

    字段

    寄存器描述

    Memory management fault

    PRI_4

    SHPR1

    Bus fault

    PRI_5

    Usage fault

    PRI_6

    SVCall

    PRI_11

    SHPR2

    PendSV

    PRI_14

    SHPR3

    SysTick

    PRI_15

    如果要修改内核外设的优先级,只需要修改下面三个寄存器对应的某个字段即可。

     

    18-1 SHPR1寄存器

     

    18-2 SHPR2寄存器

     

    18-3 SHPR3寄存器

    在系统定时器中,配置优先级为(1UL << __NVIC_PRIO_BITS) - 1UL),其中宏__NVIC_PRIO_BITS4,那计算结果就等于15,可以看出系统定时器此时设置的优先级在内核外设中是最低的。

    1 // 设置系统定时器中断优先级

     2 NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);

    SysTick初始化函数

    代码 182 SysTick初始化函数

    1 /**

     2   * @brief  启动系统滴答定时器 SysTick

     3   * @param  

     4   * @retval

     5   */

     6 void SysTick_Init(void)

     7 {

     8     /* SystemFrequency / 1000    1ms中断一次

     9      * SystemFrequency / 100000  10us中断一次

    10      * SystemFrequency / 1000000 1us中断一次

    11      */

    12     if (HAL_SYSTICK_Config(SystemCoreClock / 100000)) {

    13         /* Capture error */

    14         while (1);

    15     }

    16 }

    SysTick初始化函数由用户编写,里面调用了SysTick_Config()这个固件库函数,通过设置该固件库函数的形参,就决定了系统定时器经过多少时间就产生一次中断。

    SysTick中断时间的计算

    SysTick定时器的计数器是向下递减计数的,计数一次的时间TDEC=1/CLKAHB,当重装载寄存器中的值VALUELOAD减到0的时候,产生中断,可知中断一次的时间TINT=VALUELOAD * TDEC中断= VALUELOAD/CLKAHB,其中CLKAHB =216MHz。如果设置为216,那中断一次的时间TINT=216/216MHz =1us。不过1us的中断没啥意义,整个程序的重心都花在进出中断上了,根本没有时间处理其他的任务。

    SysTick_Config(SystemCoreClock / 100000))

    SysTick_Config()的形我们配置为SystemCoreClock / 100000=216MHz /100000=2160,从刚刚分析我们知道这个形参的值最终是写到重装载寄存器LOAD中的,从而可知我们现在把SysTick定时器中断一次的时间TINT=2160/216MHz =10us

    SysTick定时时间的计算

    当设置好中断时间TINT后,我们可以设置一个变量t,用来记录进入中断的次数,那么变量t乘以中断的时间TINT就可以计算出需要定时的时间。

    SysTick定时函数

    现在我们定义一个微秒级别的延时函数,形参为nTime,当用这个形参乘以中断时间TINT就得出我们需要的延时时间,其中TINT我们已经设置好为10us。关于这个函数的具体调用看注释即可。

    1 /**

     2   * @brief   us延时程序,10us为一个单位

     3   * @param

     4   *   @arg nTime: Delay_us( 1 ) 则实现的延时为 1 * 10us = 10us

     5   * @retval  

     6   */

     7 void Delay_us(__IO u32 nTime)

     8 {

     9     TimingDelay = nTime;

    10

    11     while (TimingDelay != 0);

    12 }

    函数Delay_us()中我们等待TimingDelay0,当TimingDelay0的时候表示延时时间到。变量TimingDelay在中断函数中递减,即SysTick每进一次中断即10us的时间TimingDelay递减一次。

    SysTick中断服务函数

    1 void SysTick_Handler(void)

     2 {

     3     TimingDelay_Decrement();

     4 }

    中断复位函数调用了另外一个函数TimingDelay_Decrement(),原型如下:

    1 /**

     2   * @brief  获取节拍程序

     3   * @param  

     4   * @retval

     5   * @attention  SysTick 中断函数 SysTick_Handler()调用

     6   */

     7 void TimingDelay_Decrement(void)

     8 {

     9     if (TimingDelay != 0x00) {

    10         TimingDelay--;

    11     }

    12 }

    TimingDelay的值等于延时函数中传进去的nTime的值,比如nTime=100000,则延时的时间等于100000*10us=1s

    主函数

    1 int main(void)

     2 {

     3     /* 系统时钟初始化成216 MHz */

     4     SystemClock_Config();

     5     /* LED 端口初始化 */

     6     LED_GPIO_Config();

     7     /* 配置SysTick 10us中断一次,

     8     时间到后触发定时中断,

     9     *进入stm32f7xx_it.

    10     c文件的SysTick_Handler处理,通过数中断次数计时

    11     */

    12     SysTick_Init();

    13

    14     while (1) {

    15

    16         LED_RED;

    17         Delay_us(100000);   // 10000 * 10us = 1000ms

    18

    19         LED_GREEN;

    20         Delay_us(100000);   // 10000 * 10us = 1000ms

    21

    22         LED_BLUE;

    23         Delay_us(100000);   // 10000 * 10us = 1000ms

    24

    25     }

    26 }

    主函数中初始化了LEDSysTick,然后在一个while循环中以1s的频率让LED闪烁。

  • 相关阅读:
    LeetCode OJ 112. Path Sum
    LeetCode OJ 226. Invert Binary Tree
    LeetCode OJ 100. Same Tree
    LeetCode OJ 104. Maximum Depth of Binary Tree
    LeetCode OJ 111. Minimum Depth of Binary Tree
    LeetCode OJ 110. Balanced Binary Tree
    apache-jmeter-3.1的简单压力测试使用方法(下载和安装)
    JMeter入门教程
    CentOS6(CentOS7)设置静态IP 并且 能够上网
    分享好文:分享我在阿里8年,是如何一步一步走向架构师的
  • 原文地址:https://www.cnblogs.com/firege/p/9323029.html
Copyright © 2011-2022 走看看