平台: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 三个必须知道的"坑"(实测验证)
- ODISSL 的映射不是手册上想当然的那样 。手册表格容易让人误以为 CH16 → odis0,但实测本芯片 CH16 的
flag_out是接到odis[3],即ODISSL = 3才选中 CH16。写代码前务必实测确认。
- 封波不碰
eMIOS_OUDIS寄存器 。OUDIS是给 MC/MCB 以及 A2→A1、B2→B1 传送禁用用的,与输出冻结无关。封波后eMIOS_OUDIS不会置位,去写/清它也没任何效果------这是正常的,别被它误导。
- 封波不改变任何引脚配置 。它不是"改引脚功能",而是改变 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) |
六、注意事项(踩坑总结)
- ODISSL 一定要实测。不同芯片/不同 eMIOS 实例的 flag_out → odis 连线可能不同,别照抄手册表格想当然。本例实测 CH16 → odis3(ODISSL=3)。
- 封波 ≠ OUDIS 寄存器 。
eMIOS_OUDIS是传送禁用,与输出冻结无关。封波后它不置位、写它也无效,属正常现象。
- 封波不改引脚。它是内部信号路由,读 pad 配置看不到变化。
- 边沿触发:上升沿锁存 FLAG 才封波;稳态高电平(无新沿)不重复封波。
- 恢复用清 FLAG :
Cn[ODIS]保持 armed,避免清/置 ODIS 造成的 re-arm 窗口漏封波。
- 注释用英文:工程为 GBK 编码环境,中文注释会乱码,务必用 ASCII 英文注释。
七、验证
用 S32DS 自带工具链 powerpc-eabivle-gcc(gcc 4.9.4,-mcpu=e200z4 -mvle -mbig)按工程完整编译参数实测,main.c 编译 零错误零警告。
软件仿真测试结果如下图,封波功能正常。
