STM32:SysTick定时器
Posted caesura-k
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32:SysTick定时器相关的知识,希望对你有一定的参考价值。
1 系统时钟树
单片机首先要在正确的时钟频率下才能正常工作,几乎外设的使用都会用到时钟,先了解一下原理也是不错的;
时钟树对系统的时钟结构进行了描述,时钟来源主要有4个,分别是HSI, HSE, LSI, LSE;
1.1 HSI 高速内部时钟信号: 由内部的RC振荡器生成,8MHz,
1.2 HSE 高速外部时钟信号:由外部的时钟源接入,4-16MHz;
1.3 LSI 低速内部时钟信号: 由内部的RC振荡器生成,40KHz;
1.4 LSE 低速外部时钟信号:由外部32.768KHz的时钟源接入;
1.5 PLL 锁相环时钟信号:由HSI和HSE倍频而来;
2 SysTick定时器
定义:systick定时器为24位倒数计数器,产生的中断由由NVIC控制;时钟频率为AHB或AHB/8;
当处理器处于低功耗模式下的时候,可能会终止SYSCLK时钟源,于是systick也随之终止;
作用:可以为多任务系统的任务设置一个执行周期,如果在时钟周期内任务未完成,就产生systick中断,保证了系统的稳定性;
可以为操作系统提供“心跳”,提供周期性的定时;
2.1 SysTick的4个寄存器
(插入表格)
2.2 SysTick的标准库封装
2.2.1寄存器全部封装于core_cm3.h中,并且需要#include"stm32f10x.h";
/*以下内容位于标准库的core_cm3.h中; *首先是将systick的4个寄存器封装成了SysTick_Type结构体; *然后用宏定义一个指向该结构体类型的指针SysTick_BASE,指针本身表示地址,*指针表示取出地址的数据;这么一想,指针搭配寄存器很好用啊; *然后用宏定义寄存器的位,这样就可以通过指针实现寄存器的位操作了,&&为布尔运算符,&为逻辑运算符; */ typedef struct { __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register STK_CTRL寄存器*/ __IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register STK_LOAD寄存器*/ __IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register STK_VAL寄存器*/ __I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register STK_CALIB寄存器*/ } SysTick_Type; /* Memory mapping of Cortex-M3 Hardware */ #define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010) /*!< SysTick Base Address */ #define SysTick ((SysTick_Type *) SysTick_BASE) /*!< SysTick configuration struct */ /* STK_CTRL寄存器 位定义 */ #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 */ /* STK_LOAD寄存器 位定义 */ #define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ #define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ /* STK_VAL寄存器 位定义 */ #define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ #define SysTick_VAL_CURRENT_Msk (0xFFFFFFul << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ /* STK_CALIB寄存器 位定义 */ #define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ #define SysTick_CALIB_NOREF_Msk (1ul << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ #define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ #define SysTick_CALIB_SKEW_Msk (1ul << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ #define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ #define SysTick_CALIB_TENMS_Msk (0xFFFFFFul << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */
2.2.2 SysTick的配置使能函数 SysTick_Config(uint32_t ticks)
/*以下一行的声明在stm32f10x.h中第161行;*/ #define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */ /*以下函数位于core_cm3.h中第1680行开始; *初始化加载值寄存器,当前值寄存器;然后打开CTRL控制寄存器,配置使能systick; */ #if (!defined (__Vendor_SysTickConfig)) || (__Vendor_SysTickConfig == 0) static __INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */ 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 */ } #endif /*当SysTick的中断标志位置1后,会进入中断函数void SysTick_Handler(void);位于stm32f10x_it.c内第135行开始; *这些中断以及中断函数,都是在.s启动文件内映射的;
*/ void SysTick_Handler(void) { }
2.2.3 SysTick的时钟源配置函数 SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
/*以下函数位于misc.c中第199行开始;用来设置systick的时钟源是AHB还是AHB/8; *输入实参为SysTick_CLKSource_HCLK或SysTick_CLKSource_HCLK_Div8; *SysTick是在core_cm3.h中定义的结构体指针;其他参数又在misc.h中重新定义了;然后*/ void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) { /* 检查参数是否有效的,因为输入实参只有两个值;其他值的话会影响寄存器的其他位,assert_param()的声明常用来检查输入值是否有效; */ assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource)); if (SysTick_CLKSource == SysTick_CLKSource_HCLK) { SysTick->CTRL |= SysTick_CLKSource_HCLK; } else { SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8; } }
2.3 systick的延时应用
运行环境为keil5,前提是按 <STM32:预备知识> 配置好了固件库工程,尤其是头文件路径的配置;
以下三小节为三种systick定时器的使用方法;
2.3.1 以下代码提供了一个systick应用的实例,主要是对systick的四个寄存器进行配置;
标准库中systick一共提供了两个函数供调用,此处用了SysTick_CLKSourceConfig()来配置加载值,函数原型位于misc.c中;
/*delay.c文件源码*/ #include "delay.h" int delay_ms(int ms) { u32 load_ms = 0; u32 load_total = 0; u32 temp_systick = 0; /*直接用算好的0x00FF_FFFF内的数值加载则可以省略以下4步; *0xFFFFFF=16777215;AHB为72M,systick为8分频,则定时约1.86秒; *1 设置systick的时钟为AHB/8; *2 每毫秒内systick时钟源震动的次数; *3 本例的定时范围不超过1.86秒,超过的话返回1表示失败;*/ SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); load_ms = SystemCoreClock/(8*1000); load_total = ms*load_ms; if(load_total > 0xFFFFFF) return 1; SysTick->LOAD = load_total; SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | (~SysTick_CTRL_CLKSOURCE_Msk); do{ temp_systick = SysTick->CTRL; }while(!(temp_systick&SysTick_CTRL_COUNTFLAG_Msk)); SysTick->CTRL = ~SysTick_CTRL_ENABLE_Msk; SysTick->LOAD = 0; SysTick->VAL = 0; return 0; }
/*delay.h文件源码*/ #ifndef _DELAY_H_ #define _DELAY_H_ #include "stm32f10x.h" int delay_ms(int ms); #endif
2.3.2 systick也可以直接调用core_cm3.h中提供的SysTick_Config()函数来配置开启systick;注意事项如下;
(1) 输入实参ticks是用来直接赋值给ATK_LOAD寄存器的;
(2) 需要编写位于stm32f10x_it.c中的SysTick_Handler(void),来设置中断的处理任务;
(3) 如果在实时操作系统中用来作为周期性心跳什么的这样应该很方便,只是延个时用不着这么配置把;
2.3.3 假设我们的实时操作系统中已经开启了systick中断,并且一直在运行,那么我们可以通过不断比较SysTick->VAL的值,来编写一个延时函数;
这样编写延时函数的优点是不会占用额外的中断,也不会占用systick;
/*systick计数范围为0到val_load;val_orginal为开始计数值,val_current为当前计数值; *通过对比VAL寄存器中开始值和当前值的差值,来达到计时的效果; *本例前提为系统时钟72M,AHB/8为时钟源;且计数范围不超过val_load; */ int delay_ms(int ms) { u32 val_load = SysTick->LOAD; u32 user_count = (SystemCoreClock/(8*1000))*ms; u32 val_orginal = 0, val_current = 0; u32 cnt_tempif = 0, cnt_tempelse = 0, cnt_sum = 0 ; u32 cnt_flag = 0; val_orginal = SysTick->VAL; do{ val_current= SysTick->VAL; if(val_orginal >= val_current) cnt_tempif = val_orginal-val_current; else cnt_tempelse = val_load-val_orginal; cnt_sum = cnt_tempif + cnt_tempelse; cnt_flag++; if(cnt_flag > val_load) return 1; }while(cnt_sum < user_count); return 0; }
以上是关于STM32:SysTick定时器的主要内容,如果未能解决你的问题,请参考以下文章