Diagnostic experiment: restore the commit d8df5d4 polling decode
algorithm/thresholds unchanged, while P3.6 ISR now only increments an
edge counter (no decode state machine). Successful decodes print the
edge-count delta over the decode window to compare against the
EV1527 theoretical 50-edges-per-frame, to validate/refute the
edge-interrupt glitch-noise hypothesis.
441 lines
15 KiB
C
441 lines
15 KiB
C
#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));
|
||
|
||
// DEBUG: 临时去掉 P3.6 内部上拉,测试是否是它干扰了真实信号接收 (isr_jump.asm 向量重定向
|
||
// 修好后,即使没有上拉、芯片关闭时引脚悬空乱跳也只是空耗 CPU,不会再导致重启,可以安全去掉测试)
|
||
// 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; // 清除挂起的中断标志
|
||
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);
|
||
}
|
||
|
||
/* =========================================================================
|
||
* DEBUG 实验:验证方案 —— 解码逻辑临时切回已验证工作的轮询版本,
|
||
* P3.6 边沿中断只做纯计数,不参与解码。轮询每次解码成功时,把"解码开始
|
||
* 前"和"解码成功后"的中断边沿计数快照打印出来,跟 EV1527 协议理论边沿数
|
||
* (同步头 2 个 + 24 位数据 × 每位 2 个 = 50 个) 对比,用来验证:"轮询解码
|
||
* 成功的这段时间里,真实硬件边沿次数是否稳定、是否符合协议"。
|
||
* ========================================================================= */
|
||
|
||
// 纯计数用的边沿计数器 (由 RF_HandleEdgeInterrupt 递增,不做任何解码状态机处理)
|
||
static volatile u32 dbg_isr_edge_count = 0;
|
||
|
||
/**
|
||
* @brief DEBUG: P3.6 边沿中断处理 —— 本实验阶段只做计数,不参与任何解码逻辑
|
||
*/
|
||
void RF_HandleEdgeInterrupt(void)
|
||
{
|
||
dbg_isr_edge_count++;
|
||
}
|
||
|
||
/**
|
||
* @brief DEBUG: 打印 P3.6 边沿中断纯计数快照 (本实验阶段唯一保留的排查计数器)
|
||
*/
|
||
void RF_PrintDebugCounters(void)
|
||
{
|
||
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 DEBUG 对照实验:恢复自 commit d8df5d4 已验证工作的轮询版本实现,
|
||
* 解码成功时额外打印解码前后 P3.6 中断边沿计数快照,用于验证边沿中断
|
||
* 在一次成功解码期间的实际触发次数是否稳定、是否符合 EV1527 协议理论值 (50)。
|
||
*/
|
||
bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||
{
|
||
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;
|
||
|
||
edge_before = dbg_isr_edge_count; // DEBUG: 解码尝试开始前的边沿计数快照
|
||
|
||
// ====== 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;
|
||
}
|
||
}
|
||
|
||
// 捕获引导头高电平时间
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* @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); // 关断射频
|
||
}
|