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. 恢复用清 FLAG :Cn[ODIS] 保持 armed,避免清/置 ODIS 造成的 re-arm 窗口漏封波。
  1. 注释用英文:工程为 GBK 编码环境,中文注释会乱码,务必用 ASCII 英文注释。

七、验证

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

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


相关推荐
La Pulga15 小时前
【FreeRTOS源码学习笔记】任务延时列表
笔记·mcu·学习·嵌入式·freertos·实时操作系统
星野云联AIoT技术洞察2 天前
传统设备接入 Tuya:从 DP、串口联调到样机与 OTA 验收
mcu·智能家居·涂鸦智能·涂鸦开发·tuya生态接入·tuya开发·mcu sdk
意法半导体STM323 天前
【官方原创】LAT1717 STM32N6 如何使用单个XSPI驱动双片相同HyperRAM
stm32·单片机·嵌入式硬件·mcu·stm32n6·hyperram
La Pulga4 天前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga5 天前
【FreeRTOS源码学习笔记】任务切换
单片机·mcu·学习·嵌入式·freertos·实时操作系统
La Pulga7 天前
【FreeRTOS源码学习笔记】链表的实现
数据结构·笔记·stm32·mcu·链表·嵌入式·freertos
La Pulga7 天前
【FreeRTOS源码学习笔记】任务创建
笔记·stm32·mcu·学习·嵌入式·freertos·实时操作系统
矿渣渣7 天前
ERPC 在 HPM6E80 MCU 中的应用方法
嵌入式软件·双核通信·erpc
secondyoung9 天前
Cortex-R52与TriCore详细对比
arm开发·单片机·嵌入式硬件·mcu
电子科技圈9 天前
芯科科技2026年Tech Talks技术系列讲座再增添全新主题共探无线SoC架构演进与安全合规
科技·嵌入式硬件·mcu·物联网·安全·架构·iot