Detailed example of PWM output experiment

table of Contents

PWM output experiment

The working principle of PWM (take up counting as an example)

Comprehensive experiment of PWM and AFIO pin remapping

Library function instructions

Basic knowledge points of PWM configuration

GPIO port function remapping

The meaning of clock division

What are the two comparison modes of PWM?

The role of the preload register (TIMx_CCMR1)

What is the PWM output polarity?

PWM configuration considerations

How to set the duty cycle?

Library function configuration steps

Step 1: Enable the peripheral bus clock of GPIO/AFIO/TIM

Step 2: Configure the remapping of pin functions

Step 3: Configure the basic properties of the timer

Step 4: Configure PWM output mode

Step 5: Configure the preload register

Step 6: Formally enable the TIM function

Comprehensive code example

Main.c

Led.c

Led.h.

Timer.c

Timer.h

operation result


PWM output experiment

The working principle of PWM (take up counting as an example)

 

The value in the CCRX register is used to compare with the value in the counter. When the value in the CNT counter is equal to the CCRX register, the general timer output signal level flips. Finally, when the counter counts to MAX (the value in ARR), (the "update interrupt" can also be triggered) immediately returns to 0 and restarts counting up.

Comprehensive experiment of PWM and AFIO pin remapping

Library function instructions

Library function name

Features

RCC_APB1PeriphClockCmd() / RCC_APB2PeriphClockCmd()

APB bus peripheral clock enable

GPIO_Init ()

GPIO initialization function

GPIO_PinRemapConfig ()

GPIO port remapping function

TIM_ARRPreloadConfig()

Enable or disable the preload register of TIMx on ARR

 

TIM_TimeBaseInit()

Initialize timer

TIM_OCInit()

Initialize comparison parameters

TIM_Cmd()

Timer enable

TIM_SetCompare2 ()

Constantly change the comparison value CCRx to achieve different duty cycle effects

Basic knowledge points of PWM configuration

GPIO port function remapping

First of all, we must be clear about which pin we want to remap the TIM function to:

 

 

We learned from AFIO's GPIO multiplexing function remapping that when we use the "TIM3 partial partial remapping" function, we can map the TIM3_CH3 pin of TIM3 to the PB5 pin, and use it to drive LED0 to display the PWM function .

At this time, the following points should be noted:

① The GPIO output mode must be configured as "multiplexed push-pull output". As shown in the figure below, only when configured as multiplexed can signals of other non-IO functions be output through this GPIO pin. Only when configured as push-pull , It can output high and low levels without depending on the external level, and the push-pull output meets the output requirements of the TIM3 function.

 

 

② Configure the remapping function of GPIO pins

Significance and function: The function of TIM3 does not belong to PB5, but we can know that "TIM3 can remap the function to the PB5 pin" when we remap the TIM3 function to the PB5 pin, we must enable AFIO clock and configuration remapping register.

GPIO_InitTypeDef GPIO_InitStructure;  
  
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);    //使能定时器3时钟  
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟  
  
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5      
  
  //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2  
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出  
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO  

The meaning of clock division

When we configure TIM, we will call this function: TIM_TimeBaseInit();

Among them, its structure parameter will configure the "TIM_ClockDivision" attribute.

Let's first understand how TIMER3 is obtained as a clock divided by PSC:

 

 

Our experiment uses the clock from the APB1 bus (AHB clock frequency is 72MHz). We noticed that in AHB->APB1 divider->APB1, if the division factor of the APB1 prescaler is 1, then the clock frequency of TIMXCLK is " TIMCLK=APB1=AHB", if the division factor of the APB1 prescaler is N (N is not 1), then the clock frequency of TIMXCLK is "TIMCLK=2xAPB1=2xAHB/N".

But when we call the initialization clock source library function provided by ST, the default division factor of the APB1 divider is 2, so the final TIMXCLK=2xAPB1=2xAHB/2=AHB.

The clock division here represents the prescaler coefficient of APB1.

What are the two comparison modes of PWM?

 

 

The role of the preload register (TIMx_CCMR1)

 

 

When we update the value of ARR during operation, if OC1PE in the preload register is set to 1, then the ARR value will be put into the counter in the next cycle; if it is set to 0, then the ARR value will be loaded into the counter immediately.

What is the PWM output polarity?

 

 

PWM configuration considerations

Since we output the PWM signal to the PB5 pin, then we don't need to configure the GPIO pin attributes as usual and assign a specific level to PB5. Because PB5 is not a different IO pin, its state is determined by the PWM output level it receives.

How to set the duty cycle?

We only defined the following parts in timer.c:

TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM输出模式1——当计数器值小于比较值时输出有效电平;当计数器值大于比较值时输出无效电平  
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable; // PWM输出模式使能  
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 配置有效电平——高电平有效  
TIM_OC2Init(TIM3, &TIM_OC_InitStructure); // 配置PWM输出比较的属性  

 

This code only explains "how to set the PWM output properties of TIMER3's 2 ports (PWM mode, effective level, enable or disable)", we did not set the "comparison value", so the default duty cycle is It is 100%.

We need to set the CCRx comparison value after configuring the PWM of the timer. The code is as follows:

TIM_SetCompare2(TIM3, CompareValue);  // 用于修改CCRx的值进而修改PWM占空比

 

Here I configure it in the main function, which is used to continuously modify the CCRx comparison value to produce an LED light effect with a gradual change in brightness. Similarly, we can also modify the value of ARR to adjust the PWM period. The corresponding library functions are as follows:

TIM_SetAutoreload(TIM3, NEW_ARR); // 用于修改ARR的值进而修改PWM周期  

 

Library function configuration steps

Step 1: Enable the peripheral bus clock of GPIO/AFIO/TIM

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟  
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟  

 

Step 2: Configure the remapping of pin functions

GPIO_InitTypeDef GPIO_InitStructure;  
PIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5      
  
  //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形 GPIOB.5  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2  
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出  
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

  

Step 3: Configure the basic properties of the timer

  //初始化TIM3  
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值   
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim  
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式  
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

  

Step 4: Configure PWM output mode

TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM输出模式1——当计数器值小于比较值时输出有效电平;当计数器值大于比较值时输出无效电平  
TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable; // PWM输出模式使能  
TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 配置有效电平——高电平有效  
TIM_OC3Init(TIM3, &TIM_OC_InitStructure); // 配置PWM输出比较的属性  

 

Step 5: Configure the preload register

TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能比较值预加载寄存器  
TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能预加载值寄存器  

 

TIM_OC3PreloadConfig()

Used to configure the register "used to preload CCRx value"

TIM_ARRPreloadConfig()

Used to configure the register "used to preload ARR value"

Step 6: Formally enable the TIM function

 

We can see in the block diagram of the register workflow that we can officially enable the TIM function after we configure all the properties of the timer.

TIM_Cmd(TIM3, ENABLE);  //使能TIM3  

 

Comprehensive code example

Main.c

#include "led.h"  
#include "sys.h"  
#include "timer.h"  
  
 int main(void)  
 {        
    u16 CompareValue=0;  
    u8 Dir=1;       
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);      //设置NVIC中断分组2:2位抢占优先级,2位响应优先级  
    LED_InitConfig();                //LED端口初始化  
    TIMER_InitConfig(899,0);     //不分频。PWM频率=72000000/900=80Khz  
       
    while(1)  
    {  
        if(Dir == 1)   
        {  
            CompareValue++;  
        }  
        else   
        {  
            CompareValue--;  
        }  
          
        if(CompareValue >= 300)  
        {  
            Dir = 0;  
        }  
        else  
        {  
            Dir = 1;  
        }  
          
        TIM_SetCompare2(TIM3, CompareValue);  
    }      
 }  

 

Led.c

#include "led.h"  
#include "stm32f10x.h"  
  
void LED_InitConfig()  
{  
    GPIO_InitTypeDef GPIO_InitStructure;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); // 使能LED1的外设时钟  
      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOE, &GPIO_InitStructure); // 配置LED1的输出属性  
      
    GPIO_SetBits(GPIOE, GPIO_Pin_5); // 配置初始引脚输出电平为低电平  
}  

 

Led.h.

#ifndef _LED_H  
#define _LED_H  
  
#include "sys.h"  
  
void LED_InitConfig();  
  
#define LED1 PEout(5)  
  
#endif  

 

Timer.c

#include "timer.h"  
#include "led.h"  
#include "stm32f10x.h"  
#include "delay.h"  
  
void TIMER_InitConfig(u16 ARR, u16 PR)  
{  
    GPIO_InitTypeDef GPIO_InitStructure;  
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  
    TIM_OCInitTypeDef TIM_OC_InitStructure;  
    NVIC_InitTypeDef NVIC_InitStructure;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE); // 使能GPIOB的外设时钟  
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能TIM3的外设时钟  
      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 一定要选择“复用推挽输出”  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置GPIOB的引脚属性  
      
    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); // 引脚重映射配置  
      
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB到TIMxCLK之间没有分频  
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式  
    TIM_TimeBaseInitStructure.TIM_Period = ARR; // 0x1388 = 5000  
    TIM_TimeBaseInitStructure.TIM_Prescaler = PR; // 0x1C20 = 7200  
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); // 周期长度为1s  
      
    TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择PWM输出模式1——当计数器值小于比较值时输出有效电平;当计数器值大于比较值时输出无效电平  
    TIM_OC_InitStructure.TIM_OutputState = TIM_OutputState_Enable; // PWM输出模式使能  
    TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 配置有效电平——高电平有效  
    TIM_OC2Init(TIM3, &TIM_OC_InitStructure); // 配置PWM输出比较的属性  
      
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能比较值预加载寄存器  
    TIM_ARRPreloadConfig(TIM3, ENABLE); // 使能预加载值寄存器  
      
    TIM_Cmd(TIM3, ENABLE); // TIM3使能  
      
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  
    NVIC_Init(&NVIC_InitStructure); // 配置NVIC嵌入式中断向量优先级  
      
    TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE); // 定时器具体中断模式配置  
}  
  
void TIM3_IRQHandler()  
{  
    delay_init();  
    if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET)  
    {  
        delay_ms(500);  
        LED1 = !LED1;  
    }  
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);  
}  

 

Timer.h

#ifndef _TIMER_H  
#define _TIMER_H  
  
#include "sys.h"  
  
void TIMER_InitConfig(u16 ARR, u16 PR);  
  
#endif  

 

operation result

LEDx status

Status change position

LED1 turns on alternately

PWM compare interrupt

The brightness of LED0 varies with the duty cycle

PWM output level

Since I used delay_ms(500) as the delay, you can clearly see that LED1 changes alternately due to the comparison interrupt state, but because the PWM comparison value gradually changes slowly by +1, you need a period of time to carefully observe the brightness changes of LED0.

 

Guess you like

Origin blog.csdn.net/weixin_45590473/article/details/108062177