系统滴答定时器
STM32中的定时器:
内核:SysTick 的系统定时器
定时器的工作原理
如:想要:1ms的定时
时钟源: 72MHz
分频器:72分频
72MHz / 72 = 1MHz1/1M s = 1us
1s = 1000ms = 1000 000 us
装载值:1000
1000 * 1 us = 1000 us = 1ms
系统定时器:
概述
处理器有一个24位的定时器:SysTick.它可以从装载值向下计数到0
24bit:计数器可以计数到 2的24次方-1
当向下计数到0时,会在下一个周期将重装载值 加载到 LOAD寄存器然后在下一个周期里重新向下计数
概述:
Systick定时器,是一个简单的定时器,对于CM3、CM4内核芯片,都有Systick定时器。Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
使用介绍
本文介绍的滴答定时器主要是用于延时,下面我们通过两种方式来实现延时功能,一种是把滴答定时器当成普通的定时器来使用,另一种是模拟实时操作系统(RTOS),将滴答定时器作为心跳时钟,并采用时间摘取法来获得延时。
寄存器的介绍
SysTick->CTRL, --控制和状态寄存器
SysTick->LOAD, --重装载寄存器
SysTick->VAL, --当前值寄存器
SysTick->CALIB, --校准值寄存器
SysTick control and status register (STK_CTRL) 控制和状态寄存器
![image-20201118225635370](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118225722628-2089644598.png)
![image-20201118225939353](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118225941782-462328710.png)
SysTick reload value register (STK_LOAD) 重装载值寄存器
![image-20201118230139306](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118230141375-2113494864.png)
![image-20201118230231449](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118230233330-325075170.png)
SysTick current value register (STK_VAL) 定时器当前值寄存器
![image-20201118230627718](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118230632618-1125924674.png)
![image-20201118230715714](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118230717496-2100945142.png)
定时器定时时间的计算:
实现1ms来一次的定时
时钟源:72MHz
分频 1分频
72MHz 1/72M s 1/72us
装载值给多少???
1ms (目标) / (1/72)us (时钟周期)
1000 /(1/72) = 72000 (计数次数)
系统定时器的配置
//调用函数
SysTick_Init(72000);
//系统定时器的配置,自己的函数
void SysTick_Init(uint32_t reload)
{
SysTick->CTRL &= ~(0x1<<16);
SysTick->CTRL |= (0x1<<2);
SysTick->CTRL |= (0x1<<1);
SysTick->LOAD = (reload & 0x00FFFFFF);
//SysTick->VAL = (0x00);只有当VAL值为0时,计数器自动重载RELOAD。
SysTick->VAL = 0;
NVIC_SetPriority(SysTick_IRQn, 0);
NVIC_EnableIRQ(SysTick_IRQn);
SysTick->CTRL |= (0x1<<0);
}
//系统时钟中断
vu32 System_Time = 0;
void SysTick_Handler(void)
{
System_Time ++;
}
/** \brief System Tick Configuration \details Initializes the System Timer and its interrupt, and starts the System Tick Timer. Counter is in free running mode to generate periodic interrupts. \param [in] ticks Number of ticks between two interrupts. \return 0 Function succeeded. \return 1 Function failed. \note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b> must contain a vendor-specific implementation of this function. */ __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); /* Reload value impossible */ } SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */ NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */ SysTick->VAL = 0UL; /* 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 (0UL); /* Function successful */ }
定时器的使用
阻塞型的延时函数
![image-20201118233259233](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118233301321-512270436.png)
void Delay_ms(uint32_t time)
{
uint32_t tick = System_Time + time;
//阻塞型的延时
while(System_Time < tick);
//在中断中Systerm_time的值会改变,中断返回还是回到这个断点,直到条件不成立,才能出去这个while循环
}
非阻塞型的延时---类似于时间片的分配
时间片分配的思想,使用非阻塞型的查询方式,来判断时间片是否满足要求,当时间片满足要求之后,将时间片的初值清零。
![image-20201118233408377](https://img2020.cnblogs.com/blog/2019456/202011/2019456-20201118233409853-1014083528.png)
vu32 System_Time = 0;
vu32 Led1_TaskTime[2] = {0, 500};
vu32 Led2_TaskTime[2] = {0, 300};
vu32 Key_TaskTime[2] = {0, 10};
void SysTick_Handler(void)
{
System_Time ++;
Led1_TaskTime[0] ++;
Led2_TaskTime[0] ++;
Key_TaskTime[0] ++;
}
//.h文件声明
extern vu32 Led1_TaskTime[2];
extern vu32 Led2_TaskTime[2];
extern vu32 Key_TaskTime[2];
//while(1)查询
if(Key_TaskTime[0] > Key_TaskTime[1])
{
if(Get_KeyValue())
{
Beep_Toggle(BEEP_PORT, BEEP_PIN);
}
Key_TaskTime[0] = 0;
}
if(Led1_TaskTime[0] > Led1_TaskTime[1])
{
Led_Toggle(LED1_PORT, LED1_PIN);
Led1_TaskTime[0] = 0;
}
if(Led2_TaskTime[0] > Led2_TaskTime[1])
{
Led_Toggle(LED2_PORT, LED2_PIN);
Led2_TaskTime[0] = 0;
}
//按键获取键值
u8 Get_KeyValue(void)
{
u8 key_value = 0;
static u8 count = 0;
if((KEY_PORT->IDR & (0x1<<KEY_PIN)) == 0)
{
count++;
}
else if(count > 2)
{
key_value = 1;
count = 0;
}
else
{
count = 0;
}
return key_value;
}