STM32G431RBT6上有高级定时器TIM1/8、通用定时器TIM2~4、基本定时器TIM6/7。经常用到的是通用定时器TIM2~4,可以选择其中一个作为一个总控定时器(定时20ms),在这个总控上去安排不同的任务(比较像分时复用?)。
复制下上次的LED工程,重命名为TIM,加入定时器的控制,做一个流水灯效果。
这里我们选用TIM4做为总控定时器。
一、在STM32CubeMX中打开TEST.ios,进行定时器的配置
在比赛资源包中的STM32G4系列微控制器参考手册资料中可以找到定时器所使用的时钟线。
可以看到TIM4是挂在APB1下(按之前设置,时钟频率为80MHz)
设置计数参数,TIM4定时器定时10ms,作为基准。
TIM4的设置完成,更新工程。
二、编程实现流水灯
在Keil 5中打开工程,先编译一次,无错误再进行下一步。
新建一对interrupt文件,用来放置用户编写的有关中断的函数,如定时器、串口中断。
将文件导入到工程中,操作详见上一章“蓝桥杯嵌入式之点灯”。
interrupt.h中先写:
#ifndef __INTERRUPT_H
#define __INTERRUPT_H
#include "main.h"
#endif
接着编写interrupt.c
#include "interrupt.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{//10ms
if(htim->Instance == TIM4)
{
}
}
在使用定时器(TIM)时,HAL库提供了一些回调函数(Callback Function),这些函数在定时器事件发生时被调用。其中,HAL_TIM_PeriodElapsedCallback()
是当定时器的定时周期结束时被调用的回调函数。
- "Period":定时周期,指定时器的时间间隔。
- "Elapsed":已过,表示指定的时间间隔已经流逝。
- "Callback":回调,指一个函数,在某个事件发生时自动被调用。
当定时器的定时周期结束时,HAL库会自动调用 HAL_TIM_PeriodElapsedCallback()
函数,并将指向定时器句柄的指针 TIM_HandleTypeDef
作为参数传递给它。通过这个回调函数,用户可以实现自定义的操作,例如在定时器周期结束时执行一些特定的任务、更新计数器或重新加载计时器的值等。
其中的if语句为判断中断来源是否来自TIM4,这样就可以使用多个定时器中断任务而不会冲突。
添加流水灯函数:
#include "interrupt.h"
#include "led.h"
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{//10ms
// 声明静态变量用于计时和记录LED位置
static unsigned int tim4_count = 0; // 用于TIM4计数,实现分时复用
static unsigned char led_addr = 0; // 用于记录亮灯的位置,随时间移动形成流水灯效果
// 判断中断来源是否是TIM4
if(htim->Instance == TIM4)
{
tim4_count++; // TIM4计数递增
// 每500ms进行一次灯的移位
if(tim4_count == 50)
{
tim4_count = 0; // 重置计数变量,很重要
led_addr = (led_addr + 1) % 8; // 移位操作
led_disp(0x01 << led_addr); // LED显示函数,根据新的LED位置控制LED灯亮灭
}
}
}
static
:这个关键字用于声明一个静态变量。静态变量在程序的整个生命周期内都存在,并且只会被初始化一次。它的作用域限定在当前文件中,因此在其他文件中无法直接访问。在函数内部声明的静态变量,其生命周期也是整个程序的运行期间,但是作用域限定在声明的函数内部。
(led_addr + 1) % 8
:这是一个表达式,它由两部分组成。首先,led_addr + 1
对 led_addr
的值加1,即将 LED 地址递增1。然后,% 8
将结果对8取模,即计算余数,使得 led_addr
在取值范围0到7之间循环。
综合起来,这行语句的作用是将 led_addr
的值加1,并且如果超过了7,则将其重置为0,从而实现了在LED地址范围内循环递增。
在interrupt.h中声明函数
把头文件包含到main.c中
在main()函数中加入代码
led_disp()函数详见上一章“蓝桥杯嵌入式之点灯”。
最后编译下载到板子上。
三、演示效果
流水灯(20ms间隔版)