Files
stc32g128k/Drivers/rf.c
edisondeng a7a2cc9465 暂存
2026-07-31 13:57:06 +08:00

457 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 边沿中断增量解码状态机
* ========================================================================= */
// 计时阈值 (单位: tick0.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); // 关断射频
}