写在前面:
本文代码基于STM32G431Rx开发板,G4系列基本通用
本文主要内容:
- Systick计时器的特点
- 相关的寄存器
- 代码中主要调用的库函数
- 软件运行配置
- 自定义延迟时长的4种代码实现
Systick定时器简介
Systick定时器是一个简单的定时器,对于ST的CM3,CM4,CM7内核芯片,都有Systick定时器(没有差别)
特点
常用来做延时,或者实时系统的心跳时钟(心跳时钟用在实时操作系统中进行调度)
- 可以节省MCU资源,不用浪费一个定时器
- 比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟
Systick定时器就是系统滴答定时器,一个24位的倒计数定时器
- 计到0时,将从RELOAD寄存器(初始值存储在这个寄存器中,默认值为2^24)中自动重装载定时初值
- 只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作
SysTick定时器被捆绑在NVIC(是一个嵌入式的中断向量控制寄存器)中,用于产生SYSTICK异常(异常号:15,异常级别最低,异常号越小中断优先级越高)
- 优先级可以被设置
- 单片机系统分为前台和后台:前台平时不做事,只有有任务的时候被后台唤醒
相关寄存器
冯诺依曼结构中,寄存器地址编号同时包含了RAM和ROM的地址
寄存器 | 地址(相差4个字节) | 功能 |
---|---|---|
CTRL | 0xE000E010 | SysTick 控制和状态寄存器 |
LOAD | 0xE000E014 | SysTick 自动重装载除值寄存器 |
VAL | 0xE000E018 | SysTick 当前值寄存器 |
CALIB | 0xE000E01C | SysTick 校准值寄存器 |
上图为程序运行时寄存器的实际状态
控制和状态寄存器CTRL
默认情况下采用内核时钟(FCLK)
调用HAL_SYSTICK_CLKSourceConfig()
函数,传递参数为1,即采用内核时钟(传递参数为0则采用外部时钟源)
重装载数值寄存器LOAD
32位寄存器,但只用了24位
当前值寄存器VAL
Systick库函数
HAL库中的Systick相关函数
stm32fGxx_hal_cortex.h
文件中HAL_SYSTICK_CLKSourceConfig ()
:用于Systick时钟源选择(需要用户自己调用)core_cm4.h
文件中SysTick_Config (uint32_t ticks)
:初始化Systick时钟为HCLK,并开启中断(传递参数为滴答时钟的计数值,按照毫秒计数一次)void HAL_IncTick(void)
:在滴答定时器中断里面被调用,全局变量uwTick每毫秒加1void HAL_Delay(uint32_t Delay)
:阻塞式延迟,默认单位是msuint32_t HAL_GetTick(void)
:用于获取全局变量uwTick当前的计数uint32_t HAL_GetTickPrio(void)
:获取滴答时钟优先级HAL_StatusTypeDef HAL_SetTickFreq(uint32_t Freq)
:设置中断频率uint32_t HAL_GetTickFreq(void)
:获取时钟中断频率void HAL_SuspendTick(void)
:挂起滴答定时器void HAL_ResumeTick(void)
:恢复滴答定时器
Systick中断服务函数
void SysTick_Handler (void);
每毫秒响一次(默认情况下滴答时钟的中断服务号为15,也就是Systick时钟的优先级为15)
配置
STM32CubeMx: Clock
MHz对应微秒级,KHz对应毫秒级,\( \frac{1*16}{16000}=1ms \)
系统刚复位的时候,即外部时钟没有生效时,要先使用内部时钟(HSI RC,16MHz),此时系统时钟SYSCLK为16MHz(16000000Hz = 16000KHz)
外部时钟稳定后(即不震荡)才能被使用
下面两张图为STM32CubeMx中Clock的设置部分:
中断
使用NVIC,需要做三件事:
- 首先为我们使用的中断源配置中断向量表
- 配置NVIC寄存器来启用NVIC中断和设置NVIC终端的优先级
使能中断:
- 与向量表里名称相对应的ISR(中断服务程序)
void Systick_Handler(void){ ... }
- 有了中断向量表配置和ISR原型定义,我们可以配置NVIC来处理Systick定时器中断
总结:通常情况下,需要做两件事——设置中断的优先级,然后使能中断源。NVIC寄存器位于系统控制空间
时钟源
HAL_SYSTICK_CLKSourceConfig
函数(在stm32g4xx_hal_cortex.c
头文件中)
该函数从未被调用
初始值、中断与控制
SysTick_Config
函数(在core_cm4.h
头文件中——放到头文件里作为内联函数使用)
在该函数中改变了CTRL寄存器的值
Systick应用
us(微秒级)延时
HAL_Delay
实例
法1:利用HAL_Delay()
在main.c
函数中增加下列代码(注意代码的书写位置严格按照注释,否则可能报错!)
在stm32g4xx_it.c
中,增加下列代码
法2:利用SysTick_Handler ()中断服务
法3:利用SysTick_Handler()中断服务和回调函数
函数指针与回调函数
函数指针
- 每个函数都占用一段内存单元,他们有一个入口地址(起始地址)
- 在C语言中,函数名代表函数的入口地址
- 可以定义一个指针变量,接收函数的入口地址,让他指向函数,这就是指向函数的指针,也称为函数指针
- 通过函数指针可以调用函数,它也可以作为函数的参数
定义
函数指针定义的格式为:
类型名(*变量名)(参数类型表);
- 类型名指定函数返回值的类型,变量名时指向函数的指针变量的名称
例如:
int (*funptr) (int, int);
- 定义一个函数指针
funptr
,它可以指向有两个整型参数且返回值类型为int的函数
- 定义一个函数指针
调用函数
- 通过函数指针调用函数的一般格式为:
(*函数指针名)(参数表)
例如:
int fun (int x, int y); int (*funptr) (int, int); funptr = fun; (*funptr) (3, 5); // 变量的内容发生改变 //(*funptr)是一个整体,指向函数fun
回调函数
回调函数就是一个通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数
来自Stack Overflow某位大神简洁明了的表述:A "callback" is any function that is called by another function which takes the first function as a parameter。 也就是说,函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。
- 回调函数可用于通知机制
法4(法3简化版,推荐使用)
在variable.h
头文件中,添加下列结构体声明
typedef struct SystickHandler{
uint32_t u32Counter;
uint32_t u32Period;
void (*pInit)(void);
void (*pCallBack)(void);
}stSystickHandler;
在main.c
中的Private variables
部分,添加下列代码,实现对结构体内变量的初始化
void LedFlash(void);
// 初始化结构体内的变量
stSystickHandler sSystickHandler = {
.u32Counter = 0, // 计数器
.u32Period = 1000, // 设置周期为1000,即1s闪烁一次
.pCallBack = LedFlash, // 回调函数
};
// 回调函数
void LedFlash(void){
// 当计数器的值大于周期时,LED闪烁一次,并将计数器清零
if(sSystickHandler.u32Counter > sSystickHandler.u32Period - 1){
HAL_GPIO_TogglePin(TickLED_GPIO_Port, TickLED_Pin);
sSystickHandler.u32Counter = 0;
}
// 计数器+1,不闪烁
else{
sSystickHandler.u32Counter ++;
}
}
在stm32g4xx_it.c
中的External variables
部分,添加下列外部变量声明
extern stSystickHandler sSystickHandler;
在stm32g4xx_it.c
中的void SysTick_Handler(void)
函数中,添加下列代码,调用回调函数
if(sSystickHandler.pCallBack){
sSystickHandler.pCallBack();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。