2.10. PWM

2.10.1. Overview

AC792N的PWM可以分为两种,分别是MCPWM(Motor Control PWM) 和TMR_PWM。支持crossbar到任意普通IO

MCPWM即电机控制脉冲宽度调制器(Motor Control Pulse Width Modulation),芯片总共有4组MCPWM,每组又MCPWM可分H和L两个通道。

TMR_PWMTIMER模块的PWM输出功能,AC792N总共6个Timer,PWM驱动只支持使用其中4个(注意:这4个TMR中由于SDK的Audio和Video占用了2个TMR,一般开发者仅能使用TMR4TMR5作PWM输出,这里建议能用MCPWM就不要用TMR PWM了)。

支持如下特性:

  • 支持反向输出;

  • 周期/占空比相关寄存器带缓冲,输出期间修改频率、占空比,不会出现突变。

其中MCPWM还支持死区时间配置。

例程: apps/common/example/peripheral/pwm/pwm_test.c ,可使用hello_demo工程测试,使用时在app_config.h中使能USE_PWM_TEST_DEMO宏开关。

2.10.2. PWM参数配置

1.配置结构体

pwm_platform_data结构体

// pwm.h
typedef struct {
    float duty;                         ///< 用于带2位小数点占空比的PWM,0.00%-100.00%
    u8 deathtime;                       ///< 死区时间,最大值31
    u8  point_bit;                      ///< 小数点精度:0-2
    u32 pwm_ch;                         ///< PWM通道使能
    u32 freq;                           ///< PWM输出频率配置
} pwm_config_t;

struct pwm_platform_data {
    int timer_pwm_port[TIME_PWM_MAX];   ///< TIMER PWM IO口配置
    int mcpwm_port[MCPWM_MAX_NUM * 2];  ///< MCPWM IO配置
    pwm_config_t pwm_config;            ///< PWM参数配置
    u8 hd_level;                        ///< IO强驱等级
};
  • 占空比duty

    占空比参数范围0.00 ~ 100.00,支持最高2位小数精度的占空比,精度通过point_bit参数控制。(最高精度还受输出频率影响,详见占空比小节)。

  • 死区时间deathtime

    参数范围0 ~ 31。单位为时钟源(LSB)周期时间。

  • 精度point_bit

    参数范围0~2。0-精度1%,1-精度0.1%,2-精度0.01%。

  • 通道使能pwm_ch

    可选通道如下

    MCPWM_H

    MCPWM_L

    TMR_PWM

    PWMCH0_H

    PWMCH0_L

    PWM_TIMER2_OPCH

    PWMCH1_H

    PWMCH1_L

    PWM_TIMER3_OPCH

    PWMCH2_H

    PWMCH2_L

    PWM_TIMER4_OPCH

    PWMCH3_H

    PWMCH3_L

    PWM_TIMER5_OPCH

    需要使能多个通道时使用|符号分隔,比如:

    .pwm_ch = PWMCH0_H | PWMCH0_L | PWM_TIMER2_OPCH,
    

    注意,pwm_ch使能通道的同时,需要配合timer_pwm_port/mcpwm_port参数配置好IO出口,才能正确初始化输出pwm信号。

  • 频率freq

    目标pwm频率。单位Hz,参数范围1 ~ F(src)。无法保证实际的pwm频率一定是所设置的频率,驱动只会按最接近.freq的频率来设置,详见频率小节。

    建议开发者选择输出频率时,选择能够整除PWM时钟源(即LSB)的频率。

  • TMR_PWM输出口配置timer_pwm_port

    TMR_pwm的输出口配置。4个TMR_PWM的出口IO顺序如下:

    #define TCFG_PWM1_TIMER_PWM_REMAP_IO        { -1/*PWM2*/, -1/*PWM3*/, -1/*PWM4*/, -1/*PWM5*/, }
    
  • MCPWM输出口配置mcpwm_port

    MCPWM输出出口配置。4个MCPWM的出口IO顺序如下:

    #define TCFG_PWM1_MCPWM_REMAP_IO            {\
                                                    -1/*MCPWM0H*/, -1/*MCPWM1H*/, -1/*MCPWM2H*/, -1/*MCPWM3H*/,\
                                                    -1/*MCPWM0L*/, -1/*MCPWM1L*/, -1/*MCPWM2L*/, -1/*MCPWM3L*/,\
                                                }
    
  • 强驱档位hd_level

    输出PWM的IO的强驱档位配置,配置参数范围0 ~ 3,驱动电流见GPIO章节的gpio_drive_strength_t

结构体配置示例1——MCPWM

// board_develop_AC792XX.h
#define TCFG_PWM1_TIMER_PWM_REMAP_IO        { -1/*PWM2*/, -1/*PWM3*/, -1/*PWM4*/, -1/*PWM5*/, }
#define TCFG_PWM1_MCPWM_REMAP_IO            {\
                                                -1/*MCPWM0H*/, -1/*MCPWM1H*/, IO_PORTA_04/*MCPWM2H*/, -1/*MCPWM3H*/,\
                                                -1/*MCPWM0L*/, -1/*MCPWM1L*/, IO_PORTA_06/*MCPWM2L*/, -1/*MCPWM3L*/,\
                                            }
#define TCFG_PWM1_CH_MAPPING                PWM_TIMER2_OPCH
#define TCFG_PWM1_FREQUENCY                 4800
#define TCFG_PWM1_DUTY                      50
#define TCFG_PWM1_DECIMAL_POINT             2

PWM_PLATFORM_DATA_BEGIN(pwm_data1)
    .timer_pwm_port         = TCFG_PWM1_TIMER_PWM_REMAP_IO,
    .mcpwm_port             = TCFG_PWM1_MCPWM_REMAP_IO,
    .pwm_config = {
        .pwm_ch             = TCFG_PWM1_CH_MAPPING,
        .freq               = TCFG_PWM1_FREQUENCY,
        .duty               = TCFG_PWM1_DUTY,
        .point_bit          = TCFG_PWM1_DECIMAL_POINT,
    },
PWM_PLATFORM_DATA_END()

结构体配置示例2——TMR_PWM

// board_develop_AC792XX.h
#define TCFG_PWM1_TIMER_PWM_REMAP_IO        { -1/*PWM2*/, -1/*PWM3*/, IO_PORTA_04/*PWM4*/, -1/*PWM5*/, }
#define TCFG_PWM1_MCPWM_REMAP_IO            {\
                                                -1/*MCPWM0H*/, -1/*MCPWM1H*/, -1/*MCPWM2H*/, -1/*MCPWM3H*/,\
                                                -1/*MCPWM0L*/, -1/*MCPWM1L*/, -1/*MCPWM2L*/, -1/*MCPWM3L*/,\
                                            }
#define TCFG_PWM1_CH_MAPPING                PWM_TIMER4_OPCH
#define TCFG_PWM1_FREQUENCY                 2000000
#define TCFG_PWM1_DUTY                      50
#define TCFG_PWM1_DECIMAL_POINT             1

2.注册设备

REGISTER_DEVICES(device_table) = {    };中添加注册项。

REGISTER_DEVICES(device_table) = {
    { "pwm1",   &pwm_dev_ops,  (void *)&pwm_data1 },
};

2.10.3. PWM驱动使用

1.打开设备

操作:使用dev_open()打开设备。如果open设备时,传入了pwm_platform_data类型的配置参数,那么PWM驱动将按照传入参数的配置进行初始化。如果传入的参数是NULL,则按照Board中REGISTER_DEVICES()注册的配置进行初始化。
参数:arg0-设备名;arg1-pwm_platform_data类型变量的地址。
返回值:打开的设备的句柄。后续其他操作可通过该句柄对该设备进行操作。
注意:PWM驱动不支持dev_open()多次。
struct pwm_platform_data pwm;                   ///< 自定义配置
void *hdl = dev_open("pwm1", (u32)&pwm);        ///< 传入配置参数,则按传入的参数初始化
void *hdl = dev_open("pwm1", NULL);             ///< 传入NULL,则按Board中注册的配置初始化

2.关闭设备

操作:使用dev_close()关闭设备。
参数:PWM设备open时获得的句柄。
返回值:固定为0。
dev_close(hdl);

2.10.4. PWM IOCTL命令

PWM设备的IOCTL命令:

#define PWM_MAGIC                       'P'
#define IOCTL_PWM_GET_DUTY              _IOR(PWM_MAGIC,1,pwm_config_t *)                ///<  获取占空比,通过读取结构体形参中的duty获取,获取的pwm通道占空比是看传参中的pwm_ch,不支持多通道
#define IOCTL_PWM_SET_DUTY              _IOW(PWM_MAGIC,2,pwm_config_t *)                ///<  设置占空比,通过结构体形参中的duty设置,设置的PWM通道占空比是看传参中的pwm_ch,其他传参pwm_config_t *的IOCTL同理。支持多通道
#define IOCTL_PWM_SET_STOP              _IOW(PWM_MAGIC,3,pwm_config_t *)                ///<  停止,支持多通道
#define IOCTL_PWM_SET_RUN               _IOW(PWM_MAGIC,4,pwm_config_t *)                ///<  运行,支持多通道
#define IOCTL_PWM_SET_FORDIRC           _IOW(PWM_MAGIC,5,pwm_config_t *)                ///<  正向,支持多通道
#define IOCTL_PWM_SET_REVDIRC           _IOW(PWM_MAGIC,6,pwm_config_t *)                ///<  反向,支持多通道
#define IOCTL_PWM_SET_DEATH_TIME        _IOW(PWM_MAGIC,7,pwm_config_t *)                ///<  死区时间为系统频率周期时间的整数倍,最大值31,支持多通道
#define IOCTL_PWM_SET_FREQ              _IOW(PWM_MAGIC,8,pwm_config_t *)                ///<  设置频率,支持多通道
#define IOCTL_PWM_SET_REMOV_CHANNEL     _IOW(PWM_MAGIC,9,struct pwm_platform_data *)    ///<  删除通道,与IOCTL_PWM_SET_ADD_CHANNEL成对使用
#define IOCTL_PWM_SET_ADD_CHANNEL       _IOW(PWM_MAGIC,10,struct pwm_platform_data *)   ///<  增加通道,与IOCTL_PWM_SET_REMOV_CHANNEL成对使用

1.IOCTL_PWM_GET_DUTY

功能:获取指定通道的寄存器中实际配的PWM占空比。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。读回的占空比会放在pwm_config -> duty中。
注意:一次只能读一个通道(MCPWM的H和L需要分开读取)。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch = PWMCH3_H;

dev_ioctl(pwm_hdl, IOCTL_PWM_GET_DUTY, (u32)&pwm_cfg);

2.IOCTL_PWM_SET_DUTY

功能:设置指定通道的占空比以及占空比精度。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch    = PWMCH3_H | PWMCH3_L;
pwm_cfg.duty      = 60;
pwm_cfg.point_bit = 2;

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_DUTY, (u32)&pwm_cfg);

3.IOCTL_PWM_SET_STOP

功能:使指定通道的PWM信号停止输出。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch = PWMCH3_H | PWMCH3_L;

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_STOP, (u32)&pwm_cfg);

4.IOCTL_PWM_SET_RUN

功能:使指定通道的PWM信号开始输出。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch = PWMCH3_H | PWMCH3_L;

//早期版本dev_open后,IO直接输出信号。现改为dev_open后需要使用IOCTL_PWM_SET_RUN才输出信号。-2025.12.05
dev_ioctl(pwm_hdl, IOCTL_PWM_SET_RUN, (u32)&pwm_cfg);

5.IOCTL_PWM_SET_FORDIRC

功能:设置指定通道PWM输出信号正向输出(驱动默认正向输出)。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch = PWMCH3_H | PWMCH3_L;

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_FORDIRC, (u32)&pwm_cfg);

6.IOCTL_PWM_SET_REVDIRC

功能:设置指定通道PWM输出信号反向输出。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch = PWMCH3_H | PWMCH3_L;

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_REVDIRC, (u32)&pwm_cfg);

7.IOCTL_PWM_SET_DEATH_TIME

功能:设置指定通道的死区时间。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch = PWMCH3_H | PWMCH3_L;
pwm_cfg.deathtime = 1;

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_DEATH_TIME, (u32)&pwm_cfg);

8.IOCTL_PWM_SET_FREQ

功能:设置指定通道的频率。通过接口所传入的结构体变量中的pwm_ch来指定通道。
参数pwm_config_t结构的变量的地址。
注意:设置频率的同时,传入的参数中dutypoint_bit也需要是目标值。
pwm_config_t pwm_cfg;
pwm_cfg.pwm_ch    = PWMCH3_H | PWMCH3_L;
pwm_cfg.duty      = 60;
pwm_cfg.point_bit = 2;
pwm_cfg.freq      = 3000;

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_FREQ, (u32)&pwm_cfg);

9.IOCTL_PWM_SET_REMOV_CHANNEL

功能:移除通过IOCTL_PWM_SET_ADD_CHANNEL添加的输出通道。
参数struct pwm_platform_data结构的变量的地址。
注意:传入的参数中的pwm_chport需要添加通道时的一致。
struct pwm_platform_data pwm_temp = {
    .mcpwm_port        = {
        -1/*MCPWM0H*/, -1/*MCPWM1H*/, IO_PORTA_08/*MCPWM2H*/, -1/*MCPWM3H*/,
        -1/*MCPWM0L*/, -1/*MCPWM1L*/, IO_PORTA_10/*MCPWM2L*/, -1/*MCPWM3L*/,
    },
    .pwm_config        = {
        .pwm_ch        = PWMCH2_H | PWMCH2_L,
    },
};

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_REMOV_CHANNEL, (u32)&pwm_temp);

10.IOCTL_PWM_SET_ADD_CHANNEL

功能:添加输出通道。
参数struct pwm_platform_data结构的变量的地址。
struct pwm_platform_data pwm_temp = {
    .timer_pwm_port    = {-1/*TMR2*/, -1/*TMR3*/, -1/*TMR4*/, -1/*TMR5*/},
    .mcpwm_port        = {
        -1/*MCPWM0H*/, -1/*MCPWM1H*/, IO_PORTA_08/*MCPWM2H*/, -1/*MCPWM3H*/,
        -1/*MCPWM0L*/, -1/*MCPWM1L*/, IO_PORTA_10/*MCPWM2L*/, -1/*MCPWM3L*/,
    },
    .pwm_config        = {
        .pwm_ch        = PWMCH2_H | PWMCH2_L,
        .freq          = 9600,
        .duty          = 50,
        .point_bit     = 1,
    },
};

dev_ioctl(pwm_hdl, IOCTL_PWM_SET_ADD_CHANNEL, (u32)&pwm_temp);
dev_ioctl(pwm_hdl, IOCTL_PWM_SET_RUN, (u32)&pwm_temp.pwm_config);

2.10.5. 输出频率

目标输出频率

配置目标输出频率的寄存器值计算公式:

R(prd) = F(src) / DIV / F(target) = F(src) / (2 ^ R(psc)) / F(target)

  • R(prd) —— 周期配置寄存器,最大值65535

  • R(psc) —— 分频配置寄存器,范围0 ~ 15

  • DIV ——分频值。DIV =2 ^ R(psc),即DIV范围为{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768}

  • F(src) —— PWM模块时钟源频率,驱动中TMR_PWMMCPWM都固定时钟源为LSB

  • F(target) —— 目标pwm输出频率。

驱动中设置输出频率的程序,其实就是根据频率计算公式找出能够满足R(prd)小于65536最小分频系数R(psc)

为什么需要找最小分频系数R(psc)R(prd)的值除了决定频率外,还影响了占空比精度。R(prd)值越大,占空比精度可以越高,因此R(pwm)在满足小于65535的同时,应该尽量的大,这时R(psc)分频系数就需要最小。详见占空比精度章节。

实际输出频率

由上述公式可知,实际PWM输出频率为:F(out) = F(src) / DIV / R(prd) 。该公式可以看出:

  • 目标频率F(target)必须小F(src)。即PWM初始化的配置参数.freq的值必须小于LSB时钟频率。PWM驱动中判断目标频率大于时钟源频率时,实际输出频率会被配置为与时钟源同频。

  • 实际输出频率F(out)无法保证等于F(target),并且目标频率F(target)约接近时钟源频率F(src),偏差可能越大。驱动只能让实际输出频率F(out)尽量可能接近F(target)。例子:

    • 如果时钟源频率F(src)为48M,目标频率F(target)为5M,那么可得DIV = 1, R(pwm) = 9,实际输出频率F(out)为5.33M,偏差0.33M。

    • 如果时钟源频率F(src)为48M,目标频率F(target)为32M,那么可得DIV = 1, R(pwm) = 1,实际输出频率F(out)为48M,偏差16M。

2.10.6. 占空比及精度

2.10.6.1. 占空比

驱动中占空比计算公式:DUTY = R(pwm) / R(prd)

  • DUTY —— 目标占空比。

  • R(pwm) —— 占空比寄存器

2.10.6.2. 占空比精度

除了初始化参数``.point_bit``外,占空比参数能达到的精度还受R(prd) 的值影响。R(prd)范围为0 ~ 65535,比如:

  • R(prd)值为100,那么占空比精度最高只能为1%

  • R(prd)值为1000,那么占空比精度最高只能为0.1%

  • R(prd)值为10000,那么占空比精度最高能到0.01%

image1

2.10.7. 死区时间

死区时间(Dead Time)在电子和电力系统中是一个重要的参数,它通常指的是在某些开关或转换过程中,为了避免由于元件的延迟或反向恢复特性而引起的短路或不稳定现象,特意设置的一段不进行操作的时间间隔。

AC792N的死区时间:

  • 仅MCPWM有死区时间,且需要一个MCPWM的H和L通道同时启用才有效。

  • 实际死区时间为(deathtime + 1)个时钟源(LSB)周期。

  • 实际死区只能通过dev_ioctl()命令方式配置,dev_open()不对该参数处理。

  • 开启死区时间后,通道的占空比由H通道的参数决定。

  • 如果死区参数为N,一个PWM周期里的2次电平跳变的死区时间为N * T,一个PWM周期总共有2 * N * T的死区时间。

image2

2.10.8. 常见问题说明

  1. 设置PWM的占空比无效。

    PWM占空比精度受到所设置的PWM频率.point_bit参数影响。设置时钟频率越高,能达到的占空比精度越低,因此可能出现微调占空比时,实际输出占空比不变的情况。详见PWM占空比小结。

  2. 设置PWM的频率无效。

    PWM频率无法做到线性调节。实际输出频率取目标频率的近似值,设置时钟频率越高,线性度越低,微调频率时输就可能出现不变的情况。详见PWM输出频率小节。

  3. 使用TMR_PWM出现一开始有输出,后面又没有输出。

    TMR_PWM本身是Timer定时器的pwm输出功能,对应Timer定时器可能被其它地方使用了。

    建议开发者优先使用MCPWM。

  4. MCPWM模块硬件BUG

    AC792N的MCPWM,当R(pwm) = R(prd) - 1时,输出波形的占空比为100%,而正常应该是非100%的占空比。

    该问题对于目标频率较高的影响比较大,降低目标频率可以降低影响。举例:

    • 较高目标频率——时钟源48M,PWM频率0.48M,占空比精度0,对应R(prd) = 100,那么可达到的占空比有[0, 1, 2, …, 97, 98, 100]`,无法达输出99%占空比。

    • 降低目标频率——时钟源48M,PWM频率0.048M,占空比精度1,对应R(prd) = 1000,那么可达到的占空比有[0, 0.1, 0.2, ..., 99.7, 99.8, 100],无法达输出99.9%占空比。虽然仍然有影响,但相比于上面0.48M的例子影响小。

2.10.9. API参考

MCPWMCHx_H

PWMCH0_H
PWMCH1_H
PWMCH2_H
PWMCH3_H
PWMCH4_H
PWMCH5_H
PWMCH6_H
PWMCH7_H

MCPWMCHx_L

PWM_CHL_OFFSET
PWMCH0_L
PWMCH1_L
PWMCH2_L
PWMCH3_L
PWMCH4_L
PWMCH5_L
PWMCH6_L
PWMCH7_L

TIMER PWM 最多四个通道

TIME_PWM_MAX
TIME_PWM_OFFSET
PWM_TIMER2_OPCH
PWM_TIMER3_OPCH
PWM_TIMER4_OPCH
PWM_TIMER5_OPCH

PWM dev_ioctl CMD

PWM_MAGIC
IOCTL_PWM_GET_DUTY

获取占空比,通过读取结构体形参中的duty获取,获取的pwm通道占空比是看传参中的pwm_ch,不支持多通道

IOCTL_PWM_SET_DUTY

设置占空比,通过结构体形参中的duty设置,设置的PWM通道占空比是看传参中的pwm_ch,其他传参pwm_config_t *的IOCTL同理。支持多通道

IOCTL_PWM_SET_STOP

停止,支持多通道

IOCTL_PWM_SET_RUN

运行,支持多通道

IOCTL_PWM_SET_FORDIRC

正向,支持多通道

IOCTL_PWM_SET_REVDIRC

反向,支持多通道

IOCTL_PWM_SET_DEATH_TIME

死区时间为系统频率周期时间的整数倍,最大值31,支持多通道

IOCTL_PWM_SET_FREQ

设置频率,支持多通道

IOCTL_PWM_SET_REMOV_CHANNEL

删除通道,与IOCTL_PWM_SET_ADD_CHANNEL成对使用

IOCTL_PWM_SET_ADD_CHANNEL

增加通道,与IOCTL_PWM_SET_REMOV_CHANNEL成对使用

PWM platform data

const struct device_operations pwm_dev_ops

Defines

MCPWM_MAX_NUM
struct pwm_config_t

Public Members

float duty

用于带2位小数点占空比的PWM,0.00%-100.00%

u8 deathtime

死区时间,最大值31

u8 point_bit

小数点精度:0-2

u32 pwm_ch
u32 freq
struct pwm_platform_data

Public Members

int timer_pwm_port[TIME_PWM_MAX]
int mcpwm_port[MCPWM_MAX_NUM * 2]
pwm_config_t pwm_config
u8 hd_level

IO强驱等级