红外遥控RGB全彩灯:STC89C52RC解码NEC协议与AMC7135恒流驱动 简介一套基于STC89C52RC单片机与HS0038B一体化红外接收头的RGB全彩灯控制综合方案面向单片机初学者、电子DIY爱好者及红外遥控应用开发者适用于智能照明、氛围灯或相关课程项目。资源以Keil工程为核心包含可编译的主程序C文件、可烧录的hex文件同时提供高清电路图PDF与遥控器编码示意图并保留编译过程中生成的lst、m51、obj、lnp、plg及备份文件便于核对工程构建过程与查错。源码对各个功能模块分别注释完整展示了红外解码、按键映射、PWM调色以及AMC7135恒流驱动1W RGB-LED的实现逻辑读者可依据电路图直接焊接实物验证即便未提供Proteus仿真模型也能通过实际制作检验设计适合课程设计、毕业设计或DIY作品参考。压缩包共14个文件整体大小仅35KB轻量便捷。目前已有401人学习下载可快速上手红外控制彩灯项目。1. 红外遥控RGB全彩灯STC89C52RC、HS0038B和AMC7135怎么配合解压RGB-LED.zip里面没有仿真只有能跑起来的实物方案红外控制灯.c、红外控制灯.hex、电路图.pdf以及一张遥控器编码.bmp。整条链路从红外按键到RGB灯芯只有三个关键芯片STC89C52RC负责解码和调度HS0038B一体化接收头把38kHz红外载波还原成单片机认识的脉冲AMC7135则把PWM信号变成稳定的恒流输出去推1W的RGB-LED。1W灯珠的额定电流在350mA左右三路全开接近1A普通IO口直接驱动是不可能恒流驱动不是可选项而是必选项。这套资料对两类人比较合适。一类是刚开始做红外遥控想把NEC协议从波形到代码完整吃下来的单片机使用者另一类是做过LED灯但一直在用限流电阻想换恒流驱动思路的硬件工程师。前者能从HS0038B的解调方式和定时器测宽逻辑里把协议搞透后者能直接借鉴AMC7135加PWM调光的电路布局省去自己算三路限流电阻的麻烦。2. 硬件骨架HS0038B、AMC7135与STC89C52RC的引脚分工2.1 三个器件为什么能组成一套完整的RGB控制器STC89C52RC是经典8051内核5V供电IO口多价格也低适合做红外解码和PWM调度这类中小型控制任务。它的问题在于IO驱动能力有限常规IO根本带不动1W功率等级的LED灯珠更不能直接在三路不同正向压降的LED上做精确调光。所以它在系统里只负责“脑力活”读取红外接收头吐出来的脉冲判断按键再把PWM信号送到恒流驱动芯片的使能端。HS0038B是集成度很高的一体化红外接收头内部完成红外光接收、前置放大、带通滤波、解调输出几件事中心频率38kHz正好覆盖绝大多数红外遥控器的载波频率。它输出的不是原始红外波形而是已经解调好的电平信号单片机只要量电平宽度就能还原协议不需要自己搭三极管放大电路和检波电路。用示波器看它的OUT引脚空闲时高电平收到红外信号时低电平脉冲与NEC协议波形正好反相这一点在后文写解码时非常关键。AMC7135是350mA低压差恒流源芯片接在每路LED灯珠的供电回路里作用是维持固定输出电流。三颗灯珠分别配一片AMC7135R、G、B三路的电流互相独立即使灯珠正向压降有差异也不会出现某一路偏亮某一路偏暗的问题。把这个恒流芯片和单片机输出的PWM配合起来就能做到“恒流下调占空比”既保护灯珠又能平滑改变颜色。2.2 HS0038B输出的是反相NEC波形不是原始数据写解码程序之前先把HS0038B的波形翻译清楚。NEC协议里发送端用38kHz载波表示有效脉冲比如引导码是9ms载波加4.5ms空闲HS0038B输出反相载波段对应低电平空闲段对应高电平。于是单片机看到的引导码是“9ms低电平加4.5ms高电平”逻辑1对应“0.56ms低电平加1.69ms高电平”逻辑0对应“0.56ms低电平加0.56ms高电平”。判断NEC位流最常用的办法是捕捉HS0038B输出脚的下降沿测量相邻两次下降沿的间隔。因为NEC协议里每一位都是紧挨着的两次下降沿之间的时间就是当前位的周期逻辑0约1.125ms逻辑1约2.25ms引导码约13.5ms。把接收头接在单片机的外部中断输入上每次下降沿都触发一次中断在中断里读定时器计数值就能区分位和引导码。HS0038B引脚方向连接目标说明OUT输出P3.2INT0解调后电平空闲高有效低VCC电源5V需并联100nF去耦电容GND电源地与单片机共地初始化代码很简单重点是开启外部中断下降沿触发void infrared_io_init(void) { IT0 1; // INT0 下降沿触发配合HS0038B低有效输出 EX0 1; // 使能外部中断0 EA 1; // 开总中断 }IT0置1后外部中断0只在P3.2引脚从高电平跳变到低电平时响应正好对应NEC每一位的起始时刻。采集信号时不需要轮询IO口CPU可以腾出来处理PWM等任务。注意STC89C52RC的IO上电默认是准双向口P3.2配置成输入也能直接读代码里的设置不必额外改P3M1寄存器。如果换用其他单片机要确认外部中断是否支持下降沿触发部分新平台默认只有上升沿或双边沿触发。2.3 AMC7135的恒流通道与使能端调光AMC7135的典型接法是LED灯珠正极接电源负极接AMC7135的OUT引脚AMC7135内部把电流稳在350mAGND接地。单片机不直接串联在灯珠回路里而是把PWM信号接到AMC7135的使能脚EN/CE上通过控制芯片的通断来调节LED平均电流。通道单片机引脚AMC7135使能脚灯珠R通道P2.0EN/CE红色LEDG通道P2.1EN/CE绿色LEDB通道P2.2EN/CE蓝色LED市场上AMC7135有两种常见封装一种带使能脚可以直接接单片机PWM一种只有三脚不带使能端。如果手里的是三脚版本就不能直接调光需要在输出回路里串联一颗NMOS用单片机PWM控制NMOS栅极再通过NMOS的通断去切AMC7135的电源。资料中若以四脚封装为基准则直接按表格接线即可。2.4 供电要注意三路全开的瞬时电流1W的RGB-LED灯珠每路额定电流350mA三路同时开到最大占空比电源需要提供大约1A的电流。USB口的5V供电虽然电压合适但很多电脑前置USB或充电头输出能力不足PWM全开时电压跌落会导致HS0038B误码率上升表现为距离稍远就丢按键。建议用5V/1A以上的适配器并且在HS0038B的VCC和地之间并联10uF电解电容和100nF瓷片电容避免AMC7135开关瞬间的电流纹波影响红外接收灵敏度。3. NEC红外协议从遥控器编码.bmp到定时器0解码3.1 NEC帧格式与HS0038B反相后的实际波形NEC协议的一帧数据由引导码、地址码、地址反码、键码、键码反码组成一共32位。引导码之后先发地址码低8位再发地址码反码之后是键码和键码反码。这四段每段8位且都是LSB先发也就是每个字节的最低位最先出现在波形上。NEC帧段位数作用引导码9ms低4.5ms高标识一帧开始地址码8位区分遥控器型号地址反码8位校验地址键码8位按键编号键码反码8位校验键码在HS0038B的输出端引导码的实际波形是9ms低加4.5ms高。数据位的区分标准不变两位之间的下降沿间隔接近1.125ms时判为逻辑0接近2.25ms时判为逻辑1超过12ms则认为是引导码。别用高电平宽度判断因为HS0038B输出的高电平时间在逻辑1时是1.69ms在逻辑0时是0.56ms测量下降沿间隔更稳定不受空闲期高电平拖尾影响。3.2 用定时器0测量下降沿间隔解码STC89C52RC工作在12MHz晶振、12T模式下定时器每1us计一个数16位定时器可计时65.5ms足够覆盖NEC最长的一帧。解码的思路是把定时器0当成一个自由运行的毫秒计外部中断0每次触发时读当前计数然后立刻清零这样上一次清零到本次中断的时间间隔就是当前脉冲周期。volatile unsigned long rx_bits; volatile unsigned char rx_bit_cnt; volatile unsigned char rx_done; void timer0_ir_init(void) { TMOD 0xF0; TMOD | 0x01; // 定时器016位定时模式 TH0 0; TL0 0; TR0 1; // 启动只计数不开中断 } void ir_isr(void) interrupt 0 { unsigned int now; unsigned int diff; now (TH0 8) | TL0; TH0 0; TL0 0; diff now; if (diff 13000) { rx_bit_cnt 0; rx_bits 0; return; } if (diff 800) { rx_bit_cnt 0xFF; return; } if (diff 1800) { rx_bits | ((unsigned long)1) rx_bit_cnt; } if (rx_bit_cnt 32) { rx_done 1; rx_bit_cnt 0; } }这段代码里diff大于13000约对应13.5ms的引导码周期此时把位计数清零准备接收数据。diff小于800us的脉冲被当作干扰丢弃避免环境光或零星红外信号破坏状态。如果diff在1800us以上判定为逻辑1否则判定为逻辑0因为NEC逻辑1的周期约2.25ms逻辑0约1.125ms两个区间相隔足够远。收满32位后置rx_done标志主循环查询这个标志即可。注意这里rx_bits的低8位存地址码第8到15位存地址反码第16到23位存键码第24到31位存键码反码。由于NEC是LSB先发收到的32位比特流按先后顺序排列后字节边界天然对齐不需要额外反转位序。若参考别的教程发现对方做了位反转那多半是对方把接收顺序理解成了MSB先发这里以本代码位序为准。3.3 对照遥控器编码.bmp写键值表解压包里那张遥控器编码.bmp拍摄的是实际遥控器的按键波形或者已经解析好的编码对照里面能看到用户码和每个按键的键值。程序里要把用户码定义成宏和接收帧中的地址码做比对校验通过后再把键码交给主逻辑。#define USER_CODE 0x45 /* 修改成遥控器编码.bmp里的实际用户码 */ void handle_ir(void) { unsigned char addr, inv_addr, key, inv_key; addr rx_bits 0xFF; inv_addr (rx_bits 8) 0xFF; key (rx_bits 16) 0xFF; inv_key (rx_bits 24) 0xFF; if (addr ! USER_CODE) return; if (inv_addr ! (unsigned char)~addr) return; if (inv_key ! (unsigned char)~key) return; switch (key) { case 0x01: /* 按实际编码替换 */ set_color(20, 0, 0); break; case 0x02: set_color(0, 20, 0); break; default: break; } }地址反码和键码反码是NEC协议自带的校验段分别对地址码和键码取反。若反码数据对不上说明这一帧在传输或解码过程中出错直接丢弃比继续执行动作更安全。写键值表时不要凭记忆猜键码一定以资料里那张遥控器编码.bmp为准不同遥控器的键码差异很大同一个按键在不同品牌遥控器上数值可能完全不同。3.4 重复码对系统的影响按住NEC遥控器按键不放芯片会以110ms为间隔重复发送引导码不再发送完整32位数据。由于前面代码里收到大于13000us的间隔就重置位计数重复码的11.25ms周期不会被误判成引导码所以不会产生rx_done标志。这带来一个行为按住按键时系统只在按下瞬间执行一次动作这是合理的避免了RGB灯在长按过程中来回切换颜色。若希望长按时循环换色可以利用一个定时器在无新按键时持续触发或者在解码时单独识别重复码并置一个repeat标志。资料里没有要求做长按连续响应保持单次触发即可代码也最简单。4. 主控逻辑按键分发、PWM调光与RGB三通道混色4.1 帧校验之后再分发按键动作解码得到的32位数据帧只是原始信号真正决定灯珠动作的是键码校验通过后的switch分发。主循环里不断查询rx_done一旦发现新帧就进入处理函数先校验地址和反码再执行对应操作。#define KEY_ON_OFF 0x10 #define KEY_BRIGHT_UP 0x11 #define KEY_BRIGHT_DOWN 0x12 #define KEY_COLOR_NEXT 0x13 void set_color(unsigned char r, unsigned char g, unsigned char b) { pwm_r r; pwm_g g; pwm_b b; } void handle_ir(void) { unsigned char key; if (rx_bits 0xFF ! USER_CODE) return; if (((rx_bits 8) 0xFF) ! (unsigned char)~USER_CODE) return; key (rx_bits 16) 0xFF; switch (key) { case KEY_ON_OFF: lamp_enable !lamp_enable; break; case KEY_BRIGHT_UP: if (bright 10) { bright; refresh_pwm(); } break; case KEY_BRIGHT_DOWN: if (bright 1) { bright--; refresh_pwm(); } break; case KEY_COLOR_NEXT: color_idx (color_idx 1) % 7; apply_color_preset(color_idx); break; default: break; } }这段代码把亮度等级bright和具体PWM占空比分开处理。bright只保存1到10的档位refresh_pwm函数负责把bright映射到PWM比较值并更新pwm_r、pwm_g、pwm_b。这样做的目的是让亮度调整和颜色切换互不干扰切换颜色时保留当前亮度档位亮度增减时不改变色相。4.2 定时器1做软件PWM驱动AMC7135的三路使能端STC89C52RC没有硬件PWM模块需要靠定时器中断模拟。资料中工程里用的方案是用定时器1产生固定周期的中断在中断服务程序里对每个PWM通道做比较输出。这里的参数选择比较讲究PWM频率太低会看到LED闪烁太高又会让单片机忙于中断处理连解码和主循环都被拖慢。volatile unsigned char pwm_r, pwm_g, pwm_b; volatile unsigned char pwm_cnt; volatile unsigned char lamp_enable; volatile unsigned char bright; void timer1_isr(void) interrupt 3 { TH1 0xFF; TL1 0xCE; // 12MHz与12T模式下约50us if (pwm_cnt 20) pwm_cnt 0; // 20次中断构成一个PWM周期约1kHz if (lamp_enable) { P2_0 (pwm_cnt pwm_r) ? 1 : 0; P2_1 (pwm_cnt pwm_g) ? 1 : 0; P2_2 (pwm_cnt pwm_b) ? 1 : 0; } else { P2_0 0; P2_1 0; P2_2 0; } }定时器1重装值0xFFCE让中断约50us触发一次20次循环得到一个1ms的PWM周期也就是1kHz频率。这个频率下肉眼完全看不到闪烁AMC7135的使能端响应也足够快。PWM比较值pwm_r、pwm_g、pwm_b的范围是0到20因此亮度分辨率为5%对遥控调光来说够用。若用256级占空比则每级间隔0.4%实际调光时很难察觉变化反而白白增加中断开销。参数数值含义PWM中断间隔约50us定时器1重装0xFFCEPWM周期1ms20次中断PWM频率约1kHz无可见闪烁亮度分辨率20级每级约5%注意三个PWM通道共用一个pwm_cnt计数器但同时比较所以三路相位完全一致。如果三路相位错开会造成混色时亮度叠加不均匀尤其是在低占空比段可能出现颜色往某一路偏移的现象。共用一个计数器是比较稳妥的做法。4.3 亮度映射需要做Gamma校正把bright从1到10直接放大成占空比是常见的错误做法。LED灯珠的视觉亮度与电流大致呈线性关系但人眼对暗部亮度变化更敏感所以占空比线性变化时暗档之间的视觉差异远大于亮档。低亮度时每升一档感觉跳变很大高亮度时几乎看不出变化。资料工程里如果只要求基本功能可以直接线性映射想做得专业一点可以用一张查表完成Gamma校正code unsigned char gamma_curve[11] { 0, 0, 1, 2, 3, 5, 8, 12, 16, 19, 20 }; void refresh_pwm(void) { unsigned char pwm_max gamma_curve[bright]; /* 当前颜色固定为红色用gamma后的占空比整体缩放 */ pwm_r pwm_r * pwm_max / 20; pwm_g pwm_g * pwm_max / 20; pwm_b pwm_b * pwm_max / 20; }gamma_curve里低档位的增量小、高档位增量大模拟人眼感知的等距变化。使用查表而不是现场计算幂函数是因为STC89C52RC没有硬件乘法器浮点运算是很大的开销。上面代码用整数乘法和除法PWM最大值20所以计算量很小不会影响定时器中断的实时性。5. Keil工程编译、STC-ISP烧录与实物调试5.1 uvproj是Keil C51老工程先装对工具链解压出来的红外控制灯.uvproj是Keil uVision4时代的工程文件后缀不带x。Keil4老版本可以直接打开新装Keil5时要注意区分C51和MDK-ARM两套工具链。RGB-LED.zip这个项目用的是C51编译器安装Keil5后要单独添加C51支持包否则打开工程会提示设备不支持或者编译时找不到C51.exe。打开后如果弹出工程迁移提示直接确认即可。由于文件里保留了红外控制灯.uvproj.bak、红外控制灯.uvopt.bak和大量.LST、.M51、.plg文件这些是编译过程产生的中间文件不影响使用。实际修改源码只需要编辑红外控制灯.c编译后重新生成hex文件。5.2 生成HEX文件的方式GUI勾选或命令行编译常规做法是在Keil里打开工程进入Project菜单下的Options for Target选择Output标签页勾选Create HEX File然后重新编译。编译通过后在工程输出目录下会生成红外控制灯.hex。这个文件的每一行都是Intel HEX格式STC-ISP下载程序能直接识别。C51.exe 红外控制灯.c DB OE BR(4) BL51.exe 红外控制灯.OBJ TO 红外控制灯.abs OH51.exe 红外控制灯.abs用命令行也能完成同样的流程C51.exe负责把C源码编译成目标文件BL51.exe链接目标文件生成绝对地址的可执行文件OH51.exe再把可执行文件转换成hex。工程里保留的.LST、.OBJ和.M51文件正是这套命令行工具的产物。对新手来说在Keil界面里勾选即可命令行方式只用于排查工具链安装问题。5.3 STC-ISP下载时注意冷启动时序STC89C52RC的程序下载依赖串口和冷启动时序。操作顺序是先打开STC-ISP软件选择单片机型号STC89C52RC选择对应的串口加载红外控制灯.hex然后点击下载按钮最后再给待烧录的板子上电。如果板子已经上电软件会一直等待此时需要按一下复位电路或者断电重上电让单片机在上电瞬间进入ISP引导区。下载参数里波特率不需要设置到最高尤其是USB转TTL模块不稳定时把波特率降到9600或4800能明显提高成功率。如果下载过程中提示“握手失败”先检查串口号是否正确再检查单片机供电是否稳定最后确认USB转TTL的TXD和RXD是否接反。5.4 实物调试的故障排查表将程序烧进去后最常出现的问题集中在供电、红外接收和PWM三处。以下按故障现象排列由概率高到概率低给出处理思路。故障现象优先检查处理方法上电完全不亮电源输出能力换5V/1A以上适配器遥控近距离能亮远距离不行HS0038B供电纹波在VCC并联10uF和100nF电容按任意键都没反应遥控器协议确认遥控器是NEC协议而非RC5三路中有两路正常一路不变AMC7135使能脚三脚版本需另加NMOS灯亮时有明显闪烁PWM周期过大检查TH1/TL1重装值是否被改动解码偶尔错键反码校验关闭确认代码里没有屏蔽inv_key校验HS0038B的供电纹波是最隐蔽的问题。AMC7135在PWM全开瞬间会让地线上出现几十毫伏的波动这个波动耦合到HS0038B内部就会造成误触发。实测中在HS0038B的VCC引脚附近直接焊接100nF瓷片电容比在电源输入端放一个大电容效果更好因为高频噪声发生在芯片引脚处。6. 进阶HSV色相渐变与RGB白平衡修正6.1 用分段线性HSV让颜色平滑过渡颜色切换如果只做固定几档跳变遥控器的可玩性不高。在51上做经典HSV转RGB计算涉及浮点和三角函数开销较大更稳妥的做法是直接对色相值分段线性插值。设定色相h为一个0到255的变量6个颜色区间各占约42.6级每个区间内部只有一路渐升、一路渐降。void hue_to_rgb(unsigned char h, unsigned char *r, unsigned char *g, unsigned char *b) { unsigned char region h / 43; unsigned char rest h % 43; unsigned char up (unsigned char)((unsigned int)rest * 20 / 42); unsigned char dn 20 - up; switch (region) { case 0: *r 20; *g up; *b 0; break; case 1: *r dn; *g 20; *b 0; break; case 2: *r 0; *g 20; *b up; break; case 3: *r 0; *g dn; *b 20; break; case 4: *r up; *g 0; *b 20; break; default:*r 20; *g 0; *b dn; break; } }代码中region把整个色相环分成6段rest表示当前段内偏移量up从0逐步升到20dn从20逐步降到0。每段的颜色演进规律是红到绿、绿到蓝、蓝到红中间经过黄、青、品红三个过渡色。主循环里每20ms把h加1RGB三路输出就会沿色相环缓慢变化形成渐变效果。6.2 白平衡修正才是白光纯正的关键三路都给满占空比20得到的不是白光是偏色光。原因是人眼对红绿蓝三色光的敏感度差异极大同样350mA下绿色灯珠的视觉亮度远高于红蓝RGB三路等电流时混出来往往是偏青或偏绿的白。修正白平衡的方法是在白光预设里给三路设置不同权重而不是让三路等值输出。code unsigned char white_balance[3] {20, 12, 17}; void set_white(void) { pwm_r white_balance[0]; pwm_g white_balance[1]; pwm_b white_balance[2]; }数组里的三个值只是初始示例实际必须根据手头灯珠重新标定。把灯放在白墙上收窄光圈用手机或简易照度计分别测量单色光在占空比20时的亮度然后把最亮的那一路权重设为20其余两路按比例下调直到三路亮度读数接近一致。标定后的白平衡还会随电流变化但1W灯珠工作在350mA附近时漂移不大固定一组权重即可。6.3 用渐变功能验证整条链路把hue_to_rgb接到定时器主循环里就是一个很好的系统验证方法如果渐变过程中出现某一路突然跳变多数是PWM比较值更新与定时器中断不同步如果颜色在某个区间反复卡住说明解码键值表里混入重复键码如果渐变顺畅但白色偏色再回头标定白平衡权重。这个验证方式也能同时检验AMC7135的线性度停在某一颜色上把占空比从0逐步推到20观察亮度曲线是否平滑过渡。本文还有配套的精品资源点击获取