基于PIC10F206单片机的双协议红外遥控发射器设计与实现

📅 2026/6/17 8:48:51
基于PIC10F206单片机的双协议红外遥控发射器设计与实现
1. 项目概述与核心价值最近在折腾一个智能家居的小项目需要用一个非常小巧的控制器来发射红外遥控信号控制家里的老电视和空调。市面上常见的方案要么是ESP8266这类Wi-Fi模块体积和功耗对于嵌入式场景来说还是偏大要么就是专用的红外发射芯片功能又太单一。于是我把目光投向了Microchip原Atmel的PIC10F206这颗MCU。它只有6个引脚8位架构价格低廉但用来实现RC5和SIRC这两种最主流的红外协议却是绰绰有余。这个项目就是基于PIC10F206设计并实现一个通用的红外遥控发射器。简单来说这个项目能让你用一颗比米粒大不了多少的芯片制作出一个万能遥控器的“心脏”。它特别适合集成到一些DIY的智能开关、自定义遥控面板或者需要隐蔽控制传统红外家电的项目中。比如你可以把它塞进一个智能插座里让插座不仅能通断电源还能通过红外遥控你家的空调实现“插座红外遥控”二合一的功能这正是当前“红外遥控智能插座”这类热门创意的硬件核心。无论你是电子爱好者、嵌入式开发者还是智能家居的DIY玩家通过这个项目你都能彻底掌握从协议分析、单片机编程到硬件调试的红外遥控全链路知识。2. 红外协议核心RC5与SIRC深度解析在动手写代码和画电路图之前我们必须先吃透要实现的两种红外协议飞利浦的RC5和索尼的SIRC。这是整个项目的基石协议理解错了后面所有工作都是白费。2.1 飞利浦RC5协议曼彻斯特编码的典范RC5协议以其简洁和强抗干扰性著称它采用双相相位编码也就是我们常说的曼彻斯特编码。它的每一位数据都用一个电平跳变来表示具体规则是逻辑“1”用“高-低”跳变表示逻辑“0”用“低-高”跳变表示。这种编码方式的优点是无论信号持续多长时间每个位中间必然有一次跳变便于接收端同步时钟而且对载波占空比不敏感。一个完整的RC5帧是14位其结构如下两位起始位Start Bits总是逻辑“1”用于帧同步。一位控制位Toggle Bit每次按下同一个键这一位会翻转0变1或1变0。这个设计非常巧妙用于区分是“长按”还是“重复按下”。如果接收端连续收到两个相同的帧控制位相同则认为是长按如果控制位翻转了则认为是新的按键动作。五位系统地址System Address用来区分不同类型的设备比如电视、音响等。范围是0-31。六位指令Command具体的操作指令比如音量加、频道减等。范围是0-63。RC5的位时长是固定的1.778ms载波频率通常是36kHz或38kHz。这意味着每一位数据无论它是1还是0其持续时间都是1.778ms。在这个时间内会用36kHz的方波对红外LED进行调制。例如要发送一个逻辑“1”就在前0.889ms发射36kHz载波后0.889ms关闭发送逻辑“0”则相反前0.889ms关闭后0.889ms发射载波。整个帧的发送时间大约是14 * 1.778ms ≈ 24.89ms。注意在编程时定时器的精度至关重要。1.778ms这个时间需要尽可能精确否则接收设备可能无法正确解码。对于PIC10F206的内部4MHz RC振荡器需要进行校准或者使用外部晶振来保证时序。2.2 索尼SIRC协议脉冲宽度编码的代表索尼的SIRC协议与RC5完全不同它采用脉冲宽度编码。逻辑“1”和逻辑“0”通过高电平脉冲的宽度来区分而低电平的间隔是固定的。SIRC协议有多种格式最常见的是12位、15位和20位格式。我们以最普遍的12位格式为例起始脉冲Start Pulse一个长达2.4ms的高电平脉冲后面跟随一个0.6ms的低电平标志着帧的开始。7位命令Command首先发送命令字节的LSB最低有效位。5位设备地址Device Address接着发送设备地址的LSB。那么如何区分“1”和“0”呢规则是逻辑“1”高电平脉冲宽度为1.2ms随后是0.6ms的低电平。逻辑“0”高电平脉冲宽度为0.6ms随后是0.6ms的低电平。可以看到SIRC协议中每一位的周期是固定的1.2ms0.6ms1.8ms或0.6ms0.6ms1.2ms这里需要澄清实际上无论是1还是0其总周期并不严格固定因为高电平宽度不同。但低电平间隔固定为0.6ms。因此一个“1”位总时长1.8ms一个“0”位总时长1.2ms。SIRC的载波频率通常是40kHz。SIRC协议没有像RC5那样的Toggle位所以如果按住按键不放接收端会重复收到完全相同的帧。在编程实现时我们需要精确控制2.4ms、1.2ms、0.6ms这几个关键时间点。2.3 协议对比与选型思考为什么选择同时实现这两种协议因为它们几乎覆盖了市面上80%以上的消费电子设备。飞利浦的RC5协议被众多欧洲品牌的电视、音响采用而索尼的SIRC协议则是日系设备索尼、松下等的标配。实现一个双协议发射器其通用性会大大增强。从实现难度上看RC5协议因为采用曼彻斯特编码对时序的对称性要求极高编程上需要更精细的定时器控制。SIRC协议看似简单但需要生成三种不同宽度的高电平脉冲2.4ms1.2ms0.6ms对定时器的灵活性要求更高。对于资源极其有限的PIC10F206来说如何用一种高效、准确的方式同时驾驭这两种协议是本次设计的核心挑战。3. 硬件系统设计与核心器件选型硬件设计的目标是在满足功能的前提下做到极致的简单、小巧和低成本。PIC10F206的极限资源768字节程序存储器64字节RAM6个I/O口决定了我们必须精打细算。3.1 主控芯片PIC10F206的极限挖掘PIC10F206是一颗6引脚实际可用I/O为4-5个的8位单片机核心资源如下程序存储器768 Words约1.5KB机器码对于我们这个项目代码需要高度优化。数据存储器64字节RAM这意味着我们不能定义大的数组变量要尽可能用全局变量并谨慎使用函数调用栈。定时器一个8位定时器TMR0和一个看门狗定时器。红外协议对时序要求苛刻TMR0将是我们最宝贵的资源。振荡器支持内部4MHz RC振荡器和外部振荡器。为了获得更稳定的红外载波和位时序强烈建议使用外部陶瓷谐振器或晶振。这里我们选择价格低廉的4MHz陶瓷谐振器配合芯片内部的PLL倍频电路是不支持的所以系统时钟就是4MHz指令周期为1μs4个时钟周期。引脚分配方案GP0配置为输出连接红外发射LED的驱动三极管基极。这是我们的信号输出引脚。GP1配置为输入可以连接一个轻触按键用于触发发射或切换协议。GP2配置为输入可以连接一个拨码开关用于选择RC5或SIRC协议。GP3此引脚是MCLR主复位通常上拉电阻接VDD用作复位输入不能作为普通I/O。GP4, GP5可以配置为输入用于连接更多的设置开关比如选择设备地址。3.2 红外发射电路驱动与调制红外发射部分的核心是将单片机GPIO输出的数字信号转换为被高频载波调制的、足够强度的红外光信号。典型电路如下单片机GPIOGP0输出已调制好的协议波形即在需要发射红外光时输出高频方波在协议规定的低电平期间输出持续低电平。限流电阻R1连接在GP0和NPN三极管如S8050的基极之间限制基极电流通常选择1kΩ。NPN三极管作为开关使用。当基极为高电平时导通为红外LED提供电流通路。红外发射LED建议使用专用的红外发射管其波长一般为940nm与绝大多数家电接收头匹配。不要用普通红色LED代替波长不对无法工作。LED限流电阻R2这是关键它的阻值决定了红外LED的发射功率和距离。计算公式为R2 (Vcc - Vled - Vce_sat) / Iled。Vcc电源电压假设为3.3V或5V。Vled红外LED正向压降通常为1.2V-1.5V。Vce_sat三极管饱和压降约0.2V。Iled期望的LED电流。普通5mm红外LED的连续工作电流建议在20-50mA。如果想获得更远的遥控距离可以短暂地使用100mA甚至更高的脉冲电流需注意LED和三级管的瞬时功耗。举例Vcc5V Vled1.3V Vce_sat0.2V Iled50mA。则 R2 (5 - 1.3 - 0.2) / 0.05 70Ω。我们可以选择一个68Ω的常用电阻。此时LED功耗约为(5-1.3-0.2)0.050.175W三极管功耗约为0.20.050.01W均在安全范围内。实操心得为了提高遥控距离我通常会采用“脉冲驱动”法。即选择一颗能承受较大脉冲电流的红外LED比如脉冲电流可达1A的型号然后减小R2阻值让瞬时电流达到200-300mA。同时在软件上要确保发射时间足够短一帧红外信号也就几十毫秒避免LED和三级管过热。实测这种方法可以将遥控距离从标准的5-8米轻松提升到15米以上非常适合需要穿墙或远距离控制的应用。3.3 电源与辅助电路考虑到这是一个微型发射器电源方案力求简单供电采用两节AAA7号电池约3V或一颗CR2032纽扣电池3V。PIC10F206的工作电压范围是2.0V-5.5V3V供电完全没问题且有利于降低功耗。去耦电容在芯片的VDD和VSS引脚之间务必就近放置一个0.1μF的陶瓷电容用于滤除电源噪声保证单片机稳定运行尤其是发射高频载波时。按键与开关所有连接到GPIO的按键和拨码开关另一端接地并在GPIO口上启用内部弱上拉电阻。这样可以省去外部上拉电阻进一步简化电路。整个硬件原理图可以画得非常精简核心就是MCU、红外驱动、电源和几个设置接口。PCB布局时应将红外LED放置在板子边缘避免被其他元件遮挡。4. 软件架构与关键代码实现软件是项目的灵魂目标是在PIC10F206极其有限的内存和算力下精准、高效地生成RC5和SIRC波形。我们将采用“状态机精确定时器中断”的架构。4.1 系统初始化与全局定义首先我们需要进行芯片的初始化配置并定义一些全局变量和常量。// PIC10F206 使用XC8编译器示例代码 #include xc.h // 芯片配置字设置使用内部RC振荡器看门狗关闭代码保护关闭 #pragma config MCLRE OFF, CP OFF, WDTE OFF, FOSC_INTOSCIO // 定义协议类型 #define PROTOCOL_RC5 0 #define PROTOCOL_SIRC 1 // RC5协议常量 #define RC5_BIT_TIME 1778 // 单位微秒 (us) #define RC5_CARRIER_HALF_CYCLE 13 // 载波半周期 38kHz: 1/(38k*2) ≈ 13.16us // SIRC协议常量 (12位格式40kHz载波) #define SIRC_START_HIGH 2400 // 起始位高电平 2.4ms #define SIRC_BIT_SPACE 600 // 位间隔低电平 0.6ms #define SIRC_BIT1_HIGH 1200 // 逻辑1高电平 1.2ms #define SIRC_BIT0_HIGH 600 // 逻辑0高电平 0.6ms #define SIRC_CARRIER_HALF_CYCLE 12 // 载波半周期 40kHz: 1/(40k*2) 12.5us // 全局变量 volatile unsigned char current_protocol; volatile unsigned int rc5_frame_data; // 存储14位RC5帧 volatile unsigned int sirc_frame_data; // 存储12位SIRC帧 volatile unsigned char bit_counter; // 已发送位数计数器 volatile unsigned char sending_state; // 发送状态机状态 volatile unsigned char carrier_toggle; // 载波翻转标志初始化函数void init(void)需要完成以下工作配置GPIO方向GP0输出GP1/GP2输入。启用GPIO内部弱上拉如果按键使用。配置TMR0定时器选择预分频器使其每1us或一个载波半周期产生一次中断。由于我们需要非常精细的时间控制微秒级这里将TMR0配置为1:2预分频在4MHz系统时钟下TMR0每2个指令周期2μs加1。我们可以通过软件计数来匹配不同的时间要求。启用TMR0溢出中断和全局中断。4.2 核心状态机与中断服务程序整个红外发射过程由一个状态机驱动在TMR0中断服务程序ISR中执行。这是最核心、最需要优化效率的部分。状态机设计IDLE状态等待发送命令。检测到按键后根据拨码开关读取current_protocol加载相应的帧数据rc5_frame_data或sirc_frame_data初始化bit_counter和状态变量进入START状态。START状态发送协议的起始部分。对于RC5是第一个1.778ms的位包含载波调制对于SIRC是2.4ms的高电平脉冲。发送完成后进入DATA状态。DATA状态循环发送数据位。根据当前协议和当前要发送的位0或1设置下一个时间片应该输出高电平还是低电平以及持续时间。每发送完一位bit_counter加1。当所有位发送完毕进入STOP状态。STOP状态发送完成清理状态返回IDLE状态。中断服务程序ISR伪代码逻辑void __interrupt() isr(void) { if (T0IF) { // TMR0溢出中断 T0IF 0; // 清除中断标志 // 更新一个精细的微秒计数器用于测量更长间隔 us_counter; // 载波生成逻辑如果当前处于需要发射载波的状态 if (carrier_enable) { carrier_toggle ^ 1; // 翻转载波状态 if (carrier_toggle) { GP0 1; // 输出高电平产生红外光 } else { GP0 0; // 输出低电平关闭红外光 } // 重置TMR0使其在半个载波周期后再次中断 TMR0 256 - CARRIER_HALF_CYCLE_COUNT; } else { // 非载波期间处理协议状态机 switch (sending_state) { case STATE_START: if (us_counter START_DURATION) { us_counter 0; sending_state STATE_DATA; // 准备发送第一位数据 prepare_next_bit(); } // 在此期间GP0根据协议要求保持高或低 break; case STATE_DATA: if (us_counter CURRENT_BIT_DURATION) { us_counter 0; bit_counter; if (bit_counter TOTAL_BITS) { sending_state STATE_STOP; } else { prepare_next_bit(); } } break; case STATE_STOP: GP0 0; // 确保LED关闭 sending_state STATE_IDLE; carrier_enable 0; break; } // 根据状态机决定TMR0的下次中断时间用于测量us_counter // 可能设置为一个较长的值如50us以减少中断频率节省CPU TMR0 256 - 25; // 例如约50us中断一次来检查时间 } } }关键技巧这里有一个矛盾点。载波频率很高38kHz对应26.3μs周期需要频繁中断每13μs来翻转GPIO。而协议位时间很长几百微秒到几毫秒不需要这么高的中断频率。我的解决方案是双定时器模式在需要发射载波的阶段TMR0专注于产生精确的载波方波us_counter由软件在载波中断中累加每次中断加13μs。在不需要载波低电平间隔或发送SIRC的固定电平时则切换TMR0到一个较长的定时间隔用于更新us_counter和检查状态机。这需要对TMR0进行动态重载增加了编程复杂度但这是在小资源MCU上实现高精度混合时序的实用方法。4.3 协议数据帧的组装函数我们需要编写函数将用户想要发送的设备地址和命令组装成符合RC5或SIRC标准的帧数据。对于RC5需要处理Toggle位。可以定义一个全局变量rc5_toggle来记录状态。unsigned int assemble_rc5_frame(unsigned char address, unsigned char command) { static unsigned char toggle 0; unsigned int frame 0; frame 0x3FFF; // 先假设所有位为1方便后续操作 // 清除起始位位置然后设置起始位为1实际上就是保持为1 // 设置控制位 if (toggle) { frame ~(1 11); // 第11位从0开始计为控制位设为0 } else { // 控制位为1默认已是1无需操作 } toggle ^ 1; // 翻转 // 设置5位系统地址 (bit10-bit6) address 0x1F; // 确保5位 for (char i0; i5; i) { if (address (1 i)) { // 对应位为1保持为1 } else { frame ~(1 (10-i)); // 对应位清0 } } // 设置6位命令 (bit5-bit0) command 0x3F; // 确保6位 for (char i0; i6; i) { if (command (1 i)) { // 对应位为1 } else { frame ~(1 (5-i)); } } return frame; }对于SIRC组装相对简单注意命令和地址都是先发送LSB。unsigned int assemble_sirc_frame(unsigned char address, unsigned char command) { unsigned int frame 0; // SIRC 12位格式低7位是命令接着5位是地址 // 我们需要构建一个12位的数方便后续逐位发送 frame (command 0x7F); // 低7位是命令 frame | ((address 0x1F) 7); // 接着5位是地址 return frame; }5. 调试、优化与实测问题排查软件硬件搭建好后真正的挑战才刚刚开始。调试红外项目一台红外接收管、一个示波器或逻辑分析仪是必不可少的。5.1 调试工具与方法红外接收管测试最简单的方法是用一个常见的红外接收头如VS1838B三个引脚VCC GND OUT将其OUT引脚连接到一个LED或通过一个三极管放大后接扬声器。当接收到正确的红外信号时LED会闪烁或扬声器会发出“哒哒”声。这能快速验证你的发射器是否有信号发出。示波器/逻辑分析仪观测这是最准确的调试手段。将示波器探头接在发射器GP0引脚或红外LED的阳极上。观测载波放大时间轴你应该能看到频率为38kHzRC5或40kHzSIRC的方波。观测协议波形拉开时间轴你应该能看到完整的、符合RC5或SIRC标准的脉冲序列。将测量光标放在脉冲上检查高电平、低电平的宽度是否与协议规定一致如RC5的1.778ms SIRC的1.2ms/0.6ms。误差最好控制在5%以内。手机摄像头辅助观测大多数手机摄像头的CMOS传感器对红外光敏感。在黑暗环境下用手机摄像头对着工作的红外LED你会在屏幕上看到紫色的光点。这可以用来定性判断LED是否在闪烁但无法分析波形。5.2 常见问题与解决方案实录在实际制作中我踩过不少坑这里总结出来供你参考问题现象可能原因排查步骤与解决方案完全无反应接收头不动作1. 红外LED装反或损坏。2. 三极管驱动电路错误NPN/PNP搞混电阻值过大。3. 单片机根本没运行电源、复位问题。4. GPIO输出模式配置错误。1. 用万用表检查LED正向压降。2. 测量三极管基极电压发射时应有0.7V左右变化。3. 检查VDD电压用示波器看GP0是否有任何输出。4. 确认TRIS寄存器配置正确。有反应但距离非常近1米1. 红外LED驱动电流太小R2阻值太大。2. 载波频率偏差太大。3. 红外LED型号不对或质量差。1. 计算并减小R2阻值增大驱动电流注意不要超过LED和三极管极限。2. 用示波器测量载波频率调整定时器重载值。PIC内部RC振荡器误差可能达5%建议换用外部晶振。3. 更换为专用的、高发射功率的红外LED。某些设备能控制某些不能1. 协议选择错误用RC5发给了只认SIRC的设备。2. 设备地址设置错误。3. 波形时序误差在临界点某些接收芯片容错性差。1. 确认目标设备使用的协议。可以网上搜索设备型号“红外协议”。2. 尝试不同的地址码。通常电视地址是0音响是1等需要查阅协议文档或抓取原装遥控器码值。3. 用示波器精调位时序确保误差最小。遥控时灵时不灵1. 电源干扰。发射时大电流导致MCU复位或程序跑飞。2. 软件Bug状态机在某些条件下卡死。3. 环境光干扰强烈的日光灯或太阳光。1. 在MCU的VDD和GND之间并接一个100μF的电解电容稳定电源。2. 加强软件看门狗和异常状态恢复机制。3. 避免强光直射接收头或给接收头加装遮光罩。功耗过高电池消耗快1. 红外LED持续微小漏电。2. 单片机未进入低功耗模式。1. 确保在空闲状态驱动三极管完全截止GP0输出低电平。2. 在IDLE状态让单片机进入SLEEP模式仅靠外部按键中断唤醒这是省电的关键。5.3 性能优化与功能扩展在基础功能实现后可以考虑以下优化和扩展低功耗优化如前所述在非发射状态将PIC10F206置于SLEEP模式功耗可降至个位数微安级别使纽扣电池供电成为可能非常适合长期待机的遥控器。学习功能增加一个红外接收头让这个发射器能够学习并存储其他遥控器的信号。这需要额外的存储空间可使用外置EEPROM如24C02并编写复杂的解码程序。对于PIC10F206来说资源非常紧张实现完整的通用学习功能比较困难但可以针对固定的一两种协议进行简化学习。多按键支持通过GP1、GP2、GP4、GP5连接多个按键矩阵实现一个多键遥控器。需要在软件中增加按键扫描和不同键值对应不同红外码的功能。无线触发将GPIO连接的轻触按键换成无线接收模块如常见的2262/2272编码解码模块或更高级的蓝牙、Wi-Fi模块的IO口。这样这个红外发射器就变成了一个受无线信号控制的“红外转发器”是智能家居中控的常见形态。这个基于PIC10F206的红外遥控发射器项目虽然芯片简单但“麻雀虽小五脏俱全”它涵盖了嵌入式开发中硬件选型、电路设计、低层驱动、状态机编程、精确定时和调试排错等核心环节。成功实现它不仅能让你获得一个实用的万能遥控核心模块更能让你对红外通信和微型单片机开发有深刻的理解。当你拿着自己做的这个小板子成功控制客厅电视换台的那一刻那种成就感是无可替代的。