LEO卫星里的IO脉冲事件通信,毛刺误触发解析及处理方案

标签:嵌入式 抗干扰设计 MCU FPGA 输入捕获 卫星电子

摘要:芯片手册"有效沿至少维持3个GPIO时钟周期"常被误读成硬件抗毛刺阈值。基于低轨卫星 AS32X601 与 FPGA 脉冲 IO 通信的真实场景,给出多层抗干扰架构、RC 与定时器参数选型、输入捕获高频踩坑点和可直接落地的测试清单。

场景

FPGA 输出 ms 级电平脉冲作为事件(过顶触发、相机就绪、时序同步),MCU 检测边沿执行逻辑。低轨 LEO 轨道下,外部 IO 可能受到耦合干扰;芯片内部状态触发器也可能发生单粒子瞬态 SET。外部干扰脉冲常见为几 ns 到几百 ns,但是否被 GPIO 采样并形成中断,取决于 GPIO 控制器时钟、采样相位、输入电平和内部时序。内部寄存器 SET 与外部 RC、输入捕获滤波是不同层面的问题,不能混为一谈。

1 一个被误读的参数

手册写"有效沿高电平至少维持 3 个 GPIO 控制器时钟周期",这里的准确含义是:GPIO 控制器按自身时钟采样输入,只有被连续采样到的电平变化才可能形成中断沿;3 个周期是识别下限,不是接收端的抗毛刺能力,也不是对所有 ns 级毛刺都必然触发的保证。毛刺是否被采到,取决于采样相位、脉冲宽度、输入波形和控制器时钟。因此,不能写成"没有最小脉宽门限",也不能把 3 个周期当作抗干扰指标。

以 48 MHz GPIO 控制器时钟、1 ms 高电平脉冲为例(仅作换算示例;GPIO 控制器时钟取决于其所在总线,GPIOE 位于 AxiLite4 Bus2,实际值应通过 SMU_GetClocksFreq() 读取 AXILite4Bus2_Frequency 确认):

项目 数值
3 个 GPIO 时钟周期 62.5 ns
示例高电平宽度 1 ms
宽度 / 采样识别下限 约 1.6×10⁴
事件速率 稀疏事件;若最小间隔约 1 ms,则连续满速时不超过约 500 Hz

事件速率低、相对采样下限有较大余量,适合加入 RC 低通、定时器捕获和软件脉宽判据。但"1 ms 高 + 1 ms 低"的连续波形只有约 500 Hz,不能写成 1 kHz。若系统规定事件最小间隔为 2 ms,才会落到约 500 Hz 以下;具体速率必须按协议定义,不能只看高电平宽度。

2 边沿中断 vs 定时器输入捕获

两种方案都应先做基本电气防护:ESD/过压钳位、限流、确定空闲电平,以及按线缆和边沿速率评估的 RC/外部施密特。是否额外加外部施密特,要看 FPGA 输出边沿、线缆长度和噪声频谱;AS32X601 的数字输入和复用输入默认带有内部施密特,模拟模式才关闭。外部 RC/施密特也不能替代芯片内部状态触发器的 TMR、CRC、看门狗等系统级防护。

环节 普通边沿中断(GPIO 中断) 定时器输入捕获
第一级毛刺抑制 无用户可配置数字滤波;输入按 GPIO 控制器时钟采样,约 3 个周期为识别下限 有同步采样;可配置 TIM_ICFilter
第二级数字滤波 无,只能进中断后软件补救 有,TIM_ICFilter 以 f_DTS 分频和连续同电平采样实现
时间戳/测宽 软件计时,受中断延迟抖动影响 硬件捕获,定时器时钟级精度
毛刺升高表现 CPU 占用线性上涨,极端看门狗复位 毛刺落入捕获窗口时可能置位 CC1OF/CC2OF、使捕获序列错位;可计数并可上报

注意:ICFilter 只对经过定时器输入同步器的信号起作用;如果 SET 发生在采样之后的芯片内部状态位,外部 RC 和 ICFilter 都不能被当作保护手段。

边沿中断的致命点在顺序:一旦毛刺被 GPIO 采样并触发中断,软件才"有机会"判断它是毛刺,而在判断之前 CPU 已经被打断。毛刺密集时"中断风暴 → CPU 饥饿 → 看门狗复位",且地面难复现、日志只有看门狗超时,极难定位。

边沿中断的风险来自"高概率毛刺 + 采样后直接进入业务主入口 + 可能的灾难性故障"三者组合。满足以下条件可合规用于告警、唤醒类信号:不作为业务脉冲主入口、外部电气防护充分、软件做齐清标志 + 冷却窗口 + 速率熔断三件套、故障后果可降级、用单边沿触发。

c 复制代码
/* AS32X601:GPIOE Pin10 下降沿中断 + 冷却窗 + 速率熔断 + 受控恢复 */
#include <stdint.h>

#define EDGE_COOLDOWN_TICKS       20u      /* 200 µs @10 µs/tick */
#define EDGE_RATE_WINDOW_TICKS    1000u    /* 10 ms @10 µs/tick */
#define EDGE_RATE_LIMIT           8u       /* 每统计窗允许的原始边沿数 */
#define EDGE_RECOVER_TICKS        1000u    /* 10 ms 后尝试恢复 */
#define FAULT_GLITCH_STORM        0x0001u

extern uint32_t Bsp_GetMonotonic10us(void);
extern void App_EdgeEvent(void);
extern void App_EdgeTelemetry(uint32_t fault_code);

static volatile uint32_t s_last_edge;
static volatile uint32_t s_win_start;
static volatile uint32_t s_edge_cnt;
static volatile uint32_t s_edge_veto;
static volatile uint32_t s_disable_tick;
static volatile uint8_t  s_edge_disabled;

void Edge_Input_Init(void)
{
    GPIO_InitTypeDef g;
    PLIC_InitTypeDef p;

    GPIOE_CLK_ENABLE();
    PLIC_CLK_ENABLE();

    GPIO_StructInit(&g);
    g.GPIO_Pin   = GPIO_Pin_10;
    g.GPIO_Mode  = GPIO_Mode_IN;
    g.GPIO_IType = GPIO_IPU;     /* 下降沿触发,空闲应为高;数据手册 RPU 典型值 48 kΩ */
    GPIO_Init(GPIOE, &g);

    PLIC_StructInit(&p);
    p.PLIC_IRQChannel    = GPIOE_IRQn;      /* 端口级中断,一个端口一个入口 */
    p.PLIC_IRQPriority   = 1;
    p.PLIC_IRQChannelCmd = ENABLE;
    PLIC_Init(&p);

    GPIO_ClearITPendingBit(GPIOE, GPIO_Pin_10);
    GPIO_ITConfig(GPIOE, GPIO_Pin_10, GPIO_ITType_EDGEDOWN, ENABLE);
}

void GPIOE_IRQ_Handler(void)      /* 本例只使能了 PE10,直接判它即可 */
{
    uint32_t now;

    if (GPIO_GetITStatus(GPIOE, GPIO_Pin_10) != SET) {
        return;
    }

    GPIO_ClearITPendingBit(GPIOE, GPIO_Pin_10);

    if (s_edge_disabled != 0u) {
        return;
    }

    now = Bsp_GetMonotonic10us();

    if ((uint32_t)(now - s_win_start) >= EDGE_RATE_WINDOW_TICKS) {
        s_win_start = now;
        s_edge_cnt = 0u;
    }

    /*
     * 先累计原始边沿和冷却否决,再判断冷却窗。
     * 否则高频毛刺一直被冷却窗拦截,速率熔断可能永远达不到。
     */
    ++s_edge_cnt;
    if (s_edge_cnt > EDGE_RATE_LIMIT) {
        s_edge_disabled = 1u;
        s_disable_tick = now;
        GPIO_ITMaskConfig(GPIOE, GPIO_Pin_10, DISABLE);  /* DISABLE = 屏蔽 */
        GPIO_ClearITPendingBit(GPIOE, GPIO_Pin_10);
        App_EdgeTelemetry(FAULT_GLITCH_STORM);
        return;
    }

    if ((uint32_t)(now - s_last_edge) < EDGE_COOLDOWN_TICKS) {
        ++s_edge_veto;
        return;
    }

    s_last_edge = now;
    App_EdgeEvent();
}

void Edge_Input_TryRecover(uint32_t now)
{
    if ((s_edge_disabled == 0u) ||
        ((uint32_t)(now - s_disable_tick) < EDGE_RECOVER_TICKS)) {
        return;
    }

    /* 空闲必须回到确定的无效高电平,再清残留标志和解除屏蔽。 */
    if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_10) != GPIO_IRead_Vih) {
        return;
    }

    GPIO_ClearITPendingBit(GPIOE, GPIO_Pin_10);
    s_last_edge   = now - EDGE_COOLDOWN_TICKS;
    s_win_start   = now;
    s_edge_cnt    = 0u;
    s_edge_veto   = 0u;
    s_edge_disabled = 0u;
    GPIO_ITMaskConfig(GPIOE, GPIO_Pin_10, ENABLE);  /* ENABLE = 解除屏蔽 */
}

/* 由周期任务读取后上报遥测;ISR 内不做格式化或打印。 */
uint32_t Edge_Input_GetVetoCount(void)
{
    return s_edge_veto;
}

601 上还要注意三点:中断是端口级 的(GPIOE_IRQn 只有一个 GPIOE_IRQ_Handler 入口);本例只在 PE10 上使能了中断,所以入口里判 PE10 一个脚就够,但若以后同口再使能其它脚,必须在这里补上对应的脚位判断 ,漏判会丢中断;GPIO_ITMaskConfig 的 DISABLE 才是屏蔽 (源码里 DISABLE → IMR |= Pin);PLIC 的 claim/complete 已由 SDK 的 PLIC_SwitchMEXTI() 处理,ISR 内不要再重复 claim/complete。

上电初始化后必须在周期任务或主循环中调用 Edge_Input_TryRecover(now);否则熔断屏蔽后不会自动恢复,只能等复位。App_EdgeTelemetry() 在中断里调用,同样只应做入队或置标志,串口打印、Flash 写日志这类操作必须放到任务上下文。

冷却窗口挡不住"间隔刚好大于冷却窗"的毛刺,必须冷却窗 + 速率熔断一起上。

选型参考:业务事件脉冲 → 输入捕获;电源/过温告警、外部唤醒 → 边沿中断(配三件套);定时器资源耗尽 → 边沿中断 + 更强外围硬件 + 严格协议。

3 多层架构与各级滤除能力

  1. 电气防护(ESD/限流/钳位、确定空闲电平)→ 2. 外围 RC/施密特(按边沿和噪声条件选用)→ 3. 定时器同步采样 + TIM_ICFilter(硬件粗滤)→ 4. 软件脉宽/周期判据 → 5. 帧级 CRC/序号/重传(最终兜底)。把被否决的边沿数上报遥测,在轨可间接评估干扰和采样异常。

以 1ms 脉冲为例,各层能滤掉多宽扰动:

层级 机制 可滤除宽度
电气防护 串阻 + 钳位/ESD 器件 + 确定空闲电平 限流、抑制振铃、ESD 泄放;不负责数字表决
RC 低通 τ=100µs <50µs
施密特 3.3V 下保证 VIL≤1.2V、VIH≥1.5V;内部数字输入默认使能 抑制缓边沿和阈值附近抖动;不能等同于外部可调滤波
TIM_ICFilter 硬件滤波 f_DTS 分频 + 连续同电平采样 示例 ICF=0x9、45MHz、DIV1 约 1.4µs
软件表决 100kHz×16 拍 <170µs(16×10µs=160µs)
脉宽判据 0.7~1.5ms 窗口 非标称脉宽全丢
帧协议 CRC+序号+重传 单脉冲构不成有效帧

对于外部耦合到输入线的 ns~几百 ns 干扰,RC 和 TIM_ICFilter 可以显著降低误采样概率,但不能写成"一定被消掉"。是否通过还取决于干扰幅度、发生相位、GPIO 控制器时钟和输入路径;芯片内部状态触发器发生 SET 时,更不受这两层控制。因此应通过注入测试和遥测确认,而不能用"概率可忽略"替代设计保证。

4 毫秒脉冲参数选型

约束:τ ≤ T/10 ,阈值穿越延迟 t≈0.69τ(VT+≈0.5VDD)。

最小脉宽 T R C τ=R×C 50%阈值穿越延迟 可滤扰动(保守值)
0.5ms 4.7kΩ 10nF 47µs 32µs(6.5%) <20µs
1ms 10kΩ 10nF 100µs 69µs(6.9%) <50µs
2ms 10kΩ 15nF 150µs 104µs(5.2%) <70µs
5ms 10kΩ 47nF 470µs 324µs(6.5%) <230µs

表中的"可滤扰动"不是精确截止频率,而是按输入从稳定无效电平起跳、阈值约 0.5VDD 做的保守估算。实际穿越点要按 VIH/VIL、施密特特性、源阻抗和实际波形标定;如果干扰从相反电平起跳,穿越时间还会不同。

两条设计约束:R 不能只按"越大越稳"选 ,要核算输入漏电流、VIH/VIL 和源端驱动能力;若按输入漏电流 ±10µA 估算,10kΩ 会引入 0.1V 偏置,47kΩ 可达 0.47V,具体极限必须以 AS32X601 数据手册为准。电容优先 C0G/NP0;X7R 的电压系数和温度漂移会让 RC 时间常数变化,是否可用于辐照环境还要按器件等级和试验数据确认。

定时器配置(100kHz tick=10µs):判决窗 ≤T/3,采样周期远小于判决窗。

最小脉宽 T 表决拍数 判决窗 脉宽判据
0.5ms 8 80µs 0.35~0.75ms
1ms 16 160µs 0.7~1.5ms
2ms 32 320µs 1.4~3.0ms
5ms 100 1.0ms 3.5~7.5ms

tick 不能直接拿 CPU 主频。TIM0 的 CK_INT 来自 AxiLite4 Bus1,应在运行时用 SMU_GetClocksFreq() 读取 AXILite4Bus1_Frequency;其他 TIM 也要先确认时钟来源。16 位定时器(TIM3/4/6/7)在 100kHz、10µs/tick 下约 655ms 回绕,测更长的稀疏事件间隔时必须做软件扩展或改用 32 位定时器;TIM0 在同样 tick 下约 11.9 小时回绕,超过该时长仍需软件扩展。

TIM_ICFilter 的时间窗不是固定的 5µs。以 45MHz、TIM_CKD_DIV1、IC1F=0x9 为例,滤波器约为 1.4µs(再叠加同步延迟),实际值按 CK_INT/CKD 和 ICF 编码计算并示波器确认。延迟预算示例:RC 69µs + 软件表决 160µs ≈ 0.23ms,占 1ms 脉宽约 23%;理论值只作初选,实测标定后再定判据上下限。

5 输入捕获两个高频坑

  1. 双沿捕获错位 :脉冲外多个上升沿会让捕获序列永久错位。用 PWM 输入模式(从模式复位),上升沿复位 CNT,两个捕获值同源于同一个复位点,硬件自动对齐、可自恢复。对于重复的 PWM 波形,CCR1 表示相邻上升沿间隔,CCR2 表示高电平宽度;对于单次、稀疏事件,只有 CCR2 的高电平宽度可直接用于事件判据,CCR1 不能强行解释成协议周期。
  2. CC1OF/CC2OF 捕获溢出 :捕获值未读又有新边沿会置对应溢出标志。检测到必须作废当前配对状态重新同步,不能只清标志,否则可能用旧值拼出虚假测量。
c 复制代码
/* AS32X601:TIM0 CH1(PB11)PWM 输入模式 + 从模式复位 + 下降沿即时结算 */
#include <stdint.h>

#define CAP_TICK_HZ             100000u  /* 10 µs/tick,按实际 TIM0 时钟整除 */
#define CAP_IC_FILTER           0x9u     /* f_DTS/8,N=8;45MHz、DIV1 约 1.4µs */
#define CAP_MIN_HIGH_TICKS      70u      /* 0.7ms,必须按波形和RC标定 */
#define CAP_MAX_HIGH_TICKS      150u     /* 1.5ms,必须按波形和RC标定 */
#define CAP_FAULT_CLOCK         0x0101u
#define CAP_FAULT_OVERFLOW      0x0102u

extern void App_CapturePulse(uint32_t high_ticks, uint32_t interval_ticks);
extern void App_CaptureTelemetry(uint32_t fault_code);

static volatile uint32_t s_interval_ticks;   /* 相邻上升沿间隔;单次事件可能为0 */
static volatile uint32_t s_capture_overflow;
static volatile uint32_t s_capture_veto;
static volatile uint8_t  s_interval_valid;
static volatile uint8_t  s_pulse_seen;
static volatile uint8_t  s_frame_active;

static void Tim0_Capture_Init(void)
{
    GPIO_InitTypeDef        g;
    TIM_TimeBaseInitTypeDef tb;
    TIM_ICInitTypeDef       ic;
    PLIC_InitTypeDef        p;
    SMU_ClocksTypeDef       clks;
    uint32_t                tim_clk_hz;
    uint32_t                prescaler;

    GPIOB_CLK_ENABLE();
    PLIC_CLK_ENABLE();
    HTIM0_CLK_ENABLE();

    if (SMU_GetClocksFreq(&clks) != SUCCESS) {
        App_CaptureTelemetry(CAP_FAULT_CLOCK);
        return;
    }

    tim_clk_hz = clks.AXILite4Bus1_Frequency;  /* TIM0 的 CK_INT,不是 CPU 主频 */
    if ((tim_clk_hz < CAP_TICK_HZ) || ((tim_clk_hz % CAP_TICK_HZ) != 0u)) {
        App_CaptureTelemetry(CAP_FAULT_CLOCK);
        return;
    }
    prescaler = tim_clk_hz / CAP_TICK_HZ;

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_HTIM0_M2);  /* PB11 -> TIM0_CH1 */
    GPIO_StructInit(&g);
    g.GPIO_Pin   = GPIO_Pin_11;
    g.GPIO_Mode  = GPIO_Mode_IN;
    g.GPIO_IType = GPIO_IPD;      /* 空闲为低,避免 FPGA 未配置时悬空;上升沿开始计宽 */
    GPIO_Init(GPIOB, &g);

    TIM_TimeBaseStructInit(&tb);
    tb.TIM_CounterMode = TIM_CounterMode_Up;
    tb.TIM_ClockDivision = TIM_CKD_DIV1;
    tb.TIM_Period      = 0xFFFFFFFFU;             /* TIM0 是 32 位定时器 */
    tb.TIM_Prescaler   = prescaler - 1u;           /* 100kHz -> 10µs/tick */
    TIM_TimeBaseInit(TIM0, &tb);

    TIM_ICStructInit(&ic);
    ic.TIM_Channel     = TIM_Channel_1;
    ic.TIM_ICPolarity  = TIM_ICPolarity_Rising;
    ic.TIM_ICSelection = TIM_ICSelection_DirectTI;
    ic.TIM_ICFilter    = CAP_IC_FILTER;
    TIM_PWMIConfig(TIM0, &ic);                     /* IC1=上升沿,IC2自动配成下降沿 */

    TIM_SelectInputTrigger(TIM0, TIM_TS_TI1FP1);   /* 上升沿复位 CNT */
    TIM_SelectSlaveMode(TIM0, TIM_SlaveMode_Reset);/* PWMIConfig 不会自动配置从模式 */

    /*
     * TimeBaseInit 会写 EGR.UG,可能留下 Update 标志。
     * 本例不开启 Update 中断,只清理捕获和更新标志。
     */
    TIM_ClearFlag(TIM0, TIM_FLAG_Update);
    TIM_ClearITPendingBit(TIM0, TIM_IT_CC1);
    TIM_ClearITPendingBit(TIM0, TIM_IT_CC2);
    TIM_ClearFlag(TIM0, TIM_FLAG_CC1OF);
    TIM_ClearFlag(TIM0, TIM_FLAG_CC2OF);

    s_interval_ticks  = 0u;
    s_capture_overflow = 0u;
    s_capture_veto     = 0u;
    s_interval_valid   = 0u;
    s_pulse_seen       = 0u;
    s_frame_active     = 0u;

    TIM_ITConfig(TIM0, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);

    PLIC_StructInit(&p);
    p.PLIC_IRQChannel    = TIM0_IRQn;
    p.PLIC_IRQPriority   = 1;
    p.PLIC_IRQChannelCmd = ENABLE;
    PLIC_Init(&p);
    TIM_Cmd(TIM0, ENABLE);
}

void TIM0_IRQ_Handler(void)       /* 注意:不是 TIM0_IRQHandler */
{
    uint32_t hi;
    uint32_t interval;

    /*
     * 溢出时先读捕获寄存器,再清溢出和CCxIF,避免旧值进入业务层。
     * 这里不使用 Update 中断;从模式复位产生的 UIF 不能用于本业务。
     */
    if ((TIM_GetFlagStatus(TIM0, TIM_FLAG_CC1OF) == SET) ||
        (TIM_GetFlagStatus(TIM0, TIM_FLAG_CC2OF) == SET)) {

        (void)TIM_GetCapture1(TIM0);
        (void)TIM_GetCapture2(TIM0);

        if (TIM_GetFlagStatus(TIM0, TIM_FLAG_CC1OF) == SET) {
            TIM_ClearFlag(TIM0, TIM_FLAG_CC1OF);
        }
        if (TIM_GetFlagStatus(TIM0, TIM_FLAG_CC2OF) == SET) {
            TIM_ClearFlag(TIM0, TIM_FLAG_CC2OF);
        }
        TIM_ClearITPendingBit(TIM0, TIM_IT_CC1);
        TIM_ClearITPendingBit(TIM0, TIM_IT_CC2);

        ++s_capture_overflow;
        s_interval_valid = 0u;
        s_pulse_seen     = 0u;
        s_frame_active   = 0u;
        App_CaptureTelemetry(CAP_FAULT_OVERFLOW);
        return;
    }

    /*
     * 上升沿:记录一帧开始,并保存"上一次上升沿到本次上升沿"的间隔。
     * 第一个孤立脉冲没有前一个上升沿,因此 interval_valid 保持为 0。
     */
    if (TIM_GetITStatus(TIM0, TIM_IT_CC1) == SET) {
        uint32_t cc1 = TIM_GetCapture1(TIM0);

        if ((s_pulse_seen != 0u) && (cc1 != 0u)) {
            s_interval_ticks = cc1;
            s_interval_valid  = 1u;
        } else {
            s_interval_valid = 0u;
        }
        s_pulse_seen   = 1u;
        s_frame_active = 1u;
        TIM_ClearITPendingBit(TIM0, TIM_IT_CC1);
    }

    /* 下降沿:读取高电平宽度,在本沿完成业务结算,不等待下一次上升沿。 */
    if (TIM_GetITStatus(TIM0, TIM_IT_CC2) == SET) {
        hi = TIM_GetCapture2(TIM0);
        interval = (s_interval_valid != 0u) ? s_interval_ticks : 0u;
        TIM_ClearITPendingBit(TIM0, TIM_IT_CC2);

        if ((s_frame_active == 0u) ||
            (hi < CAP_MIN_HIGH_TICKS) ||
            (hi > CAP_MAX_HIGH_TICKS)) {
            ++s_capture_veto;
            s_frame_active = 0u;
            /*
             * 脉宽越界说明本次上升沿本身就是毛刺,不能再作为
             * 下一次"相邻上升沿间隔"的基准。
             */
            if ((hi < CAP_MIN_HIGH_TICKS) || (hi > CAP_MAX_HIGH_TICKS)) {
                s_pulse_seen = 0u;
            }
            return;
        }

        App_CapturePulse(hi, interval);
        s_frame_active = 0u;
    }
}

/* 由周期任务读取后上报遥测。 */
uint32_t Tim0_Capture_GetVetoCount(void)     { return s_capture_veto; }
uint32_t Tim0_Capture_GetOverflowCount(void) { return s_capture_overflow; }

移植前先查手册:在本芯片这组配置下,PWM 输入模式是 CCR1 = 相邻上升沿间隔、CCR2 = 高电平宽度。不同 MCU 的 PWM 输入例程不一定使用相同的极性、触发源和寄存器语义。对单次、稀疏的 LEO 事件,应以 CC2 下降沿和高电平宽度做主判据;只有协议明确要求测周期时,才把 CCR1 的间隔纳入判决。

App_CapturePulse() 运行在中断上下文:这里只应做入队、置标志或通知任务,脉宽换算、协议解析、日志和遥测等重负载必须放到任务上下文。否则捕获中断被拉长,下一次边沿到来前寄存器未及时读走,反而会推高 CCxOF 风险。

6 协议兜底 + 踩坑清单

输入捕获治不了"脉宽恰好合法"的巧合毛刺,协议层必须兜底:帧起始符 + 数据序列 + 序号 + CRC + ACK 重传,把"单脉冲=事件"改成"整帧=事件"。简单编码如 1ms 高+1ms 低+1ms 高,单个毛刺凑不出完整事件。

易踩坑:

  1. RC 不是越大越好,τ> T/10 会削窄有效脉冲,表现为"偶发丢事件",比误触发更难排查;
  2. 别指望 TIM_ICFilter 主责,窗口就几 µs;
  3. 上电复位期 IO 门控,等 FPGA 就绪握手后再开接收;FPGA PLL 锁定和配置完成时间按具体器件手册确认,不要把某个固定值当成通用保证;
  4. MCU 引脚作为输入时,GPIO_OStrength 不控制外部信号边沿。是否降低驱动、减缓边沿,应在 FPGA 输出侧按信号完整性、串扰和 EMI 评估;不要用 MCU 的 4.5mA 档作为边沿控制手段;
  5. 空间环境:关键逻辑 TMR、未用 IO 不悬空、看门狗兜底、毛刺否决计数上遥测(关注 SET/SEU/SAA)。

7 测试清单(只看"不误触发"不够)

测试 注入条件 预期
窄毛刺抑制 1ns/100ns/1µs/10µs 否决计数涨,无事件输出
脉宽下边界 0.69T / 0.71T 前者丢后者收
脉宽上边界 1.4T / 1.6T 前者收后者丢
输入电平边界 VILmax/VIHmin 附近及带裕量电平 验证不落入不确定区;3.3V 下 0.6VDD=1.98V 不能当作无效高电平
密集毛刺 100kHz 持续 10s CPU 正常,不看门狗复位
上电抖动 握手前制造 IO 抖动 接收保持关闭
温压拉偏 高低温+电压复测边界 判据正常
GPIO 边沿中断熔断 高频连续边沿 冷却计数涨,超限关中断上报

关键:注入干扰后否决计数不动,说明某层防护没生效。

结语

IO 抗毛刺是多层防护责任划分:电气层、RC/施密特、硬件滤波、软件判据、协议层层层接力,没有哪一层能独立解决全部干扰。外部耦合瞬态和芯片内部 SET 必须分开评估;外部滤波能降低干扰进入采样链的概率,但不能保证内部状态位不被翻转。真正要把握的是"过滤毛刺"和"别把有效脉冲滤掉"的平衡。所有结论都带适用前提,属于工程权衡------同一个边沿中断,放业务脉冲入口是隐患,做告警输入就是标准方案。读结论,更要看前提。

有帮助欢迎点赞收藏,星载硬件、抗辐照问题评论区交流。

相关推荐
Tel1992530800439 分钟前
旋转变压器型编码器转多种绝对值协议 旋变转SSI/BISS-C/ENDAT2.2 RS422RTU协议+增量 转换器
单片机·自动化·工业自动化·仪器仪表·工控设备
2501_922639581 小时前
率能半导体 SS6813H双通道H桥驱动芯片:40V/2.5A,覆盖POS打印机、安防相机、办公自动化、游戏机、机器人五大应用场景
单片机·嵌入式硬件·硬件工程
智购科技无人售货机厂家2 小时前
自动售货机物联网平台为什么会出现在线状态不一致?~YH
python·单片机·嵌入式硬件·django·pygame
Riwuarua10 小时前
从近似0基础开始FPGA开发——Part.25:GT 高速收发器架构与 KU5P 资源规划
fpga开发·架构
zhangrelay10 小时前
新版|-|单片机原理与接口技术-2026持续改进优化-从MEGA2560-ESP32与Wokwi到ROS2-Lyrical与具身智能栈
笔记·单片机·嵌入式硬件·学习
comyccccc11 小时前
STM32F103C8T6最小系统板设计全解析学习笔记
单片机·1024程序员节
Industio_触觉智能11 小时前
把NPU检测直接嵌进采集终端|RK3576 AI采集终端全链路拆解教程-下
嵌入式硬件·ai·硬件架构·npu·ai视觉·rk3576·采集终端
en.en..12 小时前
STM32 GPIO ODR 与 BSRR 寄存器完整对比详解
单片机·嵌入式硬件
星夜离尘5613 小时前
FPGA 三点中值滤波:不补零、不丢背压样本的流式实现
fpga开发