SysTick定时器
一、SysTick定时器
SysTick定时器是存在于Cortex-M3上的一个外设,一个24 位的倒计数定时器,计到0 时,将从RELOAD 寄存器中自动重装载定时初值,在下一个时钟沿重新加载(回绕到)LOAD 寄存器中的值,只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
STM32F1xx官方资料:《STM32F10xxx Cortex-M3编程手册》
二、SysTick定时器的作用
(1)专用于产生RTOS的系统滴答时钟
(2)可用于裸机程序中短时间精确延时函数
(3)可用作普通定时中断功能
三、SysTick定时器的相关寄存器
SysTick控制和状态寄存器(STK_CTRL)
位bit | 名称 | 描述 |
---|---|---|
16 | COUNFLAG | 当定时器倒计数到0时,该为由硬件自动置1 |
2 | CLKSOURCE | 时钟源选择 0 : AHB / 8 , 1 :AHB |
1 | TICKINT | SysTick 异常请求使能 0:不产生中断, 1:产生中断 |
0 | ENABLE | 计数器使能 0:禁用 ,1:使能 |
SysTick自动重装载值寄存器(STK_LOAD)
LOAD寄存器存放的是计数器的初始值,是一个恒定不变的值
RELOAD 值的范围是 0x00000001-0x00FFFFFF
SysTick当前值值寄存器(STK_VAL)
CORRENT首先存放的是LOAD寄存器中值,每来一个时钟,CORRENT存放的值减1
当减到0时,在下一个时钟到来重新从LOAD寄存器取值。
四、SysTick定时器相关库函数
SYSTICK寄存器在标准库中的封装
(1)SysTick_CLKSourceConfig
/**
* @brief Configures the SysTick clock source.
* @param SysTick_CLKSource: specifies the SysTick clock source.
* This parameter can be one of the following values:
* @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
* @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
* @retval None
*/
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{
/* Check the parameters */
assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
{
SysTick->CTRL |= SysTick_CLKSource_HCLK;
}
else
{
SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;
}
}
(2)SysTick_Config
* @brief Initialize and start the SysTick counter and its interrupt.
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
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 */
}
SysTick_Config函数配置的状况是:默认使用AHB时钟,会产生中断,中断优先级为低,并且最末尾启动了定时器。
如果要使用时钟AHB/8则应该先调用SysTick_Config函数后调SysTick_CLKSourceConfig函数
SYSTICK定时器的2种工作模式
(1)中断方式
(2)查询方式
SYSTICK定时器的定时计算
(1)公式:重装载值 = systick时钟频率(Hz) * 想要的定时时间(S)
(2)举个栗子(1ms) CNT = 72000000Hz * 0.001S = 72000
五、 SysTick定时器中断 SysTick_Handler()
功能:SYSTICK中断实现LED每隔500ms闪烁一次
在main函数中
(1)设置好时钟打开相应的GPIO时钟模块
(2)将相应GPIO配置为推挽输出
(3)NVIC的配置
(4)SysTick时钟源和ticks值(LOAD值)
(5)在stm32f10x_it.c相应的ISR中取反LED电平
#include "stm32f10x.h"
// 函数声明
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void SYSTICK_Configuration(void);
int main(void)
{
RCC_Configuration(); //时钟配置
GPIO_Configuration(); //GPIO配置
NVIC_Configuration(); //NVIC配置
SYSTICK_Configuration();
while (1);
}
void RCC_Configuration(void)
{
// 因为在起始文件中执行main函数之前,调用了SystemInit函数,将主频设置到72Mhz
/* Enable Key Button GPIO Port, GPIO_LED and AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //打开GPIOB的时钟
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_RESET); //初始化PB8为0
}
void NVIC_Configuration(void)
{
// NVIC_InitTypeDef NVIC_InitStructure;
#ifdef VECT_TAB_RAM
/* Set the Vector Table base location at 0x20000000 */
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //分配中断向量表
#else /* VECT_TAB_FLASH */
/* Set the Vector Table base location at 0x08000000 */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
}
void SYSTICK_Configuration(void)
{
// AHB主频为72MHz,定时时间500ms = 72000000 * 0.5 = 36000000(重新装载值)
// 2^24 = 16777216 < 36000000
SysTick_Config(36000000 / 8);
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //将SysTick时钟源设为AHB/8
}
void SysTick_Handler(void)
{
// 在中断程序中让LED取反
GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)!GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_8));
// 清挂起中断
}
六、SysTick定短时间精确延时函数 delay()
#include "stm32f10x.h"
// 函数声明
void RCC_Configuration(void);
void GPIO_Configuration(void);
void delay_us(unsigned int us);
void delay_ms(unsigned int ms);
#define DELAY 1000
int main(void)
{
RCC_Configuration(); //系统时钟配置
GPIO_Configuration(); //配置GPIO
while (1)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_SET); // 亮
delay_ms(DELAY);
GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_RESET); // 灭
delay_ms(DELAY);
}
}
void RCC_Configuration(void)
{
/* Enable Key Button GPIO Port, GPIO_LED and AFIO clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// PB8
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_WriteBit(GPIOB, GPIO_Pin_8, Bit_SET); // 默认输出1让LED亮
}
// 用systick计数器来帮我们实现us级别的精确延时
// 这个函数要成立有2个条件:1、主频必须是72MHz
// 2、us要小于 1864135
void delay_us(unsigned int us)
{
unsigned int tmp = 0;
// 思路是先把systick的时钟源设置好,然后给一个正确的ticks
// 然后使能systick,while循环等待countflag置位则时间到
SysTick->LOAD = us * 9; // 72MHz主频,9MHz systick时钟 9 * (1 / 8)
SysTick->VAL = 0;
SysTick->CTRL = 0x01; // 时钟源是AHB/8,禁止中断,使能systick
do
{
tmp = SysTick->CTRL;
}while (!(tmp & (1<<16)));
// 跳出do while循环说明时间到了,关闭定时器即可
SysTick->VAL = 0;
SysTick->CTRL = 0x00;
}
// ms不能大于 1864
void delay_ms(unsigned int ms)
{
unsigned int tmp = 0;
// 思路是先把systick的时钟源设置好,然后给一个正确的ticks
// 然后使能systick,while循环等待countflag置位则时间到
SysTick->LOAD = ms * 9000; // 72MHz主频,9MHz systick时钟
SysTick->VAL = 0;
SysTick->CTRL = 0x01; // 时钟源是AHB/8,禁止中断,使能systick
do
{
tmp = SysTick->CTRL;
}while (!(tmp & (1<<16)));
// 跳出do while循环说明时间到了,关闭定时器即可
SysTick->VAL = 0;
SysTick->CTRL = 0x00;
}