GD32开发实战指南(基础篇) 第5章 跳动的心脏-Systick

发布者:心灵之旅最新更新时间:2024-11-08 来源: elecfans关键字:GD32  开发实战  Systick 手机看文章 扫描二维码
随时随地手机看文章

开发环境:

MDK:Keil 5.30

开发板:GD32F207I-EVAL

MCU:GD32F207IK

Cortex-M的内核中包含Systick定时器了,只要是Cortex-M系列的MCU就会有Systick,因此这是通用的,下面详细分析。


1 Systick工作原理分析

SysTick 定时器被捆绑在 NVIC 中,用于产生 SysTick 异常(异常号 :15)。在以前,操作系统和所有使用了时基的系统都必须有一个硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。滴答中断对操作系统尤其重要。例如,操作系统可以为多个任务分配不同数目的时间片,确保没有一个任务能霸占系统 ;或者将每个定时器周期的某个时间范围赐予特定的任务等,操作系统提供的各种定时功能都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。

1683642280703y06zbdqjnq

Cortex-M3 在内核部分包含了一个简单的定时器——SysTick。因为所有的 CM3 芯片都带有这个定时器,软件在不同芯片生产厂商的 CM3 器件间的移植工作就得以简化。该定时器的时钟源可以是内部时钟(FCLK,CM3 上的自由运行时钟),或者是外部时钟。不过,外部时钟的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能大不相同。因此,需要阅读芯片的使用手册来确定选择什么作为时钟源。在 GD32 中SysTick 以 HCLK(AHB 时钟)或 HCLK/8 作为运行时钟,见上图。

SysTick 定时器能产生中断,CM3 为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其他系统软件在 CM3 器件间的移植变得简单多了,因为在所有 CM3 产品间,SysTick 的处理方式都是相同的。SysTick 定时器除了能服务于操作系统之外,还能用于其他目的,如作为一个闹铃、用于测量时间等。 Systick 定时器属于Cortex 内核部件 ,可以参考《ARM Cortex-M3 权威指南》((英)JosephYiu 著,宋岩译,北京航空航天大学出版社出版)来了解。

2 Systick寄存器分析

在传统的嵌入式系统软件按中通常实现 Delay(N) 函数的方法为:

for(i = 0; i <= x; i ++);

x --- ;

对于GD32系列微处理器来说,执行一条指令只有几十个 ns,进行 for 循环时,要实现 N 毫秒的 x 值非常大,而且由于系统频率的宽广,很难计算出延时 N 毫秒的精确值。针对GD32 微处理器,需要重新设计一个新的方法去实现该功能,以实现在程序中使用 Delay(N)。

Cortex-M3 的内核中包含一个 SysTick 时钟。SysTick 为一个 24 位递减计数器,SysTick 设定初值并使能后,每经过 1 个系统时钟周期,计数值就减 1。计数到 0 时,SysTick 计数器自动重装初值并继续计数,同时内部的 COUNTFLAG 标志会置位,触发中断 (如果中断使能情况下)。

在 GD32 的应用中,使用 Cortex-M3 内核的 SysTick 作为定时时钟,设定每一毫秒产生一次中断,在中断处理函数里对 N 减一,在Delay(N) 函数中循环检测 N 是否为 0,不为 0 则进行循环等待;若为 0 则关闭 SysTick 时钟,退出函数。

注: 全局变量 TimingDelay , 必须定义为 volatile 类型 , 延迟时间将不随系统时钟频率改变。

Cortex-M3中的Systick部分内容属于NVIC控制部分,一共有4个寄存器,名称和地址分别是:

  • STK_CTRL,0xE000E010--控制寄存器

1683642281331ygg5hvor5x

第0位:ENABLE,Systick 使能位

(0:关闭Systick功能;1:开启Systick功能)

第1位:TICKINT,Systick 中断使能位

(0:关闭Systick中断;1:开启Systick中断)

第2位:CLKSOURCE,Systick时钟源选择

(0:使用HCLK/8 作为Systick时钟;1:使用HCLK作为Systick时钟)

第16位:COUNTFLAG,Systick计数比较标志,如果在上次读取本寄存器后,SysTick 已经数到了0,则该位为1。如果读取该位,该位将自动清零

  • STK_LOAD, 0xE000E014--重载寄存器

1683642281747f5i5uqwxre

Systick是一个递减的定时器,当定时器递减至0时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24位的寄存器最大计数0xFFFFFF。

  • STK_VAL, 0xE000E018--当前值寄存器

1683642282071e7f33le3xp

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

  • STK_CALRB, 0xE000E01C--校准值寄存器

1683642282361vwfhmxvgzx

校准值寄存器提供了这样一个解决方案:它使系统即使在不同的CM3产品上运行,也能产生恒定的SysTick中断频率。最简单的作法就是:直接把TENMS的值写入重装载寄存器,这样一来,只要没突破系统极限,就能做到每10ms来一次 SysTick异常。如果需要其它的SysTick异常周期,则可以根据TENMS的值加以比例计算。只不过,在少数情况下, CM3芯片可能无法准确地提供TENMS的值(如, CM3的校准输入信号被拉低),所以为保险起见,最好在使用TENMS前检查器件的参考手册。


SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于测量时间等。要注意的是,当处理器在调试期间被喊停( halt)时,则SysTick定时器亦将暂停运作。


3 Systick定时器实现

SysTick属于Cortex-M内核的部分,因此其相关的定义在core_cm3.h文件中。


3.1 main文件分析

主函数如下:


/*

    brief      main function

    param[in]  none

    param[out] none

    retval     none

*/

int main(void)

{

    //systick init

    sysTick_init();

    /* configure LED1 GPIO port */

    led_init(LED1);


    /* configure LED2 GPIO port */

    led_init(LED2);


    /* configure LED3 GPIO port */

    led_init(LED3);


    /* configure LED4 GPIO port */

    led_init(LED4);


    while(1) 

    {

        /* turn on LED1, turn off LED4 */

        led_on(LED1);

        led_off(LED4);

        /*delay 500ms*/

        delay_ms(500);


        /* turn on LED2, turn off LED1 */

        led_on(LED2);

        led_off(LED1);

        /*delay 500ms*/

        delay_ms(500);


        /* turn on LED3, turn off LED2 */

        led_on(LED3);

        led_off(LED2);

        /*delay 500ms*/

        delay_ms(500);


        /* turn on LED4, turn off LED3 */

        led_on(LED4);

        led_off(LED3);

        /*delay 500ms*/

        delay_ms(500);

    }

}

在 main 函数中,sysTick_init和 delay_us() 这两个函数比较陌生,它们的功能分别是配置好 SysTick 定时器和进行精确延时。整个 main 函数的流程就是初始化 LED 及SysTick 定时器之后,就进入死循环,点亮LED的时间为精确的 500 ms。


3.2 gd32f207i_systick_eval.c文件分析

配置并启动 SysTick

我们看一下systick_init()这个函数,其功能是启动系统滴答定时器 SysTick。


/*

    brief      SysTick init

    param[in]  none

    param[out] none

    retval     none

*/

void sysTick_init(void)

{

/* SystemFrequency / 1000    1ms中断一次

  * SystemFrequency / 100000  10us中断一次

  * SystemFrequency / 1000000 1us中断一次

  */

    /* setup systick timer for 1000Hz interrupts */

    if(SysTick_Config(SystemCoreClock / 100000U)){

        /* capture error */

        while(1){

        }

    }


    // 关闭滴答定时器  

    SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;


    /* configure the systick handler priority */

    NVIC_SetPriority(SysTick_IRQn, 0x00U);

}

本函数实际上只是调用了 SysTick_Config() 函数,它是属于内核层的 Cortex-M3 通用函数,位于 core_cm3.h 文件中。若调用 SysTick_Config() 配置 SysTick 不成功,则进入死循环,初始化 SysTick 成功后,先关闭定时器,在需要的时候再开启。SysTick_Config() 函数无法在GD32 外设固件库文件中找到其使用方法。所以我们在 Keil 环境下直接跟踪这个函数到 core_cm3.h 文件,查看函数的定义。


/** \brief  System Tick Configuration


    The function initializes the System Timer and its interrupt, and starts the System Tick Timer.

    Counter is in free running mode to generate periodic interrupts.


    \param [in]  ticks  Number of ticks between two interrupts.


    \return          0  Function succeeded.

    \return          1  Function failed.


    \note     When the variable __Vendor_SysTickConfig is set to 1, then the

    function SysTick_Config is not included. In this case, the file device.h

    must contain a vendor-specific implementation of this function.


 */

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)

{

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


  SysTick->LOAD  = ticks - 1;                                  /* set reload register */

  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */

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

  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |

                   SysTick_CTRL_TICKINT_Msk   |

                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */

  return (0);                                                  /* Function successful */

}

在这个函数定义的前面有关于它的注释,如果我们不想去研究它的具体实现,可以根据这段注释了解函数的功能 :这个函数启动了 SysTick ;并把它配置为计数至 0 时引起中断 ;输入的参数 ticks 为两个中断之间的脉冲数,即相隔 ticks 个时钟周期会引起一次中断 ;配置 SysTick 成功时返回 0,出错时返回 1。但是,这段注释并没有告诉我们它把 SysTick 的时钟设置为 AHB 时钟还是 AHB/8,这是一个十分关键的问题,于是,我们将对这个函数的具体实现进行分析,与大家再分享一下如何分析底层库函数。分析底层库函数,要有 SysTick 定时器工作分析的知识准备。


检查输入参数

SysTick_Config() 第 3 行代码是检查输入参数 ticks,因为 ticks 是脉冲计数值,要被保存到重载寄存器 STK_LOAD 寄存器中,再由硬件把 STK_LOAD 值加载到当前计数值寄存器 STK_VAL 中使用,STK_LOAD 和 STK_VAL 都是 24 位的,所以当输入参数 ticks 大于其可存储的最大值时,将由这行代码检查出错误并返回。


位指示宏及位屏蔽宏

检查 ticks 参数没有错误后,就稍稍处理一下把 ticks-1 赋值给 STK_LOAD 寄存器,要注意的是减 1,若 STK_VAL 从 ticks−1 向下计数至 0,实际上就经过了 ticks 个脉冲。这句赋值代码使用了宏 SysTick_LOAD_RELOAD_Msk,与其他库函数类似,这个宏是用来指示寄存器的特定位置或进行位屏蔽的。


/* SysTick Control / Status Register Definitions */

#define SysTick_CTRL_COUNTFLAG_Pos         16                                             /*!< SysTick CTRL: COUNTFLAG Position */

#define SysTick_CTRL_COUNTFLAG_Msk         (1ul << SysTick_CTRL_COUNTFLAG_Pos)            /*!< SysTick CTRL: COUNTFLAG Mask */


#define SysTick_CTRL_CLKSOURCE_Pos          2                                             /*!< SysTick CTRL: CLKSOURCE Position */

#define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)            /*!< SysTick CTRL: CLKSOURCE Mask */


#define SysTick_CTRL_TICKINT_Pos            1                                             /*!< SysTick CTRL: TICKINT Position */

#define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)              /*!< SysTick CTRL: TICKINT Mask */


#define SysTick_CTRL_ENABLE_Pos             0                                             /*!< SysTick CTRL: ENABLE Position */

#define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)               /*!< SysTick CTRL: ENABLE Mask */


/* SysTick Reload Register Definitions */

#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */

[1] [2]
关键字:GD32  开发实战  Systick 引用地址:GD32开发实战指南(基础篇) 第5章 跳动的心脏-Systick

上一篇:【技术分享】星空派GD32开发板LVGL移植经验分享
下一篇:keil中GD32 MCU IAP中APP的存储地址如何设置?

推荐阅读最新更新时间:2026-03-25 10:20

GD32开发实战指南(基础篇) 第5章 跳动心脏-Systick
开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK Cortex-M的内核中包含Systick定时器了,只要是Cortex-M系列的MCU就会有Systick,因此这是通用的,下面详细分析。 1 Systick工作原理分析 SysTick 定时器被捆绑在 NVIC 中,用于产生 SysTick 异常(异常号 :15)。在以前,操作系统和所有使用了时基的系统都必须有一个硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。滴答中断对操作系统尤其重要。例如,操作系统可以为多个任务分配不同数目的时间片,确保没有一个任务能霸占系统 ;或者将每个定时器周期的某个时间范围赐予特定的
[单片机]
从棉花糖到跳动心脏,触觉机器人能“感受”材料柔软度
如果用指尖按一颗棉花糖,很容易感觉出它是软的。如果把一块硬饼干放在棉花糖上面,用指尖去压硬饼干,人类仍能分辨出下面的棉花糖是软的。研究人员希望创造出一种具备同样能力的机器人平台。据最新一期《美国国家科学院院刊》报道,瑞士洛桑联邦理工学院研究人员开发出一种柔软度表达接口(SORI),实现了这一目标。 软度表达接口SORI。 图片来源:贾马尼·卡耶/瑞士洛桑联邦理工学院 对柔软物体的触觉在许多行动和互动中发挥着至关重要的作用,如判断鳄梨的成熟度,或是牵着爱人的手。但理解和再现这种感觉极具挑战性,因为这涉及复杂的感觉和认知过程。柔软度感知的两个主要元素是皮肤提示(来自指尖皮肤的感觉反馈)和动觉提示(关于手指关节作用力的反馈)。通过
[机器人]
从棉花糖到<font color='red'>跳动</font><font color='red'>的</font><font color='red'>心脏</font>,触觉机器人能“感受”材料柔软度
GD32开发实战指南(基础篇) 第14章 内部温度传感器
开发环境: MDK:Keil 5.30 开发板:GD32F207I-EVAL MCU:GD32F207IK 1 内部温度传感器工作原理 GD32 有一个内部的温度传感器,可以用来测量 CPU 及周围的温度(TA)。该温度传感器在内部和 ADCx_IN16 输入通道相连接,此通道把传感器输出的电压转换成数字值。温度传感器模拟输入推荐采样时间是 17.1μs。GD32 的内部温度传感器支持的温度范围为: -40~125度。精度比较差,为±1.5℃左右。 GD32 内部温度传感器的使用很简单,只要设置一下内部 ADC,并激活其内部通道就差不多了。关于 ADC 的设置,我们在前面的章节已经进行了详细的介绍,这里就不再多说。接下来我们介
[单片机]
美国研制出跳动心脏机器 可彻底革新心脏手术
  据英国《每日邮报》报道,美国北卡罗来纳州大学制造的最新心脏机器“动力心脏系统”可以通过加压液体推动死动物心脏再度跳动,使其能像一颗起死回生的活心脏一样工作。科学家称此心脏系统将能彻底革新心脏手术。   科学家表示,此机器能帮助科学家做好与心脏手术相关的各种新技术的相关实验,而不需消耗过多的时间和财力。相比之下,传统的同类实验则需要在活动物身上进行,而且还需做临床试验。一般情况下,在医疗设备应用到心脏手术之前,都需要在活猪体内做活体试验,这是因为猪心脏的心瓣膜和人类心脏的心瓣膜极为相似。   然而,这类活体试验非常昂贵且费时,而且科学家还得经过允许才能利用活动物做试验。设计此系统的北卡罗来纳州大学的安德鲁•理查德表示,此
[医疗电子]
基于迅为iTOP-3568开发Linux驱动开发实战:menuconfig图形化配置实验
选择迅为iTOP-3568开发板,您将获得完整的驱动开发套件与工业级稳定性保障;该套件提供从基础教程到进阶实战的全套代码示例,能助您快速掌握核心驱动开发技巧。 menuconfig图形化配置实验 学习把驱动编译进内核之前需要先掌握menuconfig图形化配置界面的知识。menuconfig 是一套图形化的配置工具,在内核源码顶层目录下输入make menuconfig命令可以打开 图形化配置界面。 6.1 图形化界面的操作 图形化配置界面主要有以下四种。 make config(基于文本的最为传统的配置界面,不推荐使用) make menuconfig(基于文本菜单的配置界面) make xconfig(要
[嵌入式]
基于迅为iTOP-3568<font color='red'>开发</font>板<font color='red'>的</font>Linux驱动<font color='red'>开发</font><font color='red'>实战</font>:menuconfig图形化配置实验
低成本STM32实战开发T-BOX技术:高效可靠车联网解决方案
引言:为什么需要T-BOX? 在“十四五”交通安全规划背景下,“两客一危”车辆(旅游包车、长途客车、危险品运输车)的智能化管控成为行业刚需。远程信息处理器(T-BOX) 作为车联网的核心节点,承担着车辆状态感知、远程控制、数据加密传输等关键任务。本文基于 STM32F105 主控芯片,设计了一款低成本、高可靠的T-BOX系统,助力新能源汽车行业实现车路云协同的智能化管理。 系统总体设计:T-BOX的架构与核心功能 T-BOX系统由四大核心模块构成(如图1所示): STM32主控单元:负责数据处理与调度; GNSS定位模块:支持多卫星系统联合定位; 4G通信模块:实现车云双向数据传输; 双CAN总线接口:解析车辆ECU的J
[单片机]
低成本STM32<font color='red'>实战</font><font color='red'>开发</font>T-BOX技术:高效可靠<font color='red'>的</font>车联网解决方案
基于STM32F103精灵开发板点亮LED灯实战教程:以PA0为例
一、引言 在嵌入式开发领域,STM32系列单片机凭借其强大的性能和丰富的外设深受开发者喜爱。普中STM32 – F103 – 精灵开发板是初学者入门STM32开发的优质选择。点亮LED是STM32开发中最基础的实验之一,通过这个实验,我们可以熟悉开发板的GPIO(通用输入输出)功能,为后续更复杂的项目开发奠定基础。本文将详细介绍如何在上电后点亮连接在PA0引脚上的LED。 二、硬件连接原理 在普中STM32 – F103 – 精灵开发板上,LED的点亮原理基于GPIO端口的电平控制。一般来说,LED的阳极连接到开发板的电源(如3.3V),阴极通过限流电阻连接到STM32的GPIO引脚(这里是PA0) 。当PA0引脚输出低电平时
[单片机]
基于STM32F103精灵<font color='red'>开发</font>板点亮LED灯<font color='red'>实战</font>教程:以PA0为例
《STM32库开发实战指南 》USART
补充几个当时还不会的知识点。 发送寄存器 寄存器 功能 TE 发送使能 TXE 发送单个字节的时候使用,检查发送寄存器为空? TC 发送字符串的时候使用,实质上时多次调用发送单字符的函数,但是最后一次退出前要检查TC,发送完成寄存器 TXIE 发送完成中断使能 printf的多态 我们可以在Keil5中的工程选项中,勾选Use MicroLIB,这样我们就可以在工程中使用stdio.h头文件了。 当然了,单片机又没有屏幕和键盘,所以原来printf、scanf的实现方法当然不能用了,不过我们可以通过串口的方式来实现其。按照串口的方法,将要显示的内容输出到串口,将要获得的字符从串口中读取,那么无论是printf
[单片机]
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved