实验目的
1 深入了解STM32定时器的工作原理,掌握脉宽调制(PWM)的生成方法。
2 掌握使用STM32F103的Tim2~Tim5中的一个定时器的某一个通道与LED相连,并利用定时器计数方式控制LED周期性地亮-灭。
3 学习如何在STM32F103上使用定时器的PWM模式,以呼吸灯的方式使LED渐亮渐灭,并通过Keil虚拟示波器观察PWM输出波形。
4 利用另一个定时器通道编程采集上述PWM输出信号,并获取其周期和脉宽,并将数据通过串口输出显示。
5 学习HC-SR04超声波测距模块的工作原理,并使用STM32F103完成一个超声波测距方案(选做)。
实验任务
一. 使用STM32F103的 Tim2~Tim5其一定时器的某一个通道pin(与GPIOx管脚复用,见下图),连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。
二. 接上,采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整到一个满意效果。使用Keil虚拟示波器,观察 pwm输出波形。
三. 再接上,采用定时器的另外一个通道,编程采集上面的pwm输出信号,获得其周期和脉宽,并重定向输出到串口显示。
四. 学习 HC-SR04超声波测距模块工作原理,使用 stm32F103 完成一个超声波测距方案(第9周之前选做)。
https://zhuanlan.zhihu.com/p/546833349
基于STM32和HC-SR04模块实现超声波测距功能(标准库)
实验内容
深入了解STM32定时器原理,掌握脉宽调制pwm生成方法。
一. 使用STM32F103的 Tim2~Tim5其一定时器的某一个通道pin(与GPIOx管脚复用,见下图),连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。
1定时器
1)定时器概念
能够对内部时钟信号或外部输入信号进行计数,数值达到设定要求时,向CPU发起中断请求,完成外部程序的运行。
本质就是进行计数,选择内部时钟脉冲,作为计数器时,技术信号的来源选择非周期脉冲信号。
STM32中定时器可分为高级定时器、通用定时器、基本定时器三类,他们都是由一个可编程的16位预分频器(TIMX_PSC)驱动的16位。
2串口通信与点灯
1)创建新项目
2)选择芯片
3)配置RCC
4)配置SYS
5)配置IO口输出
6)配置定时器2和定时器3
分频系数那里虽然写的是71,但系统处理的时候会自动加上1,所以实际进行的是72分频。由于时钟我们一般会配置为72MHZ,所以72分频后得到1MHZ的时钟。1MHZ的时钟,计数5000次,得到时间5000/1000000=0.005秒。也就是每隔0.005秒定时器2会产生一次定时中断。
7)配置中断
开启定时器2和定时器3的中断。
生成定时器2和定时器3中断优先级配置代码
8)配置USART
波特率为115200,1位停止位,无校验位
9)时钟配置
10)配置项目
11)生成项目
12)代码
启动定时器
定时器中断回调函数
二. 接上,采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整到一个满意效果。使用Keil虚拟示波器,观察 pwm输出波形。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time_cnt =0;
static uint32_t time_cnt3 =0;
if(htim->Instance == TIM2)
{
if(++time_cnt >= 400)
{
time_cnt =0;
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);
}
}
if(htim->Instance == TIM3)
{
if(++time_cnt3 >= 1000)
{
time_cnt3 =0;
HAL_UART_Transmit(&huart1,hello,20,100000);
}
}
}
13)烧录,实验结果
引脚:A1–LED灯
接上,采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整到一个满意效果。使用Keil虚拟示波器,观察 pwm输出波形。
uint16_t duty_num3 = 10;
uint16_t duty_num4 = 10;
`HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
``HAL_Delay(50);
duty_num3 = duty_num3 + 10;
duty_num4 = duty_num4 + 10;
if(duty_num3 > 500)
{
duty_num3 = 0;
}
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,duty_num3);
if(duty_num4 > 500)
{
duty_num4 = 0;
}
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,duty_num4);
10)编译烧录,实验结果
引脚
A6–LED
PB6 – PC13
11)PWM输出波形
采用定时器的另外一个通道,编程采集上面的pwm输出信号,获得其周期和脉宽,并重定向输出到串口显示。
代码如下
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
uint8_t i = 0;
float Duty = 0;
float Frequency = 0;
uint16_t Cap_val1 = 0;
uint16_t Cap_val2 = 0;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
MX_TIM2_Init();
/* USER CODE BEGIN WHILE */
printf("串口通信测试\r\n");
HAL_TIM_Base_Start_IT(&htim2); // 使能定时器及其更新中断
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 使能定时器及其PWM输出
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); // 使能定时器及其输入捕获
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2); // 使能定时器及其输入捕获
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 10); // 设置一个PWM波形进行测量
while (1)
{
// 串口发送 频率 占空比
printf("Cap_val1 is :%d , Cap_val2 is : %d \r\n", Cap_val1, Cap_val2);
printf("Duty is :%0.2f%% Frequency is : %0.2f ms\r\n", Duty, Frequency);
HAL_Delay(1000);
}
}
// 定时TIM2 定时亮灯的中断函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *tim)
{
if (tim == &htim2)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
}
// 定时输入捕获回调函数 计算占空比和频率
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM1)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
Cap_val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
}
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
{
Cap_val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
Duty = 100 - (float)Cap_val2 / (float)Cap_val1 * 100;
Frequency = 0.001 * Cap_val1;
}
}
}
运行结果
实验总结
在实验中,我深入研究和理解了STM32定时器的工作原理。我掌握了控制LED开/关状态的定时器计数方法,并成功实现了LED以2秒的频率周期性开/关。随后,我学习了脉宽调制(PWM)的生成方法,并将LED控制模式切换为PWM模式。通过适当调整周期和占空比,我成功地实现了LED以呼吸灯的形式逐渐打开和关闭。
在观察PWM输出波形的过程中,我使用了Keil虚拟示波器。通过观察波形,我可以直观地了解PWM信号的变化,从而验证我的编程结果。这使我更加熟悉使用示波器进行波形分析的方法。
在这次实验使我更加熟悉了STM32F103单片机的应用和编程,也增强了我的实践能力和解决问题的能力。