“This is the second day of my participation in the November Gwen Challenge. See details of the event: The last Gwen Challenge 2021”.

In the use of STM32 MCU output PWM waveform, usually can directly use the timer provided by the PWM mode. The timer output frequency can be set by automatically reloading the register (TIMx_ARR), and then the duty cycle can be set by capturing/comparing register 1(TIMx_CCRx). A timer has only one autoreload register (TIMx_ARR), but has four channels of capture/compare registers 1(TIMx_CCR1, TIMx_CCR2, TIMx_CCR3, TIMx_CCR4). Therefore, when using a timer to output PWM waveform, the frequency is unified adjustment, the frequency of the four channels is the same, but the duty cycle can be set independently for each channel. Compare registers TIMx_CCR1, TIMx_CCR2, TIMx_CCR3 and TIMx_CCR4 to set duty cycle of 4 channels respectively.

If the central alignment mode is used for counting, then the timer count waveform is as follows:The triangle wave is how the counter counts, starting at zero and increasing gradually to the load value ARR, and then decreasing to zero. The blue waveform is the CCR value of the comparison register. When the counter is counting, it will compare the calculated value with the CCR value of the comparison register every time. When the counter value is less than the CCR value, the output low level. When the counter value is greater than the CCR value, the output high level.

When you need to change the duty cycle, you only need to change the value of the comparison register CCR.Change the ARR value to set the frequency of the output PWM waveform, and change the CCR value to change the duty cycle of the PWM wave. But in this way to output PWM wave, a timer of 4 channels output PWM wave frequency is the same, so can use a timer output 4 channels of different frequencies of PWM wave? By observing the OC1M bits in the capture/compare mode register 1(TIMx_CCMR1), it is found that in addition to the PWM mode there is a flip mode in which the output level is flipped when the value of the counter CNT is equal to the value of the compare register CCR.So you can through this function, in the process of output PWM wave to change the VALUE of CCR, then the output level will keep flipping, so you can manually change the frequency of the PWM wave.For example, when the CCR value is set to CCR1 for the first time and then the comparison interrupt is enabled, the interrupt will be triggered when the counter value CNT equals CCR1, and then the CCR value will be changed to CCR2 during the interrupt. Then, when the counter is equal to CCR2, an interrupt is triggered, in which the value of the next CCR is set. After triggering an interrupt, the output PWM waveform level will be flipped once. In the counter from 0 to ARR count, the PWM waveform can be reversed many times. In PWM mode, the waveform is flipped once after each count cycle, but in flip mode, a count cycle can be flipped several times, which can change the frequency of the output PWM waveform. Also, the duty cycle can be changed each time a different CCR value is set.

This is done in code.

#include "timer2.h"
/ / value
u16 CCR1_Val = 32768;
u16 CCR2_Val = 16384;
u16 CCR3_Val = 8192;
u16 CCR4_Val = 4096;

void TIM2_PWM_Init( u16 arr, u16 psc )
{

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
	// Enable the clock
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );

    // Set the I/O port
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init( GPIOA, &GPIO_InitStructure );

    // Set the interrupt priority
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &NVIC_InitStructure );

    // Set basic timer parameters
    TIM_TimeBaseInitStructure.TIM_Period = arr;
    TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				// Only advanced timers need to be set. Other timers need not be set.
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure );

    // Set the working mode
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;					// Set the timer to work in flip mode
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

    // Set 4 channels to compare register values
    TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
    TIM_OC1Init( TIM2, &TIM_OCInitStructure );							//TIM2_OC1 PA0

    TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
    TIM_OC2Init( TIM2, &TIM_OCInitStructure );							//TIM2_OC2 PA1

    TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
    TIM_OC3Init( TIM2, &TIM_OCInitStructure );							//TIM2_OC1 PA2

    TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
    TIM_OC4Init( TIM2, &TIM_OCInitStructure );							//TIM2_OC1 PA3

    // Forbid automatic preloading
    TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable );
    TIM_OC2PreloadConfig( TIM2, TIM_OCPreload_Disable );
    TIM_OC3PreloadConfig( TIM2, TIM_OCPreload_Disable );
    TIM_OC4PreloadConfig( TIM2, TIM_OCPreload_Disable );

    // Enable the timer
    TIM_Cmd( TIM2, ENABLE );
    TIM_ITConfig( TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE );
}
Copy the code

Firstly, the timer is initialized. Arr and PSC are used to set the automatic reloading value and frequency division coefficient of the timer respectively. Here, the auto-load value of the ARR is set to the maximum 0xFFFF, which is 65535, which is equivalent to the counter starting at 0 and ending at 65535. Set the PSC to 0, that is, regardless of frequency, and the timer operates at the frequency of 72MHz.

Set the timer working mode to flip mode, and then set the comparison values for the four channels in turn. Auto reload needs to be turned off, otherwise you cannot manually change the comparison value. Finally enable timer and 4 channel comparison interrupt.

Now comes the most important part, changing the frequency and duty cycle of each channel during interrupts.

// The duty cycle is 0 -- 100
u16 CCR1_dc = 20;
u16 CCR2_dc = 40;
u16 CCR3_dc = 60;
u16 CCR4_dc = 80;

u32 capture = 0;
u8 flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;
u16 setcap = 0;

void TIM2_IRQHandler( void )
{
    if( TIM_GetITStatus( TIM2, TIM_IT_CC1 ) ! = RESET ) { TIM_ClearITPendingBit( TIM2, TIM_IT_CC1 ); capture = TIM_GetCapture1( TIM2 );// Set duty cycle
        if( flag1 == 0 )
        {
            flag1 = 1;
            setcap = capture + ( u32 )CCR1_Val * CCR1_dc / 100;
        }
        else
        {
            flag1 = 0;
            setcap = capture + ( u32 )CCR1_Val  * ( 100 - CCR1_dc ) / 100;
        }
        TIM_SetCompare1( TIM2, setcap  );
    }

    if( TIM_GetITStatus( TIM2, TIM_IT_CC2 ) ! = RESET ) { TIM_ClearITPendingBit( TIM2, TIM_IT_CC2 ); capture = TIM_GetCapture2( TIM2 );if( flag2 == 0 )
        {
            flag2 = 1;
            setcap = capture + CCR2_Val * CCR2_dc / 100;
        }
        else
        {
            flag2 = 0;
            setcap = capture + CCR2_Val  * ( 100 - CCR2_dc ) / 100;
        }
        TIM_SetCompare2( TIM2, setcap );
    }


    if( TIM_GetITStatus( TIM2, TIM_IT_CC3 ) ! = RESET ) { TIM_ClearITPendingBit( TIM2, TIM_IT_CC3 ); capture = TIM_GetCapture3( TIM2 );if( flag3 == 0 )
        {
            flag3 = 1;
            setcap = capture + CCR3_Val * CCR3_dc / 100;
        }
        else
        {
            flag3 = 0;
            setcap = capture + CCR3_Val  * ( 100 - CCR3_dc ) / 100;
        }
        TIM_SetCompare3( TIM2, setcap );
    }

    if( TIM_GetITStatus( TIM2, TIM_IT_CC4 ) ! = RESET ) { TIM_ClearITPendingBit( TIM2, TIM_IT_CC4 ); capture = TIM_GetCapture4( TIM2 );if( flag4 == 0 )
        {
            flag4 = 1;
            setcap = capture + CCR4_Val * CCR4_dc / 100;
        }
        else
        {
            flag4 = 0;
            setcap = capture + CCR4_Val  * ( 100 - CCR4_dc ) / 100; } TIM_SetCompare4( TIM2, setcap ); }}Copy the code

After entering the interrupt, first read the value of the current comparison register, because the value of the comparison register is always accumulated, until the value of the comparison register is equal to ARR, will be cleared. So each time we interrupt, we need to read the current comparison value, and then add a value for the next comparison value. Read the current comparison value and store it in Capture, then add a value for the next comparison value. Since the duty cycle is to be changed, the increased value has two cases, namely, the high level duration value and the low level duration value. In order to distinguish the high and low level, a flag bit is used here to judge. The value of the flag bit is only 0 and 1. 0 is the time of the high level and 1 is the time of the low level. CCRx_Val stores the count value of the whole cycle. When the flag bit is 0, CCRx_Val is multiplied by the percentage of high level to calculate the cumulative time of high level. When the flag bit is 1, CCRx_Val is multiplied by the percentage of the low level to calculate the cumulative time of the low level. CCRx_dc stores duty cycle values ranging from 0 to 100, indicating duty cycle from 0% to 100%. The CCRx_Val value of each channel is modified when the period needs to be changed, and the CCRx_dc value of each channel is modified when the duty cycle needs to be changed.

int main( void )
{
    u16 i = 0;
    delay_init();
    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
    TIM2_PWM_Init( 65535.0 );
    while( 1) {}}Copy the code

Then initialize the timer in the main function. The output waveform of the four channels of timer 2 is as follows:

It can be seen from the waveform that the frequency and duty cycle of the four channels of Timer 2 are different. Note Through the comparison mode in the timer, the same timer can output PWM waves of different frequencies.