历史上的今天

今天是:2024年08月26日(星期一)

正在发生

2019年08月26日 | 【STM32H7教程】第22章 STM32H7的SysTick实现多组软件定时器

发布者:小牛队 来源: eefocus关键字:STM32H7  SysTick  软件定时器 手机看文章 扫描二维码
随时随地手机看文章

22.1 初学者重要提示


比通用定时器要容易掌握很多,因为嘀嗒定时器的功能比较的单一,根据ARM的说法,此定时器就是专门为RTOS的系统时钟节拍而设计。


本章节为大家讲解的多组软件定时器实现方案非常实用,建议初学者熟练掌握。


22.2 Systick基础知识


关于滴答定时器,初学者仅需了解到以下几点知识就够了。



Systick是Cortex-M7内核自带的组件,其它几个常用的硬件异常HardFault,SVC和PendSV也都是是内核自带的,其中Systick,SVC和PendSV的中断优先级是可编程的,跟SPI中断、ADC中断、UART中断等一样,都在同一个NVIC下配置的。而HardFault是不可编程的,且优先级要比可编程的都要高。


Systick是一个24位的递减计数器,用户仅需掌握ARM的CMSIS软件提供的一个函数SysTick_Config即可,原代码如下:

1. /**

2.   brief   System Tick Configuration

3.   details Initializes the System Timer and its interrupt, and starts the System Tick Timer.

4.            Counter is in free running mode to generate periodic interrupts.

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

6.   return          0  Function succeeded.

7.   return          1  Function failed.

8.   note    When the variable __Vendor_SysTickConfig is set to 1, then the

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

10.            must contain a vendor-specific implementation of this function.

11. */

12. __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)

13. {

14.   if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)

15.   {

16.     return (1UL);                                            /* Reload value impossible */

17.   }

18.

19.   SysTick->LOAD  = (uint32_t)(ticks - 1UL);                  /* set reload register */

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

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

22.   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |

23.                    SysTick_CTRL_TICKINT_Msk   |

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

25.   return (0UL);                                             /* Function successful */

26. }

  第12行,函数的形参用于配置滴答定时器LOAD寄存器的数值,由于滴答定时器是一个递减计数器,启动后是将LOAD寄存器的数值赋给VAL寄存器,然后VAL寄存器做递减操作,等递减到0的时候重新加载LOAD寄存器的数值继续做递减操作。

函数的形参表示内核时钟多少个周期后触发一次Systick定时中断,比如形参配置为如下数值。


-- SystemCoreClock / 1000  表示定时频率为 1000Hz, 也就是定时周期为  1ms。


-- SystemCoreClock / 500   表示定时频率为 500Hz,  也就是定时周期为  2ms。


-- SystemCoreClock / 2000  表示定时频率为 2000Hz, 也就是定时周期为  500us。


注:SystemCoreClock是STM32H7的系统主频400MHz。


  第20行,此函数设置滴答定时器为最低优先级。

  第22行,配置滴答定时器的控制寄存器,使能滴答定时器中断。滴答定时器的中断服务程序实现比较简单,没有清除中断标志这样的操作,仅需填写用户要实现的功能即可。

控制及其状态寄存器的位定义:


重装寄存器定义,最大值2^24 – 1 = 16777215,配置的时候注意别超出范围了。


22.3 多组软定时器驱动设计

22.3.1 软件定时器框架

为了方便大家理解,先来看下软件定时器的实现框图:



1、  第1阶段,初始化:


通过函数bsp_InitTimer初始化滴答定时器和实现软件定时器所需的结构体。


2、  第2阶段,软件定时器初始化:


  可以通过函数bsp_StartTimer做单次定时器初始化,单次定时器执行一次就结束。下次还想使用,需要重新创建。

  可以通过函数bsp_StartAutoTimer做周期性定时器初始化,可以周期性的一直运行下去。

3、 第3阶段,滴答定时器中断里面更新每个软件定时器的计数:


在滴答定时器中断里面通过调用函数bsp_SoftTimerDec实现每个软件定时器的计数更新。


4、  第4阶段,检测时间到和停止运行


  通过函数bsp_CheckTimer可以检测单次或者周期定时器的时间是否到。时间到后就可以执行用户任务。

  如果不想某个单次或者周期性定时器执行,直接调用函数bsp_StopTimer停止即可。

22.3.2 程序分析之相关的变量定义

在bsp_timer.h 中定义了结构体类型SOFT_TMR。


#define TMR_COUNT 4      /* 软件定时器的个数 (定时器ID范围 0 - 3) */

 

typedef enum

{

TMR_ONCE_MODE = 0, /* 一次工作模式 */

TMR_AUTO_MODE = 1 /* 自动定时工作模式 */

}TMR_MODE_E;

 

/* 定时器结构体,成员变量必须增加__IO 即 volatile,因为这个变量在中断和主程序中同时被访问,

有可能造成编译器错误优化。

*/

typedef struct

{

volatile uint8_t Mode; /* 计数器模式,1次性 */

volatile uint8_t Flag; /* 定时到达标志  */

volatile uint32_t Count; /* 计数器 */

volatile uint32_t PreLoad; /* 计数器预装值 */

}SOFT_TMR;

在bsp_timer.c 中定义SOFT_TMR结构体数组变量。


/* 定于软件定时器结构体变量 */ 

static SOFT_TMR s_tTmr[TMR_COUNT];

每个软件定时器对象都分配一个结构体变量,这些结构体变量以数组的形式存在将便于我们简化程序代码行数。


22.3.3 程序分析之初始化

初始化函数如下:


1. /*

2. *****************************************************************************************************

3. * 函 数 名: bsp_InitTimer

4. * 功能说明: 配置systick中断,并初始化软件定时器变量

5. * 形    参:  无

6. * 返 回 值: 无

7. *****************************************************************************************************

8. */

9. void bsp_InitTimer(void)

10. {

11. uint8_t i;

12.

13. /* 清零所有的软件定时器 */

14. for (i = 0; i < TMR_COUNT; i++)

15. {

16. s_tTmr[i].Count = 0;

17. s_tTmr[i].PreLoad = 0;

18. s_tTmr[i].Flag = 0;

19. s_tTmr[i].Mode = TMR_ONCE_MODE; /* 缺省是1次性工作模式 */

20. }

21.

22. /*

23. 配置systic中断周期为1ms,并启动systick中断。

24.

25.     SystemCoreClock 是固件中定义的系统内核时钟,对于STM32H7,一般为 400MHz

26.

27.     SysTick_Config() 函数的形参表示内核时钟多少个周期后触发一次Systick定时中断.

28.     -- SystemCoreClock / 1000  表示定时频率为 1000Hz, 也就是定时周期为  1ms

29.     -- SystemCoreClock / 500   表示定时频率为 500Hz,  也就是定时周期为  2ms

30.     -- SystemCoreClock / 2000  表示定时频率为 2000Hz, 也就是定时周期为  500us

31.

32.     对于常规的应用,我们一般取定时周期1ms。对于低速CPU或者低功耗应用,可以设置定时周期为 10ms

33.     */

34. SysTick_Config(SystemCoreClock / 1000);

35. }

  第14-20行是软件定时器结构体的初始化部分,设置初始值。实际创建软件定时器会重新做初始化。

  第32行是本章22.2小节已经讲解。

22.3.4 程序分析之单次定时器创建

单次定时器创建函数bsp_StartTime。


1. /*

2. ******************************************************************************************************

3. * 函 数 名: bsp_StartTimer

4. * 功能说明: 启动一个定时器,并设置定时周期。

5. * 形    参:  _id  : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。

6. * _period : 定时周期,单位1ms

7. * 返 回 值: 无

8. ******************************************************************************************************

9. */

10. void bsp_StartTimer(uint8_t _id, uint32_t _period)

11. {

12. if (_id >= TMR_COUNT)

13. {

14. /* 打印出错的源代码文件名、函数名称 */

15. BSP_Printf("Error: file %s, function %s()rn", __FILE__, __FUNCTION__);

16. while(1); /* 参数异常,死机等待看门狗复位 */

17. }

18.

19. DISABLE_INT();  /* 关中断 */

20.

21. s_tTmr[_id].Count = _period; /* 实时计数器初值 */

22. s_tTmr[_id].PreLoad = _period; /* 计数器自动重装值,仅自动模式起作用 */

23. s_tTmr[_id].Flag = 0; /* 定时时间到标志 */

24. s_tTmr[_id].Mode = TMR_ONCE_MODE; /* 1次性工作模式 */

25.

26. ENABLE_INT();  /* 开中断 */

27. }

  第12-17行是为了防止用户设置的ID参数超过范围。

其中BSP_Printf是在bsp.h文件定义的,用于调试阶段排错。


#define BSP_Printf    printf   /* 使用这个宏定义的话,正常执行printf */


#define BSP_Printf(...)        /* 如果使用这个宏定义的话,什么都不执行 */


  第19-26行是临界段,结构体变量赋值前后做了开关中断操作。因为此结构体变量在滴答定时器中断里面也要调用,防止变量赋值出问题。

开关中断函数也是在bsp.h文件里面定义的。


#define ENABLE_INT()     __set_PRIMASK(0)      /* 使能全局中断 */


#define DISABLE_INT()    __set_PRIMASK(1)      /* 禁止全局中断 */


22.3.5 程序分析之周期性定时器创建

周期性定时器创建函数bsp_StartAutoTimer。


1. /*

2. ******************************************************************************************************

3. * 函 数 名: bsp_StartAutoTimer

4. * 功能说明: 启动一个自动定时器,并设置定时周期。

5. * 形    参:  _id   : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。

6. * _period : 定时周期,单位10ms

7. * 返 回 值: 无

8. ******************************************************************************************************

9. */

10. void bsp_StartAutoTimer(uint8_t _id, uint32_t _period)

11. {

12. if (_id >= TMR_COUNT)

13. {

14. /* 打印出错的源代码文件名、函数名称 */

15. BSP_Printf("Error: file %s, function %s()rn", __FILE__, __FUNCTION__);

16. while(1); /* 参数异常,死机等待看门狗复位 */

17. }

18.

19. DISABLE_INT();  /* 关中断 */

20.

21. s_tTmr[_id].Count = _period;      /* 实时计数器初值 */

22. s_tTmr[_id].PreLoad = _period; /* 计数器自动重装值,仅自动模式起作用 */

23. s_tTmr[_id].Flag = 0; /* 定时时间到标志 */

24. s_tTmr[_id].Mode = TMR_AUTO_MODE; /* 自动工作模式 */

25.

26. ENABLE_INT();  /* 开中断 */

27. }

 这个函数跟前面22.3.4小节中讲的单次定时器是一样的,仅第24行的赋值不同,这个函数是周期性的,而22.3.4小节里面的是单次定时器。

22.3.6 程序分析之停止定时器运行

定时器停止运行函数是bsp_StopTimer。


1. /*

2. ******************************************************************************************************

3. * 函 数 名: bsp_StopTimer

4. * 功能说明: 停止一个定时器

5. * 形    参: _id   : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。

6. * 返 回 值: 无

7. ******************************************************************************************************

8. */

9. void bsp_StopTimer(uint8_t _id)

10. {

11. if (_id >= TMR_COUNT)

12. {

13. /* 打印出错的源代码文件名、函数名称 */

14. BSP_Printf("Error: file %s, function %s()rn", __FILE__, __FUNCTION__);

15. while(1); /* 参数异常,死机等待看门狗复位 */

16. }

17.

18. DISABLE_INT();  /* 关中断 */

19.

20. s_tTmr[_id].Count = 0; /* 实时计数器初值 */

21. s_tTmr[_id].Flag = 0; /* 定时时间到标志 */

22. s_tTmr[_id].Mode = TMR_ONCE_MODE; /* 自动工作模式 */

23.

24. ENABLE_INT();  /* 开中断 */

25. }

  这个函数跟前面22.3.4和22.3.5小节中的函数框架一样,仅是把结构体变量中的计数器和时间到标志都置位成0,从而让软件定时器停止运行。

22.3.7 程序分析之检测定时器时间到

检测定时器时间到的函数是bsp_CheckTimer。


1. /*

2. ******************************************************************************************************

3. * 函 数 名: bsp_CheckTimer

4. * 功能说明: 检测定时器是否超时

5. * 形    参:  _id  : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。

6. *     _period : 定时周期,单位1ms

7. * 返 回 值: 返回 0 表示定时未到, 1表示定时到

8. ******************************************************************************************************

9. */

10. uint8_t bsp_CheckTimer(uint8_t _id)

11. {

12. if (_id >= TMR_COUNT)

13. {

14. return 0;

15. }

16.

17. if (s_tTmr[_id].Flag == 1)

18. {

19. s_tTmr[_id].Flag = 0;

20. return 1;

21. }

22. else

23. {

24. return 0;

25. }

26. }

  第12到15行是检查ID是否有效。

  第17到25行是判断时间到标志值Flag是否置位,如果置位表示时间已经到,如果为0,表示时间还没有到。

22.3.8 程序分析之滴答定时器中断的处理

软件定时器的主要功能是通过滴答定时器中断实现的,函数的调用关系是滴答定时器中断函数SysTick_Handler调用SysTick_ISR,而SysTick_ISR调用bsp_SoftTimerDec。

[1] [2] [3]
关键字:STM32H7  SysTick  软件定时器 引用地址:【STM32H7教程】第22章 STM32H7的SysTick实现多组软件定时器

上一篇:【STM32H7教程】第23章 STM32H7的MPU内存保护单元(重要)
下一篇:【STM32H7教程】第21章 STM32H7的NVIC中断分组和配置(重要)

推荐阅读

四年成就行业黑马哈工大机器人集团(HRG)成立于2014年12月,由黑龙江省政府、哈尔滨市政府、哈尔滨工业大学联合创建,现在哈工大机器人集团已经由一个行业新人一跃成为了排头兵。销售额从第一年的3亿元,到后来的6亿元,再到2017年的10亿元。如今,企业市场估值已达40多亿元。除了上述产品,哈工大机器人集团在智慧工厂、工业机器人、服务机器人、特种机...
华为消费者业务CEO、华为常务董事余承东宣布在柏林IFA2019上正式发布麒麟新芯片。他表示,我们在5G时代下重构芯片想象,希望带给世界全新的科技体验。 早前余承东宣布华为IFA2019将于9月6日在德国柏林举行。不出意外,麒麟990将在本次活动中亮相。同时在深圳举行的华为开发者大会上,华...
设计包含PROTEUS仿真电路图和程序代码。设计能实现步进电机的启停控制,加减档调速并显示当前档位和速度。/***步进电机调速实验***//***实验内容:通过程序能够实现电机的启停,正反转,加减速。同时显示电机当前档位和速度。***/#include<reg52.h>#define uchar unsigned char#define uint unsigned intvoid Delay_ms(uint x);//延时函数void Ke...
由于参加飞思卡尔智能车比赛,铺赛道变成了一个重要的环节。而此次铺赛道用了过去留下的赛道进行拼接,导致赛道的长度不好计算,因此,博主与小伙伴想了一些办法估算长度,下面介绍Matlab图像矫正估算赛道长度的方法。首先由于赛道过长的原因,无法获得整个赛道的图,俯视图则更不为可能。因此,本次方法为采集两张图片进行分别处理计算再相加。图片如下图...

史海拾趣

问答坊 | AI 解惑

美国人设计的555电路分析

这是美国人设计的555电路,请555电路高手分析当合上AN 开关后(不松开),线圈KA得电情况 另外C945在555的复位端(4端)是起什么作用…

查看全部问答∨

电话机相关技术资料

坛里有电话机相关技术资料下载吗?有的话,请大家提供以下,谢谢啦!…

查看全部问答∨

求助啊,新手对Linux驱动的疑惑

下面是2440板子上的4个按键的驱动程序中的一些代码: struct button_irq_desc {     int irq;         int pin;         int pin_setting;         int number; &nbs ...…

查看全部问答∨

低格10秒后硬盘的mbr手写恢复问题 求助 小弟叩首

    昨天用填充零低格硬盘时选错了硬盘,10秒之后觉得不对强行关机,把低格后的硬盘接在另一台电脑上后,用WinHex查看mbr,不出所料,全部是0.从自己机器硬盘复制前446个字节后,后面64位分区表搞不定了。     我用数据修复软 ...…

查看全部问答∨

中断没响应,大家帮帮忙.

目标:启用RTC CPU:ARM 710 过程: >0x18跳到中断服务例程指令(IRQ) >配置RTC各参数.最后使能RTC_CRH.GEN >配置EIC.有EIC_SIR3(配置服务例程偏移及等级(15),RTC全局中断向量号为3)           EIC_IV ...…

查看全部问答∨

如何在Pocket PC上安装应用程序?

我用VS2005写了一个小程序,已经成功运行部署到Pocket PC上,但是不知道怎么才能把应用程序安装到Pocket PC上?请指教。…

查看全部问答∨

STM32的运行速度到底是多少?

刚才做实验发现一个大问题,使用ST的固件库 执行如下程序 while(1){    GPIO_SetBits(GPIOB,GPIO_Pin_11);    GPIO_ResetBits(GPIOB,GPIO_Pin_11); }用示波器看完整的波形周期竟然 ...…

查看全部问答∨

TI收购Luminary!!!

美国,达拉斯-2009 年 5月14日,德州仪器公司(NYSE:TXN)宣布,为了扩展 MCU产品线,将收购Luminary Micro (全球领先的基于ARM Cortex-M3的32位MCU的供应商) 。Luminary Micro公司旗下的Stellari ...…

查看全部问答∨

ccs运行时停留在启动界面了,是怎么回事呢

这个故障的原因比较多,你用什么芯片呀…

查看全部问答∨

FLASH写不进去,怎么回事?

FLASH是SST39VF400A,原理图用的和CCS文档中的提供的,DSP是TMS320C6701,程序跑起来,运行到写数据就死了,不知道怎么回事? 代码如下: void main() {     int f;         f = Check_SST_39VF400A();   &nbs ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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