Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
PWM mode, also known as pulse-width modulation mode, can generate a square wave with adjustable frequency and duty cycle. The TIMx_ARR register determines the frequency and the duty cycle of the TIMx_CCRx register. In the hardware circuit, PWM wave is usually produced by a triangle wave and reference value into the comparator, and then the comparator output is PWM wave.
V1 is a triangle wave generator, the amplitude is 5V, the frequency is 1K, V2 is a DC source, the voltage is 3V, the two waveform into the comparator, and then the comparator output is PWM wave. The output waveform is as follows:
When the voltage value of V1 is greater than V2, the comparator outputs a high level. When V1 voltage value is less than V2, the comparator output is low. You change V2, you change the duty cycle.
In the MCU, the value of register ARR is equivalent to the value of V1, and the value of CCR is equivalent to the value of V2. When the values of THE ARR and CCR registers are set, the counter value will be compared with these two values in real time during the counting process of the timer. When the counter value is less than CCR, the low level will be output; when the counter value is greater than CCR, the high level will be output. When the value of the counter is equal to ARR, the counter is cleared.
When setting PWM counting mode, there are generally three modes, counting mode up, counting mode down, central alignment mode.
Counting up mode: refers to the counter incrementing from 0, when the count is equal to ARR, the counter is cleared to 0, and then incrementing again from 0.
Down-count mode: the counter decays from ARR, resets to ARR when the count equals 0, and then decays again from ARR.
Central-aligned mode: when the counter 0 begins to increment and when the count equals ARR, the timer begins to decrement. When the counter value decreases to 0, the incrementing count begins again.
The upcount pattern is often used because it is simpler and easier to understand. The following code to achieve PWM output function.
void TIM3_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // Enable timer 3 clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // Enable GPIO and AFIO clock multiplexing
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); Timer3 partially remaps TIM3_CH2(PA7)-- >PB5 TIM3_CH1(PA6)-->PB4
// Set this pin to reuse output function, output TIM3 CH2 PWM pulse waveform gPIob.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// Initialize TIM3
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
// Initialize the TIM3_CH2 PWM mode
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
// Enable TIM3 preloaded register on CCR2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
/ / can make TIM3
TIM_Cmd(TIM3, ENABLE);
}
void TIM1_PWM_Init(u16 arr, u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitSturcture;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStructure);
TIM_OCInitSturcture.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitSturcture.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitSturcture.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitSturcture);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_Cmd(TIM1, ENABLE); // Enable the counter
TIM_CtrlPWMOutputs(TIM1,ENABLE); // Enable the main output
}
Copy the code
Here through two timer output 2 PWM wave. The counting mode used is upward counting mode. The PWM mode is PWM2.
Next, set the PWM frequency and duty cycle in the main program.
int main(void)
{
u16 led_pwm_val=0;
u8 dir=1;
delay_init(); // Delay function initialization
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
TIM3_PWM_Init(899.0); // Regardless of frequency. The PWM frequency = 72000000/900 = 80 KHZ
TIM1_PWM_Init(899.0);
while(1)
{
delay_ms(10);
if(dir) led_pwm_val++;
else led_pwm_val--;
if(led_pwm_val>300) dir=0;
if(led_pwm_val==0) dir=1; TIM_SetCompare2(TIM3,led_pwm_val); TIM_SetCompare1(TIM1,led_pwm_val); }}Copy the code
Two arguments are passed from the main function to the initialization function. The first argument is to set the ARR value and the second argument is to set the clock’s frequency division coefficient. If the ARR value is set to 900, the system automatically increments the value by 1 (because the ARR value is at least 1), so if the ARR value is set to 899, the system actually writes the value to the ARR register as 899+1, and then sets the clock divider coefficient to 0, that is, the default clock frequency is 72MHz regardless of frequency. Thus the PWM frequency is the system clock divided by the auto-load value. Set the PWM frequency to 72MHz/900=80K.
To change the duty ratio, use the TIM_SetCompare() function, which internally sets the value of the CCR register. The code sets the VALUE of CCR up from 0 to 300 and then down to 0 again. The PWM output port is connected to a LED lamp, you can see the LED lamp slowly change from light to off, to achieve the effect similar to the breathing lamp.