PWM——基于STM32F407ZGT6开发板

PWM是什么?

STM32微控制器中,PWM代表脉冲宽度调制(Pulse Width Modulation)。PWM是一种用于控制电子设备的技术,通过调整信号的脉冲宽度和周期,可以模拟出不同的电压或功率级别。

在STM32中,PWM功能常用于控制电机速度、调节LED亮度、产生音频信号等应用。通过调整PWM的占空比(高电平时间占总周期的比例),可以控制输出信号的平均电压或功率。例如,如果PWM信号的占空比为50%,即高电平时间等于总周期的一半,那么输出信号的平均电压或功率也将为输入电压或功率的一半。

STM32微控制器提供了多个PWM通道,每个通道可以配置为不同的输出引脚,并具有灵活的配置选项,例如频率、占空比、极性等。开发者可以使用STM32的PWM功能来实现精确的电子设备控制。

定义

PWM是脉冲宽度调制(Pulse Width Modulation)的缩写,它是一种调制技术,用于控制模拟信号的平均值。通过调整脉冲的宽度和周期,PWM可以模拟出不同的电压或功率级别。

在PWM中,信号由一系列固定周期的脉冲组成。脉冲的宽度表示信号的高电平时间,而周期表示脉冲的重复时间。通过改变脉冲的宽度与周期之间的比例,可以控制信号的平均电压或功率。

例如,如果脉冲的宽度占周期的一半,即50%的占空比,那么输出信号的平均电压或功率也将为输入电压或功率的一半。通过改变占空比,可以实现对输出信号的精确控制。占空比:高电平占整个电平周期的持续时长

PWM广泛应用于各种领域,包括电机控制、LED亮度调节、音频信号生成等。它是一种高效、精确的控制技术,常用于模拟信号的数字化处理和电子设备的调节与控制。

若要实现PWM输出,我们需要用到定时器的输出比较功能。

我们在查看《stm32f4xx中文参考手册》时,发现这4个通道的功能似乎有所重复:

实际上这两者是有区别的,输出比较模式和PWM模式都可以用来输出PWM波,在功能上两者有相同之处,对于一个定时器这两种方式都可以做到四路输出PWM,每一路PWM占空比都可调。

也有不同之处,输出比较模式可以方便的调节每一路PWM波的频率,可以输出四路频率不同,占空比不同的PWM。但是PWM模式如果想要调节PWM波的频率,那么就只能重新设置预分频系数或者自动重装载寄存器ARR,并且输出的四路PWM频率必定一致。

PWM模式是输出比较模式的子集。

代码参数

(1) TIM_OCMode:比较输出模式选择,总共有八种,常用的为PWM1/PWM2。它设定CCMRx 寄存器OCxM[2:0] 位的值。

(2) TIM_OutputState:比较输出使能,决定最终的输出比较信号OCx 是否通过外部引脚输出。它设定TIMx_CCER 寄存器CCxE/CCxNE 位的值。

(3) TIM_OutputNState: 比较互补输出使能,决定OCx 的互补信号OCxN 是否通过外部引脚输出。它设定CCER 寄存器CCxNE 位的值。

(4) TIM_Pulse:比较输出脉冲宽度,实际设定比较寄存器CCR 的值,决定脉冲宽度。可设置范围为0 至65535。

(5) TIM_OCPolarity:比较输出极性,可选OCx 为高电平有效或低电平有效。它决定着定时器通道有效电平。它设定CCER 寄存器的CCxP 位的值。

(6) TIM_OCNPolarity:比较互补输出极性,可选OCxN 为高电平有效或低电平有效。它设定TIMx_CCER 寄存器的CCxNP 位的值。

(7) TIM_OCIdleState:空闲状态时通道输出电平设置,可选输出1 或输出0,即在空闲状态(BDTR_MOE 位为0) 时,经过死区时间后定时器通道输出高电平或低电平。它设定CR2 寄存器的OISx 位的值。

(8) TIM_OCNIdleState:空闲状态时互补通道输出电平设置,可选输出1 或输出0,即在空闲状态(BDTR_MOE 位为0) 时,经过死区时间后定时器互补通道输出高电平或低电平,设定值必须与TIM_OCIdleState 相反。它设定是CR2 寄存器的OISxN 位的值。

分频系数:84-1

arr值:2000-1

比较值:480-1

可知:

输送给定时器4的时钟频率为:APB1时钟频率2/84=482/84=1Mhz

PWM周期为:在1Mhz的时钟频率下。数2000个数 1/1Mhz*2000=2ms

占空比为:高电平时间/PWM周期=数480个数/数2000个数=480/2000=24%

pwm频率为:1/pwm周期 = 1/2ms=500Hz

工作原理

PWM的工作原理基于对脉冲的宽度和周期进行调制。下面是PWM的基本工作原理:

设定目标数值:首先,确定需要控制的目标数值,例如调节电机的速度或LED的亮度。这个目标数值通常以一个百分比或占空比的形式表示。

确定频率:选择PWM信号的频率,即脉冲的周期。频率决定了脉冲的重复速率,通常以赫兹(Hz)表示。常见的频率范围是几百赫兹到几十千赫兹。

计算占空比:根据目标数值和所选频率,计算所需的占空比。占空比表示高电平时间占周期的比例。例如,如果目标是50%的亮度或速度,则占空比为50%。

生成PWM信号:使用计时器和计数器来生成PWM信号。计时器根据所选频率生成一个固定周期的计时事件,并从0开始计数。计数器在每个计时事件中递增,当计数值小于占空比所对应的计数阈值时,输出为高电平;否则,输出为低电平。

输出控制:根据计数器的值,控制输出引脚的电平状态。在计数值小于阈值时,输出为高电平;在计数值大于等于阈值时,输出为低电平。这样就形成了一系列固定周期、宽度可变的脉冲信号。

通过调整占空比,可以控制输出信号的平均电压或功率。占空比越高,输出信号的平均电压或功率就越高,而占空比越低,输出信号的平均电压或功率就越低。

使用PWM,可以实现精确的控制,例如精确调节电机的速度或改变LED的亮度级别。PWM技术的优点包括高效率、精度高以及对输出设备影响小等。

计数器寄存器 (TIMx_CNT) 自动装载寄存器 (TIMx_ARR) 捕获/比较寄存器(TIMx_CCRx)

向上计数模式:

输出过程: 当0-t1这段时间,计数器寄存器的CNT的值是小于CCR,输出高电平。 当t1-t2这段时间,计数器寄存器的CNT的值是大于CCR且小于ARR的,输出低电平。 当CNT的值达到ARR里的值时,产生溢出事件,自动清零再次从0开始向上计数。

应用

通过PWM实现呼吸灯的效果

代码

#include "pwm.h"


void tim14_pwm_led0(uint16_t psc, uint32_t arr)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//通电还是断电
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);	//时钟使能
	
	GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14);		//复用为定时器14
	
	//开始配置F端口下的9号跟10号引脚
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; //led0 led1
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF; //复用模式
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉模式
	GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed; //引脚响应速度
	GPIO_Init(GPIOF, &GPIO_InitStructure);

	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseStructure.TIM_Prescaler = psc-1;								//PSC值
	TIM_TimeBaseStructure.TIM_Period = arr-1;										//ARR值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);
		
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 							//pwm模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;		//输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;				//输出极性低
	TIM_OC1Init(TIM14, &TIM_OCInitStructure);
	
	TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);				//使能TIM14在OC1上的CCR预装载寄存器
	TIM_ARRPreloadConfig(TIM14, ENABLE);			//定时器14 ARR寄存器
	
	TIM_Cmd(TIM14, ENABLE);			//定时器使能	
}

//重写TIM_SetCompare1 - 固定定时器TIM14 - 方便传参只传一个
void PWM_SetCompare1(uint32_t Compare)
{
  TIM_SetCompare1(TIM14,Compare);
}
#include "stm32f4xx.h"
#include "usart.h"
#include "led.h"
#include "stdio.h"
#include "timer.h"
#include "pwm.h"
#include "delay.h"
int main(void)
{
	SysTick_Init();//必须要初始化滴答定时器的函数,这样延时函数才能正常用
	//f = 84Mhz/ (83+1)  / (999+1)  = 1Khz
	//pwm频率 = 时钟频率/(分频值+1)/(自动重装载值+1)
	tim14_pwm_led0(84, 1000);
	uint32_t cmp;
	u8 flag = 1;
	u8 direction = 1;

  while (1)
  {
      if (direction)  
      {  
     		for (cmp=0; cmp < 800; cmp += 1)  // 渐亮  
				{  
					PWM_SetCompare1(cmp);
					delay_ms(1); // 根据期望的渐变速度调整延时  
				}  
			direction = 0; // 切换方向  
			}
			else
			{  
				for (cmp=800; cmp > 0; cmp -= 1)   // 渐暗  
				{  
					PWM_SetCompare1(cmp); 
					delay_ms(1); // 根据期望的渐变速度调整延时  
				}  
					direction = 1; // 切换方向  
			}  
  }
}
通过PWM控制蜂鸣器唱歌

代码

#include "stm32f4xx.h"
#include "uart.h"
#include "led.h"
#include "stdio.h"
#include "timer.h"
#include "pwm.h"
#include "delay.h"

#define   L1     262-1//低调 do 的频率
#define   L2     294-1//低调 re 的频率
#define   L3     330-1//低调 mi 的频率
#define   L4     350-1//低调 fa 的频率
#define   L5     392-1//低调 sol 的频率
#define   L6     440-1//低调 la 的频率
#define   L7     494-1//低调 si 的频率
                                               
#define   M1     524-1//中调 do 的频率
#define   M2     588-1//中调 re 的频率
#define   M3     660-1//中调 mi 的频率
#define   M4     700-1//中调 fa 的频率
#define   M5     784-1//中调 sol 的频率
#define   M6     880-1//中调 la 的频率
#define   M7     988-1//中调 si 的频率
 
#define   H1     1048-1//高调 do 的频率
#define   H2     1176-1//高调 re 的频率
#define   H3     1320-1//高调 mi 的频率
#define   H4     1480-1//高调 fa 的频率
#define   H5     1640-1//高调 sol 的频率
#define   H6     1760-1//高调 la 的频率
#define   H7     1976-1//高调 si 的频率
 
#define   S      0//不发音
int main(void)
{
    SysTick_Init(); //系统延时
    //f = 84Mhz/ (83+1)  / (999+1)  = 1Khz
    //pwm频率 = 时钟频率/(分频值+1)/(自动重装载值+1)
    tim13_pwm_led0(84, 3816);
    //PSC值   84MHz/84 = 1M = 1000000                                
    //ARR值   1000000/216低音=3816最大   计数值范围0~3816
    const uint16_t tone_low[7] = {L1,L2,L3,L4,L5,L6,L7};
    const uint16_t tone_mid[7] = {M1,M2,M3,M4,M5,M6,M7};
    const uint16_t tone_high[7] = {H1,H2,H3,H4,H5,H6,H7};
	int wind_rise1[]={H1,M7,H1,H2,M7,H1,H1,S, //爱你不跪的模样
        H1,H2,H3,H2,H3,H2,H3,H3,H2,H3,H5,H3,S, //爱你对峙过绝望不肯哭一场
        M6,M7,H1,H2,M7,H1,H1,H1,M7,H1,H2,M7,H1,H1,S, //爱你破烂的衣裳却敢堵命运的枪
        H1,H2,H3,H2,H3,H2,H3,H3,H2,H3,H5,H3,S, //爱你和我那么像缺口一样
        H5,H3, //去吗
        H5,H3,S, //配吗
        H5,H3,H5,H6,H3,H5,S, //这褴褛的披风
        H5,H3,//战吗
        H5,H3,S, //战啊
        H5,H3,H5,H6,H3,H5,H5,H5,H3,H2,H2,H2,H1,H3,H3,H2,H2,H2,H1,H1,M6,M6,S,S, //以最卑微的梦致那黑夜中的呜咽与怒吼
        H5,H5,H3,H2,H2,H2,H1,H3,H3,H2,H2,H2,H1,H1,M6,M6,S,S, //谁说站在光里才算英雄
};
    uint8_t i;
	 int length1 = sizeof(wind_rise1)/sizeof(wind_rise1[0]);
    while(1)
    {
		for(i=0;i<(length1/2);i++)
        {
            set_beep(wind_rise1[i]);
			delay_ms(250);
        }
    }
}
#include "pwm.h"

void tim13_pwm_led0(uint16_t psc, uint32_t arr)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//通电还是断电
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);	//时钟使能
	
	GPIO_PinAFConfig(GPIOF, GPIO_PinSource8, GPIO_AF_TIM13);		//复用为定时器14
	
	//开始配置F端口下的9号跟10号引脚
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8; //led0 led1
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF; //复用模式
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉模式
	GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed; //引脚响应速度
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
	TIM_TimeBaseStructure.TIM_Prescaler = psc-1;	// PSC值 84MHz/84 = 1M = 1000000
	TIM_TimeBaseStructure.TIM_Period = arr-1;		//ARR值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM13, &TIM_TimeBaseStructure);
	
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 	//pwm模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	 TIM_OCInitStructure.TIM_Pulse = 3816 / 2;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;				//输出极性低
	TIM_OC1Init(TIM13, &TIM_OCInitStructure);
	
	TIM_OC1PreloadConfig(TIM13, TIM_OCPreload_Enable);	//使能TIM14在OC1上的CCR预装载寄存器
	TIM_ARRPreloadConfig(TIM13, ENABLE);	//定时器14 ARR寄存器
	TIM_Cmd(TIM13, ENABLE);			//定时器使能
}
//重写TIM_SetCompare1 - 固定定时器TIM14 - 方便传参只传一个
void PWM_SetCompare1(uint16_t loud) //period计数值,pulse比较值 占空比 - 计数值的一半
{
    uint16_t period = 1000000 / loud;
    TIM_SetAutoreload(TIM13,period);
  TIM_SetCompare1(TIM13,period/2);
}


void set_beep(uint16_t f)
{
    if(f==0){ //如果f=0则不发出声音
        TIM_SetAutoreload(TIM13,1);
        TIM_SetCompare1(TIM13,0);
    }else{ //发出指定频率的声音
        TIM_SetAutoreload(TIM13,(1000000/f));
        TIM_SetCompare1(TIM13,(1000000/f)/15);//这个是为了改变蜂鸣器的音量,音量小一点音调的变化更清晰,听起来更清楚
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值