Systick定时器延时
一、系统定时器
1. 目的
Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。
2. 简介
SysTick 叫做系统滴答时钟、系统定时器,属于 Cortex-M4内核中的一个外设(外围设备),也是属于NVIC的一部分,并且是24bit向下递减的计数器。
==最大计数:== 2^24-1 (向下计数到0,然后触发中断)
3. SysTick寄存器
序号 | 寄存器 | 名称 | 描述 |
---|---|---|---|
1 | CTRL | Systick控制和状态寄存器 | 使能Systick定时器并设置时钟源 |
2 | LOAD | Systick自动重装载寄存器 | 当倒数至零时,将被重装载值 |
3 | VAL | Systick当前值寄存器 | 读取时返回当前倒数值,写它则使之清零 |
4 | CALB | Systick校准值寄存器 | 不常用 |
(1)Systick控制和状态寄存器
==外部时钟==是 HCLK(AHB总线时钟) 的1/8 (stm32f401 是 168/8=21M)
==内核时钟==是 HCLK时钟 (stm32f401 是 84M)
(2)Systick自动重装载寄存器
(3)Systick当前值寄存器
(4)Systick校准值寄存器
4. 计时原理
time = 中断次数*一次中断时间
重装载次数为n,一次中断时间为t=1/SYSCLK,中断时间为T;
T = n * t
二、精准延时函数
1. 固件库函数延时
int n=0;//全局变量
void SysTick_Init(void)
{
//uint32_t SystemCoreClock = 84000000;
//SystemFrequency / 1000 1ms 中断一次
//SystemFrequency / 100000 10us 中断一次
//SystemFrequency / 1000000 1us 中断一次
if (SysTick_Config(SystemCoreClock/1000000)) /*让系统中断一次耗时1us*/
{
while (1);
}
}
void delay_us(__IO u32 nTime)
{
n =nTime;
while(n>0);
}
delay_ms(__IO u32 nTime)
{
n =nTime*1000;
while(n>0);
}
void SysTick_Handler(void)
{
if(n!=0x00)
{
n--;
}
}
2. 寄存器延时
寄存器延时比固件库函数写的更精简,推荐使用这种延时。
#define PLL_m 8
/*----------------Systick微秒级延时----------------*/
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD = SystemCoreClock/PLL_m/1000000*nus;//设置重装载值 = 84000000/8/1000000 * n (1us中断一次)
SysTick->VAL=0X00;//清空计数器
SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick->CTRL;//读取当前倒计数值
}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
/*----------------Systick毫秒级延时----------------*/
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD = SystemCoreClock/PLL_m/1000*nms;//设置重装载值 = 84000000/8/1000 * n (1ms中断一次)
SysTick->VAL=0X00;//清空计数器
SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick->CTRL;//读取当前倒计数值
}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
3. 软延时
在延时精度要求低的情况下建议采用软延时,减少中断次数。
/*----------------软微秒级延时----------------*/
void Delay_us(uint32_t Delay_us)
{
volatile unsigned int num;
volatile unsigned int t;
for (num = 0; num < Delay_us; num++)
{
t = 11;
while (t != 0)
{
t--;
}
}
}
/*----------------软毫秒级延时----------------*/
void Delay_ms(uint16_t Delay_ms)
{
volatile unsigned int num;
for (num = 0; num < Delay_ms; num++)
{
Delay_us(1000);
}
}
/*----------------软I2C延时----------------*/
void I2C_Delay(void)
{
uint8_t i;
for (i = 0; i < 100; i++);
}
三、编程总结
- 注意systick的时钟,首先需要先设置systick时钟源。
- 接着设置重装载寄存器的值。
- 清除当前数值寄存器的值。
- 配置控制与状态寄存器。
- 在main函数中循环调用延时函数实现灯闪烁。
- systick延时在之后起着重要作用,许多程序涉及到延时函数的调用,因此写好一个精准的延时函数必不可少!