更多细节参考这篇
Sys_Tick 是 ARM Cortex-M4 内核的一部分, 他提供24位递减计数器, 可以用来生成周期性的中断,适合用来左滴答定时器或者提供时间基准
SysTick_Config(SystemCoreClock/1000 ) ; //位与CMSIS 下的core_cm4.h 1760 行
函数来配置SysTick定时器的重载值
,该值基于系统时钟频率和中断频率所需的值来确定
我这里设置的是1ms中断一次(STM32F4 的系统时钟是100MHZ 一个时钟周期有 1 / 100 000 000 s 也就是10 ns ,实现1ms 需要 100 000 个 10ns )
2. 选择时钟源 : 选择SysTick的时钟源 , 一般选择系统时钟
2. 中断使能
extern volatile uint32_t SysTickUptime ;
void SysTick_Handler(void)
{
SysTickUptime++ ;
}
/* 毫秒级运行定时器 , 返回的是毫秒
SysTickUptime 溢出时间大概49天 */
uint32_t millis(void)
{
return SysTickUptime ;
}
/* 毫秒级延时函数 */
void delay_ms(uint32_t ms)
{
uint32_t now_time = millis();
while(millis() - now_time < ms) { }
}
static uint32_t usTicks ;
// SystemCoreClock = 100 MHZ
usTicks = SystemCoreClock/1000000 ; //1us的滴答数 usTicks = 100
uint32_t ms , cycle_cnt ;
do{
ms = SysTickUptime ;
cycle_cnt = SysTick->VAL ;
}while(ms != SysTickUptime ) ;
(ms * 1000 ) + ( ( (SystemCoreClock/1000) - cycle_cnt ) / usTicks ) ;
1. 通过将ms(毫秒计数)* 1000 得到了系统启动以来的时间(以微妙为单位)
2. 计算 SystemCoreClock/1000 - cycle_cnt 得到自上次中断以来经过的时钟周期数 , SystemCoreClock/1000 是SysTick 定时器的重装值 , 表示1ms内的时钟周期数 , 从这个值减去 SysTick-> VAL 得到了自从上次中断以来经过的时钟周期数
3. 最后 将这个值除以 usTicks(每 微秒的时钟周期数) 将周期转换成微妙
最后 函数的返回值也就是自系统启动以来的总微秒
/* 微秒级计数函数 */
uint32_t micros ( void )
{
uint32_t ms , cycle_cnt ;
do{
ms = SysTickUptime ;
cycle_cnt = SysTick->VAL ;
}while(ms != SysTickUptime ) ;
return (ms * 1000 ) + ( ( (SystemCoreClock/1000) - cycle_cnt ) / usTicks ) ;
}
/* 微秒级延时函数 */
void delay_us(uint32_t us)
{
uint32_t now_time = micros();
while(micros() - now_time < us) { }
}
这里的关键是SysTick是递减计数的 , 并在到达零时重置和触发中断 cycle_cnt 变量存储的时从前的计数器值到下一个零点的周期数 , 这个值要用 SysTick定时器的重载值减去 才是已经过去的时钟周期数
#ifndef DELAY_H
#define DELAY_H
#include "stm32f4xx.h"
void Delay_Init(void);
uint32_t millis(void) ; //毫秒级运行定时器
uint32_t micros(void) ; //微秒级运行定时器
void delay_ms(uint32_t ms) ; //毫秒级延时函数
void delay_us(uint32_t us) ; //毫秒级延时函数
#endif
#include "stm32f4xx.h"
#include "Delay.h"
#include "stm32f4xx_it.h"
static uint32_t usTicks ;
volatile uint32_t SysTickUptime = 0 ; //systick 每1ms 中断一次
void Delay_Init(void)
{
SysTick_Config(SystemCoreClock/1000 ) ;
/*
这里的系统时钟是100mhz,一个时钟周期有 1 / 100 000 000 s 也就是一个时钟周期有 10ns
我们需要计数到1ms,也就是100 000个周期,也就是100 000个10ns
所以 SystemCoreClock/1000
1ms 开启systick中断一次
*/
usTicks = SystemCoreClock/1000000 ; //1us的滴答数 usTicks = 100
}
/* 毫秒级运行定时器 , 返回的是毫秒
SysTickUptime 溢出时间大概49天 */
uint32_t millis(void)
{
return SysTickUptime ;
}
/* 毫秒级延时函数 */
void delay_ms(uint32_t ms)
{
uint32_t now_time = millis();
while(millis() - now_time < ms) { }
}
/* 微秒级计数函数 */
uint32_t micros ( void )
{
uint32_t ms , cycle_cnt ;
do{
ms = SysTickUptime ;
cycle_cnt = SysTick->VAL ;
}while(ms != SysTickUptime ) ;
return (ms * 1000 ) + ( ( (SystemCoreClock/1000) - cycle_cnt ) / usTicks ) ;
}
/* 微秒级延时函数 */
void delay_us(uint32_t us)
{
uint32_t now_time = micros();
while(micros() - now_time < us) { }
}
/*举个简单的例子理解一下 假如 我们这里选择 100mhz
此时SysTickUptime = 100 表示自系统启动以来已经过了100ms
在某一时刻,我们调用micros函数,SysTick->VAL当前的值是50000
(表示自上次SysTick中断以来已经过了50000个周期)
那么此时是多少毫秒呢
也就是计算 SysTickUptime * 1000 + (1000000 - SysTick->VAL) / usTicks
(1000000 - SysTick->VAL) / usTicks 表示走过的50000个周期 有少个usTicks 1个usTicks 也就是1us
*/