Systick 的信号来源于系统时钟,不分频为168MHz,8 分频为 21MHz,从下图的时钟树就可以看出来。 ---这是F4的,,F1的位72MHz的
F10系列的滴答时钟---72Mhz
♈控制及状态寄存器(CTRL)
因为是查询式,所以我们不用第1位
- 0位(打开滴答时钟)
- 2位(选择时钟源,我们一般选择外部时钟源)
- 16位(查看是否数到了零)
♈重装载数值寄存器(LOAD)
这个寄存器就比较简单了,这个就是重新向滴答时钟里加载计时次数,可以看到总共有24位可设置,所以重新加载值最大不能超过24位。
♈当前数字寄存器(VAL)
这个寄存器作用在读取的时候返回当前计数值,在写入的时候就是计数器清零,同时还清除掉了SysTick->CTRL第16位的标志。
♈校准数值寄存器(CALIB)
以上四种寄存器,我们常用的有:
- SysTick->CTRL(控制和状态寄存器)
- SysTick->RELOAD(重装载数值寄存器)
- SysTick->VAL(当前数值寄存器)
实现思路:
利用 systick定时器为递减计数器,设定初值并使能它后,它会每过一个系统时钟周期计数器进行减,直到计数到 0时,SysTick计数器会自动重装初值并继续计数,同时触发中断。
在时钟树里可以看到,对于Cortex时钟,有两种选择方法,
一、是选用经过八分频的外部时钟源,
二、是选用内核时钟,我们一般在设置的时候选用八分频后的外部时钟源,我们之前设置了系统时钟为168MHZ,经过八分频后变为了21MHZ,也就是说,滴答时钟的频率是21MHZ,那让滴答时钟计1次,时间过去了1/21μs,滴答时钟计21次,才是1μs,于是我们有了以下的设计:
- 计1μs:我们向计数器里放入21
- 计1ms:我们向计数器里放入21000
1ms(毫秒)=1μs(微秒)
配置滴答时钟步骤如下:- 1、清空计数器
- 2、重新装载数值
- 3、打开定时器
- 4、等待计时器数到0
- 5、关闭计数器
- 6、清空计数器
/********************
4个Systick寄存器CTRL: Systick控制和状态寄存器
RELOAD(LOAD): Systick自动重装载初值寄存器
VAL: Systick当前值寄存器
CALIB: Systick校准寄存器
1.选择时钟频率为 24MHz 的时钟,即周期为 1/24000000s---计21个数为1us
2.VAL中存放的数值在每一个Systick周期后减1,当减为0后,VAL就会从LOAD中加载初值,再进行倒计数。******计US******
//2^24 -1=16777215=0xffffff
SysTick->VAL=0;//计数器清零
SysTick->LOAD=num*24; //设置重装值
SysTick->CTRL|=1;//使能计数器
while(!(SysTick->CTRL &(1<<16)));//阻塞判断定时时间是否到达,判断SysTick->CTRL的位
SysTick->CTRL =0; //失能滴答定时器
********************/void delay_new_us(u32 num) { u32 dealer;//商 u32 rem;//余数 u32 temp; dealer=(21*num/0xffffff);//2^24 -1=16777215=0xffffff rem=(21*num)%0xffffff; while(dealer)//商大于0时 { SysTick->LOAD=(u32)0xffffff; //装载最大可装载值 SysTick->VAL=0; //清空计数器 SysTick->CTRL|=1; //使能计数器,开始计数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0)) dealer--; if(!dealer)//a为0,表示装载最大可装载值的次数用完 { SysTick->CTRL&=0xfffffffe;//关闭计数器 } } SysTick->LOAD=rem;//装载余数 SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=1; //重新使能计数器,开始计数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0)) SysTick->CTRL&=0xfffffffe;//关闭计数器 SysTick->VAL =0X00; //清空计数器 }
代码解析:
通过个人改良,原本的滴答正常配置只能计2^23个数,就是798MS左右就爆了,但我通过取余的方式,将取最大数值的余数出来,再重装进去,重新定时,就可以实现超过一秒的定时,这样重复重装-------MS的延时同理,,*21000就行
注意:
主函数必须进行滴答定时初始化函数的调用,不然使用不了
封装成延时函数用就行,很精准
SysTick.c
#include "SysTick.h" void SysTick_Init(void) { SysTick->CTRL &=~(1<<2);//选择时钟源为21MHZ--寄存器 //SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //--库函数 SysTick->VAL=0;//计数器清零 } void delay_new_us(u32 num) { u32 dealer;//商 u32 rem;//余数 u32 temp; dealer=(21*num/0xffffff);//2^24 -1=16777215=0xffffff rem=(21*num)%0xffffff; while(dealer)//商大于0时 { SysTick->LOAD=(u32)0xffffff; //装载最大可装载值 SysTick->VAL=0; //清空计数器 SysTick->CTRL|=1; //使能计数器,开始计数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0)) dealer--; if(!dealer)//a为0,表示装载最大可装载值的次数用完 { SysTick->CTRL&=0xfffffffe;//关闭计数器 } } SysTick->LOAD=rem;//装载余数 SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=1; //重新使能计数器,开始计数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0)) SysTick->CTRL&=0xfffffffe;//关闭计数器 SysTick->VAL =0X00; //清空计数器 } void delay_new_ms(u32 num) { u32 a;//商 u32 b;//余数 u32 temp; a=(21000*num/0xffffff);//2^24 -1=16777215=0xffffff b=(21000*num)%0xffffff; while(a)//商大于0时 { SysTick->LOAD=(u32)0xffffff; //装载最大可装载值 SysTick->VAL=0; //清空计数器 SysTick->CTRL|=1; //使能计数器,开始计数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0)) a--; if(!a)//a为0,表示装载最大可装载值的次数用完 { SysTick->CTRL&=0xfffffffe;//关闭计数器 } } SysTick->LOAD=b;//装载余数 SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=1; //重新使能计数器,开始计数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(0x10000)));//while(计数器未使能&&(!CTRL计数器倒计数到0)) SysTick->CTRL&=0xfffffffe;//关闭计数器 SysTick->VAL =0X00; //清空计数器 }
SysTick.h
#ifndef __SYSTICK_H #define __SYSTICK_H #include "stm32f4xx.h" void SysTick_Init(void);//初始化滴答时钟 void delay_new_us(u32 num); void delay_new_ms(u32 num); #endif
x形参就不用说了吧,这个给多少数值对应延时多少