#include "rf.h" #include "timer.h" #include "../App/config.h" #include "../App/rgb.h" #include "intrins.h" // ATR5179 射频前端天线开关控制引脚定义 sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到发射 TX 路径) sbit RF_RX = P3^7; // V2 控制端 (1: 选通天线到接收 RX 路径) // 射频芯片数据与电源控制引脚定义 (根据新版 PCB 引脚对调修正) sbit RF_TX_DAT = P2^1; // LR690L 发射调制数据输入引脚 (DIN) - 物理连接 Pin 22 (P2.1) sbit RF_RX_DATA = P3^6; // LR690L 接收解调数据输出引脚 (DATA) - 物理连接 Pin 19 (P3.6) // 声明外部延时及串口打印函数 (在 system.c 中定义) extern void Delay_us(u16 us); extern void Delay_ms(u16 ms); extern void Uart_SendString(char *s); extern void Uart_SendHex4(u8 val); extern void Uart_SendHex8(u8 val); extern void Uart_SendHex16(u16 val); extern void Uart_SendHex20(u32 val); extern void Uart_SendHex32(u32 val); extern void Uart_SendByte(u8 dat); extern volatile u16 ms_tick; // 全局毫秒滴答 (0~999 循环),用于半成品帧看门狗超时判断 /** * @brief 初始化无线射频芯片相关的 GPIO 引脚方向与状态 * @details 同时在此完成 Timer0 自由运行时间戳基准与 P3.6 (RF_RX_DATA) 常态双边沿中断的 * 一次性开机配置,供 RF_HandleEdgeInterrupt() 增量解码状态机全程使用。 */ void RF_Init(void) { // EAXFR = 1 允许访问扩展特殊功能寄存器 (XSFR) EAXFR = 1; // 1. 配置 P2.0 (RF_TX) 和 P2.1 (RF_TX_DAT) 为推挽输出模式 (M1=0, M0=1) P2M1 &= ~((1 << 0) | (1 << 1)); P2M0 |= ((1 << 0) | (1 << 1)); // 2. 配置 P3.7 (RF_RX) 和 P3.5 (SHUT) 为推挽输出模式 (M1=0, M0=1) // 配置 P3.6 (RF_RX_DATA) 为高阻输入模式 (M1=1, M0=0)以匹配接收解调输入 P3M1 &= ~((1 << 5) | (1 << 7)); P3M1 |= (1 << 6); P3M0 &= ~(1 << 6); P3M0 |= ((1 << 5) | (1 << 7)); // 2.1 开启 P3.6 内部上拉:LR690L 在 SHUT=1 (休眠/发射时也会关断接收) 时数据脚为高阻输出, // 若不加上拉会悬空乱跳。P3.6 中断现在常态开启,悬空噪声会引发中断风暴,必须靠上拉钳位。 P3PU |= 0x40; // 3. 设置系统默认的初始电平 RF_TX = 0; // 断开发射天线通路 SHUT = 1; // 默认拉高 SHUT,使 LR690L 处于休眠关闭状态 RF_RX = 0; // 断开接收天线通路 RF_TX_DAT = 0; // 调制发送数据线置低 // 4. 启动 Timer0 常驻自由运行,作为边沿中断增量解码的时间戳基准 (实现见 Drivers/timer.c) Timer0_Init(); // 5. P3.6 (RF_RX_DATA) 双边沿中断常态开启 (原来只在 Enter_Low_Power_Sleep 里临时开启, // 现在改为开机后永久开启,休眠时复用同一路中断做唤醒源,不再需要单独切换) P3IM1 |= 0x40; P3IM0 |= 0x40; // 双边沿触发 P3INTF = 0x00; // 清除挂起的中断标志 // DEBUG: 临时屏蔽 P3.6 中断允许,测试是否是 Port3_Isr 触发本身(不管里面执行什么)导致重启, // 定位完成后改回 P3INTE |= 0x40; // P3INTE |= 0x40; // 开启 P3.6 中断允许 (全局 EA 由 Timer1_Init() 统一开启) } /** * @brief 配置无线射频收发系统的运行模式与天线切换 * @param mode 运行模式 (0-休眠隔离, 1-接收工作, 2-发射工作) */ void RF_SetMode(u8 mode) { if (mode == 0) // ====== 模式 0: 休眠隔离模式 (Sleep/Idle) ====== { SHUT = 1; // 强制拉高 SHUT 引脚,使 LR690L 彻底进入低功耗待机 RF_RX = 0; // 关闭接收天线开关,防止外界电磁杂波串入接收电路 RF_TX = 0; // 关闭发射天线开关 } else if (mode == 1) // ====== 模式 1: 接收工作模式 (RX Mode) ====== { SHUT = 0; // 拉低 SHUT 使能 LR690L 芯片,开始解调接收 RF_TX = 0; // 关闭发射开关 RF_RX = 1; // 选通天线到 RX 接收路径,使能接收链路高频放大 } else if (mode == 2) // ====== 模式 2: 发发射工作模式 (TX Mode) ====== { SHUT = 1; // 接收机停机,防止发射强信号顶坏接收前端 RF_RX = 0; // 关闭接收开关 RF_TX = 1; // 选通天线到 TX 发射路径,使能天线发射 } } /** * @brief 使用 Timer0 自由运行时间戳实现的高精度微秒延时 * @param us 延时微秒数 * @note 旧版实现会启停复位 Timer0,与本文件新增的常驻自由运行用法冲突 (会导致 RX 时间戳永久停摆), * 改为对运行中的 Timer0 做起止快照比较,纯只读、不影响其自由运行状态。 */ void RF_Delay_us(u16 us) { u32 start = Timer0_GetTimestamp(); u32 wait_ticks = (u32)us * 2UL; // 0.5us/tick while ((u32)(Timer0_GetTimestamp() - start) < wait_ticks); } /* ========================================================================= * EV1527 边沿中断增量解码状态机 * ========================================================================= */ // 计时阈值 (单位: 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; } /** * @brief P3.6 (RF_RX_DATA) 边沿中断增量解码处理 (由 main.c 的 Port3_Isr 调用) * @details 每次电平跳变调用一次。用本次跳变与上次跳变的时间差,还原刚刚结束的那段电平的宽度, * 驱动一个三态状态机 (空闲搜索 -> 同步头确认 -> 逐位收集) 增量推进解码, * 不在中断内做任何阻塞等待,无论是否解码成功都会在很短时间内返回。 */ 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(); } } } /** * @brief 半成品帧看门狗:若中断状态机卡在同步头确认/数据收集阶段太久 (信号中途被干扰打断), * 超时后强制复位回空闲搜索,防止后续真实信号被误当作"半成品帧的延续"而永远解不出来。 */ static void RF_CheckDecodeWatchdog(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(); } } /** * @brief 阻塞式检测并解码一个合法的 EV1527 射频信号数据帧 * @details 改造后不再自行轮询电平做阻塞测量,真正的解码工作已经在 P3.6 边沿中断 * (RF_HandleEdgeInterrupt) 中增量完成;本函数只是非阻塞地检查中断解码结果, * 函数签名与调用方式保持不变,RfMonitorApp/PairApp/RF_DiagnosticMode 均无需改动。 */ bit EV1527_Decode(u32 *out_addr, u8 *out_data) { bit ready; RF_CheckDecodeWatchdog(); EA = 0; // 与中断互斥,保证 32 位地址与结果标志的原子性读取 ready = rf_decoded_ready; if (ready) { *out_addr = rf_decoded_addr; *out_data = rf_decoded_data; rf_decoded_ready = 0; } EA = 1; return ready; } /** * @brief 按照 EV1527 协议单次调制发送一帧 24 位射频数据 (Sync + 24bit Data) */ void EV1527_TxFrame(u32 addr, u8 dat) { u8 i; u32 tx_val; // 合并 20 位地址与 4 位数据,成为 24 位完整 EV1527 帧 tx_val = (addr << 4) | (dat & 0x0F); // 1. 同步头脉冲: 高电平 1T (350us) + 低电平 31T (10850us) RF_TX_DAT = 1; RF_Delay_us(350); RF_TX_DAT = 0; RF_Delay_us(10850); // 2. 依次发送 24 个数据位 (MSB First) for (i = 0; i < 24; i++) { if (tx_val & (0x800000UL >> i)) { // 逻辑 "1": 高电平 3T (1050us) + 低电平 1T (350us) RF_TX_DAT = 1; RF_Delay_us(1050); RF_TX_DAT = 0; RF_Delay_us(350); } else { // 逻辑 "0": 高电平 1T (350us) + 低电平 3T (1050us) RF_TX_DAT = 1; RF_Delay_us(350); RF_TX_DAT = 0; RF_Delay_us(1050); } } } /** * @brief 调制发射 25 帧 EV1527 射频同步电平信号以确保接收侧可靠触发 */ void EV1527_Transmit(u32 addr, u8 dat) { u8 r; bit ea_bak = EA; // 备份并屏蔽全局中断,确保发射高低电平周期的绝对纯净与防抖 EA = 0; RF_SetMode(2); // 切换为发射状态,接通天线与射频模块通道 Delay_ms(5); // 稍作延时使射频通路稳定 // 连续发射 25 帧以保证接收设备顺利锁定并解码 for (r = 0; r < 25; r++) { EV1527_TxFrame(addr, dat); // 去除帧与帧之间可能导致波形不连续的 Delay_ms } RF_TX_DAT = 0; // 结束发射,拉低数据脚 RF_SetMode(1); // 恢复为常态后台接收模式 EA = ea_bak; // 还原全局中断开关状态 } /** * @brief RF 收发诊断测试功能 (支持发送与接收监听打印) */ void RF_DiagnosticMode(char choice) { if (choice == 't') { u8 i; Uart_SendString("=== RF TX Test: Transmitting 20 test frames... ===\r\n"); // 开启发射模式并配置天线开关 RF_SetMode(2); Delay_ms(5); for (i = 0; i < 20; i++) { // 连续发送测试包,每次发送伴随马达短震及指示灯闪烁 MOTOR = 1; RGB_Send(0, 150, 0, 0, 150, 0); // 绿光闪烁 EV1527_TxFrame(0x37A86UL, 0x01); MOTOR = 0; RGB_Send(0, 0, 0, 0, 0, 0); Delay_ms(150); // 帧间隔 } RF_TX_DAT = 0; RF_SetMode(1); // 重新恢复接收模式 Uart_SendString("[RF] Transmit Done!\r\n"); } else if (choice == 'r') { u16 wait_ms; u32 rx_addr; u8 rx_data; Uart_SendString("=== RF RX Test: Listening for 5 seconds... ===\r\n"); RF_SetMode(1); // 配置进入接收模式并使能天线 Delay_ms(10); // 5秒的非阻塞监听 for (wait_ms = 0; wait_ms < 5000; wait_ms++) { if (EV1527_Decode(&rx_addr, &rx_data)) { Uart_SendString("[RF] Decoded successfully! Addr: 0x"); Uart_SendHex32(rx_addr); Uart_SendString(", Cmd/Data: "); Uart_SendHex8(rx_data); Uart_SendString("\r\n"); // 收到信号后马达震动并亮青色灯提醒 MOTOR = 1; RGB_Send(0, 150, 150, 0, 150, 150); Delay_ms(100); MOTOR = 0; RGB_Send(0, 0, 0, 0, 0, 0); } Delay_ms(1); } Uart_SendString("=== RF RX Test End ===\r\n"); } } /** * @brief 电气回环自检测试 (物理短路 P2.1 与 P3.6 自检) * @details 用户通过短路 P2.1 和 P3.6 来实现 GPIO 的物理连通性自检 */ void Loopback_Test(void) { u16 match_count = 0; u16 i; EAXFR = 1; RF_SetMode(1); // 开启接收芯片 Delay_ms(100); Uart_SendString("Starting RF Hardware Loopback Test (P2.1 -> P3.6)...\r\n"); Uart_SendString("Please use a metal tweezers to short-circuit P2.1 (Pin 22) and P3.6 (Pin 19)!\r\n"); for (i = 0; i < 100; i++) { RF_TX_DAT = 1; Delay_us(500); if (RF_RX_DATA == 1) match_count++; RF_TX_DAT = 0; Delay_us(500); if (RF_RX_DATA == 0) match_count++; } Uart_SendString("Loopback Match Count: "); Uart_SendHex16(match_count); Uart_SendString("/200\r\n"); if (match_count > 150) { Uart_SendString("Result: PASS! Pin P2.1 and P3.6 are electrical connected successfully!\r\n"); } else { Uart_SendString("Result: FAIL! GPIO coupling check failed. Short-circuit the pins and test again.\r\n"); } RF_SetMode(0); // 关断射频 }