自学内容网 自学内容网

STM32 PWM波形详细图解

目录

前言

一 PWM介绍

1.1 PWM简介

1.2 STM32F103 PWM介绍

1.3 时钟周期与占空比

二.引脚映像关系

2.1引脚映像与寄存器

2.2 复用功能映像

三. PWM 配置步骤

3.1相关原理图

3.2配置流程

3.2.1 步骤一二:

3.2.2 步骤三: 

3.2.3 步骤四五六七: 

3.2.4 步骤八:

3.3 PWM 详细代码

3.3.1 PWM.C 

3.3.2 main.c 

四.PWM波形 

4.1 波形查看

4.2 PWM更新频率

4.2.1不使用delay

4.2.2 使用delay


前言

      步骤一:通过配置ARR(自动重装载值寄存器)PSC(预分频器)的值,来设置CNT(计数器)的定时周期、计数频率

      步骤二:再改变CCR(捕获/比较寄存器)的值,通过CNT与CCR的比较,可对PWM占空比进行调整

      经过步骤一和步骤二:即可输出频率占空比都可以调制的PWM波形

注:

 ARR(自动重装载值寄存器)

 PSC(预分频器)

 CNT(计数器)

 CCR(捕获/比较寄存器)

一 PWM介绍

1.1 PWM简介

脉冲宽度调制:PWM是一种数字信号控制技术,其中数字信号的占空比被用来控制模拟信号的幅度。占空比是指在一个周期内,信号处于高电平状态的时间与总周期时间的比例。        

PWM是"Pulse Width Modulation"的缩写,中文意思是“脉冲宽度调制”。这是一种模拟信号控制方法,通过改变电信号的占空比来控制功率输出或模拟信号的幅度。PWM广泛应用于各种电子系统中,包括但不限于以下几个领域:

  1. 电机控制:PWM用于控制电机的转速和力矩,通过调整电机驱动器的输入电压或电流的占空比来实现。

  2. LED调光:在LED照明中,PWM可以控制LED的亮度,通过改变电流的占空比来调节亮度,而不会改变LED的色温。

  3. 音频信号合成:PWM也用于数字音频处理,通过调制脉冲的宽度来合成模拟音频信号。

  4. 电源管理:在开关电源中,PWM用于控制开关元件的开关频率和占空比,以调节输出电压和电流。

  5. 通信:某些通信协议使用PWM来传输数据,通过调制脉冲的宽度来编码信息。

  6. 测量和控制:PWM信号可以用于测量距离、速度等物理量,也可以用于控制各种执行器。

PWM信号的主要特点包括:

  • 周期性:PWM信号是周期性重复的,具有固定的频率。
  • 占空比:PWM信号的占空比是指高电平状态在整个周期中所占的比例。
  • 分辨率:PWM的分辨率取决于信号的周期和能够分辨的最小脉冲宽度,高分辨率的PWM可以提供更平滑的模拟控制。
  • 易于生成和控制:PWM信号可以通过数字电路或微控制器轻松生成和调整。

在实际应用中,PWM信号通常由定时器或专用的PWM硬件生成,然后通过数字到模拟转换器(DAC)或直接通过功率放大器输出到负载。通过精确控制PWM信号的频率和占空比,可以实现对各种电子设备的精确控制。

1.2 STM32F103 PWM介绍

在STM32F103中除了基本定时器(定时器6和定时器7),通用和高级定时器都可以用来进行PWM输出。

1.3 时钟周期与占空比

在时基单元中,我们通过对PSC、ARR 大小进行配置,来设置计数器CNT的定时周期、计数频率。

因为前面的时基单元中,已经设置完定时器时钟频率。可以通过TIMx_CCRx(捕获/比较寄存器,也就是上面的CCR),输出占空比可调的PWM波形

注:因为CNT在前面设置向上或向下计数模式后就不用更改了,所以到这一步只需要对CRR的值进行设置,也可以通过while()循环,不断给TIMx_CCRx寄存器赋新的值,来进行脉宽占空比的调整。

  输出频率和占空比都可以调制的PWM波形

•PWM频率:  Freq = CK_PSC / (PSC + 1) / (ARR + 1)

•PWM占空比:  Duty = CCR / (ARR + 1)

•PWM分辨率:  Reso = 1 / (ARR + 1)

二.引脚映像关系

2.1引脚映像与寄存器

高级定时器:TIM1和TIM8是高级定时器

通用定时器:TIM2、TIM3、TIM4和TIM5是通用定时器

基本定时器:TIM6和TIM7是基本的定时器

2.2 复用功能映像

  1. 引脚重映射:比如当您需要将TIM3的某些通道映射到不同的GPIO引脚上时,可以使用复用功能映像。例如,当默认的TIM3通道引脚不能满足您的硬件设计需求,或者您需要将多个通道映射到同一个引脚上时,可以使用复用功能映像来改变引脚映射。

  2. PWM输出到特定引脚:如果您需要将PWM信号输出到特定的GPIO引脚,而这个引脚不是TIM3的默认输出引脚,您可以通过复用功能映像来实现。例如,将TIM3的CH2映射到PB5,或者将所有四个通道映射到PC6、PC7、PC8、PC9。

下面以TIM3为例

    TIM3是STM32微控制器中的一个通用定时器,它具有四个独立的通道,分别是CH1、CH2、CH3和CH4。这些通道可以被配置为输入捕获、输出比较或PWM输出模式,用于各种定时和控制应用。

    每个通道都有自己的捕获/比较寄存器(CCR),可以独立设置,以实现不同的定时和控制功能。例如,TIM3_CH1默认引脚为PA6,TIM3_CH1部分重映像引脚为PB4,TIM3_CH1完全重映像引脚为PC6。

三. PWM 配置步骤

3.1相关原理图

这里使用PWM控制LED呼吸灯。这里控制PC8的绿色LED灯,实现呼吸灯的效果。

这里的PC8端口,是TIM3通道3使用完全重映射

//部分重映射
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
    //完全重映射
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);

    既:GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);

3.2配置流程

一:使能定时器3和相关IO口时钟。

使能定时器3时钟:RCC_APB1PeriphClockCmd();

使能GPIOC时钟:RCC_APB2PeriphClockCmd();

二:初始化IO口为复用功能输出。函数:GPIO_Init();

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;     

三:这里我们是要把PC8用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

四:初始化定时器:ARR,PSC等:TIM_TimeBaseInit();

五:初始化输出比较参数:TIM_OC3Init();

六:使能预装载寄存器: TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

七:使能定时器:TIM_Cmd();

八:不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare4();

3.2.1 步骤一二:

一:使能定时器3和相关IO口时钟。

使能定时器3时钟:RCC_APB1PeriphClockCmd();

使能GPIOC时钟:RCC_APB2PeriphClockCmd();

二:初始化IO口为复用功能输出。函数:GPIO_Init();

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

/* 步骤一:使能定时器3和相关IO口时钟。开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

/*  步骤二:初始化IO口为复用功能输出 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOC,&GPIO_InitStructure);
3.2.2 步骤三: 

   

三:这里我们是要把PC8用作定时器的PWM输出引脚,所以要重映射配置,所以需要开启AFIO时钟。同时设置重映射。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);

/*  步骤三:设置重映射 */
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改变指定管脚的映射
3.2.3 步骤四五六七: 

四:初始化定时器:ARR,PSC等:TIM_TimeBaseInit();

五:初始化输出比较参数:TIM_OC3Init();

六:使能预装载寄存器: TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

七:使能定时器:TIM_Cmd();

/*  步骤四:初始化定时器 */
TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

/*  步骤五:初始化输出比较参数:TIM_OC3Init(); */
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;//计数值与TIM_Pulse匹配,输出低电平
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC3Init(TIM3,&TIM_OCInitStructure); //输出比较通道3初始化

/*  步骤六:使能预装载寄存器 */
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能TIMx在 CCR3 上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//使能预装载寄存器

/*  步骤七:使能定时器3 */
TIM_Cmd(TIM3,ENABLE); //使能定时器
3.2.4 步骤八:

八:不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare4();

int main()
{
u16 i=0;  
u8 fx=0;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
LED_Init();
TIM3_CH3_PWM_Init(500,72-1); //0.5毫秒,(频率是2KHZ)

while(1)
{

if(fx==0)
{
i++;
if(i==500)
{
fx=1;
}
}
else
{
i--;
if(i==0)
{
fx=0;
}
}
        //可直接改变CCR的值(通道3也就是CCR3的值)
TIM_SetCompare3(TIM3,i);  //i值最大可以取499,因为ARR最大值是499.
delay_ms(5);
}
}

3.3 PWM 详细代码

3.3.1 PWM.C 

pwm.c

#include "pwm.h"
#include "led.h"

/*******************************************************************************
* 函 数 名         : TIM3_CH3_PWM_Init
* 函数功能   : TIM3通道3 PWM初始化函数
* 输    入         : per:重装载值
 psc:分频系数
* 输    出         : 无
*******************************************************************************/
void TIM3_CH3_PWM_Init(u16 per,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

/* 步骤一:使能定时器3和相关IO口时钟。开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

/*  步骤二:初始化IO口为复用功能输出 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOC,&GPIO_InitStructure);

/*  步骤三:设置重映射 */
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改变指定管脚的映射

/*  步骤四:初始化定时器 */
TIM_TimeBaseInitStructure.TIM_Period=per;   //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);

/*  步骤五:初始化输出比较参数:TIM_OC3Init(); */
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//计数值与TIM_Pulse匹配,输出低电平
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC3Init(TIM3,&TIM_OCInitStructure); //输出比较通道3初始化

/*  步骤六:使能预装载寄存器 */
TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能TIMx在 CCR3 上的预装载寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE);//使能预装载寄存器

/*  步骤七:使能定时器3 */
TIM_Cmd(TIM3,ENABLE); //使能定时器

}
3.3.2 main.c 

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "time.h"
#include "pwm.h"

int main()
{
u16 i=0;  
u8 fx=0;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
LED_Init();
TIM3_CH3_PWM_Init(500,72-1); //周期0.5毫秒,(频率是2KHZ)
/*   PSC = 72;   ARR = 500;     
    周期 = 500/1000000 = 5/10000 =0.0005秒 = 0.5毫秒 
      频率 = 2KHZ
 */



/*LED亮灭时间2秒(亮1秒,灭1秒),
t = 1000/500 =2;   */
while(1)
{

if(fx==0)
{
i++;
if(i==500)
{
fx=1;
}
}
else
{
i--;
if(i==0)
{
fx=0;
}
}
//可直接改变CCR的值(通道3也就是CCR3的值)
TIM_SetCompare3(TIM3,i);  //i值最大可以取499,因为ARR最大值是499.
delay_ms(2);
}
}

四.PWM波形 

4.1 波形查看

    假设 CPU 的时钟频率为 1 MHz(1 微秒/周期),并且循环体内的指令执行需要 10 个周期(这是一个粗略的估计,下面实际测试是差不多的),那么每次循环大约需要 10 微秒。因此,一个完整的亮灭周期大约需要:

     这里的if(fx==500),递增和递减共需:500*2 = 1000次计算:

    1000×10 微秒=10000 微秒=10 毫秒1000×10微秒=10000微秒=10毫秒

    所以,LED 亮灭一次的周期大约为 10 毫秒。这意味着 LED 每 10 毫秒亮灭一次。但请注意,这个估计值可能与实际值有所不同,具体取决于 CPU 的执行速度和循环体内的指令数量。

 

波形大致为:因为pwm周期

4.2 PWM更新频率

注意

更改delay_ms()延时函数的大小:是修改LED灯亮灭的周期;

LED亮灭周期是由频率(时基单元PSC ARR CNT)占空比(CCR)控制的

dalay_ms()函数是为了控制PWM信号的更新速率:在代码中,delay_ms() 函数控制了 i 值更新的速率,即控制了 PWM 信号占空比变化的速率。如果没有这个延迟,i 的值会非常快地在 0 到 500 之间变化,导致 PWM 信号的频率非常高,这可能超出了人眼的感知范围,使得 LED 的亮度看起来是恒定的。

4.2.1不使用delay

1 不使用delay_ms()函数

  循环中没有包含任何延迟,这意味着 i 的值会非常快速地在 0 到 500 之间变化,没有任何停留。由于没有延迟,i 的值变化得太快,导致人眼无法察觉到 LED 的亮度变化,看起来就像是 LED 一直亮着。

4.2.2 使用delay

2 使用delay_ms(1)函数后:这里就是LED的亮灭周期为1秒

  • 从 0 增加到 500 需要 500 次循环。
  • 从 500 减少到 0 也需要 500 次循环。
  • 因此,一个完整的亮灭周期需要 1000 次循环。

每次循环的时间为 1 毫秒,所以一个完整的亮灭周期的时间为:

1000×1 毫秒=1000 毫秒=1 秒1000×1毫秒=1000毫秒=1秒

如下图


原文地址:https://blog.csdn.net/weixin_61125362/article/details/140401841

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!