2.11. PWM

Overview

提供PWM应用示例、常用相关API介绍和常见问题。

2.11.1. PWM硬件IO说明

1.MCPWM是有多通道的PWM硬件模块,总共8组PWM通道,每组PWM包含2通道(即H和L通道),MCPWM包含死区时间设置、反向电平设置等,专用与驱动电机。

MCPWM为: - PWMCH0_H、PWMCH0_L:硬件IO为PA3、PA4

  • PWMCH1_H、PWMCH1_L:硬件IO为PA7、PA8

  • PWMCH2_H、PWMCH2_L:硬件IO为PC7、PC8

  • PWMCH3_H、PWMCH3_L:硬件IO为PH0、PH1

  • PWMCH4_H、PWMCH4_L:硬件IO为PC0、PC2

  • PWMCH5_H、PWMCH5_L:硬件IO为PH3、PH7

  • PWMCH6_H、PWMCH6_L:硬件IO为PB2、PB3

  • PWMCH7_H、PWMCH7_L:硬件IO为PB6、PB7

2.定时器2和定时器3的PWM

定时器的PWM可以映射任意IO。

2.11.2. 应用示例

示例演示:

  • 芯片引脚配置,频率/占空比范围

  • PWM控制运行,暂停,正反转,动态配置频率占空比

example: 具体示例代码详见 apps/common/example/peripheral/pwm/main.c ,示例工程实现需在 apps/demo/demo_DevKitBoard/include/demo_config.h 中开启宏 USE_PWM_TEST_DEMO

Note

  • 除了开启上述的宏外,还需在 ``board.c `` 添加PWM参数和PWM设备,如下代码所示(同时最多可以定义3个PWM设备,设备列表的设备名称可以自定义)

  • PWM频率最高为:LSB时钟/2,当PWM频率设置接近LSB时钟/2时,占空比也无效,设置频率高则占空比小数点point_bit应当设置为0

  • 以下代码PWM0是正反转PWM选择,PWM1是普通定时器PWM选择方法。

    //----------------------------------定义PWM设备的参数------------------------------------------//
    //例如:PWM0
    PWM_PLATFORM_DATA_BEGIN(pwm_data0)

        .port  = 0,
        .pwm_ch = PWMCH0_H | PWMCH0_L,//初始化可选多通道,如:PWMCH0_H | PWMCH0_L | PWMCH1_H ... | PWMCH7_H | PWMCH7_L | PWM_TIMER2_OPCH2 | PWM_TIMER3_OPCH3 ,
        .freq   = 1000,//频率
        .duty   = 50,//占空比
        .point_bit = 0,//根据point_bit值调节占空比小数点精度位: 0<freq<=4K,point_bit=2;4K<freq<=40K,point_bit=1; freq>40K,point_bit=0;
    PWM_PLATFORM_DATA_END()

    //例如:PWM1
    PWM_PLATFORM_DATA_BEGIN(pwm_data1)

        .port  = IO_PORTA_03,//选择定时器的TIMER PWM任意IO,pwm_ch加上PWM_TIMER3_OPCH3或PWM_TIMER2_OPCH2有效,只支持2个PWM,占用output_channel2/3,其他外设使用output_channel需留意
        .pwm_ch = PWM_TIMER2_OPCH2,//初始化可选多通道,如:PWMCH0_H | PWMCH0_L | PWMCH1_H ... | PWMCH7_H | PWMCH7_L | PWM_TIMER2_OPCH2 | PWM_TIMER3_OPCH3 ,
        .freq   = 1000,//频率
        .duty   = 50,//占空比
        .point_bit = 0,//根据point_bit值调节占空比小数点精度位: 0<freq<=4K,point_bit=2;4K<freq<=40K,point_bit=1; freq>40K,point_bit=0;
    PWM_PLATFORM_DATA_END()

    //----------------------------------设备列表添加PWM设备------------------------------------------//
    REGISTER_DEVICES(device_table) = {
            {"uart2", &uart_dev_ops, (void *)&uart2_data },
            {"rtc", &rtc_dev_ops, NULL},
            { "pwm0",   &pwm_dev_ops,  (void *)&pwm_data0},//添加pwm0子设备,"pwm0"名称可自定义
            { "pwm1",   &pwm_dev_ops,  (void *)&pwm_data1},//添加pwm1子设备,"pwm1"名称可自定义
    };
\

Note

使用说明:

  • A)例如上述程序代码中的.pwm_ch参数,当使用对应的MCPWM IO时配置:

    .port = 0,//这个参数为定时器PWM专用的,使用到定时器PWM映射任意IO才指定为IO。

    .pwm_ch= PWMCH0_H | PWMCH0_L,//例如使用PA3和PA4,

    .pwm_ch为需要打开的PWM通道,可以直接用或打开多通道,例如PWMCH1_H | PWMCH2_H | PWMCH3_H | PWMCH3_L

  • B).duty和.point_bit 参数

    当.point_bit = 0,则表明.duty的占空比参数是整数,即占空比为0%-100%,最高频率25MHz左右(最高位lsb_clk频率的一半)。

    当.point_bit = 1,则表明.duty的占空比参数可以包含1位小数点,例如.duty=20.5,即占空比为0.0%-100.0%,最高频率2.4MHz左右

    当.point_bit = 2,则表明.duty的占空比参数可以包含2位小数点,例如.duty=20.55,即占空比为0.00%-100.00%,最高频率200KHz左右

  • C)当使用的PWM硬件IO不在MCPWM的所有通道时,可以使用定时器PWM映射到任意IO,例如上述程序的硬件IO为 IO_PORTA_03

    .port = IO_PORTA_03,//选择定时器的TIMER PWM任意IO,对使用MCPWM无效

    .pwm_ch = PWM_TIMER2_OPCH2,//pwm_ch加上PWM_TIMER3_OPCH3或PWM_TIMER2_OPCH2有效,只支持2个PWM,

    说明: 当使用任意IO映射PWM时,可以用定时器2或3,即.pwm_ch = PWM_TIMER2_OPCH2,或.pwm_ch = PWM_TIMER3_OPCH3,定时器只需要用1个映射即可,当使用PWM_TIMER2_OPCH2则在其他程序不能使用定时器2,同理定时器3在使用PWM时其他程序也不能使用。

    另外:.pwm_ch参数可以或上PWMCHx_H/L初始化时同时打开PMPWM的指定通道和定时器PWM。

  • D).deathtime为死区设置时间(系统时钟的 deathtime+1倍),当使用PWMCHx_H/L才有效,并且死区时间在PWM初始化是没有进行设置,用户需要则在dev_open打开PWM之后,再进行设置,例如程序:

    pwm.deathtime = 6;//最大值31,死区时间为系统时钟的(deathtime+1)倍,使用PWMCHx_H/L有效。

    dev_ioctl(pwm_dev_handl, PWM_SET_DEATH_TIME, (u32)&pwm);//PWM死区时间设置

2.11.3. 常见问题

  • 初始化 pwm,占空比为0,第一次使能 pwm 时,会有一个周期的高电平抖动,如何消除这种抖动?

    第一种方法:初始化timer时,设置 JL_TIMERx->CNT = JL_TIMERx->PRD;

    第二种方法:如果选用了非硬件IO。而非硬件IO是通过调整IO的特殊状态来实现映射的,库里的gpio_output_channle函数在配置IO状态的顺序,会导致IO有抖动,如果对该抖动有要求的话,可以在库外自行写一个gpio_output_channle函数,注意IO的配置顺序。如果能具体到某个引脚,可以直接寄存器写法更好。可以参考下面这个函数:

    u32 user_gpio_output_channle(u32 gpio, u32 CHx_XXX_OUT, u32 out_start)//out_start是防止映射过程中,调整引脚状态引起的毛刺。0:期望一直保持低电平。 1:期望一直保持高电平
    {
        u32 ch = CHx_XXX_OUT / 0x10;
        u32 xxx_oot = CHx_XXX_OUT % 0x10;
        switch (ch) {
            case 0 :
                JL_IOMAP->CON1 &= ~(0xf << 8);
                JL_IOMAP->CON1 |= xxx_oot << 8;
                break;
            case 1:
            case 2:
                JL_IOMAP->CON3 &= ~(0xf << (20 + (ch - 1) * 4));
                JL_IOMAP->CON3 |= xxx_oot << (20 + (ch - 1) * 4);
                break;
            default:
                return -22;
        }
        if (out_start == 0) {
            switch (ch) {
            case 0:
                gpio_direction_output(gpio, 0); //先输出0
                gpio_set_direction(gpio, 0);
                gpio_set_die(gpio, 0);
                gpio_set_pull_up(gpio, 1);
                gpio_set_pull_down(gpio, 1);
                break;
            case 1:
                gpio_direction_output(gpio, 0); //先输出0
                gpio_set_direction(gpio, 0);
                gpio_set_die(gpio, 1);
                gpio_set_pull_up(gpio, 1);
                gpio_set_pull_down(gpio, 1);
                break;
            case 2:
                gpio_direction_output(gpio, 0); //先输出0
                gpio_set_direction(gpio, 0);
                gpio_set_die(gpio, 0);
                gpio_set_pull_up(gpio, 1);
                gpio_set_pull_down(gpio, 1);
                gpio_direction_output(gpio, 1); //再输出1
                break;
            }
        } else {
            switch (ch) {
            case 0:
                gpio_direction_output(gpio, 1); //先输出1
                gpio_set_direction(gpio, 0);
                gpio_set_die(gpio, 0);
                gpio_set_pull_up(gpio, 1);
                gpio_set_pull_down(gpio, 1);
                gpio_direction_output(gpio, 0); //再输出0
                break;
            case 1:
                gpio_direction_output(gpio, 1); //先输出1
                gpio_set_direction(gpio, 0);
                gpio_set_die(gpio, 1);
                gpio_set_pull_up(gpio, 1);
                gpio_set_pull_down(gpio, 1);
                gpio_direction_output(gpio, 0); //再输出0
                break;
            case 2:
                gpio_direction_output(gpio, 1); ///先输出1
                gpio_set_direction(gpio, 0);
                gpio_set_die(gpio, 0);
                gpio_set_pull_up(gpio, 1);
                gpio_set_pull_down(gpio, 1);
                break;
            }
        }
        return 0;
    }
    
  • 当使用MCPWM硬件时(PWMCHx_H 或 PWMCHx_L), 每组的H和L 2个同时使用则:频率一定相同,占空比可不同。

    例如:同通道的PWMCH0_H和PWMCH0_L频率一定相同,但是不同通道的PWMCH0_H和PWMCH1_H频率可不同,占空比都可以任意设置0.00%-100.00%

  • 定时器PWM.port参数是选择定时器的TIMER PWM任意IO,对使用MCPWM时该参数无效。

当定时器2(PWM_TIMER2_OPCH2)的PWM指定IO为PH6、当定时器3(PWM_TIMER3_OPCH3)的PWM指定IO为PC10时,不会占用ouputchannel(ouputchannel为一个可以映射任何功能到某个任意IO的硬件模块),其他IO时定时器2占用ouputchannel_2,定时器3占用ouputchannel_3。

  • .pwm_ch参数不能同时使用2个定时器映射任意IO,当需要两个PWM任意IO,在设备列表多加一个PWM子设备即可。

  • point_bit 占空比小数点参数会影响PWM的频率,频率过高,占空比小数点比较多时,可能会导致频率不对或者占空比无效。

  • 在打开PWM设备之后更改通道的频率只能选择一组通道(H和L为一组通道),比如只能选择.pwm_ch = PWMCH0_H | PWMCH0_L,或者.pwm_ch = PWM_TIMER2_OPCH2,PWM通道最多支持同一个通道的H和L设置,不能进行多通道的设置。

  • 使用到的.pwm_ch通道需在 board.c 中有定义,在配置的时候可以或上要用的,若没有则需先 dev_ioctl(pwm_dev_handl, PWM_ADD_CHANNEL, (u32)&pwm); 添加通道,例程中有例子。