新手STM32:基于HAL库的定时器和PWM输出
目录
一、定时器与PWM概述
(1)定时器概述
(2)PWM概述
二、HAL库实现定时器控制
(1)CUBEMX配置
(2)代码编写
(3)结果展示
三、PWM实现呼吸灯控制
(1)CUBEMX配置
(2)代码编写
(3)结果展示
(4)Keil仿真
四、总结
(1)定时器与 PWM 核心要点
(2)基于 HAL 库的定时器控制实践
(3)PWM 实现呼吸灯控制探索
一、定时器与PWM概述
(1)定时器概述
在 STM32 微控制器的丰富功能模块中,定时器起着不可或缺的关键作用。以 STM32F103C8T6 这款广泛应用的芯片为例,它配备了总计 8 个定时器,依据功能特性和复杂程度可细分为高级定时器(TIM1 和 TIM8)、通用定时器(TIM2 - 5)以及基本定时器(TIM6 和 TIM7)。通用定时器凭借其全面且灵活的功能特性,在众多应用场景中脱颖而出,成为开发者频繁选用的得力工具。
高级定时器:TIM1和TIM8
通用定时器:TIM2-5
基本定时器:TIM6和TIM7
通用定时器不仅能够精准地实现定时功能,还可对输入信号的脉冲长度进行精确测量(即输入捕获功能),以及依据实际需求输出各种特定波形,涵盖输出比较、产生 PWM(脉冲宽度调制)波形以及单脉冲输出等多样化操作。这种多功能性使其在诸如工业自动化控制、智能家电控制、通信设备定时同步以及电机控制等众多领域得以大展身手。例如,在工业自动化生产线中,可借通用定时器精准把控各工序执行时间间隔与顺序;于电机控制里,借 PWM 功能灵活调节电机转速与转矩,实现高效稳定运行。
(2)PWM概述
PWM(脉冲宽度调制)技术的核心要素在于占空比。占空比,作为衡量脉冲信号特征的关键指标,被定义为在一个完整信号周期内,高电平持续时间与整个信号周期时长的百分比比值,即占空比 = Tp/T(Tp 为高电平时间,T 为信号周期)。通过细腻且精准地调整占空比数值,能够巧妙地改变信号中高电平与低电平的持续时长比例关系,进而实现对外部设备的精准功率控制或模拟信号等效输出效果。
占空比(Duty Cycle),是指在一 个周期内,高电平时间占整个信号 周期的百分比,即高电平时间与周 期的比值: 占空比=Tp/T
调节占空比可以得到不同的电平持续时间。
二、HAL库实现定时器控制
基于前文的讲述,我们可以开始对定时器进行一些基本的操作了。本次实验,我们将设置一个5秒的定时器,每隔5秒从串口发送“hello windows!”;同时设置一个2秒的定时器,让LED等周期性地闪烁,实现一个多任务并发运行的功能。
(1)CUBEMX配置
首先新建一个芯片为STM32F103C8T6的项目,然后进行一些基本的配置。
SYS:配置为Serial Wire
RCC:配置为高速外部晶振
因为我们需要配置一个定时闪烁的LED灯,所以需要配置一个GPIO口进行输出,我选择的PA5进行电平输出。
然后配置我们的GPIO口,我们采用低电平点亮的方式,也就是当我们的GPIO口输出低电平时,LED灯亮起,所以我们的初始GPIO输出电平设为高电平,其他的配置保持在默认选项就行。
接下来配置我们的定时器,选择通用定时器TIM2,配置我们的时钟源为内部时钟;预分频系数配置为71(因为系统处理时会自动为我们的预分频系数加1,所以实际上的预分频系数为72),因为系统时钟树我们会设置为72MHz,分频下来就得到1MHz;计数次数设置为5000次,那么每完成一次计数,经过的时间为 5000/1MHz=0.005秒也就是5ms,换言之,每过5ms定时器TIM2就会进行一次定时中断。
配置步骤如下:
因为我们还需要定时向串口发送数据,所以需要再配置一个定时器来控制这一部分的功能。 我们同样选择一个通用定时器,这里我选择了TIM3,配置步骤与数据和TIM2相同。
接下来开启我们的定时器中断并生成中断配置代码。
接下来配置我们的系统时钟树,按照下图步骤进行配置。
因为我们还存在串口数据的发送,所以还需要配置我们的USART。我们选择配置USART1,选择Asynchronous(异步通信)并开启中断。
接下来就可以生成我们的代码文件了,按照下图的标注进行配置。
点击右上角的GENERATE CODE生成我们的代码,在加载结束后弹出的窗口点击Open Project打开我们的keil项目。
(2)代码编写
首先,我们需要在主函数内打开我们的定时器,所使用的函数为HAL_TIM_Base_Start_IT。
对于参数,“h”指的是HAL库,“tim2”指的就是TIM2,TIM3也是一样的操作。这样子就打开了我们的定时器。
因为这次代码的功能完全由中断实现,因此我们不需要再在while循环内书写代码,只需要在主函数外用一个中断回调函数实现我们的功能。
在主函数内随便找个位置书写我们的中断回调函数,为了方便起见,大家可以在注释标注USER CODE的地方书写我们自己的代码,也就是在main函数下面的
在这个函数内,首先定义我们函数内部的变量,这是我们用来保存定时器当前定时值的参数。前文所说,每当计数器结束一个周期,时间就经过0.005秒,同时我们的计数器的值也会增加1,将我们需要的时间与定时器一个周期的时间也就是0.005秒进行相除就能得到我们的计数器周期的值。然后分别配置两个定时器在各自的周期内完成自己的工作就行了。
代码的部分就这样,接下来我们就可以进行代码的烧录了。
(3)结果展示
因为这次用到了串口的缘故,为了使烧录与接收串口通信不那么麻烦,我选择了用FLYMCU烧录工具直接使用USB to TTL进行烧录。
有需要的伙伴和不了解怎样用TTL烧录的小伙伴可以看我的另一篇文章新手STM32:利用HAL库实现USART串口通信-CSDN博客文章浏览阅读727次,点赞10次,收藏14次。在本次的学习过程中,我们系统且深入地探索了串口通信领域的相关知识。首先,着重对串口通信的电平协议展开了详细的学习与研究。串口通信的电平协议作为数据传输的基础规范,有着至关重要的意义。其中涵盖了如 TTL 电平,其以较为简洁的电平逻辑,通常运用 +5V 表示逻辑 1,0V 表示逻辑 0,这种电平标准在芯片内部或短距离通信场景中发挥着关键作用,因其成本低廉且电路设计相对简易;https://blog.csdn.net/qq_74267512/article/details/144295977?fromshare=blogdetail&sharetype=blogdetail&sharerId=144295977&sharerefer=PC&sharesource=qq_74267512&sharefrom=from_link
因为我们开启的是USART1,所以我们需要找到USART1的TX与RX,它们分别是PA9和PA10,不清楚的小伙伴可以对照下图来看,我们待会儿的PWM也会对照下图。
进行烧录后,运行就能得到下面的情况。
三、PWM实现呼吸灯控制
(1)CUBEMX配置
对于SYS和RCC的配置与上述相同,这里不再赘述。我们直接开始配置定时器。
其实配置定时器的步骤也和上面一致,唯一不同的是打开PWM通道,同时计数次数也要做出相应的更改,这次我们配置TIM3和TIM4这两个通用定时器。我们选择打开其PWM的通道2,如下图所示。
由于我们采用的是PWM输出,他有固定的IO口,所以我们不需要再额外配置GPIO口。
接下来的配置都和上述一致,我只是把不同的点和值得注意的点写了出来,但是大家在配置的时候还是需要按部就班地配置。
(2)代码编写
因为我这次配置了两个外接LED灯,我想配置成两个不同呼吸频率的灯,通过对比来直观展现出占空比变化的影响。
首先还是打开我们的定时器,因为这次打开了我们的PWM,所以我们需要的函数也发生了相应的更改。
我们使用HAL_TIM_PWM_Start来实现开启定时器和PWM通道的功能。
第一个参数就是我们要打开的定时器,前文已经介绍过,第二个参数是我们打开的PWM通道,因为我们之前再CUBE里面配置的是通道二,所以我们这里就需要输入TIM_CHANNEL_2来进行通道打开。
现在来实现我们的呼吸功能,所谓呼吸灯其实是不断地修改PWM的占空比来达到我们的LED灯逐渐变亮和逐渐变暗的功能。为了实现呼吸灯先逐渐变亮后逐渐变暗的功能,首先我们得设定一个阈值,在达到阈值前,我们的占空比逐渐增大;在达到阈值后,我们的占空比逐渐变小直到为0或者其他特定值后再反过来增加。
而实现点灯的IO口,我们可以按照刚才给出的开发板的接口图对照,其中T3C2指的就是TIM3_CHANNEL2,而所对应的接口为PA7,同理可以找到T4C2的接口为PB7,将我们的两个灯以低电平点亮的方式外接在开发板上。
而实现将PWM输出的函数为:__HAL_TIM_SetCompare
从左至右的三和参数分别为:定时器、通道、占空比。其实现的功能就是在定时器指定的通道输出指定占空比的PWM脉冲。了解这些后,就可以开始我们的代码书写了。
while (1)
{
/* USER CODE END WHILE */
HAL_Delay(50); //控制呼吸灯呼吸速率
//控制占空比大小,即修改CCRx的大小
if(TIM3_direction){
TIM3_pwmVal=TIM3_pwmVal+10;
}else{
TIM3_pwmVal=TIM3_pwmVal-10;
}
//设置了ARR为499,因此每计数500为一个周期
if(TIM3_pwmVal > 500){
TIM3_direction = 0; //改变呼吸灯方向
}else if(TIM3_pwmVal == 0){
TIM3_direction = 1; //改变呼吸灯方向
}
//修改定时器3通道2的PWM的占空比
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,TIM3_pwmVal);
if(TIM4_direction){
TIM4_pwmVal=TIM4_pwmVal+20;
}else{
TIM4_pwmVal=TIM4_pwmVal-20;
}
if(TIM4_pwmVal > 500){
TIM4_direction = 0; //改变呼吸灯方向
}else if(TIM4_pwmVal == 0){
TIM4_direction = 1; //改变呼吸灯方向
}
//修改定时器4通道2的PWM的占空比
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_2,TIM4_pwmVal);
/* USER CODE BEGIN 3 */
}
当然,别忘了定义我们的全局变量,这是完成我们呼吸灯的重要参数。
uint16_t TIM3_pwmVal=0; //TIM3的占空比
uint16_t TIM4_pwmVal=0; //TIM3的占空比
uint16_t TIM3_direction=1; //控制TIM3的呼吸;1是越来越亮,0是越来越暗
uint16_t TIM4_direction=1; //控制TIM4的呼吸;1是越来越亮,0是越来越暗
代码的具体位置无所谓,但是要在我们的int main前面。接下来就可以进行代码的烧录了。
(3)结果展示
编译完成无错误后,就可以将我们的代码烧录进去。结果应该和下图一致。
(4)Keil仿真
因为身边没有真实的滤波器,所以我们选择用Keil仿真来看一下PWM的输出波形。
首先点击Keil工具栏中的魔术棒,点击Debug选项卡,按照下图方式配置。
配置完成后点击OK,再点击 开始Debug调试,然后通过下面两种方式找到我们的逻辑分析仪。
打开逻辑分析仪后,点击左上角的Set Up添加端口。
按照下面的方式添加我们的端口。
记得要将输出模式改为bit方便观察。
点击close后,我们的两个端口就保存好了。接下来找到 ,先点击左边按钮复位,复位后点击右边按钮运行。
这就是我们得到的波形,因为PWM的占空比是逐步变化的,我们将时间轴拉长一点观察的更加清晰。
四、总结
本文聚焦于 STM32 定时器与 PWM 输出,利用 HAL 库实现多任务与呼吸灯控制,涵盖理论阐释、实践步骤及结果验证,为嵌入式开发人员掌握定时器和 PWM 技术提供全面指引。
(1)定时器与 PWM 核心要点
定时器分类及特性:STM32F103C8T6 集成 8 种定时器,含高级、通用、基本三类。通用定时器应用广,具定时、输入捕获、输出波形等多功能,为本文技术实践基石。
PWM 原理剖析:占空比定义为周期内高电平占比,精准调控此参数可塑造多样电平持续时长,为 PWM 控制设备核心机制,如呼吸灯亮度渐变依赖占空比动态调整。
(2)基于 HAL 库的定时器控制实践
CUBEMX 配置流程:项目起始于芯片选型 STM32F103C8T6,细致配置 SYS、RCC、GPIO、定时器(TIM2 与 TIM3)、USART1 及系统时钟树参数,同时激活定时器中断,为功能实现设硬件基础与中断响应框架。
代码编写策略:主函数开启定时器中断后,于中断回调函数部署功能逻辑。借定时器周期与计数值关联,精准达成 5 秒串口数据发送与 2 秒 LED 闪烁多任务协同,展示定时器精准定时调度能力,满足复杂任务时序需求。
实践成果展示:借 FLYMCU 与 USB to TTL 高效烧录,运行呈现预期效果。LED 闪烁规律、串口数据按时发送,验证定时器设置与中断处理精准可靠,为实际多任务场景提供可行范例。
(3)PWM 实现呼吸灯控制探索
配置环节要点:SYS、RCC 沿用通用设置,TIM3 和 TIM4 定时器配置独特点在开启 PWM 通道 2 及适配计数次数,因 PWM 输出固有 IO 特性,免额外 GPIO 配置,简化硬件布局且契合 PWM 信号传输路径。
代码功能架构:代码以`while (1)`循环为核心,借`HAL_TIM_PWM_Start`开启定时器与 PWM 通道,`__HAL_TIM_SetCompare`动态调控占空比。依占空比增减逻辑与阈值判定,驱动呼吸灯亮暗循环切换,双灯不同呼吸频率设定凸显 PWM 灵活控制优势。
实践成果反馈:烧录后呼吸灯动态变化契合预期,Keil 仿真借逻辑分析仪呈现 TIM3 与 TIM4 通道 2 PWM 波形。占空比渐变波形直观展示呼吸灯控制原理,为优化与故障排查供有力支撑,提升开发效率与精准度。