一、SysTick基础
简介:SysTick系统定时器是 CM3 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK等于 72M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。因为 SysTick 是属于 CM3 内核的外设,所以所有基于 CM3 内核的单片机都具有这个系统定时器,使得软件在 CM3 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。
SysTick寄存器:
(1)SysTick control and status register (STK_CTRL)控制及状态寄存器
(2)SysTick reload value register (STK_LOAD)重装载数值寄存器
(3)SysTick current value register (STK_VAL)当前数值寄存器
(4)SysTick calibration value register (STK_CALIB)校准数值寄存器
注:一般只用前三个寄存器。
在控制及状态寄存器中,只有四位有效:
系统定时器工作过程:在时钟驱动下,从重装载寄存器中的值开始递减,递减到0时产生中断和置位COUNTFLAG标志,然后继续从重装载寄存器中值开始递减,周期循环。
例:如果选用AHB=72M,reload=72000,那么每个递减周期为0.001秒。
SysTick中断优先级:
(1)stm32中内核和外设都是用4个二进制数表示。
(2)中断优先级的分组对内核和外设同样适用。把内核外设的中断优先级的四个位按照外设的中断优先级来分组来解析。
(3)systick中断优先级配置的是scb->shprx寄存器;而外设的中断优先级配置的是nvic->iprx寄存器。
Systick是内核外设,寄存器结构体和库函数都在core_cm3.h中。
(1)先看Systick的寄存器结构体:
1 typedef struct
2 {
3 __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
4 __IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
5 __IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
6 __I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
7 } SysTick_Type;
(2)core_cm3.h还有这样一个宏定义:
1 #define SysTick ((SysTick_Type *) SysTick_BASE) /*!< SysTick configuration struct */
注:SysTick此时是SysTick_Type类型的结构体指针,指向SysTick_BASE。库函数中用的正是SysTick。
(3)SysTick配置库函数:
1 /* ################################## SysTick function ############################################ */
2
3 #if (!defined (__Vendor_SysTickConfig)) || (__Vendor_SysTickConfig == 0)
4
5 /**
6 * @brief Initialize and start the SysTick counter and its interrupt.
7 *
8 * @param ticks number of ticks between two interrupts
9 * @return 1 = failed, 0 = successful
10 *
11 * Initialise the system tick timer and its interrupt and start the
12 * system tick timer / counter in free running mode to generate
13 * periodical interrupts.
14 */
15 static __INLINE uint32_t SysTick_Config(uint32_t ticks)
16 {
17 if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* 重装载寄存器最大值2^24,不能超过Reload value impossible */
18
19 SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* 配置重装载寄存器初值set reload register */
20 NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* 配置中断优先级为1<<4-1=15,优先级为最低set Priority for Cortex-M0 System Interrupts */
21 SysTick->VAL = 0; /* 配置counter寄存器的值Load the SysTick Counter Value */
22 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | //配置systick时钟为72M
23 SysTick_CTRL_TICKINT_Msk | //使能中断
24 SysTick_CTRL_ENABLE_Msk; //使能systick /* Enable SysTick IRQ and SysTick Timer */
25 return (0); /* Function successful */
26 }
27
28 #endif
(4)在上述库函数中有一个优先级配置函数:
1 /**
2 * @brief Set the priority for an interrupt
3 *
4 * @param IRQn The number of the interrupt for set priority
5 * @param priority The priority to set
6 *
7 * Set the priority for the specified interrupt. The interrupt
8 * number can be positive to specify an external (device specific)
9 * interrupt, or negative to specify an internal (core) interrupt.
10 *
11 * Note: The priority cannot be set for every core interrupt.
12 */
13 static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
14 {
15 if(IRQn < 0) {//给Cortex-M3系统中断配置优先级
16 SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
17 else {//给外设中断配置优先级
18 NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
19 }
(5)中断服务函数在stm32f10x_it.h中写,函数名在启动文件startup_stm32f10x_hd.s中可以找到。当然不写中断函数,只用系统定时器计时功能也可以。
二、编程代码
由第一部分分析,系统定时器是内核外设,它的配置过程都是在core_cm3.h中完成的,实际使用时确认配置正确后,调用SysTick_Config()即可。
在之前GPIO输入输出(按键LED反转)的例程基础上写,注释bsp_key.h,user下新建SysTick文件夹,添加bsp_systick.h和bsp_systick.c(在此调用SysTick_Config()),再写主函数。
bsp_systick.h
1 #ifndef _BSP_SYSTICK_H
2 #define _BSP_SYSTICK_H
3
4 #include "stm32f10x.h"
5 #include "core_cm3.h"
6
7 void SysTick_Delay_us(uint32_t us);
8 void SysTick_Delay_ms(uint32_t ms);
9
10
11 #endif //_BSP_SYSTICK_H
bsp_systick.c
1 #include "bsp_systick.h"
2
3 void SysTick_Delay_us(uint32_t us)//微秒
4 {
5 uint32_t i;
6 SysTick_Config(72);
7 for(i=0;i<us;i++)
8 {
9 while(!((SysTick->CTRL) & (1<<16)));
10 }
11 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
12
13 }
14 void SysTick_Delay_ms(uint32_t ms)//毫秒
15 {
16 uint32_t i;
17 SysTick_Config(72000);
18 for(i=0;i<ms;i++)
19 {
20 while(!((SysTick->CTRL) & (1<<16)));
21 }
22 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭systick时钟
23
24 }
main.c
1 #include "stm32f10x.h"
2
3 #include "bsp_led.h"
4 #include "bsp_systick.h"
5
6 int main(void)
7 {
8 // 72M
9 LED_GPIO_Config();
10
11
12 while(1)
13 {
14
15 // LED_G(ON);
16 // SysTick_Delay_ms(500);
17 // LED_G(OFF);
18 SysTick_Delay_ms(500);
19 LED_G_TOGGLE;
20
21
22 }
23 }
注:不同板子,需要在bsp_led.h文件中更改GPIO引脚和时钟源。
7