[原创] 【小华工控新品HC32F448】05.通过单缓存实现TIMER6输出不同PWM占空比功能

xld0932   2024-2-1 10:56 楼主

1.概述

小华HC32F448系列MCU带有一个高级控制定时器 6(Timer6)外设,它是一个 16 位计数宽度的高性能定时器,能在各种复杂应用场景中提供丰富、灵活的搭配组合和各种中断、事件、 PWM 输出。该定时器支持锯齿波和三角波两种波形模式,可生成各种 PWM 波形(单边对齐独立 PWM、双边对称独立 PWM、双边对称互补 PWM、双边非对称 PWM等);单元间可实现软件同步和硬件同步(同步启动、停止、清零、刷新等);各基准值寄存器支持缓存功能(单级缓存和双级缓存);支持脉宽测量和周期测量;支持 2 相正交编码计数和 3 相正交编码计数;支持 EMB 控制。

 

2.功能框图

1.png

 

3.例程功能

3.1.我们参考官方的示例程序,结合EV_F448_LQ80_Rev1.0开发板,通过TIMER6的PWM三角波模式,通过单缓存输出占空比动态配置的功能

 

3.2.示例程序

#define EXAMPLE_PERIPH_WE               (LL_PERIPH_GPIO | LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM)
#define EXAMPLE_PERIPH_WP               (LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_SRAM)

#define TMR6_1_PWMA_PORT                (GPIO_PORT_A)
#define TMR6_1_PWMA_PIN                 (GPIO_PIN_08)
#define TMR6_1_PWMA_FUNC                (GPIO_FUNC_3)
#define TMR6_1_PWMB_PORT                (GPIO_PORT_A)
#define TMR6_1_PWMB_PIN                 (GPIO_PIN_07)
#define TMR6_1_PWMB_FUNC                (GPIO_FUNC_3)

#define PWM_PERIODVALUE                 (3125U)

static uint8_t  u8Duty_pos    = 1U;
static uint16_t au16Cmp_buf[] = {PWM_PERIODVALUE/10, PWM_PERIODVALUE/10, 0, PWM_PERIODVALUE/10, PWM_PERIODVALUE/10, PWM_PERIODVALUE};

static void Duty_Set(CM_TMR6_TypeDef *TMR6x, uint32_t u32Index, uint16_t cmp_value)
{
    if (TMR6_GetCompareValue(TMR6x, u32Index) != cmp_value) {
        TMR6_SetCompareValue(TMR6x, u32Index, cmp_value);
        if (cmp_value == 0U) {
            if (u32Index % 2U) {
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_B, TMR6_STAT_DOWN_CNT_MATCH_B, TMR6_PWM_HOLD);
            } else {
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_A, TMR6_STAT_DOWN_CNT_MATCH_A, TMR6_PWM_HOLD);
            }
        } else if (cmp_value == PWM_PERIODVALUE) {
            if (u32Index % 2U) {
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_B, TMR6_STAT_OVF, TMR6_PWM_INVT);
            } else {
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_A, TMR6_STAT_OVF, TMR6_PWM_INVT);
            }
        } else {
            if (u32Index % 2U) {
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_B, TMR6_STAT_OVF, TMR6_PWM_HOLD);
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_B, TMR6_STAT_DOWN_CNT_MATCH_B, TMR6_PWM_HIGH);
            } else {
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_A, TMR6_STAT_OVF, TMR6_PWM_HOLD);
                TMR6_PWM_SetPolarity(TMR6x, TMR6_CH_A, TMR6_STAT_DOWN_CNT_MATCH_A, TMR6_PWM_HIGH);
            }
        }
    }
}

static void Tmr6_UnderFlow_CallBack(void)
{
    TMR6_ClearStatus(CM_TMR6_1, TMR6_FLAG_UDF);
    Duty_Set(CM_TMR6_1, TMR6_CMP_REG_C, au16Cmp_buf[u8Duty_pos]);
    Duty_Set(CM_TMR6_1, TMR6_CMP_REG_D, au16Cmp_buf[u8Duty_pos]);
    if (++u8Duty_pos >= sizeof(au16Cmp_buf)/sizeof(au16Cmp_buf[0])) {
        u8Duty_pos = 0;
    }
}

int32_t main(void)
{
    stc_tmr6_init_t stcTmr6Init;
    stc_tmr6_pwm_init_t stcPwmInit;
    stc_irq_signin_config_t stcIrqRegiConf;
    stc_tmr6_buf_config_t stcBufConfig;

    /* Unlock peripherals or registers */
    LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
    /* Configure BSP */
    BSP_CLK_Init();

    (void)TMR6_StructInit(&stcTmr6Init);
    (void)TMR6_PWM_StructInit(&stcPwmInit);
    (void)TMR6_BufFuncStructInit(&stcBufConfig);

    FCG_Fcg2PeriphClockCmd(FCG2_PERIPH_TMR6_1, ENABLE);

    /* Timer6 PWM output port configuration */
    GPIO_SetFunc(TMR6_1_PWMA_PORT, TMR6_1_PWMA_PIN, TMR6_1_PWMA_FUNC);
    GPIO_SetFunc(TMR6_1_PWMB_PORT, TMR6_1_PWMB_PIN, TMR6_1_PWMB_FUNC);

    TMR6_DeInit(CM_TMR6_1);
    /* Timer6 general count function configuration */
    stcTmr6Init.sw_count.u32CountMode = TMR6_MD_TRIANGLE;
    stcTmr6Init.sw_count.u32ClockDiv = TMR6_CLK_DIV32;
    stcTmr6Init.u32PeriodValue = PWM_PERIODVALUE;
    (void)TMR6_Init(CM_TMR6_1, &stcTmr6Init);

    /* General compare buffer function configure */
    stcBufConfig.u32BufNum = TMR6_BUF_SINGLE;
    stcBufConfig.u32BufTransCond = TMR6_BUF_TRANS_OVF;
    (void)TMR6_GeneralBufConfig(CM_TMR6_1, TMR6_CH_A, &stcBufConfig);
    (void)TMR6_GeneralBufConfig(CM_TMR6_1, TMR6_CH_B, &stcBufConfig);
    TMR6_GeneralBufCmd(CM_TMR6_1, TMR6_CH_A, ENABLE);
    TMR6_GeneralBufCmd(CM_TMR6_1, TMR6_CH_B, ENABLE);
    /* Set General Compare RegisterA Value */
    TMR6_SetCompareValue(CM_TMR6_1, TMR6_CMP_REG_C, au16Cmp_buf[0]); /* General comprare register C, buffer for GCMAR */
    TMR6_SetCompareValue(CM_TMR6_1, TMR6_CMP_REG_D, au16Cmp_buf[0]); /* General comprare register D, buffer for GCMBR */

    /* Configure PWM output CHA */
    stcPwmInit.u32CompareValue = au16Cmp_buf[0];
    stcPwmInit.u32CountDownMatchBPolarity = TMR6_PWM_HOLD;
    stcPwmInit.u32CountUpMatchBPolarity = TMR6_PWM_HOLD;
    stcPwmInit.u32CountDownMatchAPolarity = TMR6_PWM_HIGH;
    stcPwmInit.u32CountUpMatchAPolarity = TMR6_PWM_LOW;
    stcPwmInit.u32UdfPolarity = TMR6_PWM_HOLD;
    stcPwmInit.u32OvfPolarity = TMR6_PWM_HOLD;
    stcPwmInit.u32StopPolarity = TMR6_PWM_LOW;
    stcPwmInit.u32StartPolarity = TMR6_PWM_LOW;
    (void)TMR6_PWM_Init(CM_TMR6_1, TMR6_CH_A, &stcPwmInit);
    /* CHB */
    stcPwmInit.u32CompareValue = au16Cmp_buf[0];
    stcPwmInit.u32CountDownMatchBPolarity = TMR6_PWM_HIGH;
    stcPwmInit.u32CountUpMatchBPolarity = TMR6_PWM_LOW;
    stcPwmInit.u32CountDownMatchAPolarity = TMR6_PWM_HOLD;
    stcPwmInit.u32CountUpMatchAPolarity = TMR6_PWM_HOLD;
    (void)TMR6_PWM_Init(CM_TMR6_1, TMR6_CH_B, &stcPwmInit);
    /* PWM pin function set */
    TMR6_SetFunc(CM_TMR6_1, TMR6_CH_A, TMR6_PIN_CMP_OUTPUT);
    TMR6_SetFunc(CM_TMR6_1, TMR6_CH_B, TMR6_PIN_CMP_OUTPUT);
    /* PWM output command */
    TMR6_PWM_OutputCmd(CM_TMR6_1, TMR6_CH_A, ENABLE);
    TMR6_PWM_OutputCmd(CM_TMR6_1, TMR6_CH_B, ENABLE);

    /* Enable interrupt */
    TMR6_IntCmd(CM_TMR6_1, TMR6_INT_UDF, ENABLE);

    stcIrqRegiConf.enIRQn = INT002_IRQn;
    stcIrqRegiConf.enIntSrc = INT_SRC_TMR6_1_UDF;
    stcIrqRegiConf.pfnCallback = &Tmr6_UnderFlow_CallBack;
    (void)INTC_IrqSignIn(&stcIrqRegiConf);
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIO_15);
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);

    /* Start timer6 */
    TMR6_Start(CM_TMR6_1);

    for (;;) {
        ;
    }
}

 

3.3.运行结果

我们使用逻辑分析仪来测试PA8和PA7的输出波形,看到PA8(Timer6_1 PWMA)和PA7(Timer6_1 PWMB)输出频率为1KHz(10%->10%之间的频率),且占空比按照10%->10%->0%->10%->10%->100%循环变化。

2.png

 

4.扩展

在3.3中的运行结果是不是有点看不懂,没关系,我们将代码修改一下,如下所示:

static uint16_t au16CmpB_buf[] = {PWM_PERIODVALUE/2, PWM_PERIODVALUE/2, PWM_PERIODVALUE/2, PWM_PERIODVALUE/2, PWM_PERIODVALUE/2, PWM_PERIODVALUE/2};

static void Tmr6_UnderFlow_CallBack(void)
{
    TMR6_ClearStatus(CM_TMR6_1, TMR6_FLAG_UDF);
    Duty_Set(CM_TMR6_1, TMR6_CMP_REG_C, au16Cmp_buf[u8Duty_pos]);
    Duty_Set(CM_TMR6_1, TMR6_CMP_REG_D, au16CmpB_buf[u8Duty_pos]);
    if (++u8Duty_pos >= sizeof(au16Cmp_buf)/sizeof(au16Cmp_buf[0])) {
        u8Duty_pos = 0;
    }
}

int32_t main(void)
{
......
    /* CHB */
    stcPwmInit.u32CompareValue = au16CmpB_buf[0];
    stcPwmInit.u32CountDownMatchBPolarity = TMR6_PWM_HIGH;
    stcPwmInit.u32CountUpMatchBPolarity = TMR6_PWM_LOW;
    stcPwmInit.u32CountDownMatchAPolarity = TMR6_PWM_HOLD;
    stcPwmInit.u32CountUpMatchAPolarity = TMR6_PWM_HOLD;
    (void)TMR6_PWM_Init(CM_TMR6_1, TMR6_CH_B, &stcPwmInit);
......
}

 

通过上述的修改,这样我们可以把CHB固定在50%输出占空比……运行监测的波形如下所示:

5.png

 

为了更好的理解上图中波形的含义,我们需要参考UM手册中19.3.5小节的比较输出功能,19.3.14.2小节中的双边对称独立PWM输出功能,我们可以通过如下图,可以直观理解上图中的输出波形:

 

4.png
 
6.png

 

本帖最后由 xld0932 于 2024-2-1 11:32 编辑
We are a team and we work as a team !

回复评论 (1)

xhsc
点赞  2024-9-6 04:25
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复