杰理 SDK 文档中心
首页
首页
  • 概述与入门

    • 项目概述与芯片平台
    • 环境搭建与工具链安装
    • 编译与烧录指南
    • 工程结构总览
  • 应用层与公共模块

    • GP MCU 主应用入口
    • AT 指令与调试模块
    • 电池检测与电源管理
    • EEPROM 与参数存储
    • 按键与 USB 设备驱动
    • 音频解码与 APA 语音播报
  • 外设驱动与示例

    • 高精度 ADC(HADC)
    • 通用 ADC 与定时器
    • UART / SPI / IIC 通信外设
    • MCPWM 与电机控制
    • RTC 与低功耗唤醒
    • 段码 LCD 驱动
    • NOR Flash 与红外编解码组件
  • 显示与 UI 系统

    • LCD 驱动与字库引擎
    • UI 平台与控件绘制
    • UI 工程与资源生成工具
  • 系统底层与芯片平台

    • cd09 芯片平台与预编译库
    • GPIO 与 IIC 底层驱动
    • 系统文件系统与设备模型
  • 启动引导与固件升级

    • UBOOT 引导工程
    • 固件升级机制
  • 开发工具与资源

    • 编译脚本与命令行工具
    • 音频文件转换工具
    • 硬件资料与文档资源

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

位域宏说明
bit15MCPWM_TMR_INCF递增计数标志
bit13MCPWM_TMR_UFPND借位(下溢)中断挂起位
bit12MCPWM_TMR_OFPN溢出中断挂起位
bit11MCPWM_TMR_UFCLR借位中断清除位
bit10MCPWM_TMR_OFCLR溢出中断清除位
bit9MCPWM_TMR_UFIE借位中断使能
bit8MCPWM_TMR_OFTE溢出中断使能
bit7MCPWM_TMR_CKSRC时钟源选择
bit6-3MCPWM_TMR_CKPS (4bit)时钟分频 2^i
bit1-0MCPWM_TMR_MODE (2bit)计数模式:0b01 递增(边沿对齐)、0b10 递增-递减(中心对齐)

通道寄存器 CHx_CON0 / CHx_CON1 / CHx_CMPH / CHx_CMPL

CHx_CON0 控制输出极性、使能与死区:

位域宏说明
bit15-12MCPWM_CH_DTCKPS (4bit)死区时钟分频,死区时间 = Tsys / (2^dtckps) × (dtpr+1)
bit11-7MCPWM_CH_DTPR (5bit)死区预分频值 0~31
bit6MCPWM_CH_DTEN死区使能
bit5MCPWM_CH_L_INVL 引脚输出取反(互补模式核心)
bit4MCPWM_CH_H_INVH 引脚输出取反
bit3MCPWM_CH_L_ENL 引脚输出使能
bit2MCPWM_CH_H_ENH 引脚输出使能
bit1-0MCPWM_CH_CMP_LD (2bit)比较值装载方式

CHx_CON1 控制故障保护与中断:

位域宏说明
bit15MCPWM_CH_FPND故障保护挂起标志(中断中轮询)
bit14MCPWM_CH_FCLR故障保护清除
bit11MCPWM_CH_INTEN故障保护输入中断使能
bit10-8MCPWM_CH_TMRSEL (3bit)选择绑定的 MCTMR
bit4MCPWM_CH_FPINEN故障引脚使能
bit3MCPWM_CH_FPINAUTO故障自动恢复/自动关断模式
bit2-0MCPWM_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.h、mcpwm.c

  • 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):

  1. 遍历 mcpwm_info[0..7] 找到第一个空槽位,malloc 分配 mcpwm_info_t;若 8 个槽位全满则返回 -1;
  2. 按配置使能定时器溢出中断(MCPWM_TMR_OFTE)与借位中断(MCPWM_TMR_UFIE),并清零 tmr_cnt;
  3. 保存通道寄存器指针、定时器寄存器指针,并将调用者传入的 struct mcpwm_config 完整 memcpy 到实例中(后续 start/resume 会重新装载该副本);
  4. 返回实例 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):

  1. 以 MCPWM 时钟为基准,从分频 2^0 开始逐级尝试,找到使 frequency >= clk/(65536×2^i) 的最小分频指数 i(i 上限 15),写入 TMR_CKPS;这是为了让 16 位 tmr_pr 不溢出且分辨率最优的自动分频策略;
  2. 计算周期寄存器值:
    • 中心对齐:tmr_pr = clk/(2^i) / (frequency×2) - 1(一个 PWM 周期 = 一次递增 + 一次递减);
    • 边沿对齐:tmr_pr = clk/(2^i) / frequency - 1;
  3. 按对齐方式写 TMR_MODE(0b01 递增 / 0b10 递增-递减);
  4. 临界区内写 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);                            // 调用用户回调
            }
        }
    }
}

流程要点:

  1. 硬件检测到故障信号(经过 FPIN_CON 的边沿与滤波配置)后置位 CHx_CON1.FPND,同时硬件本身已把输出拉安全电平;
  2. 中断进入后软件再清除 PWM_EN/TMR_EN,这是与硬件关断互补的第二重保险,确保定时器停止、输出彻底禁用;
  3. 若 INTEN 使能,则先关闭中断使能(防止故障持续期间反复触发中断风暴),再调用注册的用户回调 mcpwm_cb_table[ch],回调收到故障通道号;
  4. 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);
}

来源:mcpwm_demo.c、mcpwm_demo.c

配置选项

struct mcpwm_config(定义见 mcpwm.h):

字段类型默认/典型值说明
chmcpwm_ch_typeMCPWM_CH0通道号 0~3
aligned_modemcpwm_aligned_mode_typeMCPWM_CENTER_ALIGNED边沿对齐或中心对齐
frequencyu321000初始 PWM 频率(Hz),所有通道可独立
dutyu165000初始占空比,0~10000 对应 0%~100%;互补模式以 H 引脚波形为准
h_pinu16IO_PORTC_00通道 H 输出引脚,任意 IO
l_pinu16IO_PORTC_01通道 L 输出引脚,不需要填 0xff
complementary_enu810: H/L 同步输出;1: 互补输出(占空比体现于 H 引脚)
detect_portmcpwm_fpinMCPWM_FPIN_IO_PORTC_11故障源:IO_PORTC_11 / OPA 比较器 / DAC 比较器 / NONE
edgemcpwm_edgeMCPWM_EDGE_FAILL故障触发边沿:下降沿 / 上升沿 / 0xff 忽略
irq_cbmcpwm_detect_irq_callbackNULL故障中断回调 void (*)(u32 ch)
irq_priorityu161故障中断优先级
dtenu81死区使能
dtpru80死区时间 = Tsys/(2^dtckps)×(dtpr+1),0~31
dtckpsu84死区时钟分频,0~15
ufieu81借位(下溢)中断使能
ofieu81溢出中断使能

故障源枚举(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 采样(配合溢出/借位中断的中点采样)
Prev
UART / SPI / IIC 通信外设
Next
RTC 与低功耗唤醒