参考资料《 ARM Cortex™-M4F 技术参考手册》-4.5 章节 SysTick Timer(STK)
SysTick(系统滴答定时器),本质上就是一个内嵌在NVIC中的一个定时器,属于内核中的一个外设,是一个24位的向下递减的计数器,计数器每1/SYSCLK就减1,当寄存器的值减到0的时就会产生一次(硬件上的)中断,也有叫做滴答中断。主要的目的1、用来产生精确的延时。2、一般用于操作系统,用于产生时基,维持操作系统的心跳给操作系统提供一个单独的心跳(时钟)节拍。关于对于SyeTick的介绍我推荐看一下这位大神博客 https://blog.csdn.net/yx_l128125/article/details/7884423。
SysTick的寄存器总共有四个,在使用SysTick产生定时的时候,只需配置前三个寄存器,最后一个校准值寄存器不需要使用。
接下来对各个寄存器的描述
SysTick控制及状态寄存器CTRL主要用来读取当前数值寄存器里的值、选择时钟源、使能异常请求、使能定时器。
SysTick重装载数值寄存器LOAD主要是用来存储计数的值,最大为
SysTick当前数值寄存器VAL,每一个计数器每1/SYSCLK就减1,当减到0时,计数标志位置1,然后把重装载数值寄存器里的值写进当前数值寄存器,并清除计数标志位。
Sys校准数值寄存器CALIB
以上就是关于SysTick的介绍,接下来就要操作一下了,
我们要用SysTick来定时1S使LED灯状态变化一次,所以
1、首先,应先线配置SysTick相关寄存器,来产生1s的延时
2、初始化LED灯
3、编写主函数
由于Systick是嵌入在内核中的所以在硬件电路中只需要有LED模块即可,想要用SysTick实现流水灯1s切换一次状态的操作,首先就是要配置SysTick,这里我们直接用STM32官方额库函数SysTick_Config(uint32_t ticks),在内核头文件core_cm4.h中,这个函数的功能是配置系统定时器和中断,并且打开SysTick,使计数器处于自由计数模式来产生一个周期性的中断。在这个函数中形参ticks是用来设置重装载寄存器的值,最大不能超过重装载寄存器的值,当重装载寄存器的值减到0时,就会产生一个中断,然后重装载寄存器的值又写入当前计数器中重新往下递减计数,以此循环。接下来这个库函数是给该中断配置中断优先级(关于中断优先级可以查看STM32中断及NVIC概述),然后给当前数值寄存器赋初值,接下来配置时钟源,使能中断,开启定时器。当返回0时,说明函数功能已经实现,返回1则表示失败。这个初始化函数主要是配置了SysTick中的三个寄存器:LOAD、VAL、CTRL,还调用了NVIC_SetPriority()配置中断优先级函数。
因为SysTick是内核中的外设,所以时钟选择为HCLK180MHz时钟
由于官方没有 SysTick的初始化函数,只有配置函数Config所以,我们自己简单的编写一个SysTick_Init()函数
因为HCLK为180MHz,所以当ticks=SystemFrequency/1000时(180 000 000/1000),每经过1/SystemFrequency一次,ticks的值就减1,所以计数时间为ticks/(1/SystemFrequency)=1ms,,所以我们配置的SysTick为10us进一次中断。
配置好SysTick后,我们只需要再定义一个变量来记录进入中断的次数N,N*SysTick就等于我们所要定时的时间。
nTime就是我进入SysTick中断的次数,每进入一次中断服务函数,TimingDelay就减1,当TimingDelay不等于0时,延时结束。
中断服务函数如下
关于SysTick配置已经描述完,接下来初始化LED灯。
关于LED的初始化我就不再做过多的描述,可以参考STM32固件库函数点亮LED灯,代码如下
#include "stm32f4xx_gpio.h"
#define GPIO_R_Pin GPIO_Pin_10
#define GPIO_R_Port GPIOH
#define GPIO_G_Pin GPIO_Pin_11
#define GPIO_G_Port GPIOH
#define GPIO_B_Pin GPIO_Pin_12
#define GPIO_B_Port GPIOH
/************************控制LED灯亮灭的宏***************/
/*直接操作寄存器的方法控制IO*/
#define LED_PORT_OUT_HI(p,i) { p->BSRRL = i ;} //输出为高电平
#define LED_PORT_OUT_LO(p,i) { p->BSRRH = i ;} //输出为低电平
#define LED_PORT_OUT_Toggle(p,i) { p->BSRRL ^= i ;} //输出为反状态
/*定义控制IO的宏*/
#define LED_R_Toggle LED_PORT_OUT_Toggle(GPIO_R_Port,GPIO_R_Pin)
#define LED_R_ON LED_PORT_OUT_LO(GPIO_R_Port,GPIO_R_Pin)
#define LED_R_OFF LED_PORT_OUT_HI(GPIO_R_Port,GPIO_R_Pin)
#define LED_G_Toggle LED_PORT_OUT_Toggle(GPIO_G_Port,GPIO_G_Pin)
#define LED_G_ON LED_PORT_OUT_LO(GPIO_G_Port,GPIO_G_Pin)
#define LED_G_OFF LED_PORT_OUT_HI(GPIO_G_Port,GPIO_G_Pin)
#define LED_B_Toggle LED_PORT_OUT_Toggle(GPIO_B_Port,GPIO_B_Pin)
#define LED_B_ON LED_PORT_OUT_LO(GPIO_B_Port,GPIO_B_Pin)
#define LED_B_OFF LED_PORT_OUT_HI(GPIO_B_Port,GPIO_B_Pin)
#define LED_RGBOFF LED_R_OFF;\
LED_G_OFF;\
LED_B_OFF
void LED_GPIO_Config(void);
#include "bsp_led.h"
/**
* @brief 初始化控制LED的IO
* @param 无
* @retval 无
*/
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_AHB1PeriphClockCmd ( LED1_GPIO_CLK|
LED2_GPIO_CLK|
LED3_GPIO_CLK, ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_PIN;
/*设置引脚模式为输出模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
/*设置引脚的输出类型为推挽输出*/
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
/*设置引脚为上拉模式*/
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
/*设置引脚速率为2MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
/*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED2_PIN;
GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED3_PIN;
GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure);
/*关闭RGB灯*/
LED_RGBOFF;
}
配置好SysTick和LED后,接下来就开始写主函数
主函数如下
int main(void)
{
/* LED 端口初始化 */
LED_GPIO_Config();
/* 配置SysTick 为10us中断一次,时间到后触发定时中断,
*进入stm32fxx_it.c文件的SysTick_Handler处理,通过数中断次数计时
*/
SysTick_Init();
while(1)
{
LED_RED;
Delay_us(100000); // 10000 * 10us = 1000ms
//延时1s
LED_GREEN;
Delay_us(100000);
//延时1s
LED_BLUE;
Delay_us(100000);
//延时1s
}
}