目录

前言

一 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秒

    如下图

    作者:锻炼²

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 PWM波形详细图解

    发表回复