目录
一、PWM原理介绍
二、学习目的
三、cubeMX的配置
四、PWM输出代码
一、PWM原理介绍
PWM(Pulse Width Modulation,脉宽调制)是一种通过改变信号的脉冲宽度来控制电平的技术。它通过调整脉冲信号的占空比(高电平时间与周期的比例)来实现对电压或电流的精确控制。
PWM的原理可以简单描述为以下几个步骤:
-
选择一个固定的周期:PWM信号由一系列周期性的脉冲组成,其中周期是固定的,表示脉冲信号的重复时间。
-
设置一个目标值:根据需要控制的设备或系统,设置一个目标值,例如期望的电压或电流。
-
比较目标值和当前值:将目标值与一个计数器进行比较,该计数器在每个周期内递增。如果目标值大于计数器的当前值,则输出高电平;如果目标值小于计数器的当前值,则输出低电平。
-
调整占空比:通过调整目标值与计数器的比较关系,可以改变脉冲信号的占空比。占空比越大,高电平时间越长,输出电平的平均值也就越高;占空比越小,高电平时间越短,输出电平的平均值也就越低。
-
输出PWM信号:根据比较结果,生成相应的脉冲信号。通常,高电平表示逻辑1或高电平电压,低电平表示逻辑0或低电平电压。
二、学习目的
我们今天的学习目的是完成第十一届蓝桥杯嵌入式真题中的两路PWM输出部分,通过PA6、PA7两个引脚输出PWM脉冲,频率分别为100MHz、200MHz,并且通过两个按键来分别调节两路PWM输出的占空比,每次按下按键增加10%的占空比。
三、cubeMX的配置
我们需要通过定时器来完成PWM的输出,所以需要设置两个引脚为PWM输出引脚
1、打开cubeMX,选择PA6、PA7引脚并设置为定时器引脚
由上图可知,将PA6设置为TIM16_CH1,PA7设置为TIM17_CH1
这是因为我们需要选择通道1进行输出,而同为通道1的TIM3在上一节按键的使用中已经被用作按键的中断定时器,所以选择定时器16、17的通道1来进行PWM输出
2、点击Timers,选择TIM16、TIM17,勾选Activated打开使能,选择PWM Generation CH1
然后设置分频系数,预分频器值设置为8000-1,数器重载值设置为100-1,此时脉冲频率为100MHz
同理,预分频器值设置为4000-1,数器重载值设置为100-1,此时脉冲频率为200MHz
公式如下:
定时时间 = (预分频器值计数器重载值)/ 定时器时钟频率
脉冲频率 = 1 / 定时时间
3、设置脉冲数为20
四、PWM输出代码
由于定时器的代码在cubeMX配置完成后就会自动生成,所以只需要在主函数中调用以下函数来启动定时器16、17:
HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
再用以下函数设置占空比即可:
__HAL_TIM_SetCompare(&htim16, TIM_CHANNEL_1, pa6_duty);
__HAL_TIM_SetCompare(&htim17, TIM_CHANNEL_1, pa7_duty);
其中pa6_duty和pa7_duty是占空比,范围是10-90
与PWM输出相关的主函数代码如下:
main.c
// main.c
#include "main.h"
#include "tim.h"
#include "gpio.h"
#include "led.h"
#include "lcd.h"
#include "interrupt.h"
#include "stdio.h"
... // 省略部分初始代码
extern struct keys key[];
uchar pa6_duty = 10; // 定义占空比
uchar pa7_duty = 10; // 定义占空比
char view = 0; // 用于表示界面 0 or 1
char text[30]; // LCD显示的内容
void key_proc(void) // 通过按键来切换界面、调节PWM占空比
{
if(key[3].flag == 1)
{
view =! view;
key[3].flag = 0;
}
if(key[0].flag == 1)
{
pa6_duty += 10;
if(pa6_duty >= 100) pa6_duty = 10;
__HAL_TIM_SetCompare(&htim16, TIM_CHANNEL_1, pa6_duty);
key[0].flag = 0;
}
if(key[1].flag == 1)
{
pa7_duty += 10;
if(pa7_duty >= 100) pa7_duty = 10;
__HAL_TIM_SetCompare(&htim17, TIM_CHANNEL_1, pa7_duty);
key[1].flag = 0;
}
}
void disp_proc(void) // 界面的切换
{
if(view == 0)
{
sprintf(text, " Para");
LCD_DisplayStringLine(Line0,(unsigned char *)text);
LCD_DisplayStringLine(Line1,(unsigned char *)" ");
LCD_DisplayStringLine(Line2,(unsigned char *)" ");
}
if(view == 1)
{
sprintf(text, " DATA");
LCD_DisplayStringLine(Line0,(unsigned char *)text);
sprintf(text, " PA6:%d / 100 ", pa6_duty);
LCD_DisplayStringLine(Line1,(unsigned char *)text);
sprintf(text, " PA7:%d / 100 ", pa7_duty);
LCD_DisplayStringLine(Line2,(unsigned char *)text);
}
}
int main()
{
...// 省略部分初始代码
LCD_Init(); // LCD的初始化
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
HAL_TIM_Base_Start_IT(&htim3); // 开启定时器3
HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1); // 开启定时器16
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1); // 开启定时器17
while(1)
{
disp_proc();
key_proc();
}
}