table of Contents
The working principle of PWM (take up counting as an example)
Comprehensive experiment of PWM and AFIO pin remapping
Basic knowledge points of PWM configuration
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
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
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.