425 lines
14 KiB
C
425 lines
14 KiB
C
#include "rf.h"
|
||
#include "../App/config.h"
|
||
#include "intrins.h"
|
||
|
||
// ATR5179 射频前端天线开关控制引脚定义
|
||
sbit RF_TX = P2^0; // V1 控制端 (1: 选通天线到发射 TX 路径)
|
||
sbit RF_RX = P3^7; // V2 控制端 (1: 选通天线到接收 RX 路径)
|
||
|
||
// 射频芯片数据与电源控制引脚定义
|
||
sbit RF_TX_DAT = P3^6; // LT4455 发射调制数据输入引脚 (DIN)
|
||
sbit RF_RX_DATA = P2^1; // LR690L 接收解调数据输出引脚 (DATA),连接单片机中断管脚
|
||
sbit SHUT = P2^2; // LR690L 芯片休眠控制引脚 (1:休眠,0:工作)
|
||
|
||
// 声明外部延时及串口打印函数 (在 system.c 中定义)
|
||
extern void Delay_us(u16 us);
|
||
extern void Delay_ms(u16 ms);
|
||
extern u16 GetTimer0_Safe(void);
|
||
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_SendByte(u8 dat);
|
||
|
||
/**
|
||
* @brief 初始化无线射频芯片相关的 GPIO 引脚方向与初始状态
|
||
*/
|
||
void RF_Init(void)
|
||
{
|
||
// 1. 配置 P2.0 (RF_TX) 和 P2.2 (SHUT) 为推挽输出模式 (M1=0, M0=1)
|
||
// 配置 P2.1 (RF_RX_DATA) 为准双向口/输入模式 (M1=0, M0=0)
|
||
P2M1 &= ~((1 << 0) | (1 << 1) | (1 << 2));
|
||
P2M0 &= ~(1 << 1);
|
||
P2M0 |= ((1 << 0) | (1 << 2));
|
||
|
||
// 2. 配置 P3.6 (RF_TX_DAT) 和 P3.7 (RF_RX) 为推挽输出模式 (M1=0, M0=1)
|
||
P3M1 &= ~((1 << 6) | (1 << 7));
|
||
P3M0 |= ((1 << 6) | (1 << 7));
|
||
|
||
// 3. 设置系统默认的初始电平
|
||
RF_TX = 0; // 断开发射天线通路
|
||
SHUT = 1; // 默认拉高 SHUT,使 LR690L 接收芯片处于低功耗休眠关闭状态
|
||
RF_RX = 0; // 断开接收天线通路
|
||
RF_TX_DAT = 0; // 调制发送数据线置低
|
||
}
|
||
|
||
/**
|
||
* @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_RX = 1; // 选通天线切换开关到 RX 路径,连通物理接收天线
|
||
RF_TX = 0; // 断开 TX 发射路径,防止回环烧毁前端
|
||
}
|
||
else if (mode == 2) // ====== 模式 2: 射频发射模式 (Tx Mode) ======
|
||
{
|
||
SHUT = 1; // 关闭 LR690L 接收芯片,避免发射大功率波形串入接收机
|
||
RF_RX = 0; // 断开接收路径天线
|
||
RF_TX = 1; // 选通天线切换开关至 TX 路径,连通物理天线进行发射
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 按 EV1527 载波规范单次调制发送一帧 24 位无线射频数据
|
||
* @param addr 20 位的发射地址编码
|
||
* @param dat 4 位的命令状态码
|
||
* @details EV1527 是常见的无线编码格式:
|
||
* - 一个基本脉冲周期设为 T ≈ 320us。
|
||
* - 同步头脉冲: 1T 高电平 (320us) + 31T 低电平 (9920us)。
|
||
* - 数据位 '1' (逻辑1) 编码: 3T 高电平 (960us) + 1T 低电平 (320us)。
|
||
* - 数据位 '0' (逻辑0) 编码: 1T 高电平 (320us) + 3T 低电平 (960us)。
|
||
*/
|
||
void EV1527_TxFrame(u32 addr, u8 dat)
|
||
{
|
||
u8 i;
|
||
|
||
// ====== 1. 发送同步引导脉冲 ======
|
||
RF_TX_DAT = 1;
|
||
Delay_us(320); // 1T 高电平
|
||
RF_TX_DAT = 0;
|
||
Delay_us(9920); // 31T 低电平
|
||
|
||
// ====== 2. 依次调制发送 20 位物理地址码 (高位 MSB 优先发送) ======
|
||
for (i = 0; i < 20; i++)
|
||
{
|
||
if ((addr >> (19 - i)) & 1)
|
||
{
|
||
// 数据 bit 1: 3T高 + 1T低
|
||
RF_TX_DAT = 1;
|
||
Delay_us(960);
|
||
RF_TX_DAT = 0;
|
||
Delay_us(320);
|
||
}
|
||
else
|
||
{
|
||
// 数据 bit 0: 1T高 + 3T低
|
||
RF_TX_DAT = 1;
|
||
Delay_us(320);
|
||
RF_TX_DAT = 0;
|
||
Delay_us(960);
|
||
}
|
||
}
|
||
|
||
// ====== 3. 依次调制发送 4 位指令状态数据码 (高位 MSB 优先) ======
|
||
for (i = 0; i < 4; i++)
|
||
{
|
||
if ((dat >> (3 - i)) & 1)
|
||
{
|
||
// 数据 bit 1: 3T高 + 1T低
|
||
RF_TX_DAT = 1;
|
||
Delay_us(960);
|
||
RF_TX_DAT = 0;
|
||
Delay_us(320);
|
||
}
|
||
else
|
||
{
|
||
// 数据 bit 0: 1T高 + 3T低
|
||
RF_TX_DAT = 1;
|
||
Delay_us(320);
|
||
RF_TX_DAT = 0;
|
||
Delay_us(960);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 循环发射多帧无线 EV1527 信号以保证数据通信的完整性
|
||
* @param addr 20位源地址
|
||
* @param dat 4位命令码
|
||
*/
|
||
void EV1527_Transmit(u32 addr, u8 dat)
|
||
{
|
||
u8 r;
|
||
// 循环发射 25 次相同的射频帧 (累计耗时约 340 毫秒),抵御外界瞬间同频杂波干扰
|
||
for (r = 0; r < 25; r++)
|
||
{
|
||
EV1527_TxFrame(addr, dat);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 使用定时器 0 (Timer0) 高精度的测量引脚指定电平状态的持续宽度时间
|
||
* @param state 待测电平状态 (1-测量高电平持续宽度, 0-测量低电平持续宽度)
|
||
* @param timeout_us 最大等待超时微秒数上限,超时自动退出防锁死
|
||
* @return u16 最终测量的实际电平持续微秒数。若发生超时则直接返回 0。
|
||
*/
|
||
u16 GetPulseDuration(u8 state, u16 timeout_us)
|
||
{
|
||
// 根据系统晶振频率计算在 12T 模式下目标超时的 Timer0 Tick 数值上限
|
||
u16 timeout_ticks = (u16)((u32)timeout_us * (MAIN_Fosc / 1000000UL) / 12UL);
|
||
TL0 = 0; // 定时器 0 计数值清零
|
||
TH0 = 0;
|
||
TR0 = 1; // 启动定时器 0 硬件计数
|
||
|
||
// 轮询引脚:只要电平仍旧等于目标待测状态,就继续保持循环
|
||
while (RF_RX_DATA == state)
|
||
{
|
||
// 软件防卡死判定:如果当前计数值超过了设定的最大允许超时 Tick 值
|
||
if (GetTimer0_Safe() > timeout_ticks)
|
||
{
|
||
TR0 = 0; // 关断定时器
|
||
return 0; // 判定超时,返回 0
|
||
}
|
||
}
|
||
TR0 = 0; // 关断定时器
|
||
|
||
// 将最终读取的 Timer0 硬件 Tick 数值,重新换算成微秒数返回 (1 Tick = 12 / 24MHz = 0.5us)
|
||
return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL));
|
||
}
|
||
|
||
/**
|
||
* @brief 软件自适应解码接收一帧无线 EV1527 信号
|
||
* @param out_addr 解码成功后,用于回传物理地址高 20 位的指针
|
||
* @param out_data 解码成功后,用于回传指令数据低 4 位的指针
|
||
* @return bit 1-解码成功且校验通过,0-捕获失败或脉冲畸变
|
||
* @note 核心算法:
|
||
* 1. 等待并寻找合法的同步引导头脉冲 (1T高电平 + 31T低电平,其中低/高比率约为 31,本程序允许比率在 15~48 内变化以抗物理畸变)。
|
||
* 2. 依据捕获的同步低电平宽度估算基准周期 T = low_time / 31。
|
||
* 3. 随后依次高精度捕获 24 个数据位的高电平和低电平时间。
|
||
* 4. 自适应决策:对于每个 bit,如果其高电平时间 raw_h > 低电平时间 raw_l,则判定该位为 1;否则判定该位为 0。
|
||
*/
|
||
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]; // 高电平时间寄存数组 (置于 idata 区)
|
||
u16 idata raw_l[24]; // 低电平时间寄存数组
|
||
|
||
// ====== 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;
|
||
}
|
||
}
|
||
|
||
// 捕获引导头高电平时间 (放宽测量阈值上限至 2500 微秒)
|
||
high_time = GetPulseDuration(1, 2500);
|
||
if (high_time < 50 || high_time > 2500) {
|
||
if (high_time > 10) { // 剔除高频毛刺噪声,防止调试串口满载
|
||
Uart_SendString("Sync H Err: ");
|
||
Uart_SendHex16(high_time);
|
||
Uart_SendString("us\r\n");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// 捕获引导头后续的 31T 低电平时间 (设定上限 60 毫秒)
|
||
low_time = GetPulseDuration(0, 60000);
|
||
if (low_time < 1500 || low_time > 60000) {
|
||
Uart_SendString("Sync L Err H=");
|
||
Uart_SendHex16(high_time);
|
||
Uart_SendString(" L=");
|
||
Uart_SendHex16(low_time);
|
||
Uart_SendString("us\r\n");
|
||
return 0;
|
||
}
|
||
|
||
// 计算低电平与高电平的比值,以检验是否满足 EV1527 的 31:1 特征
|
||
{
|
||
u32 ratio = (u32)low_time / high_time;
|
||
if (ratio < 15 || ratio > 48) { // 允许 50% 左右的空气无线传输畸变误差
|
||
Uart_SendString("Sync Ratio Err H=");
|
||
Uart_SendHex16(high_time);
|
||
Uart_SendString(" L=");
|
||
Uart_SendHex16(low_time);
|
||
Uart_SendString(" R=");
|
||
Uart_SendHex16((u16)ratio);
|
||
Uart_SendString("\r\n");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
// 自适应估算出基准脉冲时值 T
|
||
T = low_time / 31;
|
||
if (T == 0) T = 1; // 零安全防护
|
||
|
||
// ====== 2. 依次采集接下来的 24 个数据 bit 脉冲 ======
|
||
for (i = 0; i < 24; i++)
|
||
{
|
||
u16 wait_cnt = 0;
|
||
// 等待下一个数据位跳变产生的上升沿 (低电平变高)
|
||
while (RF_RX_DATA == 0)
|
||
{
|
||
Delay_us(5);
|
||
wait_cnt++;
|
||
if (wait_cnt > 1000) // 5ms 超时判定,防无线信号中断卡死
|
||
{
|
||
return 0; // 捕获失败
|
||
}
|
||
}
|
||
|
||
// 精密测算本位的高电平与低电平宽度
|
||
raw_h[i] = GetPulseDuration(1, 5000);
|
||
if (raw_h[i] == 0) return 0; // 超时错误直接退出
|
||
|
||
raw_l[i] = GetPulseDuration(0, 5000);
|
||
if (raw_l[i] == 0) return 0;
|
||
}
|
||
|
||
// ====== 3. 串口输出捕获的时序参数 ======
|
||
Uart_SendString("Captured: Sync H=");
|
||
Uart_SendHex16(high_time);
|
||
Uart_SendString("us, L=");
|
||
Uart_SendHex16(low_time);
|
||
Uart_SendString("us, T=");
|
||
Uart_SendHex16(T);
|
||
Uart_SendString("us\r\n");
|
||
|
||
// ====== 4. 自适应决策:组合成 24 位完整射频数据帧 ======
|
||
for (i = 0; i < 24; i++)
|
||
{
|
||
// 比较本位高低电平持续宽度:
|
||
// 如果高电平大于低电平,说明宽脉冲在高,判定该位为 1 (3T高+1T低)。
|
||
// 否则高电平短,判定为 0 (1T高+3T低)。
|
||
if (raw_h[i] > raw_l[i])
|
||
{
|
||
addr = (addr << 1) | 1; // 写入 24 位物理地址与状态数据相结合的最终值
|
||
}
|
||
else
|
||
{
|
||
addr = (addr << 1);
|
||
}
|
||
}
|
||
|
||
*out_addr = addr; // 输出包含状态数据的 24 位完整射频编码作为传感器唯一 ID
|
||
*out_data = (u8)(addr & 0x0F); // 输出低 4 位作为指令/状态,保持调用兼容性
|
||
|
||
// 串口打印解码最终结果
|
||
Uart_SendString("Decoded 24bit ADDR: 0x");
|
||
Uart_SendHex32(addr);
|
||
Uart_SendString("\r\n");
|
||
|
||
return 1; // 成功解析
|
||
}
|
||
|
||
/**
|
||
* @brief 软硬件环路自检回环测试
|
||
* @details 将发射引脚置位并同时采样接收引脚,用于测试收发芯片控制连通性。
|
||
*/
|
||
void Loopback_Test(void)
|
||
{
|
||
u16 match_count = 0;
|
||
u16 i;
|
||
|
||
SHUT = 0; // 强行开启接收芯片供电
|
||
RF_RX = 1; // 选通天线到 RX
|
||
RF_TX = 0; // 发送引脚隔离
|
||
Delay_ms(100);
|
||
|
||
Uart_SendString("Starting RF Loopback Self-Test...\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! Transmitter is emitting RF, and Receiver is picking it up!\r\n");
|
||
}
|
||
else
|
||
{
|
||
Uart_SendString("Result: FAIL! No RF signal detected. Check hardware.\r\n");
|
||
}
|
||
|
||
SHUT = 1; // 关闭接收芯片
|
||
RF_RX = 0; // 断开接收通道
|
||
}
|
||
|
||
/**
|
||
* @brief 统计接收引脚在 5 秒内的电平翻转次数,用以评估物理噪声污染度或管脚短路
|
||
*/
|
||
void RF_Test_Pin(void)
|
||
{
|
||
u8 i;
|
||
u16 j;
|
||
u16 transitions = 0;
|
||
bit last_state = RF_RX_DATA;
|
||
|
||
Uart_SendString("=== RF RX Pin Data Test Start (5 seconds) ===\r\n");
|
||
Uart_SendString("SHUT pin state: ");
|
||
Uart_SendByte(SHUT ? '1' : '0');
|
||
Uart_SendString("\r\n");
|
||
|
||
// 强制设为接收模式
|
||
RF_SetMode(1);
|
||
Delay_ms(100);
|
||
|
||
Uart_SendString("SHUT pin state (after RF_SetMode(1)): ");
|
||
Uart_SendByte(SHUT ? '1' : '0');
|
||
Uart_SendString("\r\n");
|
||
Uart_SendString("Please trigger your 433MHz transmitter now...\r\n");
|
||
|
||
// 连续进行 5 秒的高频电平抖动采样
|
||
for (i = 0; i < 5; i++)
|
||
{
|
||
transitions = 0;
|
||
// 1秒循环
|
||
for (j = 0; j < 1000; j++)
|
||
{
|
||
u8 k;
|
||
// 每次内循环进行 5 次采样,每次间隔 200us
|
||
for (k = 0; k < 5; k++)
|
||
{
|
||
bit current_state = RF_RX_DATA;
|
||
if (current_state != last_state)
|
||
{
|
||
transitions++; // 统计电平边沿翻转次数
|
||
last_state = current_state;
|
||
}
|
||
Delay_us(200);
|
||
}
|
||
}
|
||
|
||
// 输出本秒的电平跳转总数,若非发射期该数值很大,表明空中有丰富噪声噪底跳变,属接收机正常表现
|
||
Uart_SendString("Sampling... Transitions in last 1s: ");
|
||
Uart_SendHex16(transitions);
|
||
Uart_SendString(", current pin value: ");
|
||
Uart_SendByte(RF_RX_DATA ? '1' : '0');
|
||
Uart_SendString("\r\n");
|
||
}
|
||
|
||
Uart_SendString("=== RF Pin Test End ===\r\n");
|
||
}
|