系统定时器(systick)
一.框架图
STK_CLK 时钟
STK_LOAD 重装载寄存器
STK_VAL 递减计数器
递减计数器(时钟驱动)→重装载寄存器的初始值开始往下递减计数到0(递减过程中 在STK_value寄存器中实时值)→产生中断(COUNTFLAG标志为1)→若计数器没有关掉,重新计数在产生中断。
COUNTFLAG 计时标志
CLKSOURCE 进行时钟源的选择
0:AHB/8 = 9MZH 1:AHB→72MHZ
TICKINT:中断使能 1:进入中断服务程序 执行中断程序 0:不进入中断服务函数
RELOAD:可以读取到正在计数的数值(value)
二.systick定时器时间的计算
1-t(定时时间):一个计数的循环时间 与reload clk相关
一个循环:reload的值计数降到0
2-CLK:可以为72m或9m 可以通过CTRL寄存器进行配置
3-RELAOD:24位值 用户自己配置
计算公式
t = reload*(1/clk) 配置的次数与递减计数器计数一次的时间
比如
CLK=72M , relaod = 72 t = 72*(1/72M) = 1us
CLK=72M , relaod = 72000 t = 72000*(1/72M) = 1ms
时间单位的换算:
1s = 1000ms = 1000 000us = 1000 000 000ns
三.systick寄存器
寄存器参数
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
} SysTick_Type;
系统时钟配置函数
/**
* @brief Initialize and start the SysTick counter and its interrupt.
*
* @param ticks number of ticks between two interrupts
* @return 1 = failed, 0 = successful
*
* Initialise the system tick timer and its interrupt and start the
* system tick timer / counter in free running mode to generate
* periodical interrupts.
*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
//判断tick的值 是否大于2^24 大于的话 则不符合规则
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
//初始化寄存器的值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
//配置中断优先级 配置为15 默认为最低的优先级
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 */
}
中断优先级配置函数
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if(IRQn < 0) {
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
}
问题
systick为内核外设 优先级与外设中断优先级相比 那个高
答:外设的中断优先级会更高
systick中断优先级配 scb-> shprx寄存器
外设的中断优先级配置的是nvic-/ipex 有优先级分组 有抢占优先级和子优先级的说法
四.systick中断优先级
1.STM32里面无论是内核还是外设都是使用4个二进制位来表示中断优先级
2.中断优先级的分组对内核与外设同样适合使用。当比较的时候,只需要把内核外设的中断优先级的四个为按照外设的中断优先级来分组来解析即可 即人为的分出抢占优先级与子优先级
优先级分组配置 中断优先级越大 优先级越低
五.编写微秒/毫秒延时函数
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
//判断tick的值 是否大于2^24 大于的话 则不符合规则
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
//初始化寄存器的值
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
//配置中断优先级 配置为15 默认为最低的优先级
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
//配置counter 计数器的值
SysTick->VAL = 0; /* Load the SysTick Counter Value */
//配置systick的时钟为72m
//使能中断过
//使能systick
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
六.代码实现
原理废话都看了那么多了 最后还得看怎么进行实现的 下面就是具体的实现 配置好后 可进行准确的1us、1ms等系统中断延时
微妙延时写法
#include "systick.h"
void Systick_Delay_us(uint32_t us)
{
//系统时钟
SysTick_Config(72);
//
uint32_t i;
for(i=0;i<us;i++)
{
//即为计数结束后 SysTick中CTRL寄存器第十六位就会置1
//等待其读数 达到后又会置0 从而实现延时
while(!((SysTick->CTRL) & (1<<16)));
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
毫秒延时写法
void Systick_Delay_ms(uint32_t ms)
{
//系统时钟
SysTick_Config(72000);
//
uint32_t i;
for(i=0;i<ms;i++)
{
//即为计数结束后 SysTick中CTRL寄存器第十六位就会置1
//等待其读数 达到后又会置0 从而实现延时
while(!((SysTick->CTRL) & (1<<16)));
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}