阅读边界:本文聚焦常见 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 类固定码建议连续两帧一致后再置有效标志,业务逻辑在主循环里处理,避免噪声误触发。
二、发射端:编码其实是在控制 DAT 的时间
以例程 TX1527(0x11, 0x22, 0x33) 为例,3 个十六进制字节一共 24bit。每个字节按 bit7 到 bit0 发送,也就是 MSB first。示波器对照波形时,不要从最低位开始数。
| 对象 | 例程时序 | 接收端如何识别 | 初学者注意 |
|---|---|---|---|
| 同步头 | H 400us + L 12400us | 低电平远大于普通数据位,定位一帧开始 | 同步低电平要和数据位明显拉开 |
| 逻辑 1 | H 1200us + L 400us | 低电平短,rf_us < RF_PULSE_MID | 本文例程用 600us 作分界 |
| 逻辑 0 | H 400us + L 1200us | 低电平长,rf_us >= RF_PULSE_MID | 接收端只测低电平也能区分 0/1 |
| 重复帧 | 默认重复 25 帧 | 接收端有机会做两帧一致确认 | 新手调试可发 25-30 帧 |
三、发射核心代码解读
发射端最重要的是三件事: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_MIN | 6000us | 同步头最小低电平 | 发射同步头偏短时可适当下调 |
| RF_SYN_MAX | 18000us | 同步头最大低电平 | 过宽会增加误识别风险 |
| RF_PULSE_MIN | 200us | 有效数据低电平最小值 | 过滤过短毛刺 |
| RF_PULSE_MAX | 1600us | 有效数据低电平最大值 | 过滤异常长脉冲 |
| RF_PULSE_MID | 600us | 0/1 分界 | 低于它记 1,否则记 0 |
五、用 11 22 33 反推一次波形
发射端默认码 0x11 0x22 0x33 对应二进制 00010001 00100010 00110011。接收端从同步头后开始数 24 个数据低电平:短低电平记 1,长低电平记 0,每 8 位合成 1 个字节。
| 目标字节 | 二进制 | 典型低电平顺序 | 串口应看到 |
|---|---|---|---|
| 0x11 | 00010001 | 长、长、长、短、长、长、长、短 | RF:11 xx xx |
| 0x22 | 00100010 | 长、长、短、长、长、长、短、长 | RF:xx 22 xx |
| 0x33 | 00110011 | 长、长、短、短、长、长、短、短 | 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 这类窗口用于避免长按时过密输出;窗口时间要按产品体验调整。
八、新手调试顺序
- 先用成熟 1527 遥控器验证接收端。频率必须和接收模块一致,先看到稳定 RF:xx xx xx。
- 接收确认无误后,再调 T1L + MCU 发射。默认码 11 22 33 是最适合初次确认的基准。
- 收不到时先看 DAT 波形。有规律同步头和数据脉冲,说明发射编码基本在工作;完全没有波形,就查程序运行、按键 IO、p_rf_tx 和 TIM1。
- 有波形但码不对,再查阈值和位序。重点看频率、天线、定时器是否 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 电平保护和地线都已实测,不只看串口输出。 |
结语
无线遥控编解码不是神秘协议,本质是“按时间发脉冲、按脉宽收脉冲”。只要先把时序、边沿、定时器和两帧校验这四件事做扎实,后面的学习码、业务动作和平台移植就有了可靠基础。