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_PWM指TIMER模块的PWM输出功能,AC792N总共6个Timer,PWM驱动只支持使用其中4个(注意:这4个TMR中由于SDK的Audio和Video占用了2个TMR,一般开发者仅能使用TMR4和TMR5作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_portTMR_pwm的输出口配置。4个TMR_PWM的出口IO顺序如下:
#define TCFG_PWM1_TIMER_PWM_REMAP_IO { -1/*PWM2*/, -1/*PWM3*/, -1/*PWM4*/, -1/*PWM5*/, }
MCPWM输出口配置
mcpwm_portMCPWM输出出口配置。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()注册的配置进行初始化。pwm_platform_data类型变量的地址。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()关闭设备。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_ch来指定通道。pwm_config_t结构的变量的地址。读回的占空比会放在pwm_config -> duty中。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_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_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_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_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结构的变量的地址。duty和point_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_ch和port需要添加通道时的一致。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_PWM和MCPWM都固定时钟源为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%

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的死区时间。

2.10.8. 常见问题说明
设置PWM的占空比无效。
PWM占空比精度受到所设置的
PWM频率和.point_bit参数影响。设置时钟频率越高,能达到的占空比精度越低,因此可能出现微调占空比时,实际输出占空比不变的情况。详见PWM占空比小结。设置PWM的频率无效。
PWM频率无法做到线性调节。实际输出频率取目标频率的近似值,设置时钟频率越高,线性度越低,微调频率时输就可能出现不变的情况。详见PWM输出频率小节。
使用TMR_PWM出现一开始有输出,后面又没有输出。
TMR_PWM本身是
Timer定时器的pwm输出功能,对应Timer定时器可能被其它地方使用了。建议开发者优先使用MCPWM。
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_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