STM32:了解定时器和PWM波形、完成相应的实验练习

本文旨在完成:
1.了解STM32定时器构造,并利用HAL库实现STM32内部计时,实现控制LED灯周期性亮灭、定时串口通信。
2.了解PWM波形,利用HAL库实现LED呼吸灯
实验器材及软件:
STM32F103C8T6,若干杜邦线飞线、一个LED、USB转TTL接口、面包板
STM32CubeMx,Keil 5 MDK,FlyMcu烧录软件、串口调试助手


一、STM32定时器了解、运用

1.stm32定时器的实现原理

 在STM32F1系列芯片中,共有8个定时器。其中有2个高级定时器(TIM 1/8)2个基本定时器(TIM 6/7)4个通用定时器(TIM 2/3/4/5 本次实验使用),具体如下图

在这里插入图片描述
以基本定时器的功能框图演示其工作流程:

1、时钟源(CK_INT,来自RRC复用时钟控制,经AHB1分频后提供)
2、计数器时钟,PSC驱动计数器计数,可以对TIMxCLK进行1~65536之间任一个数进行分频
3、计数器,16位(最大65535,只增不减,最大清零)
4、自动重装载寄存器ARR,16位,承载计数器最大数值,当计数器到达,且使能中断,定时器溢出中断
在这里插入图片描述
 定时时间计算(中断周期×中断次数):1/CK_CLK*(ARR+1)*time
 其中,1/CK_CLK为计数器计一次数的时间、(ARR+1)为一个中断周期内计数的次数,两者相乘就是一个中断周期的时间。time则是一个变量,记录中断的次数

由于本次实验使用的CubeMx建立的初始化框架并利用HAL库进行操作,所以此处只简单展示初始化结构体
在这里插入图片描述
 下面是STM32CubeMx中的定时器设置(此处我们选择TIM2 通用计时器 作为定时),在Clock时钟源处选择内部时钟,即上文功能框架图的CK_INT。随后进入到参数设置,可以以上图进行对照,下面介绍各个参数作用

在这里插入图片描述

  • Prescaler,即分频(定时器的时钟源CK_INT是来自与RCC的分频)。此处的71,但实际要加1,代表将RCC时钟频率分1/72作为时钟源给TIM2,而RCC时钟要到Clock Configuration中设置,此处已设置为72MHz,因此TIM2分得1MHz。
  • Counter Mode,计数模式,此处设为Up,向上计数。
  • Counter Period,计数周期,就是计多少数为一个周期,此处设为5000以为一个周期计5000个数,那么5000*(1/1MHz)=0.005s 就是一个中断周期的具体耗时
  • 剩下的Internal Clock Division 就是再次分频、auto_reload preload是自动预加载,我们把这个使能。

 接下来是Clock Configuration,可见右侧APB1 Timer clocks分得的是72MHz,STM32CubeMx上的时钟配置跟参考手册的有所出入,此处我理解为定时器就分到了72MHz,TIM2再分频后分到了1MHz。
在这里插入图片描述
 至此,简单介绍了STM32定时器的原理以及初始化的大概思路,连带着讲述了如何在STM32CubeMx上完成定时器的初始化。其他的初始化设置(GPIO串口之类的)在以往文章中有介绍,此处不再赘述,现附上设置完后的芯片视图
在这里插入图片描述

2.实践练习

利用stm32内部定时器中断同时完成:
1.设置一个2秒定时器,完成LED周期性闪烁
2.设置一个5秒定时器,持续向上位机发送“hello world!“

  • 此处利用的是PB12连接LED灯,下面是KEIL5中的实现代码
    在这里插入图片描述
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "string.h"

uint32_t time_led =0;
uint32_t time_uart=0;
uint8_t data[]="hello windows!\n";

void SystemClock_Config(void);
static void MX_NVIC_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_USART1_UART_Init();
  MX_NVIC_Init();
 
  HAL_TIM_Base_Start_IT(&htim2);//定时器启动函数,此处启动htim2即TIM2定时器
  while (1)
  {}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时中断调用函数
{
		if(++time_led >= 400)//每过2秒,翻转一次LED电平状态
		{
			time_led =0;
			HAL_GPIO_TogglePin(LEDB12_GPIO_Port,LEDB12_Pin);//PB12翻转电平
		}
		if(++time_uart>=1000)//每过5秒,发送一次data中的数据
		{
			time_uart=0;
			HAL_UART_Transmit(&huart1,data,strlen(data),0xFF);
		}	
}
  • 编译、烧录后实现,以下为演示片段
  • 定时2秒翻转电平,实现LED周期性亮灭
    在这里插入图片描述
  • 串口通信发送“hello windows!”,间隔5秒

在这里插入图片描述


二、PWM波形

1、了解PWM波形、STM32输出的原理

PWM,即脉冲宽度调制——通过对一系列脉冲的宽度进行调制,来等效的获得所需要的波形。此次实验要求做的呼吸灯,实际就是要求引脚输出的电压随时间递增递减,而正常情况下我们是无法实现的,我们只能改变高低电平的占空比而不能改变他们的幅值即电压。PWM调制就解决了这样的问题,它可以将占空比转化为幅值输出,即我们只要使有效电平持续时间逐渐升高,经过PWM,就能达到理想效果。
 以下介绍该调制生成PWM波形的两种方法:
在这里插入图片描述
 而在STM32F103C8芯片上,PWM波形是由TIM通用定时器输出的,每个TIM都有4个通道可以输出,即每个TIM都有4个引脚可以输出PWM波形。拿下图PA6举例,其连接有PWM3/1,即代表PA6可以输出TIM3 通道1的PWM波形。此次实验我们利用PA6和PB6,即开启PWM3/1和PWM4/1输出波形(此图不完整,未显示TIM4输出PWM的引脚)
在这里插入图片描述
 下面展示在STM32CubeMx上的初始化(HAL库),标准库初始化太过麻烦就不再介绍。如图可见,只需将TIM3和4中的Channel1设置为PWM Generation CH1即可
在这里插入图片描述
 其它初始化设置不再赘述,现附上芯片视图
在这里插入图片描述

2、实践练习

使用TIM3和TIM4,分别输出一个PWM波形,PWM的占空比随时间变化,去驱动外接的一个LED以及最小开发板上已焊接的LED(固定接在 PC13 GPIO端口),实现2个 LED呼吸灯的效果。

  • 以下为代码片段
#include "main.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

uint32_t pwm3=10;//PA6输出PWM占空比,用于外接LED
uint32_t pwm4=10;//PB6输出PWM占空比,用于STM32板PC13自接LED
int flag1=0;//用于判断PA6输出占空比递增递减
int flag2=0;//用于判断PB6输出占空比递增递减

uint16_t pwm_num[]=
{1,1,1,1,2,2,2,2,3,3,4,4,5,5,6,7,8,9,10,13,15,17,19,22,25,28,32,36,41,47,53,61,
 69,79,89,102,116,131,149,170,193,219,250,284,323,367,417,474,539,613,697,792,901,1024,1024,901,792,697,613,539,474,417,
 367,323,284,250,219,193,170,149,131,116,102,89,79,69,61,53,47,41,36,32,28,25,
 22,19,17,15,13,10,9,8,7,6,5,5,4,4,3,3,2,2,2,2,1,1,1,1};//PWM呼吸灯效果理想占空比变化数组
void SystemClock_Config(void);

//主函数
int main(void)
{
  
  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_I2C2_SMBUS_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  MX_USART1_UART_Init();
  
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//启动TIM3,CHANNEL_1输出PWM波形
	HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);//启动TIM4,CHANNEL_1输出PWM波形
	
	//循环实现占空比变化实现呼吸灯效果
  while (1)
  {
		pwm3=pwm_num[flag1++];//PA6循环赋值占空比以达到PWM波形输出
		//PB6循环赋值以达到PWM波形输出
		if(flag2<200)
		{
			pwm4+=30;
			flag2++;
		}
		if(flag2>=200)
		{
			pwm4-=30;
			flag2++;
		}
			
		if(flag1>=109)flag1=0;
		if(flag2==400)flag2=0;
		__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,pwm3);//实现PWM波形
		__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,pwm4);//实现PWM波形
		HAL_Delay(10);//延迟
  }
}

  • 注意:
    1.上方数组pwm_num存储的是呼吸特性曲线,合理地模拟了人眼适应的呼吸灯效果。
    2.为什么两个PWM输出是不一样的实现代码?因为外接LED灯一端接地,有电位差就会亮。而核心板上的LED灯是与与电源连接的,因此只有当引脚输出的电压接近电源电压,LED才会灭,但是pwm_num的最大值也达不到这个要求(只是这个pwm_num范围太小了),因此额外写了实现代码。

  • 以下为演示片段(右下角为外接LED,与PA6连接;核心板红灯上方为与PC13接LED灯,与PB6连接)

在这里插入图片描述


三、总结

 本文是基于STM32F103C8T6核心板的实验练习。通过实验,了解了STM32F103系列定时器的基本构造以及实现原理,对其定时的原理也有了初步的理解,应用更为深刻;此外还学习了PWM脉冲宽度调制,如何利用占空比来输出理想波形,在硬件上实现PWM波形的输出,并根据实际情况分析并适应性地编写对应的程序。在实验过程中,最大的问题就是如何让核心板内部LED实现呼吸灯效果,一开始是通过不断更改其引脚PC13上的输入输出模式,但最后翻找资料发现这个LED是直接与电源相连的,因此更换了思路,重新编写实现代码,最后也成功实现了呼吸灯效果,这也提醒了我,在做任何实验前,一定要细致地检查目标实现的每个前提是否是理想的,而不能想当然地粗心略过每个小的方面。


四、参考文献

零死角玩转STM32—F103指南者
http://www.mcublog.cn/stm32/2021_01/stm32cubemx-dingshiqi-led/
http://www.mcublog.cn/stm32/2021_01/stm32cubemx-pwm-huxideng/
https://blog.csdn.net/zmhDD/article/details/111942507
https://blog.csdn.net/qq_45237293/article/details/111997424

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值