MCPWM 与电机控制
AC82N SDK 的 MCPWM(Motor Control PWM)外设驱动,用于电机控制场景下的 PWM 生成:支持多通道定时器、边沿/中心对齐、互补输出、可编程死区、引脚级故障保护(FPIN)与溢出/借位中断,并对外提供统一的配置、启停、调频、调占空比 API。
Purpose and Scope
本页完整介绍 AC82N 平台上 MCPWM 外设驱动(sdk/cpu/cd09/mcpwm.c、sdk/include_lib/driver/cpu/cd09/asm/mcpwm.h)及其配套演示(sdk/cpu/demo/mcpwm_demo.c),内容包括:
- MCPWM 硬件寄存器模型与位域定义;
- 驱动资源管理(实例表、自旋锁、回调表)与初始化/去初始化流程;
- 频率、占空比(百分比与计数两种方式)的配置算法;
- 多通道同步启动(
mcpwm_mux_start)与相位控制; - 故障保护(FPIN)检测、自动关断与恢复机制;
- 溢出/借位中断与用户回调的注册方式;
- 配置选项、API 参考、故障模式与并发注意事项。
本页聚焦 MCPWM 外设本身。GPIO 复用配置、时钟树(clk_get("mcpwm"))等通用能力属于各自页面的范畴,此处仅在用到时引用说明;电机控制算法(如 FOC、六步换相)不在本 SDK 源码范围内,因此也不在本页展开。
概述
MCPWM 是专为电机/功率变换应用设计的高级 PWM 外设。与普通定时器 PWM 相比,它把电机控制最常见的需求做成了硬件特性:
- 多定时器多通道:片上共有 4 个 PWM 通道(
MCPWM_CH0~MCPWM_CH3),每个通道绑定一个 16 位定时器(TMR0~TMR3),驱动层最多可同时管理 8 个配置实例(MCPWM_NUM_MAX); - 对齐方式可选:边沿对齐(递增计数)或中心对齐(递增-递减计数),中心对齐可显著降低电机电流纹波,是 BLDC/PMSM 驱动的常用模式;
- 互补输出 + 可编程死区:每个通道的 H/L 两引脚可输出互补波形,死区时间由
dtckps/dtpr决定,避免半桥上下管直通; - 硬件故障保护:故障检测引脚(FPIN)支持 IO、比较器等输入源,触发后硬件自动关闭对应通道的 PWM 输出(
PWM_EN/TMR_EN),无需软件干预即可保护功率级; - 中断完备:定时器溢出(OF)/借位(UF)中断可用于生成 2 倍载波频率的同步点,配合
MCPWM_TMR_IDX中断实现中点采样。
驱动层(mcpwm.c)在这些硬件能力之上提供了一套“配置结构体 → 实例 id”的接口风格:应用先填充 struct mcpwm_config,调用 mcpwm_init() 获得实例 id,之后所有操作(启停、调频、调占空比)都通过 id 进行,与通道号解耦。
架构
flowchart TD
subgraph sg_App["应用层"]
App["电机控制应用<br/>(mcpwm_demo.c)"]
end
subgraph sg_Driver["MCPWM 驱动层 (mcpwm.c)"]
Init["mcpwm_init / deinit"]
Ctl["mcpwm_start / pause / resume<br/>mcpwm_mux_start"]
Freq["mcpwm_set_frequency"]
Duty["mcpwm_set_duty / set_duty_by_cnt"]
FPIN["mcpwm_fpin_cb (中断)<br/>mcpwm_fpnd_clr"]
Info["mcpwm_info[8] 实例表<br/>mcpwm_lock 自旋锁"]
end
subgraph sg_Reg["MCPWM 硬件寄存器 (JL_MCPWM)"]
TMR["TMRx_CON / CNT / PR<br/>(4×16bit 定时器)"]
CH["CHx_CON0 / CON1 / CMPH / CMPL"]
FCON["FPIN_CON (滤波/边沿)"]
MCON["MCPWM_CON0 (TMR_EN / PWM_EN)"]
end
subgraph sg_Periph["外设与引脚"]
GPIO["GPIO 复用<br/>PORT_FUNC_MCPWM0_H/L"]
IRQ["IRQ_MCPWM_CHX_IDX<br/>IRQ_MCPWM_TMR_IDX"]
FPIN_SRC["故障源<br/>IO_PORTC_11 / OPA / DAC 比较器"]
end
App --> Init
App --> Ctl
App --> Freq
App --> Duty
Init --> Info
Ctl --> Info
Freq --> Info
Duty --> Info
Info --> TMR
Info --> CH
Info --> FCON
Info --> MCON
CH --> GPIO
FCON --> FPIN_SRC
FPIN --> IRQ
IRQ --> Info
FPIN --> MCON
架构说明:应用层只与驱动 API 交互,通过 struct mcpwm_config 描述需求;驱动内部维护一个 8 槽位的 mcpwm_info[] 实例表(每个槽位保存通道寄存器指针、定时器寄存器指针与配置副本),所有寄存器写操作通过 mcpwm_lock 自旋锁串行化;硬件层 JL_MCPWM 寄存器组直接控制定时器、比较器与引脚输出;故障保护路径是硬件自动关断 + 软件中断回调(mcpwm_fpin_cb)的双保险设计,恢复时需要应用显式调用 mcpwm_fpnd_clr()。
硬件寄存器模型
MCPWM 的寄存器由四类构成,驱动通过 MCPWM_TMR_BASE_ADDR/MCPWM_TMR_OFFSET 与 MCPWM_CH_BASE_ADDR/MCPWM_CH_OFFSET 常量按通道偏移寻址(见 mcpwm.h):
定时器寄存器 TMRx_CON / TMRx_CNT / TMRx_PR
| 位域 | 宏 | 说明 |
|---|---|---|
| bit15 | MCPWM_TMR_INCF | 递增计数标志 |
| bit13 | MCPWM_TMR_UFPND | 借位(下溢)中断挂起位 |
| bit12 | MCPWM_TMR_OFPN | 溢出中断挂起位 |
| bit11 | MCPWM_TMR_UFCLR | 借位中断清除位 |
| bit10 | MCPWM_TMR_OFCLR | 溢出中断清除位 |
| bit9 | MCPWM_TMR_UFIE | 借位中断使能 |
| bit8 | MCPWM_TMR_OFTE | 溢出中断使能 |
| bit7 | MCPWM_TMR_CKSRC | 时钟源选择 |
| bit6-3 | MCPWM_TMR_CKPS (4bit) | 时钟分频 2^i |
| bit1-0 | MCPWM_TMR_MODE (2bit) | 计数模式:0b01 递增(边沿对齐)、0b10 递增-递减(中心对齐) |
通道寄存器 CHx_CON0 / CHx_CON1 / CHx_CMPH / CHx_CMPL
CHx_CON0 控制输出极性、使能与死区:
| 位域 | 宏 | 说明 |
|---|---|---|
| bit15-12 | MCPWM_CH_DTCKPS (4bit) | 死区时钟分频,死区时间 = Tsys / (2^dtckps) × (dtpr+1) |
| bit11-7 | MCPWM_CH_DTPR (5bit) | 死区预分频值 0~31 |
| bit6 | MCPWM_CH_DTEN | 死区使能 |
| bit5 | MCPWM_CH_L_INV | L 引脚输出取反(互补模式核心) |
| bit4 | MCPWM_CH_H_INV | H 引脚输出取反 |
| bit3 | MCPWM_CH_L_EN | L 引脚输出使能 |
| bit2 | MCPWM_CH_H_EN | H 引脚输出使能 |
| bit1-0 | MCPWM_CH_CMP_LD (2bit) | 比较值装载方式 |
CHx_CON1 控制故障保护与中断:
| 位域 | 宏 | 说明 |
|---|---|---|
| bit15 | MCPWM_CH_FPND | 故障保护挂起标志(中断中轮询) |
| bit14 | MCPWM_CH_FCLR | 故障保护清除 |
| bit11 | MCPWM_CH_INTEN | 故障保护输入中断使能 |
| bit10-8 | MCPWM_CH_TMRSEL (3bit) | 选择绑定的 MCTMR |
| bit4 | MCPWM_CH_FPINEN | 故障引脚使能 |
| bit3 | MCPWM_CH_FPINAUTO | 故障自动恢复/自动关断模式 |
| bit2-0 | MCPWM_CH_FPINSEL (3bit) | 故障源选择(IO/OPA/DAC 比较器) |
全局控制寄存器 FPIN_CON 与 MCPWM_CON0
FPIN_CON:bit16-23 每通道故障触发边沿(MCPWM_FPIN_EDGE),bit8-15 每通道滤波使能(MCPWM_FPIN_FLT_EN),bit0-4 全局滤波预分频(MCPWM_FPIN_FLT_PR,滤波时间 = 16×64/lsb_clk 秒);MCPWM_CON0:bit8-15 每通道定时器使能(MCPWM_TMR_EN),bit0-7 每通道 PWM 输出使能(MCPWM_PWM_EN)。注意 PWM 使能与定时器使能是分开的位,这正是多通道同步启动(先开 PWM、最后统一开定时器)能够实现无相位差的基础。
寄存器位域定义全部来源于 mcpwm.h。
驱动资源管理与实例生命周期
实例表与锁
驱动使用一个静态数组保存所有已初始化实例,每个实例由 mcpwm_init() 动态分配(malloc),并由 mcpwm_deinit() 释放:
#define MCPWM_NUM_MAX 8
static struct mcpwm_info_t *mcpwm_info[MCPWM_NUM_MAX] = {NULL};
DEFINE_SPINLOCK(mcpwm_lock);
mcpwm_info[]是实例 id 与配置的映射表:应用拿到的 id 是数组下标(0~7),与硬件通道号(0~3)无关;通道号保存在cfg.ch中,同一通道可以被不同 id 引用(如 demo 中 id0~id2 分别绑定 CH0~CH2)。mcpwm_lock自旋锁保护所有对JL_MCPWM寄存器的读-改-写序列。MCPWM 寄存器可能被中断上下文(mcpwm_fpin_cb)访问,因此使用 spinlock 而非普通互斥量,避免在中断中睡眠。- 寄存器访问通过偏移常量计算:
mcpwm_get_timerx_reg(ch)返回MCPWM_TMR_BASE_ADDR + ch*MCPWM_TMR_OFFSET,mcpwm_get_chx_reg(ch)同理;两者都带ASSERT(ch < MCPWM_CH_MAX)越界保护,且标记为AT(.volatile_ram_code)放入易失 RAM 以加快中断路径访问(见 mcpwm.c)。
初始化:mcpwm_init()
int mcpwm_init(struct mcpwm_config *mcpwm_cfg)
流程(mcpwm.c):
- 遍历
mcpwm_info[0..7]找到第一个空槽位,malloc分配mcpwm_info_t;若 8 个槽位全满则返回-1; - 按配置使能定时器溢出中断(
MCPWM_TMR_OFTE)与借位中断(MCPWM_TMR_UFIE),并清零tmr_cnt; - 保存通道寄存器指针、定时器寄存器指针,并将调用者传入的
struct mcpwm_config完整memcpy到实例中(后续start/resume会重新装载该副本); - 返回实例 id。
注意 mcpwm_init() 此时尚未配置引脚与比较值——真正的寄存器装载发生在 mcpwm_start()/mcpwm_mux_start() 调用的 mcpwm_cfg_info_load() 中。这种"先登记、后装载"的设计允许应用在初始化后、启动前继续修改配置,也使得 mcpwm_resume() 只需重新调用 mcpwm_start() 即可完整恢复波形。
配置装载:mcpwm_cfg_info_load()
这是把 struct mcpwm_config 翻译成寄存器值的核心函数(mcpwm.c):
static void mcpwm_cfg_info_load(int mcpwm_cfg_id)
{
int id = mcpwm_cfg_id;
u32 ch = (u32)mcpwm_info[id]->cfg.ch;
MCPWM_CHx_REG *ch_reg = mcpwm_get_chx_reg(ch);
mcpwm_set_frequency(id, mcpwm_info[id]->cfg.aligned_mode, mcpwm_info[id]->cfg.frequency);
mcpwm_set_duty(id, mcpwm_info[id]->cfg.duty);
u16 ch_con0 = 0;
u16 ch_con1 = 0;
if (mcpwm_info[id]->cfg.dten) {
ch_con0 |= BIT(MCPWM_CH_DTEN) | mcpwm_info[id]->cfg.dtpr << MCPWM_CH_DTPR
| mcpwm_info[id]->cfg.dtckps << MCPWM_CH_DTCKPS;
}
if (mcpwm_info[id]->cfg.complementary_en && (!mcpwm_info[id]->cfg.dten)) {
// 互补模式:通过 L_INV 让 L 引脚输出 H 的反相
ch_con0 &= ~(BIT(MCPWM_CH_L_INV) | BIT(MCPWM_CH_H_INV));
ch_con0 |= BIT(MCPWM_CH_L_INV);
} else {
ch_con0 &= ~(BIT(MCPWM_CH_L_INV) | BIT(MCPWM_CH_H_INV));
}
...
// H/L 引脚复用
if (mcpwm_info[id]->cfg.h_pin < IO_MAX_NUM) {
ch_con0 |= BIT(MCPWM_CH_H_EN);
gpio_set_mode(IO_PORT_SPILT(mcpwm_info[id]->cfg.h_pin), PORT_OUTPUT_LOW);
gpio_set_function(IO_PORT_SPILT(mcpwm_info[id]->cfg.h_pin), PORT_FUNC_MCPWM0_H + 2 * ch);
}
if (mcpwm_info[id]->cfg.l_pin < IO_MAX_NUM) {
ch_con0 |= BIT(MCPWM_CH_L_EN);
gpio_set_mode(IO_PORT_SPILT(mcpwm_info[id]->cfg.l_pin), PORT_OUTPUT_LOW);
gpio_set_function(IO_PORT_SPILT(mcpwm_info[id]->cfg.l_pin), PORT_FUNC_MCPWM0_L + 2 * ch);
}
...
}
要点:
- 互补 vs 同步:互补模式通过置位
L_INV实现,L 引脚输出 H 的反相波形;此时若同时使能死区,硬件会在两路沿之间插入死区时间。注意源码中互补(L_INV)与死区是两条独立路径——死区使能时不再额外置L_INV(硬件死区单元自动处理互补关系),这反映了"硬件死区单元 + 反相输出"两种互补实现方式不可叠加的设计约束; - 引脚复用:H 引脚复用功能号为
PORT_FUNC_MCPWM0_H + 2*ch,L 引脚为PORT_FUNC_MCPWM0_L + 2*ch,保证通道与引脚功能一一对应;l_pin填0xff则只输出 H 路; - 故障保护使能:当
detect_port < MCPWM_FPIN_NONE时,依据edge选择上升/下降沿触发,对IO_PORTC_11源自动配置上拉(下降沿)/下拉(上升沿)输入,设置滤波(FLT_EN+FLT_PR=0b111111),注册IRQ_MCPWM_CHX_IDX中断,并写CHx_CON1的FCLR|INTEN|FPINEN|FPINAUTO与FPINSEL; - 所有寄存器写入在
spin_lock/spin_unlock临界区内完成,CMP_LD=0b11保证比较值装载方式一致。
启动 / 暂停 / 恢复 / 去初始化
| API | 行为 |
|---|---|
mcpwm_start(id) | 装载配置 → 置位 TMR_EN 与 PWM_EN → 清零 tmr_cnt → dump 寄存器(mcpwm.c) |
mcpwm_mux_start(mask) | 对 mask 中每个位对应的 id:装载配置、置位 PWM_EN、清零 tmr_cnt;最后统一置位所有 TMR_EN,实现多通道零相位差同步启动(mcpwm.c) |
mcpwm_pause(id) | 清除 PWM_EN、TMR_EN、INTEN,并 gpio_disable_function 断开引脚复用(mcpwm.c) |
mcpwm_resume(id) | 等价于重新 mcpwm_start(id)(mcpwm.c) |
mcpwm_deinit(id) | 关闭 PWM/定时器、断开引脚、故障引脚恢复 PORT_HIGHZ、写 FCLR 清故障、free 实例并置空槽位(mcpwm.c) |
设计意图:TMR_EN 与 PWM_EN 分离是电机控制的关键——所有通道先各自配置并打开 PWM 输出,最后同时启动定时器,确保各相载波相位一致,避免启动瞬间产生浪涌电流。而 pause 与 deinit 的区别在于:pause 保留实例(id 仍有效,可 resume),deinit 彻底释放资源。
频率与占空比配置算法
mcpwm_set_frequency()
void mcpwm_set_frequency(int mcpwm_cfg_id, mcpwm_aligned_mode_type align, u32 frequency)
算法(mcpwm.c):
- 以 MCPWM 时钟为基准,从分频 2^0 开始逐级尝试,找到使
frequency >= clk/(65536×2^i)的最小分频指数 i(i 上限 15),写入TMR_CKPS;这是为了让 16 位tmr_pr不溢出且分辨率最优的自动分频策略; - 计算周期寄存器值:
- 中心对齐:
tmr_pr = clk/(2^i) / (frequency×2) - 1(一个 PWM 周期 = 一次递增 + 一次递减); - 边沿对齐:
tmr_pr = clk/(2^i) / frequency - 1;
- 中心对齐:
- 按对齐方式写
TMR_MODE(0b01递增 /0b10递增-递减); - 临界区内写
tmr_con与tmr_pr。
frequency == 0 时 tmr_pr = 0,用于停止计数。
mcpwm_set_duty() 与 mcpwm_set_duty_by_cnt()
void mcpwm_set_duty(int mcpwm_cfg_id, u16 duty) // duty: 0~10000 对应 0%~100%
void mcpwm_set_duty_by_cnt(int mcpwm_cfg_id, u16 duty) // duty: 直接作为比较计数值
mcpwm_set_duty(mcpwm.c):ch_cmpl = tmr_pr * duty / 10000,然后ch_cmph = ch_cmpl(H/L 使用同一比较值)。边界处理:duty >= 10000时强制tmr_cnt = 0(输出恒高),duty == 0时强制tmr_cnt = ch_cmpl(输出恒低)——利用比较值等于/大于周期值时的电平钳位行为;mcpwm_set_duty_by_cnt(mcpwm.c):直接把duty写入ch_cmph/ch_cmpl,若duty >= tmr_pr则钳位到tmr_pr。该函数标记为AT(.volatile_ram_code),适合在实时控制环路(如电流环 ISR)中高频调用。
两个函数内部都先 asm("csync") 同步流水线再读写寄存器,并在自旋锁保护下同时写 ch_cmph、ch_cmpl 与 tmr_con,保证比较值成对更新,避免中间状态导致占空比跳变。
故障保护机制(FPIN)
故障保护是 MCPWM 对电机驱动最重要的安全特性:当检测到过流、过压等故障信号时,硬件自动关断对应通道的 PWM 与定时器输出,不需要等待软件响应。
触发与自动关断
中断服务程序 mcpwm_fpin_cb() 轮询 4 个通道的 FPND 挂起位(mcpwm.c):
___interrupt
static void mcpwm_fpin_cb()
{
MCPWM_CHx_REG *ch_reg = NULL;
for (u8 ch = 0; ch < MCPWM_CH_MAX; ch++) {
ch_reg = mcpwm_get_chx_reg(ch);
if (ch_reg->ch_con1 & BIT(MCPWM_CH_FPND)) {
JL_MCPWM->MCPWM_CON0 &= ~BIT(ch + MCPWM_CON_PWM_EN);
JL_MCPWM->MCPWM_CON0 &= ~BIT(ch + MCPWM_CON_TMR_EN);
if (ch_reg->ch_con1 & BIT(MCPWM_CH_INTEN)) {
ch_reg->ch_con1 &= ~BIT(MCPWM_CH_INTEN); // 关闭故障保护输入IE使能
mcpwm_cb(ch); // 调用用户回调
}
}
}
}
流程要点:
- 硬件检测到故障信号(经过
FPIN_CON的边沿与滤波配置)后置位CHx_CON1.FPND,同时硬件本身已把输出拉安全电平; - 中断进入后软件再清除
PWM_EN/TMR_EN,这是与硬件关断互补的第二重保险,确保定时器停止、输出彻底禁用; - 若
INTEN使能,则先关闭中断使能(防止故障持续期间反复触发中断风暴),再调用注册的用户回调mcpwm_cb_table[ch],回调收到故障通道号; mcpwm_cb()内部调用用户回调后执行asm("csync")确保中断返回前写操作生效。
手动恢复:mcpwm_fpnd_clr()
故障后系统进入锁定状态,必须由软件显式恢复(mcpwm.c):
void mcpwm_fpnd_clr(u32 ch)
{
MCPWM_CHx_REG *ch_reg = mcpwm_get_chx_reg(ch);
u32 mcpwm_con = JL_MCPWM->MCPWM_CON0;
u16 ch_con1 = ch_reg->ch_con1;
asm("csync");
mcpwm_con |= BIT(ch + MCPWM_CON_TMR_EN); // 重新使能定时器
mcpwm_con |= BIT(ch + MCPWM_CON_PWM_EN); // 重新使能PWM输出
ch_con1 |= BIT(MCPWM_CH_FCLR); // 清除故障挂起
spin_lock(&mcpwm_lock);
JL_MCPWM->MCPWM_CON0 = mcpwm_con;
ch_reg->ch_con1 = ch_con1;
spin_unlock(&mcpwm_lock);
}
恢复顺序是先恢复 TMR_EN/PWM_EN 再写 FCLR 清除挂起位。应用应在确认故障源消除(例如过流信号已经撤除)后再调用,否则可能立即再次触发故障。
设计意图:采用"硬件自动关断 + 软件显式恢复"的模型,而不是硬件自动恢复(FPINAUTO 位可用于自动模式),是为了让应用层对"何时允许电机重新上电"拥有最终决定权——这在电机驱动安全规范中是必须的:任何自动重启都可能导致设备损坏或人身伤害。
中断与回调体系
MCPWM 涉及两类中断:
| 中断号 | 用途 | 注册方式 |
|---|---|---|
IRQ_MCPWM_CHX_IDX | 故障保护输入中断 | 驱动内部 request_irq(IRQ_MCPWM_CHX_IDX, cfg.irq_priority, mcpwm_fpin_cb, 0),优先级取自配置 irq_priority(默认 1) |
IRQ_MCPWM_TMR_IDX | 定时器溢出/借位中断 | 由应用自行 request_irq(IRQ_MCPWM_TMR_IDX, 0, cb, 0),配合 ofie/ufie 配置 |
定时器中断的典型用途在 demo 中展示(mcpwm_demo.c):
___interrupt
void mcpwm_timeout_callback(void)
{
MCPWM_TIMERx_REG *timer_reg = NULL;
timer_reg = (MCPWM_TIMERx_REG *)(MCPWM_TMR_BASE_ADDR);
JL_PORTC->DIR &= ~BIT(7);
if (timer_reg->tmr_con & BIT(MCPWM_TMR_OFPN)) {
//溢出中断发生在H端低电平中点
timer_reg->tmr_con |= BIT(MCPWM_TMR_OFCLR);
JL_PORTC->OUT ^= BIT(7);
}
if (timer_reg->tmr_con & BIT(MCPWM_TMR_UFPND)) {
//借位中断发生在H端高电平中点
timer_reg->tmr_con |= BIT(MCPWM_TMR_UFCLR);
JL_PORTC->OUT ^= BIT(7);
}
}
注释揭示了硬件时序特性:中心对齐模式下,溢出中断落在 H 端低电平中点,借位中断落在 H 端高电平中点——这正是电流采样/中点对齐的理想时刻,demo 借此在 PC7 上翻转电平产生 2 倍载波频率的方波,验证中断同步点。
核心流程
sequenceDiagram
participant App as 应用 (demo)
participant Drv as mcpwm.c 驱动
participant HW as JL_MCPWM 硬件
participant GPIO as GPIO/引脚
App->>Drv: mcpwm_init(&cfg) ×3
Drv->>Drv: 分配 mcpwm_info[i]、保存 cfg、使能 OF/UF 中断
Drv-->>App: 返回 id0/id1/id2
App->>Drv: mcpwm_mux_start(BIT0|BIT1|BIT2)
Drv->>Drv: 逐个 cfg_info_load(频率/占空比/死区/互补/引脚)
Drv->>GPIO: gpio_set_function(MCPWM0_H/L 复用)
Drv->>HW: 置位各通道 PWM_EN
Drv->>HW: 统一置位 TMR_EN(零相位差启动)
loop 运行中
HW->>HW: 定时器递增/递减计数
HW-->>Drv: 溢出/借位中断 (IRQ_MCPWM_TMR_IDX)
Drv-->>App: 用户回调(中点采样/电平翻转)
App->>Drv: mcpwm_set_duty(id, 新占空比)
Drv->>HW: 成对更新 CMPH/CMPL
end
Note over HW: 故障信号到达 FPIN
HW->>HW: 硬件自动关断 PWM 输出
HW-->>Drv: FPND 置位 → IRQ_MCPWM_CHX_IDX
Drv->>HW: 清除 PWM_EN/TMR_EN,关 INTEN
Drv-->>App: irq_cb(ch) 故障回调
App->>Drv: mcpwm_fpnd_clr(ch)(确认故障消除后)
Drv->>HW: 恢复 TMR_EN/PWM_EN,写 FCLR
流程解读:初始化只登记配置;mcpwm_mux_start 是实际生效点,通过"先开 PWM、后开定时器"实现多相无相位差启动;运行期占空比更新走自旋锁保护的成对寄存器写入;故障路径完全独立于正常路径——硬件先行关断,软件 ISR 补刀并通知应用,恢复必须由应用显式触发。
使用示例
以下示例均摘自 SDK 自带演示程序 mcpwm_demo.c,展示了完整的"配置 → 初始化 → 启动 → 动态调参"流程。
1. 配置三个通道(互补+死区 / 同步输出)
struct mcpwm_config usr_mcpwm_cfg0 = {
.ch = MCPWM_CH0, // 通道号
.aligned_mode = MCPWM_CENTER_ALIGNED, // 中心对齐
.frequency = 1000, // 1KHz
.duty = 5000, // 占空比50%
.h_pin = IO_PORTC_00, // H引脚
.l_pin = IO_PORTC_01, // L引脚,不需要就填0xff
.complementary_en = 1, // 0: 同步, 1: 互补
.detect_port = MCPWM_FPIN_IO_PORTC_11, // 故障保护检测IO
.edge = MCPWM_EDGE_FAILL, // 下降沿触发
.irq_cb = usr_mcpwm_detect_callback, // 故障保护回调
.irq_priority = 1, // 中断优先级
.dten = 1, // 死区使能
.dtpr = 0, // 死区时间=Tsys/(2^dtckps)*(dtpr+1)
.dtckps = 4, // 死区时钟分频
.ofie = 1, // 溢出中断
.ufie = 1, // 借位中断
};
CH0 采用中心对齐 + 互补输出 + 死区(典型的半桥/全桥驱动配置);CH1/CH2 为边沿对齐同步输出。三个配置结构体随后分别调用 mcpwm_init() 获得 id。
2. 初始化并同步启动
// 初始化分配id
usr_id0 = mcpwm_init(&usr_mcpwm_cfg0);
usr_id1 = mcpwm_init(&usr_mcpwm_cfg1);
usr_id2 = mcpwm_init(&usr_mcpwm_cfg2);
// 同时启动 三通道无相位差
mcpwm_mux_start(BIT(usr_id0) | BIT(usr_id1) | BIT(usr_id2));
若改为逐个 mcpwm_start(id0); mcpwm_start(id1); mcpwm_start(id2); 顺序启动,则三通道之间存在启动时序造成的相位差(demo 中 #if 0 分支的注释明确说明了这一点)。
3. 动态调整频率与占空比
// 频率配置接口测试
mcpwm_set_frequency(usr_id0, MCPWM_EDGE_ALIGNED, 2000); // 2K
mcpwm_set_frequency(usr_id1, MCPWM_EDGE_ALIGNED, 4000); // 4K
mcpwm_set_frequency(usr_id2, MCPWM_EDGE_ALIGNED, 8000); // 8K
// pwm cnt计数配置占空比(20% / 40% / 80%)
u32 id0_loop_cntmax = clk_get("mcpwm") / 2000;
u32 id1_loop_cntmax = clk_get("mcpwm") / 4000;
u32 id2_loop_cntmax = clk_get("mcpwm") / 8000;
mcpwm_set_duty_by_cnt(usr_id0, id0_loop_cntmax / 10 * 2);
mcpwm_set_duty_by_cnt(usr_id1, id1_loop_cntmax / 10 * 4);
mcpwm_set_duty_by_cnt(usr_id2, id2_loop_cntmax / 10 * 8);
clk_get("mcpwm") 读取 MCPWM 模块时钟,除以频率得到每周期计数值,再按比例计算占空比计数值——这是实时控制环路中推荐的用法(set_duty_by_cnt 位于易失 RAM,调用开销小)。
4. 故障保护回调与停止
void usr_mcpwm_detect_callback(u32 ch)
{
printf("usr detect ch %d\n", ch);
mcpwm_fpnd_clr(ch); //检测到故障,手动清PND恢复
}
void mcpwm_test_demo_stop(void)
{
mcpwm_deinit(usr_id0);
mcpwm_deinit(usr_id1);
mcpwm_deinit(usr_id2);
unrequest_irq(IRQ_MCPWM_TMR_IDX);
}
配置选项
struct mcpwm_config(定义见 mcpwm.h):
| 字段 | 类型 | 默认/典型值 | 说明 |
|---|---|---|---|
ch | mcpwm_ch_type | MCPWM_CH0 | 通道号 0~3 |
aligned_mode | mcpwm_aligned_mode_type | MCPWM_CENTER_ALIGNED | 边沿对齐或中心对齐 |
frequency | u32 | 1000 | 初始 PWM 频率(Hz),所有通道可独立 |
duty | u16 | 5000 | 初始占空比,0~10000 对应 0%~100%;互补模式以 H 引脚波形为准 |
h_pin | u16 | IO_PORTC_00 | 通道 H 输出引脚,任意 IO |
l_pin | u16 | IO_PORTC_01 | 通道 L 输出引脚,不需要填 0xff |
complementary_en | u8 | 1 | 0: H/L 同步输出;1: 互补输出(占空比体现于 H 引脚) |
detect_port | mcpwm_fpin | MCPWM_FPIN_IO_PORTC_11 | 故障源:IO_PORTC_11 / OPA 比较器 / DAC 比较器 / NONE |
edge | mcpwm_edge | MCPWM_EDGE_FAILL | 故障触发边沿:下降沿 / 上升沿 / 0xff 忽略 |
irq_cb | mcpwm_detect_irq_callback | NULL | 故障中断回调 void (*)(u32 ch) |
irq_priority | u16 | 1 | 故障中断优先级 |
dten | u8 | 1 | 死区使能 |
dtpr | u8 | 0 | 死区时间 = Tsys/(2^dtckps)×(dtpr+1),0~31 |
dtckps | u8 | 4 | 死区时钟分频,0~15 |
ufie | u8 | 1 | 借位(下溢)中断使能 |
ofie | u8 | 1 | 溢出中断使能 |
故障源枚举(mcpwm.h):MCPWM_FPIN_IO_PORTC_11(IO 直接检测)、MCPWM_FPIN_HADC_OPA_CMP(OPA 比较器,需先初始化 OPA)、MCPWM_FPIN_DAC_PA_CMP(PA 比较器,需先初始化 PA)、MCPWM_FPIN_NONE(不使能故障保护)。
API 参考
所有接口声明见 mcpwm.h。
int mcpwm_init(struct mcpwm_config *mcpwm_cfg)
- 功能:登记配置并分配实例 id。
- 参数:
mcpwm_cfg— 初始化配置结构体指针(内容会被拷贝保存)。 - 返回:实例 id(0~7);全部槽位占用时返回
-1;malloc失败触发 ASSERT。
void mcpwm_deinit(int mcpwm_cfg_id)
- 功能:关闭 PWM/定时器、断开引脚复用、释放实例内存。
- 参数:
mcpwm_cfg_id— 实例 id。 - 注意:id 无效时 ASSERT。
void mcpwm_start(int mcpwm_cfg_id) / void mcpwm_resume(int mcpwm_cfg_id)
- 功能:装载配置并启动(
resume等价于start)。 - 参数:
mcpwm_cfg_id— 实例 id。
void mcpwm_pause(int mcpwm_cfg_id)
- 功能:暂停输出并断开引脚复用,实例保留可恢复。
void mcpwm_mux_start(u8 mcpwm_cfg_id_mux)
- 功能:按位掩码同时启动多个通道,所有定时器统一使能,实现零相位差。
- 参数:
mcpwm_cfg_id_mux— 实例 id 的位掩码,如BIT(0)|BIT(1)。
void mcpwm_set_frequency(int mcpwm_cfg_id, mcpwm_aligned_mode_type align, u32 frequency)
- 功能:设置频率并选择对齐方式(自动分频,16 位周期寄存器)。
- 参数:
align—MCPWM_EDGE_ALIGNED/MCPWM_CENTER_ALIGNED;frequency— Hz,0时周期寄存器清零。 - 注意:调用后需重新设置占空比(频率改变会改变
tmr_pr,而比较值是相对tmr_pr的)。
void mcpwm_set_duty(int mcpwm_cfg_id, u16 duty)
- 功能:按百分比设置占空比。
- 参数:
duty— 0~10000 对应 0%~100%;>=10000恒高、==0恒低。
void mcpwm_set_duty_by_cnt(int mcpwm_cfg_id, u16 duty)
- 功能:按计数值直接设置占空比(位于易失 RAM,适合 ISR 高频调用)。
- 参数:
duty— 比较计数值,超过tmr_pr时钳位。
void mcpwm_fpnd_clr(u32 ch)
- 功能:清除故障挂起并恢复该通道 PWM 输出。
- 参数:
ch— 通道号 0~3(注意是硬件通道号,不是实例 id)。 - 注意:仅在故障源消除后调用,否则会再次触发故障。
故障模式、边界情况与并发
参数与资源边界
- ASSERT 校验:
mcpwm_get_timerx_reg/mcpwm_get_chx_reg校验ch < MCPWM_CH_MAX;mcpwm_init校验malloc成功;启用故障保护时校验edge != MCPWM_EDGE_DEFAULT。错误配置在调试期即被断言捕获,而不是静默运行产生错误波形; - 实例表满:8 个实例全部占用时
mcpwm_init返回-1,调用方应处理该返回值; - 引脚边界:
h_pin/l_pin以< IO_MAX_NUM判断是否有效,无效则跳过对应输出使能与复用配置——这允许单路输出(L 填0xff)而不报错; - 占空比钳位:
set_duty_by_cnt将超过tmr_pr的计数值钳位;set_duty对 100% 与 0% 采用强制计数停摆的方式实现恒高/恒低。
故障锁存与恢复竞态
- 故障触发后
FPND锁存、INTEN被 ISR 关闭,不会自动恢复。若应用在故障源未消除时调用mcpwm_fpnd_clr,通道会立即重新进入故障状态并再次触发中断——恢复逻辑应由上层安全策略把关; mcpwm_fpin_cb在中断中先关INTEN再调用用户回调,防止用户回调执行期间故障位持续置位导致的中断风暴;- 用户回调内不应执行耗时操作(printf、长延时等),避免阻塞其他中断;demo 中回调只做打印与
fpnd_clr。
并发与原子性
- 所有对
JL_MCPWM寄存器的读-改-写都在mcpwm_lock自旋锁临界区内完成,且关键写序列(如ch_cmph/ch_cmpl成对更新)被封装为单次临界区,防止应用线程与 ISR 并发写寄存器导致比较值撕裂; - 中断上下文(
mcpwm_fpin_cb)也会获取自旋锁,因此锁内代码必须短小(仅寄存器操作,无 printf),否则会拉长中断延迟; asm("csync")同步指令在读取寄存器前执行,确保指令流水线中此前的写操作对后续读可见,避免读到陈旧值(AC82N 流水线时序要求)。
已知设计取舍
mcpwm_set_frequency改变tmr_pr后,现有ch_cmph/ch_cmpl相对周期值会失配,调用方需重新set_duty;demo 中先调频率再调占空比的顺序印证了这一点;mcpwm_pause会断开引脚复用(gpio_disable_function),恢复时resume会重新装载配置,因此暂停期间配置可被安全修改。
性能与操作注意事项
- 易失 RAM 加速:
mcpwm_set_duty_by_cnt、mcpwm_get_timerx_reg、mcpwm_get_chx_reg标注AT(.volatile_ram_code),从 Flash 复制到 RAM 执行,消除取指等待,保证 ISR 中实时调用的确定性; - 中断优先级:故障保护中断优先级由
cfg.irq_priority配置(默认 1),电机控制应用中应给予较高优先级(数值小),确保故障响应延迟可控; - 载波频率选择:自动分频保证
tmr_pr落在 16 位范围内(最大 65535);中心对齐时实际计数频率为边沿对齐的一半,因此相同frequency下tmr_pr约为两倍,占用位宽更多——高频大占空比场景应优先选用边沿对齐; - 故障输入滤波:
FLT_PR = 0b111111提供 16×64/lsb_clk 秒的滤波时间,抑制毛刺误触发;滤波窗口越大抗扰越强,但故障响应越慢,需按应用权衡。
扩展点
- 用户故障回调:
cfg.irq_cb提供故障通知钩子,可在其中实现过流记录、状态机切换、上位机告警等业务逻辑; - 定时器溢出/借位中断:应用可自行
request_irq(IRQ_MCPWM_TMR_IDX, ...)挂接中点采样、换相时序等实时任务(demo 的mcpwm_timeout_callback是标准范式); - 多通道组合:
mcpwm_mux_start的位掩码接口天然支持任意通道组合同步启动,是三相电机驱动(CH0/CH1/CH2 各带互补+死区)的搭建基础; - 通道-定时器解耦:每个配置实例绑定独立定时器,若需多通道共享同一载波(如三相共载波),可基于
MCPWM_CH_TMRSEL位将多个通道指向同一 MCTMR 扩展实现。
测试与验证
SDK 的 mcpwm_demo.c 兼作功能验证用例,覆盖:
- 中心对齐+互补+死区(CH0)与边沿对齐同步输出(CH1/CH2)两种配置路径;
mcpwm_init三实例分配、mcpwm_mux_start同步启动(并注释对比了顺序启动的相位差问题);mcpwm_set_frequency三种频率(2K/4K/8K)与mcpwm_set_duty_by_cnt三种占空比(20%/40%/80%)的动态调整;- 溢出/借位中断(
IRQ_MCPWM_TMR_IDX)双中点触发验证(PC7 电平翻转); - 故障保护链路:IO_PORTC_11 下降沿触发 → ISR 关断 → 用户回调 →
mcpwm_fpnd_clr恢复; mcpwm_deinit与unrequest_irq的完整拆除流程。
Related Links
- mcpwm.h — 寄存器位域、枚举与 API 声明
- mcpwm.c — 驱动实现(实例管理/启动/频率/占空比/故障保护)
- mcpwm_demo.c — 三通道电机控制演示
- 相关外设页面:GPIO 复用(
PORT_FUNC_MCPWM0_H/L配置)、时钟(clk_get("mcpwm"))、定时器中断(IRQ_MCPWM_TMR_IDX)、ADC 采样(配合溢出/借位中断的中点采样)