5777M的eMIOS 封波(ODIS)功能实现:一个通道输入沿触发,冻结另一个通道 PWM 输出

平台:5777M/CCFC3007BC(PowerPC e200z4 内核)

模块:eMIOS(与 NXP MPC5777M 同款 IP)

参考:RM 37.1.3.2 "eMIOS and eTPU ODIS connections"

一、需求背景

在电机控制、电源变换等应用中,经常需要一个"硬件快速封波"的能力:当某个外部故障信号(过流/过压/故障输入)到来时,立即封锁 PWM 输出,把功率管关断,实现硬件级的快速保护

如果靠软件查询引脚电平再关 PWM,响应延迟大、实时性差。eMIOS 提供了 ODIS(Output Disable) 机制,可以在纯硬件层面完成"输入沿 → 冻结输出"的动作,软件无需介入,响应是硬件级的。

本文给出在 CCFC3007BC 上的完整实现:用 eMIOS 的一个通道做输入捕获(SAIC,上升沿触发) ,用它的 flag_out 信号去冻结另一个通道的 PWM 输出


二、ODIS 封波的硬件原理

2.1 核心结论

  • 同一 eMIOS 实例内部 ,部分通道的 emios_flag_out 信号被硬连线到本实例的 emios_odis[3:0] 输入上。
  • 输出通道 Cn 有两个关键控制位:

    • Cn[ODIS]:输出禁止使能(1 = 使能封波)
    • Cn[ODISSL]:选择 4 路 odis 输入中的哪一路(00/01/10/11 → Input0/1/2/3)
  • Cn[ODIS]=1 且选中的那路 odis 输入被断言 时,该通道的输出立即被冻结:

    • OPWFMB / OPWMB 模式 → 冻结到 EDPOL
    • 其他输出模式 → 冻结到 EDPOL 的反相
  • 通道本体仍在运行(继续产生 FLAG、比较匹配),只是输出值被"锁死"。

2.2 三个必须知道的"坑"(实测验证)

  1. ODISSL 的映射不是手册上想当然的那样 。手册表格容易让人误以为 CH16 → odis0,但实测本芯片 CH16 的 flag_out 是接到 odis[3],即 ODISSL = 3 才选中 CH16。写代码前务必实测确认。
  1. 封波不碰 eMIOS_OUDIS 寄存器OUDIS 是给 MC/MCB 以及 A2→A1、B2→B1 传送禁用用的,与输出冻结无关。封波后 eMIOS_OUDIS 不会置位,去写/清它也没任何效果------这是正常的,别被它误导。
  1. 封波不改变任何引脚配置 。它不是"改引脚功能",而是改变 eMIOS 内部的信号路由 (CH16 的 flag_out → 输出通道的 ODIS 输入)。所以封波后去读 PB12 的 pad 配置,什么都看不到变化------也是正常的。

2.3 触发方式:边沿触发,不是电平触发

实测确认:只有上升沿会置 FLAG(进而断言 flag_out 冻结输出)。如果输入维持在高电平、但没有新的上升沿,输出不会被再次封波。这是一个"沿锁存"行为,理解这一点对恢复逻辑很重要。


三、恢复输出:清 CH16 的 FLAG,不要清 ODIS

封波之后如何重新打开 PWM?实测验证的最优做法是:

清除输入通道 CH16 的 FLAG 位,即可恢复 CH0 的 PWM 输出。

为什么不用"清 Cn[ODIS] 再重新置位"的方式?

  • 清 ODIS 再置 ODIS 中间存在一个时间窗口 :如果在这个窗口内又来了一个上升沿,这次封波事件会被漏掉,输出就再也不会被冻结了(保护失效)。
  • 而清 FLAG 的方式下,Cn[ODIS] 始终保持在 1(armed) ,不存在 re-arm 窗口;下一个上升沿会自动再次封波,一个封波事件都不会丢

注意:清 FLAG 能恢复的前提是此时输入已回到低电平(非触发电平)。由于 FLAG 是"沿锁存"的,只要输入回到低电平、没有新的上升沿,清 FLAG 后输出即恢复。


四、完整代码

以下代码插入到 main_Z4_2 的外设初始化完成之后(本工程放在 McuClkPrint() 之后调用 EmiosFreeze_Init())。注释全部用英文(工程为 GBK 环境,中文注释会乱码)。

4.1 头文件引用

复制代码
#include "emios_lld.h"
#include "emios_pwm_lld.h"
#include "emios_ic_lld.h"

4.2 宏定义 + 初始化 + 恢复函数

复制代码
/* ============================================================================
 * eMIOS Output Disable (ODIS) demonstration - PWM freeze on input edge.
 *
 * Principle (RM 37.1.3.2 "eMIOS and eTPU ODIS connections"):
 *   Within the SAME eMIOS instance, the emios_flag_out signals of certain
 *   channels are hard-wired to this instance's internal emios_odis[3:0]
 *   inputs. On THIS silicon the wiring is (verified by experiment):
 *       CH16 flag_out -> odis[3]    (i.e. ODISSL = 3 selects CH16)
 *   When an output channel has Cn[ODIS]=1 and its Cn[ODISSL] selects one of
 *   the odis inputs above, asserting that odis input freezes the channel's
 *   output immediately to EDPOL (for OPWFMB/OPWMB modes) or to the complement
 *   of EDPOL (for the other output modes). The unified channel keeps running
 *   (it still produces FLAG and matches); only the output value is frozen.
 *
 * IMPORTANT - what freeze does NOT touch:
 *   The freeze is an INTERNAL eMIOS signal (CH16's flag_out routed to the
 *   output channel's ODIS input). It does NOT use the eMIOS_OUDIS register
 *   (that register is for MC/MCB and A2->A1/B2->B1 transfer-disable), and it
 *   does NOT change any pad configuration. This is why, after a freeze, the
 *   eMIOS_OUDIS register reads unset, writing/clearing it has no effect, and
 *   the PB12 pad config looks unchanged.
 *
 * This example:
 *   Input  channel : EMIOS0 CH16 (PB[12])   configured as SAIC rising-edge capture
 *   Output channel : EMIOS0 CH0  (PM12)     configured as OPWFMB PWM
 *   A RISING EDGE on CH16 sets FLAG -> flag_out[16] asserted -> odis[3]
 *   asserted -> CH0 PWM output frozen by hardware. This is EDGE-triggered, NOT
 *   level-triggered: only a rising edge latches FLAG (and thus freezes the
 *   output); a steady high level without a new edge does not re-freeze it.
 *
 * How to re-enable the PWM output after it is frozen (verified by experiment):
 *   Clear CH16's FLAG bit (EmiosFreeze_OpenPwm). This de-asserts the odis[3]
 *   input (driven by CH16 flag_out) and the output resumes. This is the
 *   RECOMMENDED way, because Cn[ODIS] stays armed (=1) the whole time, so the
 *   next rising edge on CH16 automatically freezes the output again - there is
 *   no re-arm window.
 *   DO NOT clear Cn[ODIS] to recover: if you clear then re-set ODIS around the
 *   recovery, a rising edge arriving in that window is missed and the output is
 *   never re-frozen (the freeze event is lost).
 * ============================================================================ */

#define EMIOS_FREEZE_INSTANCE       (0U)   /* eMIOS instance 0                              */
#define EMIOS_FREEZE_IN_CH          (16U)  /* input capture channel: CH16 (PB[12])          */
#define EMIOS_FREEZE_PWM_CH         (0U)   /* PWM output channel: CH0 (PM12)                */
#define EMIOS_FREEZE_PWM_ODISSL     (EMIOS_OUTPUT_DISABLE_INPUT_3) /* CH16 flag_out -> odis[3] (verified by experiment) */

static inline void EmiosFreeze_GpioConfig(uint32_t idx, uint32_t mode)
{
#ifdef RESOURCE_SUPPORT_SIUL2_IP
    SIUL2.MSCR_IO[idx].R = (SIUL2.MSCR_IO[idx].R & (~GPIO_Mode_Mask)) | (mode & GPIO_Mode_Mask);
#else
    (void)idx;
    (void)mode;
#endif
}

/* eMIOS freeze initialization - call it after peripheral initialization. */
static void EmiosFreeze_Init(void)
{
    uint32_t emiosClk = 0U;
    eMIOS_GlobalParamType globalParam =
    {
        .LowPowerModeEn      = FALSE,
        .FreezeInDebugMode   = FALSE,
        .GlobalPreClkDiv     = 75U,                               /* recomputed from the real clock below */
        .ExternalTimeBaseSel = EMIOS_ETB_DIS_COUNTER_BUSA_TO_UC,
        .ServerTimeSlotSel   = 0U
    };
    eMIOS_PWM_ParamType pwmParam =
    {
        .mode           = EMIOS_UC_MODE_PWM_OPWFMB1,              /* period/duty-cycle PWM */
        .divid          = 1U,
        .active         = (uint8_t)EMIOS_POSITIVE_PULSE,
        .timebase       = EMIOS_COUNTER_BUS_INTERNAL,             /* internal time base */
        .period         = 1000UL,
        .dutyCycle      = 500UL,
        .idealDutyCycle = 0UL,
        .deadTime       = 0UL,
        .trigTime       = 0UL
    };
    eMIOS_IC_ParamType icParam =
    {
        .mode       = EMIOS_UC_MODE_SAIC_TRIGGER_EDGE_RISING,     /* rising-edge capture */
        .timebase   = EMIOS_COUNTER_BUS_INTERNAL,
        .filter     = EMIOS_INPUT_FILTER_BYPASS,
        .desirednum = 0U,
        .startime   = 0U,
        .endtime    = 0U
    };

    /* 1. Pin config: PM12 = EMIOS0 CH0 output; PB12 = EMIOS0 CH16 input (pull-up). */
    EmiosFreeze_GpioConfig(PM12, GPIO_Mode_Out_PP | 0x0CU);
    EmiosFreeze_GpioConfig(PB12, GPIO_Mode_IPU);

    /* 2. eMIOS0 global init; set global prescaler to 1 MHz. */
    (void)CLOCK_GetModuleClockFreq(SXBAR_CLK, &emiosClk);
    globalParam.GlobalPreClkDiv = (uint16_t)(emiosClk / 1000000U);
    eMIOS_LLD_InitGlobalParam(EMIOS_FREEZE_INSTANCE, &globalParam);

    /* 3. Init the PWM output channel CH0. */
    (void)eMIOS_LLD_PWM_Init(EMIOS_FREEZE_INSTANCE, EMIOS_FREEZE_PWM_CH, &pwmParam);

    /* 4. Enable freeze on CH0: select odis[3] (= CH16 flag_out, verified) and set ODIS=1. */
    eMIOS_UC_Cn_SetODISSL(EMIOS_FREEZE_INSTANCE, EMIOS_FREEZE_PWM_CH, EMIOS_FREEZE_PWM_ODISSL);
    eMIOS_UC_Cn_SetODIS(EMIOS_FREEZE_INSTANCE, EMIOS_FREEZE_PWM_CH, TRUE);

    /* 5. Init the input capture channel CH16 (SAIC rising edge); FLAG set triggers freeze. */
    (void)eMIOS_LLD_IC_Init(EMIOS_FREEZE_INSTANCE, EMIOS_FREEZE_IN_CH, &icParam);

    /* 6. Enable eMIOS0 globally. */
    eMIOS_LLD_SetEnDisGlobal(EMIOS_FREEZE_INSTANCE, TRUE);
}

/* Re-enable the PWM output (verified): clear CH16's capture flag.
 * This de-asserts the odis[3] input (driven by CH16 flag_out), so the output
 * resumes. Cn[ODIS] stays armed (=1), so the next rising edge on CH16 freezes
 * the output again - no re-arm window, no lost freeze event.
 * Call from ISR/main-loop as needed (e.g. after the fault condition clears). */
static void EmiosFreeze_OpenPwm(void) __attribute__((unused));
static void EmiosFreeze_OpenPwm(void)
{
    eMIOS_LLD_UC_ClearFlag(EMIOS_FREEZE_INSTANCE, EMIOS_FREEZE_IN_CH);
}

4.3 调用位置

复制代码
int main_Z4_2(void)
{
    /* ... 其他外设初始化 ... */
    McuClkPrint();

    /* eMIOS freeze: after peripheral init, configure CH16 input capture to freeze CH0 PWM output. */
    EmiosFreeze_Init();

    /* ... */
}

五、配置说明

|-------|-------------------------------------------------------------|
| 项目 | 配置 |
| 输入通道 | EMIOS0 CH16(引脚 PB12),SAIC 模式,上升沿捕获,滤波旁路 |
| 输出通道 | EMIOS0 CH0(引脚 PM12),OPWFMB1 PWM,周期 1000,占空比 500(内部 1MHz 时基) |
| 封波源选择 | ODISSL = 3(对应 CH16 的 flag_out) |
| 封波使能 | Cn[ODIS] = 1 |
| 恢复方式 | 清 CH16 的 FLAG(eMIOS_LLD_UC_ClearFlag) |


六、注意事项(踩坑总结)

  1. ODISSL 一定要实测。不同芯片/不同 eMIOS 实例的 flag_out → odis 连线可能不同,别照抄手册表格想当然。本例实测 CH16 → odis3(ODISSL=3)。
  1. 封波 ≠ OUDIS 寄存器eMIOS_OUDIS 是传送禁用,与输出冻结无关。封波后它不置位、写它也无效,属正常现象。
  1. 封波不改引脚。它是内部信号路由,读 pad 配置看不到变化。
  1. 边沿触发:上升沿锁存 FLAG 才封波;稳态高电平(无新沿)不重复封波。
  1. 恢复用清 FLAGCn[ODIS] 保持 armed,避免清/置 ODIS 造成的 re-arm 窗口漏封波。
  1. 注释用英文:工程为 GBK 编码环境,中文注释会乱码,务必用 ASCII 英文注释。

七、验证

用 S32DS 自带工具链 powerpc-eabivle-gcc(gcc 4.9.4,-mcpu=e200z4 -mvle -mbig)按工程完整编译参数实测,main.c 编译 零错误零警告

软件仿真测试结果如下图,封波功能正常。


相关推荐
youcans_2 小时前
【嵌入式软件AI编程】03. STM32 VS Code 开发环境与工具链
stm32·单片机·ai编程·嵌入式软件·claude code
老猿讲编程1 天前
【Eclipse OpenSOVD学习之二】 SOVD 协议原理与标准背景
学习·eclipse·嵌入式软件·车载·sovd
qq_402995751 天前
Secure Boot Part 2 —— 安全启动流程
arm开发·mcu·安全
jianqiang.xue2 天前
ESP-IDF保姆级入门23|MQTT物联网通信全解:发布订阅模式/QoS等级/遗嘱消息/主题设计/断线重连,掌握工业级标准消息通信
stm32·单片机·mcu·物联网·51单片机·iot
youcans_2 天前
【嵌入式软件AI编程】01. 基于 STM32/Claude Code的AI编程
stm32·mcu·嵌入式·ai编程·claude code
小麦嵌入式2 天前
FPGA入门(十一):受控线性序列机 UART 发送升级
stm32·单片机·嵌入式硬件·mcu·fpga开发·硬件工程
jianqiang.xue2 天前
ESP-IDF保姆级入门29|FreeRTOS任务管理与调度全解:任务创建/状态切换/优先级调度/双核负载均衡/实时性优化,掌握嵌入式多任务编程核心
stm32·单片机·mcu·物联网·51单片机·iot
jianqiang.xue2 天前
ESP-IDF保姆级入门22|WiFi联网与网络编程全解:STA/AP双模式/TCP-UDP Socket/HTTP服务端/自动重连,掌握工业级可靠网络通信
人工智能·stm32·单片机·mcu·物联网·51单片机·iot
码农小韩3 天前
Linux应用开发(二)——Linux的文件IO
linux·嵌入式软件·嵌入式操作系统·linux应用