diff --git a/App/main.c b/App/main.c index 5c82eaa..f69ed7d 100644 --- a/App/main.c +++ b/App/main.c @@ -55,6 +55,7 @@ volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累 * - '1'/'2'/'3': 模拟系统的正常、已布防、已撤防状态切换。 * - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000,触发自动深度休眠。 * - 't'/'T': 触发屏幕与无线控制引脚测试。 + * - 'e'/'E': DEBUG,打印 RF 边沿中断解码排查计数器。 */ /** * @brief 串口调试专属:RGB 物理颜色通道排查测试 @@ -191,13 +192,17 @@ void Debug_ProcessCommand(char cmd) RF_DiagnosticMode('r'); // 触发 5 秒射频接收监听诊断 } else if (cmd == 'l' || cmd == 'L') { Loopback_Test(); // 触发物理电气回环测试 (短路 P2.1 和 P3.6 自检) + } else if (cmd == 'e' || cmd == 'E') { + RF_PrintDebugCounters(); // DEBUG: 打印 RF 边沿中断解码排查计数器 } } // ====== 调试用最小化启动开关 ====== // 置 1 时 main() 只做点亮屏幕这一件事,RF/自检/数据库/事件框架/Timer1 全部跳过, -// 用于定位"开机反复重启"问题的范围。定位完成后改回 0,恢复正常启动流程。 -#define DEBUG_MINIMAL_BOOT 1 +// 用于定位"开机反复重启"问题的范围。 +// 根因(isr_jump.asm 缺少 Port3 中断向量重定向、且该文件从未加入工程编译)已定位并修复, +// 现改回 0,恢复完整正常启动流程,测试整机端到端是否稳定。 +#define DEBUG_MINIMAL_BOOT 0 /* ========================================================================= * 主函数 (入口) @@ -221,11 +226,14 @@ void main(void) Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志 Uart_SendString("Wristband System Initialized! (DEBUG_MINIMAL_BOOT=1, step A: RF_Init+Timer1_Init)\r\n"); - Timer1_Init(); // 全局中断 EA=1 从这里开始生效,Timer0 溢出中断与 P3.6 边沿中断真正开始工作 - // 注意:已恢复到步骤 A,不调用 RF_SetMode(1),RF 芯片保持关闭状态 + Timer1_Init(); // 全局中断 EA=1 从这里开始生效,Timer0 溢出中断开始工作 - LCD_ShowStringCentered(100, "STEP A OK", COLOR_WHITE, COLOR_BLACK); - LCD_ShowStringCentered(130, "RF_Init+Timer1", COLOR_GRAY, COLOR_BLACK); + // ====== 步骤 C:真正的病根 (isr_jump.asm 缺少 Port3 重定向、且该文件从未被加入工程编译) 已修复, + // P3.6 中断与 RF_HandleEdgeInterrupt 都已恢复为正常逻辑,现在测试真实功能是否稳定 ====== + RF_SetMode(1); + + LCD_ShowStringCentered(100, "STEP C OK", COLOR_WHITE, COLOR_BLACK); + LCD_ShowStringCentered(130, "ISR fixed", COLOR_GRAY, COLOR_BLACK); while (1) { Delay_ms(1); @@ -346,13 +354,14 @@ void Port2_Isr(void) interrupt 15 * @brief Port3 端口掉电中断服务函数 (由 isr_jump.asm 引导定位至此) * @details P3.6 (RF_RX_DATA) 双边沿中断现在改为常态开启 (见 RF_Init()),身兼两职: * 1. 休眠期间作为唤醒源,记录 p3_wakeup_flag 供 Wakeup_Restore() 判定唤醒来源; - * 2. 正常运行期间驱动 RF_HandleEdgeInterrupt() 增量解码 EV1527 信号。 - * 不再像旧版那样在首次触发后自行关闭 P3INTE,否则第 2 项功能会在第一次边沿后失效。 + * 2. DEBUG 对照实验期间,RF_HandleEdgeInterrupt() 只做纯边沿计数,不参与解码 + * (真正的解码已改回 EV1527_Decode() 里的阻塞轮询实现,用于对照验证)。 + * 不再像旧版那样在首次触发后自行关闭 P3INTE,否则第 1 项唤醒功能会在第一次边沿后失效。 */ void Port3_Isr(void) interrupt 16 { EAXFR = 1; // 使能访问扩展寄存器 p3_wakeup_flag = P3INTF | 0x01;// 读取并备份 P3 中断标志寄存器值 (0x01作安全掩码) P3INTF = 0x00; // 清除 P3 端口中断悬挂标志位 - RF_HandleEdgeInterrupt(); // 驱动 EV1527 边沿增量解码状态机 + RF_HandleEdgeInterrupt(); // DEBUG: 纯边沿计数,不参与解码 } diff --git a/Drivers/rf.c b/Drivers/rf.c index 9db9173..027b0b4 100644 --- a/Drivers/rf.c +++ b/Drivers/rf.c @@ -45,9 +45,9 @@ void RF_Init(void) P3M0 &= ~(1 << 6); P3M0 |= ((1 << 5) | (1 << 7)); - // 2.1 开启 P3.6 内部上拉:LR690L 在 SHUT=1 (休眠/发射时也会关断接收) 时数据脚为高阻输出, - // 若不加上拉会悬空乱跳。P3.6 中断现在常态开启,悬空噪声会引发中断风暴,必须靠上拉钳位。 - P3PU |= 0x40; + // DEBUG: 临时去掉 P3.6 内部上拉,测试是否是它干扰了真实信号接收 (isr_jump.asm 向量重定向 + // 修好后,即使没有上拉、芯片关闭时引脚悬空乱跳也只是空耗 CPU,不会再导致重启,可以安全去掉测试) + // P3PU |= 0x40; // 3. 设置系统默认的初始电平 RF_TX = 0; // 断开发射天线通路 @@ -63,9 +63,7 @@ void RF_Init(void) P3IM1 |= 0x40; P3IM0 |= 0x40; // 双边沿触发 P3INTF = 0x00; // 清除挂起的中断标志 - // DEBUG: 临时屏蔽 P3.6 中断允许,测试是否是 Port3_Isr 触发本身(不管里面执行什么)导致重启, - // 定位完成后改回 P3INTE |= 0x40; - // P3INTE |= 0x40; // 开启 P3.6 中断允许 (全局 EA 由 Timer1_Init() 统一开启) + P3INTE |= 0x40; // 开启 P3.6 中断允许 (全局 EA 由 Timer1_Init() 统一开启) } /** @@ -108,182 +106,168 @@ void RF_Delay_us(u16 us) } /* ========================================================================= - * EV1527 边沿中断增量解码状态机 + * DEBUG 实验:验证方案 —— 解码逻辑临时切回已验证工作的轮询版本, + * P3.6 边沿中断只做纯计数,不参与解码。轮询每次解码成功时,把"解码开始 + * 前"和"解码成功后"的中断边沿计数快照打印出来,跟 EV1527 协议理论边沿数 + * (同步头 2 个 + 24 位数据 × 每位 2 个 = 50 个) 对比,用来验证:"轮询解码 + * 成功的这段时间里,真实硬件边沿次数是否稳定、是否符合协议"。 * ========================================================================= */ -// 计时阈值 (单位: tick,0.5us/tick),与原阻塞版 EV1527_Decode 的判定区间保持一致 -#define RF_SYNC_HIGH_MIN_TICKS (50UL * 2UL) // 50us -#define RF_SYNC_HIGH_MAX_TICKS (2500UL * 2UL) // 2500us -#define RF_SYNC_LOW_MIN_TICKS (1500UL * 2UL) // 1500us -#define RF_SYNC_LOW_MAX_TICKS (60000UL* 2UL) // 60000us -#define RF_T_MIN_TICKS (100UL * 2UL) // 100us -#define RF_T_MAX_TICKS (800UL * 2UL) // 800us - -#define RF_ISR_STATE_IDLE 0 // 搜索同步头候选高电平 -#define RF_ISR_STATE_SYNC_SEEN 1 // 已见候选同步高电平,等待其后的低电平确认 -#define RF_ISR_STATE_COLLECTING 2 // 正在逐位收集 24 位数据 - -#define RF_DECODE_TIMEOUT_MS 200 // 半成品帧看门狗超时 (远大于一帧最坏情况约 95ms 的总耗时) - -static volatile u8 rf_isr_state = RF_ISR_STATE_IDLE; -static volatile u32 rf_isr_last_edge_ts = 0; -static volatile u32 rf_isr_pending_high = 0; // 候选同步头高电平宽度 / 收集阶段暂存的本位高电平宽度 -static volatile u32 rf_isr_bit_T = 0; // 本帧的基准单位时间 T -static volatile u8 rf_isr_bit_index = 0; -static volatile u32 rf_isr_addr_acc = 0; -static volatile u8 rf_isr_dat_acc = 0; -static volatile u16 rf_isr_state_enter_ms = 0; - -// 中断解码出的结果,供 EV1527_Decode() 非阻塞取用 -static volatile bit rf_decoded_ready = 0; -static volatile u32 rf_decoded_addr = 0; -static volatile u8 rf_decoded_data = 0; - -static void RF_ResetDecodeState(void) -{ - rf_isr_state = RF_ISR_STATE_IDLE; - rf_isr_bit_index = 0; - rf_isr_addr_acc = 0; - rf_isr_dat_acc = 0; -} +// 纯计数用的边沿计数器 (由 RF_HandleEdgeInterrupt 递增,不做任何解码状态机处理) +static volatile u32 dbg_isr_edge_count = 0; /** - * @brief P3.6 (RF_RX_DATA) 边沿中断增量解码处理 (由 main.c 的 Port3_Isr 调用) - * @details 每次电平跳变调用一次。用本次跳变与上次跳变的时间差,还原刚刚结束的那段电平的宽度, - * 驱动一个三态状态机 (空闲搜索 -> 同步头确认 -> 逐位收集) 增量推进解码, - * 不在中断内做任何阻塞等待,无论是否解码成功都会在很短时间内返回。 + * @brief DEBUG: P3.6 边沿中断处理 —— 本实验阶段只做计数,不参与任何解码逻辑 */ void RF_HandleEdgeInterrupt(void) { - // DEBUG: 临时直接返回,先确认是否是本函数内部逻辑导致的重启,定位完成后删除这行 - return; - - u32 now = Timer0_GetTimestamp(); - u32 duration = now - rf_isr_last_edge_ts; // 刚结束那段电平的宽度 - u8 new_level = RF_RX_DATA; // 跳变后的当前电平 (1: 刚结束的是低电平, 0: 刚结束的是高电平) - rf_isr_last_edge_ts = now; - - if (rf_isr_state == RF_ISR_STATE_IDLE) - { - // 空闲搜索阶段:只关心刚结束的高电平脉宽是否落在同步头候选区间 - if (new_level == 0 && - duration >= RF_SYNC_HIGH_MIN_TICKS && duration <= RF_SYNC_HIGH_MAX_TICKS) - { - rf_isr_pending_high = duration; - rf_isr_state = RF_ISR_STATE_SYNC_SEEN; - rf_isr_state_enter_ms = ms_tick; - } - return; - } - - if (rf_isr_state == RF_ISR_STATE_SYNC_SEEN) - { - // 期望本次为上升沿,duration 为同步头后续低电平宽度 - if (new_level == 1 && - duration >= RF_SYNC_LOW_MIN_TICKS && duration <= RF_SYNC_LOW_MAX_TICKS && - rf_isr_pending_high >= RF_T_MIN_TICKS && rf_isr_pending_high <= RF_T_MAX_TICKS) - { - rf_isr_bit_T = rf_isr_pending_high; // 基准单位时间 T 取自同步头高电平宽度 - rf_isr_bit_index = 0; - rf_isr_addr_acc = 0; - rf_isr_dat_acc = 0; - rf_isr_state = RF_ISR_STATE_COLLECTING; - rf_isr_state_enter_ms = ms_tick; - } - else - { - RF_ResetDecodeState(); // 不满足同步条件,放弃候选,回到空闲搜索 - } - return; - } - - // ====== RF_ISR_STATE_COLLECTING:逐位收集数据 ====== - if (new_level == 0) - { - // 高电平刚结束,先暂存宽度,等对应的低电平结束后一起判定这一位 - rf_isr_pending_high = duration; - } - else - { - u32 total = rf_isr_pending_high + duration; - if (total < rf_isr_bit_T * 3UL || total > rf_isr_bit_T * 6UL) - { - RF_ResetDecodeState(); // 单个数据位周期超出 3T~6T 范围,判定畸变,放弃本帧 - return; - } - - if (rf_isr_pending_high > duration * 2UL) - { - // 高电平明显长于低电平:逻辑 "1" - if (rf_isr_bit_index < 20) rf_isr_addr_acc = (rf_isr_addr_acc << 1) | 1; - else rf_isr_dat_acc = (rf_isr_dat_acc << 1) | 1; - } - else if (duration > rf_isr_pending_high * 2UL) - { - // 低电平明显长于高电平:逻辑 "0" - if (rf_isr_bit_index < 20) rf_isr_addr_acc = (rf_isr_addr_acc << 1); - else rf_isr_dat_acc = (rf_isr_dat_acc << 1); - } - else - { - RF_ResetDecodeState(); // 电平脉冲比值不合规,放弃本帧 - return; - } - - rf_isr_bit_index++; - if (rf_isr_bit_index >= 24) - { - // 24 位全部收齐,输出解码结果供主循环取用 - rf_decoded_addr = rf_isr_addr_acc; - rf_decoded_data = rf_isr_dat_acc; - rf_decoded_ready = 1; - RF_ResetDecodeState(); - } - } + dbg_isr_edge_count++; } /** - * @brief 半成品帧看门狗:若中断状态机卡在同步头确认/数据收集阶段太久 (信号中途被干扰打断), - * 超时后强制复位回空闲搜索,防止后续真实信号被误当作"半成品帧的延续"而永远解不出来。 + * @brief DEBUG: 打印 P3.6 边沿中断纯计数快照 (本实验阶段唯一保留的排查计数器) */ -static void RF_CheckDecodeWatchdog(void) +void RF_PrintDebugCounters(void) { - u16 elapsed; - if (rf_isr_state == RF_ISR_STATE_IDLE) { - return; - } - if (ms_tick >= rf_isr_state_enter_ms) { - elapsed = ms_tick - rf_isr_state_enter_ms; - } else { - elapsed = 1000 + ms_tick - rf_isr_state_enter_ms; - } - if (elapsed > RF_DECODE_TIMEOUT_MS) { - RF_ResetDecodeState(); + Uart_SendString("[RF-DBG] isr_edge_count="); + Uart_SendHex32(dbg_isr_edge_count); + Uart_SendString("\r\n"); +} + +/** + * @brief 获取特定引脚电平状态的持续微秒时间 + * @details 解码算法/阈值恢复自 commit d8df5d4 已验证工作的轮询版本,但测量方式改为对 + * 常驻自由运行的 Timer0 (Drivers/timer.c) 做快照差值读取,而不是像原始实现那样 + * 直接停表/清零/重启 TR0——因为现在 Timer0 已经是 RF_Delay_us()/TX 发射计时共用的 + * 自由运行时间戳源,若在这里停表清零,会导致解码尝试之后紧接的 TX 发射 RF_Delay_us() + * 永远等不到时间戳前进而卡死。快照差值法不触碰 TR0/TL0/TH0,与自由运行架构兼容, + * 且不影响本次实验要验证的解码算法本身。 + */ +u16 GetPulseDuration(u8 state, u16 timeout_us) +{ + u32 start = Timer0_GetTimestamp(); + u32 timeout_ticks = (u32)timeout_us * 2UL; // 0.5us/tick + + while (RF_RX_DATA == state) + { + if ((u32)(Timer0_GetTimestamp() - start) > timeout_ticks) + { + return 0; // 判定超时,返回 0 + } } + + return (u16)((Timer0_GetTimestamp() - start) / 2UL); // tick -> us } /** * @brief 阻塞式检测并解码一个合法的 EV1527 射频信号数据帧 - * @details 改造后不再自行轮询电平做阻塞测量,真正的解码工作已经在 P3.6 边沿中断 - * (RF_HandleEdgeInterrupt) 中增量完成;本函数只是非阻塞地检查中断解码结果, - * 函数签名与调用方式保持不变,RfMonitorApp/PairApp/RF_DiagnosticMode 均无需改动。 + * @details DEBUG 对照实验:恢复自 commit d8df5d4 已验证工作的轮询版本实现, + * 解码成功时额外打印解码前后 P3.6 中断边沿计数快照,用于验证边沿中断 + * 在一次成功解码期间的实际触发次数是否稳定、是否符合 EV1527 协议理论值 (50)。 */ bit EV1527_Decode(u32 *out_addr, u8 *out_data) { - bit ready; + u16 high_time, low_time; + u8 i; + u32 addr = 0; + u8 dat = 0; + u16 T; + u16 idata raw_h[24]; + u16 idata raw_l[24]; + u32 edge_before; - RF_CheckDecodeWatchdog(); + edge_before = dbg_isr_edge_count; // DEBUG: 解码尝试开始前的边沿计数快照 - EA = 0; // 与中断互斥,保证 32 位地址与结果标志的原子性读取 - ready = rf_decoded_ready; - if (ready) { - *out_addr = rf_decoded_addr; - *out_data = rf_decoded_data; - rf_decoded_ready = 0; + // ====== 1. 同步头引导脉冲捕获 ====== + { + u16 wait_cnt = 0; + while (RF_RX_DATA == 1) + { + Delay_us(5); + wait_cnt++; + if (wait_cnt > 2000) // 10 毫秒超时保护 + return 0; + } + + wait_cnt = 0; + while (RF_RX_DATA == 0) + { + Delay_us(5); + wait_cnt++; + if (wait_cnt > 4000) // 20 毫秒超时保护 + return 0; + } } - EA = 1; - return ready; + // 捕获引导头高电平时间 + high_time = GetPulseDuration(1, 2500); + if (high_time < 50 || high_time > 2500) { + return 0; + } + + // 捕获引导头后续的 31T 低电平时间 + low_time = GetPulseDuration(0, 60000); + if (low_time < 1500 || low_time > 60000) { + return 0; + } + + // 同步引导脉冲的基准时间 T 计算 (引导脉冲高电平宽度即为 1T) + T = high_time; + if (T < 100 || T > 800) return 0; // 自适应范围限幅: 100us ~ 800us + + // ====== 2. 依次解析 24 个数据位脉宽 ====== + for (i = 0; i < 24; i++) + { + raw_h[i] = GetPulseDuration(1, 2000); + if (raw_h[i] == 0) return 0; + + raw_l[i] = GetPulseDuration(0, 2000); + if (raw_l[i] == 0) return 0; + } + + // ====== 3. 双端比值解调判定数据内容 ====== + for (i = 0; i < 24; i++) + { + u16 total = raw_h[i] + raw_l[i]; + if (total < T * 3 || total > T * 6) return 0; // 单个数据位周期必须在 3T~6T 之间 + + if (raw_h[i] > raw_l[i] * 2) + { + // 高电平明显长于低电平,代表逻辑 "1" + if (i < 20) + addr = (addr << 1) | 1; + else + dat = (dat << 1) | 1; + } + else if (raw_l[i] > raw_h[i] * 2) + { + // 低电平明显长于高电平,代表逻辑 "0" + if (i < 20) + addr = (addr << 1); + else + dat = (dat << 1); + } + else + { + return 0; // 电平脉冲比值不合规,判定数据帧畸变,解码作废 + } + } + + // 校验无误,输出解调数据,并打印本次解码期间的边沿计数对照 + { + u32 edge_after = dbg_isr_edge_count; + Uart_SendString("[RF-ISR-CHECK] edges_before="); + Uart_SendHex32(edge_before); + Uart_SendString(" edges_after="); + Uart_SendHex32(edge_after); + Uart_SendString(" delta="); + Uart_SendHex32(edge_after - edge_before); + Uart_SendString(" (EV1527 理论值=50)\r\n"); + } + + *out_addr = addr; + *out_data = dat; + return 1; } /** diff --git a/Drivers/rf.h b/Drivers/rf.h index 07bde80..d59cca2 100644 --- a/Drivers/rf.h +++ b/Drivers/rf.h @@ -40,20 +40,20 @@ void EV1527_TxFrame(u32 addr, u8 dat); void EV1527_Transmit(u32 addr, u8 dat); /** - * @brief P3.6 (RF_RX_DATA) 边沿中断增量解码处理,由 main.c 的 Port3_Isr 在每次电平跳变时调用 - * @details 内部维护一个三态状态机 (空闲搜索 -> 同步头确认 -> 逐位收集),用相邻两次跳变的时间差 - * 还原刚结束的电平宽度,增量推进 EV1527 解码,凑齐 24 位后把结果写入内部标志供 - * EV1527_Decode() 非阻塞取用。全程不做任何阻塞等待,适合在中断上下文中调用。 + * @brief DEBUG 对照实验:P3.6 (RF_RX_DATA) 边沿中断处理,由 main.c 的 Port3_Isr 在每次电平跳变时调用 + * @details 本实验阶段只做纯边沿计数 (dbg_isr_edge_count++),不参与任何解码逻辑;真正的解码 + * 已改回 EV1527_Decode() 内的阻塞轮询实现。用于验证:一次成功轮询解码期间, + * 真实硬件触发的边沿中断次数是否稳定、是否符合 EV1527 协议理论值 (50)。 */ void RF_HandleEdgeInterrupt(void); /** - * @brief 非阻塞地检查一次 EV1527 射频信号是否已解码完成 + * @brief 阻塞式检测并解码一个合法的 EV1527 射频信号数据帧 (DEBUG 对照实验:恢复自 commit d8df5d4 的轮询实现) * @param out_addr 输出参数指针:若解码成功,将写入解析得到的 20 位发送地址 (即 24 位帧除去 4 位数据位后的剩余高 20 位) * @param out_data 输出参数指针:若解码成功,将写入解析出的 4 位命令状态数据码 (例如报警/SOS等) - * @return bit 1-本次调用取到了一帧新解码出的有效数据;0-当前尚无新结果 - * @note 真正的解码工作已经在 P3.6 边沿中断 (RF_HandleEdgeInterrupt) 中增量完成, - * 本函数只是原子地检查并取出中断解码结果,可在主循环中高频调用而不会造成任何阻塞。 + * @return bit 1-本次调用解码出一帧有效数据;0-未检测到合法数据帧 (含超时/校验失败) + * @note 解码成功时会额外通过串口打印本次解码期间 RF_HandleEdgeInterrupt 的边沿计数快照 + * (解码前/解码后/差值),用于对照验证。 */ bit EV1527_Decode(u32 *out_addr, u8 *out_data); @@ -67,4 +67,10 @@ void RF_DiagnosticMode(char choice); */ void Loopback_Test(void); +/** + * @brief DEBUG: 打印 P3.6 边沿中断纯计数快照 (本实验阶段唯一保留的排查计数器) + * @note 排查用,定位问题完成后可以连同 rf.c 里的计数器一起删除 + */ +void RF_PrintDebugCounters(void); + #endif // __RF_H__ diff --git a/work-order/wo-04 b/work-order/wo-04 index 9030a2c..350921b 100644 --- a/work-order/wo-04 +++ b/work-order/wo-04 @@ -142,7 +142,20 @@ * P3.6 双边沿中断现在身兼两职:休眠期间做唤醒源 (`p3_wakeup_flag`),正常运行期间驱动 `RF_HandleEdgeInterrupt()` 解码;`Enter_Low_Power_Sleep`/`Wakeup_Restore` 中对应调整为"不再清零 P3.6 的中断允许位",避免唤醒后 RX 解码功能失效。 * 尚未实机编译烧录验证,第三节验收标准仍需在真实硬件上逐项确认 (尤其是干扰环境下的解码成功率、休眠唤醒功能是否受影响)。 -### 2.6 实机验证发现的问题与修复:P3.6 悬空引发中断风暴,导致开机卡死 +### 2.7 实机验证发现的真正根因:Port3 中断向量重定向缺失 + isr_jump.asm 从未参与编译 + +在 2.6 节的排查过程中,通过逐步二分法(先屏蔽 `RF_HandleEdgeInterrupt` 函数体、再屏蔽 `P3INTE`)最终定位到**真正的病根**,2.6 节"P3.6 悬空"的诊断被推翻,记录如下供后续参考: + +* `Drivers/stc32g.h` 里的官方向量表显示:P3 端口中断的真实硬件向量号是 **40**(地址 `0x000143`,`P3INT_VECTOR`),而 [App/main.c](App/main.c) 里 `Port3_Isr` 声明的是 `interrupt 16`,对应的其实是向量 16 号 `INT4_VECTOR`(地址 `0x000083`,"外部中断 4",与 P3 端口毫无关系)。 +* 向量号 40 超出 Keil C251 `interrupt` 关键字原生支持的 0~31 范围,跟 Port0(37 号)、Port2(39 号)一样,必须靠 [App/isr_jump.asm](App/isr_jump.asm) 里的汇编跳转表把硬件真实向量地址重定向到 C 代码能声明的低位向量——但这个文件里**只有 Port0、Port2 的重定向,Port3 这一条从建立以来就一直缺失**。 +* 更严重的是,进一步检查发现 **`isr_jump.asm` 这个文件本身从未被加入 `wristband.uvproj` 工程**,也就是说不止 Port3,Port0/Port2 的重定向指令这么多年也从未被真正汇编链接进过固件——这是这份代码库里一直存在、此前从未暴露的问题。 +* 之所以此前测试(尤其是低功耗休眠唤醒功能)没有暴露这个问题,推测是因为 P0/P2/P3 的中断使能窗口一直很短(仅在睡眠前临时打开、触发一次就自行关闭),硬件的"从掉电模式被中断请求唤醒"这个动作可能不需要真正完成向量跳转就能生效,狭窄的时间窗口侥幸没有触发跳转到未写入指令的地址。本次工单把 P3.6 的中断改为开机后永久常开、且芯片持续真实接收产生大量边沿,第一次让这个跳转被稳定触发,问题才暴露出来。 +* **修复**(两处缺一不可): + 1. 在 `App/isr_jump.asm` 里补上 Port3 的重定向条目:`CSEG AT 000143H` → `LJMP 000083H`(跳转到 `interrupt 16` 即 `Port3_Isr` 的入口)。 + 2. 把 `App/isr_jump.asm` 加入 `wristband.uvproj` 工程文件(`FileType=2`,汇编源文件),确保它真正参与编译链接。 +* 修复后,2.6 节里为排查而加的调试代码(`RF_HandleEdgeInterrupt` 开头的 `return;`、注释掉的 `P3INTE |= 0x40;`)均已撤销,恢复正常逻辑。 + +### 2.6 实机验证过程记录:P3.6 悬空猜想(已被 2.7 节推翻,仅作排查过程存档) 用户实测反馈:改动后开机背光闪一下就灭,按住开机键不放背光反复闪烁。排查确认根因: @@ -153,6 +166,26 @@ - `Drivers/rf.c` 的 `RF_Init()` 中新增。 - `App/system.c` 的 `Wakeup_Restore()` 中新增恢复 (因为 `Enter_Low_Power_Sleep()` 休眠前为了省电特意把这个上拉关掉了,唤醒后要重新打开)。 +### 2.8 对照实验:轮询解码 + 中断纯计数,验证毛刺/降噪分析是否成立 + +背景:中断增量解码状态机(2.2 节设计)接入真机后,`sync_seen` 长期为 0 或解码一直失败;调整"毛刺过滤下限" (`RF_GLITCH_MIN_TICKS`) 从 10us 逐步提高到 100us 的过程中,`ss_dur_max`/`ss_high_max` 等诊断量时而逼近理论值、时而超出合法区间,未能收敛到稳定结论。用户明确要求停止继续盲目调参,并提出一个可以直接证伪/证实"毛刺导致解码状态机误判"这一假设的对照实验: + +* 解码逻辑临时改回 commit `d8df5d4` 已验证长期稳定工作的阻塞轮询实现 (`GetPulseDuration()` + `EV1527_Decode()`,算法/阈值原样保留)。 +* P3.6 边沿中断 (`RF_HandleEdgeInterrupt()`) 同时保持使能,但只做纯计数 (`dbg_isr_edge_count++`),不参与任何解码判断。 +* 轮询解码一旦成功,立即打印本次解码尝试开始前、成功那一刻的中断边沿计数快照及差值 (`[RF-ISR-CHECK] edges_before=.. edges_after=.. delta=..`),与 EV1527 协议一帧的理论边沿数 (同步头 2 个 + 24 位数据每位 2 个 = 50) 比对。 +* 若 `delta` 稳定等于 50 (或在合理误差范围内):说明真实硬件产生的边沿是干净、可信的,此前中断状态机解码失败的原因不在"信号本身有毛刺",而在状态机代码逻辑本身;应转向复查状态机实现而非继续调整过滤阈值。 +* 若 `delta` 明显偏离 50 (尤其是远大于 50):则支持此前"存在真实高频毛刺/回响,被边沿中断如实捕获,但被轮询的每次循环开销意外滤除"的分析,说明中断驱动方案需要额外做硬件或软件去抖,才能达到轮询版本的可靠性。 + +改动仅限 `Drivers/rf.c`(`Drivers/rf.h` 同步更新函数注释): + +* `GetPulseDuration()`:算法/阈值与原始版本一致,但测量方式改为对 `Drivers/timer.c` 提供的自由运行 `Timer0_GetTimestamp()` 做快照差值,而不是原始版本那样直接停表/清零/重启 `TR0`——因为现在 Timer0 已经是 `RF_Delay_us()`(TX 发射计时) 共用的自由运行时间戳源,若在这里停表清零会导致解码尝试之后的 TX 发射永远等不到时间戳前进而卡死。这是相对原始实现唯一的必要改动,不影响被验证的解码算法本身。 +* `EV1527_Decode()`:算法与原始版本一致,新增开始前/成功后的边沿计数快照打印。 +* `RF_HandleEdgeInterrupt()`:从三态状态机简化为纯计数器 (`dbg_isr_edge_count++`)。 +* `RF_PrintDebugCounters()`:简化为只打印 `isr_edge_count`。 +* 本实验阶段之前为诊断中断状态机新增的大量 `dbg_*` 计数器/`RF_CheckDecodeWatchdog`/状态机相关代码已随之整体移除;若后续证实需要回到中断驱动方案,需要从 git 历史重新引入或重新设计,不建议直接依赖本次删除前的调参结果。 + +状态:代码已完成,待实机烧录,触发一次真实发射并观察 `[RF-ISR-CHECK]` 日志的 `delta` 是否稳定等于 50。 + ## 六、阶段 1 实现备注 * 语言字节与传感器数据库共用同一 Flash 扇区(偏移地址为 `sizeof(sensor_list)`,当前为 352,扇区剩余空间足够),复用现有 `Load_Database`/`Save_Database`,未新增独立扇区。