Files
stc32g128k/Drivers/rf.c

529 lines
20 KiB
C
Raw Normal View History

#include "rf.h"
2026-07-31 13:57:06 +08:00
#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);
2026-07-31 13:57:06 +08:00
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));
// P3.6 内部下拉SHUT=1(芯片关断)期间 DATA 脚高阻悬空,下拉把悬空状态钳在低电平。
// 之前误用上拉,配合当年错误的"高电平中断"配置,悬空高电平会导致中断风暴——
// 现在确认端口中断硬件只有下降沿/上升沿/低电平/高电平四选一,没有真正的"双边沿"模式,
// 已改为下面的软件乒乓单边沿切换方案,配下拉才是让悬空态落在"不触发"一侧的正确选择。
P3PD |= 0x40;
2026-07-31 13:57:06 +08:00
// 3. 设置系统默认的初始电平
RF_TX = 0; // 断开发射天线通路
SHUT = 1; // 默认拉高 SHUT使 LR690L 处于休眠关闭状态
RF_RX = 0; // 断开接收天线通路
RF_TX_DAT = 0; // 调制发送数据线置低
2026-07-31 13:57:06 +08:00
// 4. 启动 Timer0 常驻自由运行,作为边沿中断增量解码的时间戳基准 (实现见 Drivers/timer.c)
Timer0_Init();
// 5. P3.6 (RF_RX_DATA) 中断常态开启。硬件 PxIM1/PxIM0 只支持下降沿/上升沿/低电平/高电平
// 四选一,没有"双边沿都触发"的模式;(IM1=1,IM0=1) 实际是"高电平中断",会在整个高电平
// 期间不断重复触发,这正是之前边沿计数异常偏高(远超协议理论值)的真正原因。
// 现改为初始配置成"上升沿触发",配合 RF_HandleEdgeInterrupt() 里的软件乒乓切换
// (每次触发后立即翻转到相反边沿) 来模拟真正的双边沿触发,每次真实翻转只精确响应一次。
P3IM1 &= ~0x40; // IM1=0
P3IM0 |= 0x40; // IM0=1 => (0,1) 上升沿触发,先等待同步头/数据位高电平的起始
2026-07-31 13:57:06 +08:00
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 发射路径,使能天线发射
}
}
/**
2026-07-31 13:57:06 +08:00
* @brief 使 Timer0
* @param us
2026-07-31 13:57:06 +08:00
* @note Timer0 ( RX )
* Timer0
*/
void RF_Delay_us(u16 us)
{
2026-07-31 13:57:06 +08:00
u32 start = Timer0_GetTimestamp();
u32 wait_ticks = (u32)us * 2UL; // 0.5us/tick
while ((u32)(Timer0_GetTimestamp() - start) < wait_ticks);
}
/* =========================================================================
* EV1527 沿 (沿)
* @details PxIM1/PxIM0 沿/沿//"双边沿都触发"
* (IM1=1,IM0=1) "高电平中断"
* 沿 50 (
* ) RF_Init() 沿(沿)
* 沿()
* P3IM0 "刚刚发生的
* 沿",不再需要像旧版那样读取电平猜测方向,也不需要毛刺过滤器。
2026-07-31 13:57:06 +08:00
* ========================================================================= */
// 计时阈值 (单位: 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 (1200UL * 2UL) // 1200us
#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;
// 简易运行计数器:解码成功帧数 / 因校验不通过而放弃的候选帧数,供 'e' 调试指令查看
static volatile u32 dbg_frame_count = 0;
static volatile u32 dbg_abort_count = 0;
/**
* @brief DEBUG: 沿
*/
void RF_PrintDebugCounters(void)
2026-07-31 13:57:06 +08:00
{
Uart_SendString("[RF-DBG] state=");
Uart_SendHex8(rf_isr_state);
Uart_SendString(" bit_idx=");
Uart_SendHex8(rf_isr_bit_index);
Uart_SendString(" frames=");
Uart_SendHex32(dbg_frame_count);
Uart_SendString(" aborts=");
Uart_SendHex32(dbg_abort_count);
Uart_SendString("\r\n");
2026-07-31 13:57:06 +08:00
}
static void RF_ResetDecodeState(void)
2026-07-31 13:57:06 +08:00
{
rf_isr_state = RF_ISR_STATE_IDLE;
rf_isr_bit_index = 0;
rf_isr_addr_acc = 0;
rf_isr_dat_acc = 0;
}
2026-07-31 13:57:06 +08:00
/**
* @brief "刚刚结束的一段高电平"()
* @param duration (tick)
*/
static void RF_HandleHighSegmentEnd(u32 duration)
{
if (rf_isr_state == RF_ISR_STATE_IDLE)
2026-07-31 13:57:06 +08:00
{
// 空闲搜索阶段:只关心刚结束的高电平脉宽是否落在同步头候选区间
if (duration >= RF_SYNC_HIGH_MIN_TICKS && duration <= RF_SYNC_HIGH_MAX_TICKS)
2026-07-31 13:57:06 +08:00
{
rf_isr_pending_high = duration;
rf_isr_state = RF_ISR_STATE_SYNC_SEEN;
rf_isr_state_enter_ms = ms_tick;
2026-07-31 13:57:06 +08:00
}
return;
}
if (rf_isr_state == RF_ISR_STATE_COLLECTING)
{
// 本位的高电平段先暂存,等对应的低电平段结束后一起判定这一位
rf_isr_pending_high = duration;
return;
2026-07-31 13:57:06 +08:00
}
// SYNC_SEEN 阶段理应只等待低电平段 (乒乓切换保证了 高/低 严格交替出现)
// 不会在这里收到高电平结束事件;防御性地放弃当前候选,避免状态卡死
RF_ResetDecodeState();
dbg_abort_count++;
}
/**
* @brief "刚刚结束的一段低电平"()
* @param duration (tick)
*/
static void RF_HandleLowSegmentEnd(u32 duration)
{
if (rf_isr_state == RF_ISR_STATE_IDLE)
{
return; // 同步头候选还没确立前,空闲期低电平时长任意,不关心
}
if (rf_isr_state == RF_ISR_STATE_SYNC_SEEN)
2026-07-31 13:57:06 +08:00
{
// 期望这段低电平是同步头的 31T 低电平
if (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)
2026-07-31 13:57:06 +08:00
{
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;
2026-07-31 13:57:06 +08:00
}
else
2026-07-31 13:57:06 +08:00
{
RF_ResetDecodeState(); // 不满足同步条件,放弃候选,回到空闲搜索
dbg_abort_count++;
2026-07-31 13:57:06 +08:00
}
return;
2026-07-31 13:57:06 +08:00
}
if (rf_isr_state == RF_ISR_STATE_COLLECTING)
2026-07-31 13:57:06 +08:00
{
u32 total = rf_isr_pending_high + duration;
if (total < rf_isr_bit_T * 3UL || total > rf_isr_bit_T * 6UL)
{
RF_ResetDecodeState(); // 单个数据位周期超出 3T~6T 范围,判定畸变,放弃本帧
dbg_abort_count++;
return;
}
2026-07-31 13:57:06 +08:00
if (rf_isr_pending_high > duration * 2UL)
2026-07-31 13:57:06 +08:00
{
// 高电平明显长于低电平:逻辑 "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;
2026-07-31 13:57:06 +08:00
}
else if (duration > rf_isr_pending_high * 2UL)
2026-07-31 13:57:06 +08:00
{
// 低电平明显长于高电平:逻辑 "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);
2026-07-31 13:57:06 +08:00
}
else
{
RF_ResetDecodeState(); // 电平脉冲比值不合规,放弃本帧
dbg_abort_count++;
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;
dbg_frame_count++;
RF_ResetDecodeState();
2026-07-31 13:57:06 +08:00
}
}
}
2026-07-31 13:57:06 +08:00
/**
* @brief P3.6 (RF_RX_DATA) 沿 ( main.c Port3_Isr )
* @details P3IM0 "触发前配置的是哪种边沿"
* 沿"刚刚发生的是上升沿
* 沿",不需要额外读取电平判断方向。翻转完成后再把 (方向, 距上次边沿的
* )
*/
void RF_HandleEdgeInterrupt(void)
{
u32 now;
u32 duration;
bit was_rising;
now = Timer0_GetTimestamp();
was_rising = (P3IM0 & 0x40) ? 1 : 0;
duration = now - rf_isr_last_edge_ts;
rf_isr_last_edge_ts = now;
// 乒乓切换触发边沿,为下一次真实翻转做准备 (模拟双边沿触发)
if (was_rising) {
P3IM0 &= ~0x40; // 现在已进入高电平,下一次要抓它结束 (改为下降沿触发)
} else {
P3IM0 |= 0x40; // 现在已进入低电平,下一次要抓它结束 (改为上升沿触发)
}
if (was_rising) {
RF_HandleLowSegmentEnd(duration); // duration = 刚结束的低电平宽度
} else {
RF_HandleHighSegmentEnd(duration); // duration = 刚结束的高电平宽度
}
}
/**
* @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)
*
*/
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;
2026-07-31 13:57:06 +08:00
}
EA = 1;
2026-07-31 13:57:06 +08:00
return ready;
}
/**
* @brief EV1527 24 (Sync + 24bit Data)
*/
void EV1527_TxFrame(u32 addr, u8 dat)
{
u8 i;
u32 tx_val;
2026-07-31 13:57:06 +08:00
// 合并 20 位地址与 4 位数据,成为 24 位完整 EV1527 帧
tx_val = (addr << 4) | (dat & 0x0F);
2026-07-31 13:57:06 +08:00
// 1. 同步头脉冲: 高电平 1T (350us) + 低电平 31T (10850us)
RF_TX_DAT = 1;
RF_Delay_us(350);
RF_TX_DAT = 0;
RF_Delay_us(10850);
2026-07-31 13:57:06 +08:00
// 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;
2026-07-31 13:57:06 +08:00
RF_SetMode(2); // 切换为发射状态,接通天线与射频模块通道
Delay_ms(5); // 稍作延时使射频通路稳定
2026-07-31 13:57:06 +08:00
// 连续发射 25 帧以保证接收设备顺利锁定并解码
for (r = 0; r < 25; r++)
{
EV1527_TxFrame(addr, dat);
// 去除帧与帧之间可能导致波形不连续的 Delay_ms
}
2026-07-31 13:57:06 +08:00
RF_TX_DAT = 0; // 结束发射,拉低数据脚
RF_SetMode(1); // 恢复为常态后台接收模式
2026-07-31 13:57:06 +08:00
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);
2026-07-31 13:57:06 +08:00
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;
2026-07-31 13:57:06 +08:00
Uart_SendString("=== RF RX Test: Listening for 5 seconds... ===\r\n");
RF_SetMode(1); // 配置进入接收模式并使能天线
Delay_ms(10);
2026-07-31 13:57:06 +08:00
// 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");
2026-07-31 13:57:06 +08:00
// 收到信号后马达震动并亮青色灯提醒
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;
2026-07-31 13:57:06 +08:00
EAXFR = 1;
RF_SetMode(1); // 开启接收芯片
2026-07-31 13:57:06 +08:00
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");
2026-07-31 13:57:06 +08:00
for (i = 0; i < 100; i++)
{
RF_TX_DAT = 1;
Delay_us(500);
if (RF_RX_DATA == 1) match_count++;
2026-07-31 13:57:06 +08:00
RF_TX_DAT = 0;
Delay_us(500);
if (RF_RX_DATA == 0) match_count++;
}
2026-07-31 13:57:06 +08:00
Uart_SendString("Loopback Match Count: ");
Uart_SendHex16(match_count);
Uart_SendString("/200\r\n");
2026-07-31 13:57:06 +08:00
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");
}
2026-07-31 13:57:06 +08:00
RF_SetMode(0); // 关断射频
}