同一定时器输出4路不同频率且占空比可调的PWM

一般应用中,定时器可输出4路占空比独立可调的PWM,然而4路输出的频率是一致的;但在一些特殊情况下,需要使用一个定时器输出不同频率的PWM,该如何实现呢?

原理分析

定时器三个重要参数:预分频值psc,重装载值arr,比较值ccp;
一般情况下,预分频值决定了定时器的计数频率,重装载值则确定了一个PWM周期计多少个数,这两个参数组合决定了PWM的频率;而比较值则控制PWM的占空比;
以向上计数,小于比较值为有效电平,有效电平极性高为例,则PWM输出如下图:

image

不难发现,计数值等于比较值时,输出电平会进行翻转,那可不可以利用在比较捕获中断中重新设置比较值ccp,来实现在一个重装载值周期内完成多次电平的翻转呢,当然可以,而且每个通道的比较值都能够独立设置,那不就实现了不同通道翻转的频率不同,即输出不同频率的PWM嘛,如下图为一个通道在重转载周期内实现多次电平翻转;

image

软件配置

我们以CH32V307的TIM2为例,输出4路频率分别为1K,2K,8K,10K,占空比分别为10%,20%,50%,80%的PWM;

1、定时器初始化

/*******************************************************************************
* Function Name  : TIM2_PWM_Init
* Description    : Initializes the TIM2 
* Input          : arr - 重装载值
*                  psc - 预分频值
*                  ch1_ccp ~ ch4_ccp - 通道1~通道4的比较捕获值
* Return         : None
*******************************************************************************/
void TIM2_PWM_Init(u16 arr, u16 psc, u16 ch1_ccp, u16 ch2_ccp, u16 ch3_ccp, u16 ch4_ccp)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};
    TIM_OCInitTypeDef TIM_OCInitStructure={0};
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure={0};
    NVIC_InitTypeDef NVIC_InitStructure = {0};

    /* 使能时钟 */
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );

    /* GPIO配置 */
    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 );

    /* TIM配置 */
    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( TIM2, &TIM_TimeBaseInitStructure);

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;  // 翻转模式
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;  // 输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;  // 有效电平为高电平

    TIM_OCInitStructure.TIM_Pulse = ch1_ccp;  // 通道1的比较值
    TIM_OC1Init( TIM2, &TIM_OCInitStructure );
    TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable );

    TIM_OCInitStructure.TIM_Pulse = ch2_ccp;  // 通道2的比较值
    TIM_OC2Init( TIM2, &TIM_OCInitStructure );
    TIM_OC2PreloadConfig( TIM2, TIM_OCPreload_Disable );

    TIM_OCInitStructure.TIM_Pulse = ch3_ccp;  // 通道3的比较值
    TIM_OC3Init( TIM2, &TIM_OCInitStructure );
    TIM_OC3PreloadConfig( TIM2, TIM_OCPreload_Disable );

    TIM_OCInitStructure.TIM_Pulse = ch4_ccp;  // 通道4的比较值
    TIM_OC4Init( TIM2, &TIM_OCInitStructure );
    TIM_OC4PreloadConfig( TIM2, TIM_OCPreload_Disable );

    /* 中断配置 */
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; 
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  // 子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init( &NVIC_InitStructure );

    TIM_CtrlPWMOutputs(TIM2, ENABLE );  // 使能PWM输出
    TIM_ARRPreloadConfig( TIM2, ENABLE );  // 使能自动重装载
    TIM_ITConfig( TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE );  // 使能比较捕获中断
    TIM_Cmd( TIM2, ENABLE );  // 使能TIM
}
  • TIM_OCMode为Toggle翻转模式;
  • TIM_OCPreload_Disable禁止预装载功能,保证可随时更新比较值寄存器以控制输出波形;
  • 使能输出通道的比较捕获中断;

2、中断处理

u16 CCR1_dc = 10;  // 通道1的占空比
u16 CCR2_dc = 20;  // 通道2的占空比
u16 CCR3_dc = 50;  // 通道3的占空比
u16 CCR4_dc = 80;  // 通道4的占空比

u32 capture = 0;
u8 flag1 = 0, flag2 = 0, flag3 = 0, flag4 = 0;
u16 setcap = 0;
/*******************************************************************************
* Function Name  : TIM2_IRQHandler
* Description    : TIM2中断服务函数
* Input          : None
* Return         : None
*******************************************************************************/
void TIM2_IRQHandler( void )
{
    if( TIM_GetITStatus( TIM2, TIM_IT_CC1 ) != RESET )  // 判断是否为通道1的比较捕获中断
    {
        TIM_ClearITPendingBit( TIM2, TIM_IT_CC1 );  // 清除中断标志
        capture = TIM_GetCapture1( TIM2 );  // 获取通道1的比较值
        /* 设置占空比 */
        if( flag1 == 0 )
        {
            flag1 = 1;
            setcap = capture + ( u32 )CCR1_CCP * CCR1_dc / 100;
        }
        else
        {
            flag1 = 0;
            setcap = capture + ( u32 )CCR1_CCP  * ( 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_CCP * CCR2_dc / 100;
        }
        else
        {
            flag2 = 0;
            setcap = capture + CCR2_CCP  * ( 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_CCP * CCR3_dc / 100;
        }
        else
        {
            flag3 = 0;
            setcap = capture + CCR3_CCP  * ( 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_CCP * CCR4_dc / 100;
        }
        else
        {
            flag4 = 0;
            setcap = capture + CCR4_CCP  * ( 100 - CCR4_dc ) / 100;
        }
        TIM_SetCompare4( TIM2, setcap );
    }

}

总结

按以上控制输出翻转的方法即可用同一定时器输出不同频率和占空比的PWM,效果如下图:

image

参考程序:CH32V307_TIM2_PWM

posted @ 2024-07-26 14:27  WCH_CH32  阅读(1111)  评论(0编辑  收藏  举报