
简介这是一份面向单片机爱好者与嵌入式初学者的学习型无线遥控解码资料基于STC15F104W/STC15F104E单片机覆盖315-433MHz频段主流遥控芯片如1527、2262、2260、SC5211、HS2240等解码方案可实现免人工地址编码、自动适应振荡电阻、一键学习匹配多个遥控器。包内共48个文件压缩包仅337KB包含Keil工程源程序C源码、uvproj工程与hex烧录文件、AD09原理图与PCB预览、使用说明书及433MHz无线模块说明文档并附带完整W433详细源码方便对照学习与二次开发。内部晶振11.0592M利用片内EEPROM存储学习信息支持2262、1527解码输出控制可直接移植到同类遥控解码项目中。原理图提供高/低电平两种输出电路配合下载烧录说明和遥控器编码说明入门者也能按步骤搭建环境、完成调试。目前已有190人学习适合想深入理解学习型遥控器解码原理、掌握STC15F系列单片机无线接收控制的读者参考。1. 为什么学习型无线遥控解码卡在“振荡电阻”上同一只433遥控器换了一块固件写死600us脉宽的解码板就没反应而学习型无线遥控解码程序却能重新跟随。这不是射频灵敏度问题而是解码器没有去适应遥控器内部的振荡电阻。这个资源基于STC15F104W/STC15F104E自带原理图、PCB、W433详细源码和烧录说明支持1527、2262、2264、2260、SC5211、HS2240等常见协议核心是把同步码和键值脉宽做成自适应测量再写进片内EEPROM。适合正在做315-433MHz遥控拷贝方案或者在杂牌遥控器上反复调阈值的嵌入式开发者。下文按“协议→状态机→硬件→烧录”顺序拆解代码可直接落到STC15系列工程里。2. 从2262/1527帧结构到STC15F104W解码状态机2.1 2262与1527的帧结构差异先把收到的433MHz射频信号还原成数字波形。绝大多数315-433无线通信方案都用ASK/OOK调制接收模块DATA脚直接输出带载波包络的方波。要识别遥控器是哪家协议第一眼要看同步码和数据宽度。PT2262输出12位数据前8位是地址后4位是键值空闲时用低电平填充EV1527输出20位地址加4位键值地址空间更大通常被叫成学习码。SC5211、HS2240这类芯片本质上兼容EV1527或PT2262的时序帧头仍然有明显同步码后面逐位用高电平和低电平的比例表示0和1。这个比例由振荡电阻决定而不是固定脉宽。所以网上总有人问“433是引导码加键值吗”答案是否定的引导码之后还夹着地址位拷贝时必须整帧解析。芯片类型地址/数据位数同步码特征数据位编码PT22628位地址4位数据宽高电平约31个宽低电平高低电平时间比例EV152720位地址4位数据窄高电平约31个窄低电平高/低时间比例SC5211兼容EV1527同EV1527同EV1527HS2240兼容PT2262同PT2262同PT2262表格只用于快速区分实际解码时不要根据芯片型号去猜帧长因为同一颗2262在不同振荡电阻下同步码绝对时间能差出3倍。正确做法是拿同步码做动态基准再解析后续每一位。2.2 振荡电阻如何决定单位时基遥控器芯片的OSC脚外接电阻决定内部振荡频率。以EV1527为例270kΩ电阻对应约350us的单位脉宽470kΩ对应约500us换一个杂牌遥控器地址可能一样脉宽却差30%以上。固定阈值的解码程序只判断“大于800us算1小于400us算0”结果就是同一个遥控器在不同板子上表现完全不同。学习型方案的做法是先测量同步码的一个完整周期把它当作当前遥控器的单位时基T后面每一位都按T的比例判定而不是死记绝对微秒数。资源里说的“自动适应振荡电阻”就是这个过程并不是解码器真的去读电阻阻值它读的只是脉冲宽度的比值。这里有个容易忽略的细节2262和1527的同步码比例虽然接近但高电平宽度不同。EV1527同步码的高电平约等于一个单位TPT2262同步码的高电平约等于4个单位T。如果解码程序只测低电平长度很容易把两种协议混在一起必须在同步码确认阶段同时记录高电平与低电平用“高/低比值”区分协议类型再做分支解析。后面第3章的代码里就保留了这两个变量。2.3 STC15F104W 的引脚与定时器约束STC15F104W是8脚单片机VCC/GND之外只剩P3.0到P3.5六个IO。它没有外部晶振引脚内部RC振荡器可配置到11.0592MHz这个频率选择既方便串口调试也能把1T定时器的最小分辨率压到约90.4ns。接收模块DATA脚接P3.2/INT0利用下降沿触发外部中断定时器0工作在16位方式每次外部中断读取TH0、TL0清零后继续计数。资源包里的编译目标为STC15F104E与STC15F104W引脚兼容工程代码可直接迁移。需要注意定时器量程。11.0592MHz主频下16位计数器溢出周期约5.92ms而2262同步码低电平可以到4ms甚至更长。只读16位定时器可能溢出导致同步码被截断。因此解码程序必须扩展溢出计数外部中断里同时读出溢出次数和TH0/TL0组成一个32位时间戳。这也是很多“在网上找的51解码程序”在STC15上失效的原因老程序默认12T模式直接除以12后测得的值偏小而STC15默认1T模式时序完全变了。2.4 解码状态机定义解码过程不建议在主循环里轮询引脚电平除非系统没有其他任务。资源里的源程序也是用中断驱动状态机只有四段等待同步码、同步码确认、接收数据位、帧结束。C定义如下enum decode_state { ST_WAIT_SYNC, ST_SYNC_OK, ST_RX_DATA, ST_FRAME_END };等待同步码时只关心低电平持续长度超过最短同步宽度就进入确认确认状态里再测一次完整周期避免把电源毛刺当作同步头数据接收状态按位累积地址位和键值分段保存帧结束后做连续两次正确接收验证。状态切换必须有超时退出比如每100ms没收到下一步边沿就把状态拉回ST_WAIT_SYNC否则长时间无信号时状态机卡在RX_DATA学习按钮就会失灵。这个超时可放在定时器溢出中断里用计数变量实现逻辑上等价于看门狗但更轻量。3. W433源码拆解捕获、超时判决与EEPROM自学习3.1 定时器扩展捕获先写定时器0初始化工作方式116位不重载。方式1没有自动重装溢出后继续从0计数这样外部中断读到的TH0/TL0是两个边沿之间的增量不需要再额外做减法。初始化代码void timer0_init(void) { TMOD 0xF0; TMOD | 0x01; /* 定时器0方式116位 */ TH0 0; TL0 0; ET0 1; /* 使能溢出中断 */ TR0 1; EA 1; } unsigned int ov_cnt; void t0_isr(void) interrupt 1 using 1 { ov_cnt; /* 每溢出一次时间扩展65536周期 */ }TMOD先与0xF0再或0x01是为了不动定时器1的配置。ov_cnt在中断里自增外部中断读它时存在竞争风险严格做法是在读ov_cnt前临时关闭EA读完再开。代码里不关是因为STC15的INT0中断优先级高于T0实际现场基本不会丢但如果你要做量产固件建议把读ov_cnt的三个字节放进临界区。外部中断0里读取当前计数void int0_isr(void) interrupt 0 using 1 { unsigned char th TH0; unsigned char tl TL0; unsigned int ov ov_cnt; unsigned long tick; TH0 0; TL0 0; ov_cnt 0; tick ((unsigned long)ov 16) | ((unsigned int)th 8) | tl; process_edge(tick); }先读TH/TL再清零避免清零后又被中断改写。tick单位是机器周期数STC15F104W在1T模式下等于时钟周期数实际时间等于tick除以11.0592MHz。如果编译工程时把STC15设置成12T模式这里的tick要再乘12否则后面所有阈值都偏小。W433源码把这段封装在decode模块里上层只关注高低电平有多少微秒。3.2 边沿追到哪一位外部中断INT0只能配置下降沿触发或低电平触发没有直接上升沿触发模式。常见做法是切换IT0寄存器的值下降沿进入中断后马上改成上升沿再进入时改成下降沿。这样每个完整方波都能得到高电平持续时间和低电平持续时间。bit rx_level; void process_edge(unsigned long tick) { if (rx_level 0) { high_time tick; /* 刚结束的是高电平 */ rx_level 1; IT0 0; /* 改为上升沿触发 */ } else { low_time tick; /* 刚结束的是低电平 */ rx_level 0; IT0 1; /* 改回下降沿触发 */ } decode_push(high_time, low_time); }rx_level的初始值必须和接收模块无信号默认电平一致。W433模块无信号输出高电平时rx_level初始化为0第一段低电平到来时进入if分支如果模块默认输出低电平就要反过来写。资源里AD09工程有两种原理图一种标注“默认输出高电平”一种标注“默认输出低电平”对应就是这里极性选择。切换IT0的方式在低频遥控信号下足够用433MHz载波被接收模块解调后输出的是几百微秒的方波切换动作本身在几微秒内完成不会丢边沿。3.3 自适应阈值与同步码判决同步码是整个帧里最明显的部分。EV1527同步码的低电平约是单位T的31倍PT2262也接近这个比例。所以可以用低电平时间除以32得到当前单位T的估计值#define T_REF_MIN 100 #define T_REF_MAX 3000 unsigned int ref_t; unsigned int real_t; void sync_auto(unsigned long low_time) { unsigned int t; t (unsigned int)(low_time / 32UL); if (t T_REF_MIN t T_REF_MAX) { real_t t; } }除以32而不是31是为了给下降沿抖动留余量同时避免除零。real_t就是后续数据位判定的基准。T_REF_MIN和T_REF_MAX按实际遥控器范围设置资源文档里给的是用11.0592MHz内部晶振下的计数范围如果你把主频改到12MHz或者22.1184MHz必须按比例换算这两个宏否则同步码超时判断会完全不匹配。得到real_t后数据位的判定就比较简单unsigned char decode_bit(unsigned int high, unsigned int low) { unsigned int total high low; if (total (real_t * 3 / 2)) { return (high low) ? 1 : 0; } return 0xFF; /* 非法位 */ }这里用total判断是否是一个有效数据位再用高低电平比例决定0或1。PT2262和EV1527编码方式略有不同但大多数实现都可以用这个比例法兼容。我的习惯是连续两帧解析结果一致的位才采用第一帧只做粗同步第二帧才写EEPROM避免遥控器在按键抖动瞬间产生半截码。3.4 EEPROM 保存地址与键值STC15F104W片内EEPROM用IAP指令访问操作流程和传统51的外部EEPROM不大一样。写字节前需要先使能IAP设置等待参数再触发命令。一个简化写法void eeprom_write_byte(unsigned int addr, unsigned char dat) { unsigned char t; IAP_CONTR 0x80; /* 使能ISP/IAP */ IAP_TPS 12; /* 等待参数按11.0592MHz校准 */ IAP_ADDRH (unsigned char)(addr 8); IAP_ADDRL (unsigned char)addr; IAP_DATA dat; IAP_CMD 2; /* 写字节 */ IAP_TRIG 0x5A; IAP_TRIG 0xA5; for (t 0; t 20; t); /* 等待内部写完成 */ IAP_CONTR 0; }写EEPROM前要擦除扇区STC15F104W扇区大小按数据手册为准一般是512字节。可以把学习到的地址和键值放在同一个扇区开头读取时直接按地址偏移取。注意写EEPROM会阻塞CPU解码中断虽然还在响应但主循环已经停了所以不能在接收一帧数据的过程中同时写EEPROM。资源工程里的处理是学习按键按下后先等一帧完整接收并校验通过再执行EEPROM写入写完后清学习标志整个过程只有几十毫秒手按遥控器不会觉得卡顿。3.5 W433 发射源码的理解资源包里的W433.c不是纯解码它把学到的地址键值重新按位调制到433MHz发射模块。发射和接收是逆过程读取EEPROM里的0和1用real_t作为单位脉宽先把同步码发出去再逐位发送地址和键值。发送完一帧后延时10ms到20ms连续发3帧让接收端有足够时间确认。W433模块无信号时输出高电平还是低电平决定了发送代码里要不要把波形取反。这个问题在“433无线通信方案”里最常见接收模块和发射模块极性不一致时拷贝出来的遥控器按键没反应排查半天最后发现只是少了一个反相器。4. 原理图与PCB默认输出电平、晶振与W433模块取舍4.1 为什么有两张默认输出电平电路图AD09工程里有两个关键文件默认输出高电平电路图.SchDoc和默认输出低电平电路图.SchDoc。看到两张图不要以为是版本迭代它们是针对两种接收模块的适配。W433这类超外差接收模块在无信号时DATA脚为高电平数据脉冲到来时输出负脉冲部分超再生模块无信号时输出低电平数据到来时输出正脉冲。STC15F104W的INT0只能配置下降沿或低电平触发如果模块默认输出低电平直接用下降沿中断就只会收到一次下降沿后面所有上升沿信息全部丢失。解决办法是加一级反相或者把P3.2上的数据先经过三极管反相再进单片机。两张原理图分别对应这两种接法PCB上实际差别不大只有反相器件和上拉电阻的位置不同。选择哪张图还要看W433模块的手册。资料里那张433MHZ无线射频模块.txt建议先看里面通常会写明DATA脚静态电平。如果模块标称“输出高电平有效”就选默认输出高电平图标称“DATA输出低电平有效”就选默认输出低电平图。不少人在这个选择上想当然结果下载完程序后用示波器能看到波形但单片机死活进不了中断原因就是极性配反了。4.2 内部晶振频率的选型原因STC15F104W没有外部晶振引脚只能用内部RC振荡器。11.0592MHz是串口和定时器比较舒服的频率1T模式下计数分辨率约90.4ns足够分辨2262/1527的0.5T宽度数据。烧录时STC-ISP工具里有一个“输入用户程序运行时的IRC频率”选项必须选11.0592MHz。如果选成6MHz定时器测得的时间会整体偏慢约一半学习到的地址码可能还是对的但发射出去的数据位宽也会偏最终导致部分接收设备不识别。资源里的下载烧录说明文档专门强调了这个选项原理图里没有外部晶振元件所以频率全靠这一步配置。4.3 W433 接收模块接线W433模块常见引脚只有四个VCC、GND、DATA、ANT。VCC需要按模块标称电压接3.3V或5VSTC15F104W供电范围是2.4V到5.5V通常直接由5V系统供电。模块DATA脚输出TTL电平可以直连P3.2如果模块是3.3V供电建议串一个1kΩ电阻限流避免5V单片机IO口倒灌。接收模块ANT引脚悬空时也能收到强信号但距离会缩短调试时焊接一根10cm左右的导线即可。模块引脚单片机网络说明VCCVCC_3V3 或 VCC_5V必须与模块标称一致GNDGND与单片机共地DATAP3.2/INT0直接进外部中断ANTANT 网络留出天线焊接位置原理图里ANT网络到模块引脚之间通常会串一个0Ω电阻调试时方便断开天线独立测试。PCB上ANT走线要尽量短不要在顶层铺一圈覆盖天线的地铜否则天线等效电容变化接收距离可能从50m缩到5m。资源里的PCB工程把两个原理图都做了子图选定模块类型后其余电路可以直接复用。4.4 PCB上的电源与去耦STC15F104W虽然是8脚小封装但它内部是1T内核IO翻转速度快433MHz接收模块又是模拟射频前端两者共用电源时容易出现数据脚毛刺。PCB上至少在VCC处放一个10uF电解电容加一个0.1uF陶瓷电容陶瓷电容尽量靠近U1电源脚。W433模块的电源引脚也建议单独放一个0.1uF电容特别是指纹识别、智能插座这类产品继电器动作瞬间的电源跌落会造成遥控解码失败。P3.0和P3.1用作串口下载下载完成后如果悬空可能在强干扰下被误触发进入ISP模式。原理图里常见处理是加10k上拉或者下载后用程序把这两个脚配置为普通开漏输出。5. 烧录参数核对与遥控器拷贝的验证技巧5.1 STC-ISP烧录设置下载时目标芯片选STC15F104W如果资源包里的旧工程编译目标是STC15F104E型号相近烧录时选对应系列即可。硬件连接P3.0/P3.1到USB转TTL先断电再点下载上电瞬间检测到ISP握手。烧录前把接收模块DATA脚断开避免遥控信号干扰P3.2导致握手失败。IRC频率选11.0592MHzEEPROM配置按实际需要选择大小。这里有一个容易翻车的选项STC-ISP里的“复位脚用作IO”如果改掉烧进去后可能无法再用串口下载只能用冷启动重新上电的方式恢复量产固件里一般保持默认。5.2 学习与验证步骤学习型遥控器拷贝不是按住按键等灯亮那么简单必须按一帧有效编码的顺序来。常见流程按住学习键使单片机进入学习模式此时LED慢闪。按原遥控器任意键解码程序连续收到两帧有效数据后LED常亮。松开学习键把新遥控器或本板按键对着同频接收模块按一次。如果原遥控器本身是学习型需要先看资源文档里的“学习遥控器芯片设置说明”把振荡电阻参数调整到和目标设备一致。第4步很容易被忽略。文档里提到21个按键的键值编码不是简单递增个别键位会占用地址位低位代码里如果写死按键表学习到某些按键时会发现地址不一致这时应该把整个帧的地址和键值都存入EEPROM而不是只存键值。这也是学习型解码和普通解码最大的区别普通解码只关心当前按键值学习型解码必须保留完整地址才能在发射时还原出和目标遥控器一致的帧。5.3 用逻辑分析仪验证学习结果学习模式成功后把P3.1串口以115200波特率输出实际测量到的同步脉宽和地址码按位打印成十六进制。验证时直接对比学习前后的两帧地址一致、键值一致、每位的脉宽偏差在15%以内说明自适应正常。如果打印值稳定但拷贝无效看发射模块输出极性和接收模块是否相反。最后一个实用技巧把发射功率降低或用衰减器靠近接收天线测试近距离发射饱和会让接收模块输出波形畸变更容易暴露出边沿抖动和阈值余量不足的问题这也是很多拷贝方案在测试台上成功、贴到墙上失败的原因。本文还有配套的精品资源点击获取