阅读边界:本文聚焦常见 315MHz/433.92MHz ASK/OOK 遥控链路,结合远-T1L 发射模块、远系列接收 DEMO 和 1527 类固定码例程说明。2262/2272 硬件解码、HCS301 滚动码安全算法不按本文的两帧固定码逻辑直接套用。

一、先把核心结论讲清楚

  • 发射模块不是编码芯片。T1L 这类 ASK/OOK 发射模块只看 DAT:DAT 高电平时有载波,DAT 低电平时关载波;真正的 0/1 编码由 MCU 用准确的高低电平时间产生。
  • 接收模块也不是直接输出字节。接收端 MCU 看到的是 DAT 脉冲,需要用边沿中断和 1us 定时器测脉宽,再判断同步头、逻辑 1、逻辑 0。
  • 产品不能只凭一帧就动作。普通 1527 类固定码建议连续两帧一致后再置有效标志,业务逻辑在主循环里处理,避免噪声误触发。
无线遥控编解码总链路流程图
图1:从按键到产品动作的完整链路。

二、发射端:编码其实是在控制 DAT 的时间

以例程 TX1527(0x11, 0x22, 0x33) 为例,3 个十六进制字节一共 24bit。每个字节按 bit7 到 bit0 发送,也就是 MSB first。示波器对照波形时,不要从最低位开始数。

对象例程时序接收端如何识别初学者注意
同步头H 400us + L 12400us低电平远大于普通数据位,定位一帧开始同步低电平要和数据位明显拉开
逻辑 1H 1200us + L 400us低电平短,rf_us < RF_PULSE_MID本文例程用 600us 作分界
逻辑 0H 400us + L 1200us低电平长,rf_us >= RF_PULSE_MID接收端只测低电平也能区分 0/1
重复帧默认重复 25 帧接收端有机会做两帧一致确认新手调试可发 25-30 帧
TX1527发射编码流程图
图2:TX1527 的发送流程和每个 bit 的时间含义。

三、发射核心代码解读

发射端最重要的是三件事:TIM1 必须按 1us 计数,WAIT_US() 才可靠;TX_SYN() 先发同步头;TX_BYTE()d & 0x80 从最高位逐位发送。

void WAIT_US(u16 t)
{
    TIM1->CNT = 0;
    while (TIM1->CNT < t);
}

void TX_SYN(void)
{
    LL_GPIO_SetOutputPin(p_rf_tx);   // DAT 高,发射载波
    WAIT_US(400);
    LL_GPIO_ResetOutputPin(p_rf_tx); // DAT 低,关闭载波
    WAIT_US(400 * 31);               // 约 12400us,同步低电平
}

void TX_BYTE(u8 d)
{
    for (u8 i = 0; i < 8; i++) {
        if ((d & 0x80) != 0) {       // 当前最高位是 1
            DAT_HIGH(); WAIT_US(1200);
            DAT_LOW();  WAIT_US(400);
        } else {                     // 当前最高位是 0
            DAT_HIGH(); WAIT_US(400);
            DAT_LOW();  WAIT_US(1200);
        }
        d <<= 1;                     // 下一位移到 bit7
    }
}

TIM1 的常见配置是系统时钟 8MHz、Prescaler=8-1,使 TIM1 每 1us 加 1。移植到其他主频时,公式是 Prescaler = timer_clock_hz / 1000000 - 1。STM32 还要注意 APB 分频后定时器时钟可能翻倍。

四、接收端:用低电平宽度重建 24bit

推荐接收方法是“一个 DAT 边沿中断 + 一个 1us 定时器”。DAT 下降沿把 TIM1 清零;DAT 上升沿读取 TIM1->CNT,得到刚刚那段低电平的宽度 rf_us。之后所有判断都围绕 rf_us 展开。

参数例程值作用调试建议
RF_SYN_MIN6000us同步头最小低电平发射同步头偏短时可适当下调
RF_SYN_MAX18000us同步头最大低电平过宽会增加误识别风险
RF_PULSE_MIN200us有效数据低电平最小值过滤过短毛刺
RF_PULSE_MAX1600us有效数据低电平最大值过滤异常长脉冲
RF_PULSE_MID600us0/1 分界低于它记 1,否则记 0
接收端状态机流程图
图3:接收端状态机。程序并不是一次收到 3 字节,而是逐脉冲拼回 24bit。

五、用 11 22 33 反推一次波形

发射端默认码 0x11 0x22 0x33 对应二进制 00010001 00100010 00110011。接收端从同步头后开始数 24 个数据低电平:短低电平记 1,长低电平记 0,每 8 位合成 1 个字节。

目标字节二进制典型低电平顺序串口应看到
0x1100010001长、长、长、短、长、长、长、短RF:11 xx xx
0x2200100010长、长、短、长、长、长、短、长RF:xx 22 xx
0x3300110011长、长、短、短、长、长、短、短RF:xx xx 33

如果示波器上能数出这些长短低电平,但串口打印不一致,优先怀疑接收端定时器单位、阈值、位序或 EXTI 边沿方向;如果 DAT 波形本身就不是这个顺序,优先怀疑发射端 TX_BYTE()d <<= 1、按键触发次数或 DAT 引脚宏。

六、接收核心代码解读

void REC_RF_us_INT(void)
{
    if (p_rf_in == 0) {              // DAT 下降沿:低电平刚开始
        TIM1->CNT = 0;
        return;
    }

    u16 rf_us = TIM1->CNT;           // DAT 上升沿:低电平结束
    TIM1->CNT = 0;
    if (b_rx_on) return;             // 上一组有效码未处理,先不覆盖

    if (rf_us > RF_SYN_MIN && rf_us < RF_SYN_MAX) {
        b_rx_start = 1;              // 找到同步头
        rf_rx_cnt = 0;
        rf_array = 0;
        return;
    }
    if (b_rx_start == 0) return;

    if (rf_us < RF_PULSE_MIN || rf_us > RF_PULSE_MAX) {
        b_rx_start = 0;              // 脉宽异常,丢弃本帧
        return;
    }

    rf_dat[rf_array] <<= 1;          // 给新 bit 腾出最低位
    if (rf_us < RF_PULSE_MID) rf_dat[rf_array] += 1;

    if (++rf_rx_cnt != 8) return;
    rf_rx_cnt = 0;
    if (++rf_array < 3) return;      // 收满 3 字节,即 24bit
    b_rx_start = 0;

    if (same_as_last_frame()) b_rx_on = 1;
    else save_as_last_frame();
}

这段程序的关键不是 C 语法,而是状态变量:b_rx_start 表示已经找到同步头;rf_rx_cnt 表示当前字节收了几位;rf_array 表示正在写第几个字节;b_rx_on 是“连续两帧一致”的有效标志。

void CHK_RF_DECODE(void)
{
    if (b_rx_on == 0) return;
    b_rx_on = 0;
    if (rf_dat_buf is all 0xFF or all 0x00) return;

    printf("RF:%02X %02X %02X\r\n",
           rf_dat_buf[0], rf_dat_buf[1], rf_dat_buf[2]);
    RF.active_10ms = 15;             // 约 150ms 活跃窗口
}

业务代码建议从 CHK_RF_DECODE() 往后扩展:学习对码、白名单匹配、继电器动作、串口上报、长按过滤都放在主循环或任务里。中断里不要 printf、不要写 Flash、不要做长延时。

七、学习码和业务动作不要写在中断里

产品真正使用时,通常会有学习键、保存码值、删除码值、多个遥控器、不同按键动作等需求。推荐流程是:中断只负责把 24bit 解出来并置 b_rx_on;主循环读取 rf_dat_buf;再判断是否处于学习模式或正常工作模式。

  • 学习模式:长按学习键进入,收到连续两帧一致的有效码后,过滤全 0/全 FF 和重复码,再写 Flash 或 EEPROM 模拟区。
  • 正常模式:把 rf_dat_buf 与已保存列表逐个比较,匹配后再控制继电器、LED、电机或串口上报。
  • 重复处理:RF.active_10ms 这类窗口用于避免长按时过密输出;窗口时间要按产品体验调整。

八、新手调试顺序

无线遥控收发调试排查流程图
图4:先验证一端,再调另一端,最后联调。
  1. 先用成熟 1527 遥控器验证接收端。频率必须和接收模块一致,先看到稳定 RF:xx xx xx。
  2. 接收确认无误后,再调 T1L + MCU 发射。默认码 11 22 33 是最适合初次确认的基准。
  3. 收不到时先看 DAT 波形。有规律同步头和数据脉冲,说明发射编码基本在工作;完全没有波形,就查程序运行、按键 IO、p_rf_tx 和 TIM1。
  4. 有波形但码不对,再查阈值和位序。重点看频率、天线、定时器是否 1us、DAT 上升/下降沿方向,以及是否连续发送多帧。

九、必须提醒客户的工程问题

  • 频率必须一致:315MHz 发射不能配 433.92MHz 接收,这是最常见低级错误。
  • DAT 电平要安全:如果用 9-12V 编码 IC 驱动发射模块 DAT,应串保护电阻或做电平处理,资料中建议 51K 作为保护思路。
  • 天线和电源先做好:模块附近放 0.1uF + 10uF 去耦,GND 要可靠,天线远离金属、电感、大电流走线和外壳屏蔽。
  • 不要让 DAT 边沿被拉慢:强上拉、强下拉或外部 RC 都可能改变脉宽,导致阈值判断不稳定。
  • 移植到 51/STM8/STM32 时,只要保住三件事:DAT 当前电平可读、边沿能捕获、微秒定时准确。库函数名字可以不同,解码状态机不要变形。
  • 接收 DEMO 中 DAT 接 PA1 时,EXTI source 应对应 LINE1。资料里特别提醒过 LINE0/LINE1 不一致的问题,交付前要核对源码。

十、产品化时要分清“演示可用”和“量产可靠”

DEMO 跑通只说明链路能工作,量产还要考虑环境噪声、距离波动、供电跌落、按键长按、误触发和售后重新学习。建议把接收端分成三层:底层解码层只输出有效 24bit;中间匹配层做学习码、白名单、重复过滤;业务层才做继电器、灯、电机、报警或联网动作。这样后续换 MCU、换模块或改动作逻辑时,不会把解码状态机改乱。

  • 门禁、卷帘门、报警器这类风险较高的产品,建议在两帧一致基础上再加白名单、长按确认或动作互锁。
  • 低功耗产品要验证唤醒后系统时钟和 TIM 是否恢复到正确频率,否则第一次唤醒后可能全部码值都错。
  • 带继电器、电机、DC-DC 的产品,要在负载动作时复测接收稳定性,很多“距离短”本质是电源噪声或天线环境问题。

十一、什么时候不能照本文直接套

如果客户使用 2262 + 2272 硬件解码,接收端主要是地址脚、电阻振荡和输出脚匹配,通常不需要 MCU 软件解 24bit。若使用 HCS301 等滚动码,它本身包含更复杂的码流和校验机制,不能简单用“两帧固定码一致”代替安全验证。本文最适合解释普通 ASK/OOK、1527 类固定码和远系列收发 DEMO 的入门实现。

十二、交付前检查清单

检查项通过标准
发射端按键触发后 DAT 有同步头 + 24bit 数据波形,串口提示码值,重复帧数量足够。
接收端成熟遥控器近距离稳定输出 RF:xx xx xx,rf_us 短脉冲约 400us、长脉冲约 1200us。
定时器修改主频后重新确认 TIM1/TIMx 仍为 1us 计数。
中断DAT 引脚、EXTI line、NVIC 和清标志逻辑正确,中断里不做耗时业务。
产品动作动作前有白名单/学习码匹配,保留全 0/全 FF 过滤和重复码策略。
硬件频率、天线、电源、DAT 电平保护和地线都已实测,不只看串口输出。

结语

无线遥控编解码不是神秘协议,本质是“按时间发脉冲、按脉宽收脉冲”。只要先把时序、边沿、定时器和两帧校验这四件事做扎实,后面的学习码、业务动作和平台移植就有了可靠基础。