嵌入式中断与输入捕获实战:MC68HC908EY16解码RC-5协议控制LIN机器人
发布时间:2026/6/21 19:58:27
分类:文化教育
浏览:1234

1. 项目概述用MC68HC908EY16解码RC-5遥控信号并控制LIN机器人在嵌入式开发里处理异步、高速的外部信号一直是个经典难题。比如你手里拿着一个电视遥控器对着设备按一下这个“咔哒”一声背后是一连串肉眼看不见的红外光脉冲。微控制器MCU得在干着其他活比如刷新屏幕、计算数据的同时还能瞬间捕捉到这个脉冲序列并准确解读出你按的是“音量”还是“频道-”这活儿靠主程序轮询Polling基本是干不了的效率太低还容易丢数据。这时候中断服务程序ISR和输入捕获Input Capture这对黄金搭档的价值就凸显出来了。这次要聊的项目就是一个非常典型的工业级应用用一颗有些年头的但依然经典的8位微控制器——飞思卡尔Freescale现NXP的MC68HC908EY16来实现对Philips RC-5协议红外遥控信号的硬件级解码并把解码得到的命令通过LINLocal Interconnect Network总线发送出去控制一个机器人执行相应的动作比如机械臂旋转、夹爪开合。项目原文是一份飞思卡尔的应用笔记AN2560/D提供了核心思路和代码框架但很多嵌入式实战中的“坑”和“为什么这么做”的细节需要结合我们多年的踩坑经验来补全。这篇文章我就带你从头到尾拆解这个项目不仅看懂代码更要理解其背后的设计哲学和实操要点。2. 核心硬件与协议解析2.1 主角MCUMC68HC908EY16与它的定时器BMC68HC908EY16是一款基于HC08内核的8位微控制器。在当年它以高性价比和丰富的外设特别是LIN控制器在汽车电子和工业控制中很常见。对我们这个项目而言它最关键的武器是定时器模块Timer Module具体来说是定时器BTimer B及其通道0CH0的输入捕获功能。输入捕获是干嘛的简单说它能“拍照”。当MCU引脚上连接的信号比如来自红外接收头的输出发生指定的跳变比如上升沿或下降沿时定时器B当前的计数值会被瞬间“冻结”并保存到一个专门的捕获寄存器里同时产生一个中断。这样我们就精确地知道了“某个边沿发生在什么时刻”。对于RC-5这种用脉冲宽度编码的协议测量相邻边沿之间的时间间隔就是解码的全部依据。2.2 通信协议RC-5与LIN总线RC-5协议是飞利浦制定的一种红外遥控标准非常经典。它的每个帧由14位数据组成包括两位起始位总是为逻辑1。一位翻转位Toggle Bit用于区分两次按下同一个键是“按住”还是“再次按下”。五位系统地址System Address标识设备类型如电视、音响。六位命令Command具体的按键指令。它的编码采用双相相移编码Manchester编码。每一位数据都用一次电平跳变来表示逻辑“0”和逻辑“1”的区别在于跳变发生的位置。关键的时间单位是1个比特时间Bit Time规定为1.778ms。因此**半个比特时间Half-Bit Time, HBT**就是0.889ms。解码时我们就是通过测量两个上升沿之间的时间间隔是2个HBT、3个HBT还是4个HBT结合前一个比特的值来判定当前比特是0、1还是遇到了连续相同的比特。LIN总线是一种用于汽车车身控制的低成本串行通信网络单线制主从结构速度最高20kbps。在这个项目中MC68HC908EY16作为LIN节点从机将解码后的RC-5命令按照预先定义好的映射关系填充到一个LIN帧ID为0x20的数据场中然后通过LIN驱动器发送到总线上。机器人的主控制器可能是另一个MCU会监听这个ID的帧并解析数据场中的位来控制相应的伺服电机。2.3 系统框架与信号流整个系统的信号流可以这样理解红外接收头如TSOP1838接收38kHz载波的红外信号解调后输出反向的TTL电平信号有信号时输出低电平。MC68HC908EY16红外接收头输出接至PTB6/TB0引脚定时器B通道0。该引脚配置为输入捕获初始捕捉下降沿对应RC-5信号的起始沿。一旦捕获到下降沿触发中断在**输入捕获中断服务程序ISR**中开始解码流程。解码成功的14位RC-5码存入MESSAGE缓冲区。主程序循环检查IR_NEW_DATA标志。若为真读取MESSAGE。根据MESSAGE的值通过一个大的switch-case语句映射到具体的机器人动作如“手臂上抬慢速”并设置对应的LIN20_Buf[]数组位。调用LIN_PutMsg函数将数据放入LIN驱动器的发送缓冲区。通过SPI接口将接收到的14位RC-5码输出到外部的16位LED阵列上进行诊断显示。LIN总线MCU通过LIN收发器将ID 0x20的帧发送到总线机器人主控接收并执行动作。3. 解码核心输入捕获中断服务程序ISR的精细设计这是整个项目的灵魂所在也是最考验嵌入式功力的地方。原文的流程图和代码已经勾勒出了骨架我来为你填充血肉解释每一个关键决策背后的原因。3.1 状态机与变量设计解码过程本质上是一个状态机。程序用几个全局变量来维护这个状态IR_START: 布尔标志为TRUE时表示正在寻找起始位的下降沿。RX_IN_PROGRESS: 布尔标志为TRUE时表示正在接收一帧数据中的各个比特。IR_DATA: 16位无符号整数用于临时存储正在解码的14位数据。使用16位是为了操作方便高2位未用。IR_DATA_MASK: 16位掩码初始为0x4000二进制0100_0000_0000_0000。它像一个“指针”指示当前解码到的比特应该放在IR_DATA的哪个位置。每处理完一个比特或两个比特它就右移一位。PREV_BIT: 存储上一个已解码比特的值1或0。这是解码RC-5曼彻斯特编码的关键因为当前比特的值需要结合前一个比特的值和时间间隔来判断。IR_TIME: 存储从上次边沿到本次边沿所经过的定时器计数值即时间间隔。IR_ERROR: 错误标志。当测量的时间间隔不符合RC-5协议的规则时置位。IR_NEW_DATAMESSAGE: 解码完成且无误后将IR_DATA复制到MESSAGE并置位IR_NEW_DATA通知主程序。3.2 起始位的“虚拟”处理与第一个上升沿这是解码算法中最精妙也最容易让人困惑的一点。RC-5协议规定起始位是逻辑1并且以一次电平跳变开始。我们的输入捕获初始设置为捕捉下降沿因为红外接收头输出是反相的。当第一个下降沿起始沿到来时IR_START为TRUE进入起始位处理分支。这里并没有立刻解码出“1”而是做了两件事将IR_START设为FALSERX_IN_PROGRESS设为TRUE。将IR_DATA_MASK右移一位指向第一个数据比特即Toggle Bit的位置。然后程序将输入捕获改为检测上升沿并重启定时器等待第一个上升沿。这里有个关键操作在第一个上升沿的中断服务程序中如果发现IR_DATA_MASK 0x2000即指向第一个数据比特程序会执行IR_TIME IR_TIME HBT_1;。为什么因为我们的解码规则见表3规则A-D需要测量两个上升沿之间的时间。对于第一个数据比特它的前一个“上升沿”实际上是起始位的下降沿。为了复用后续比特的解码代码我们虚构了一个“虚拟上升沿”它位于起始位下降沿之前的1个HBT处。所以从“虚拟上升沿”到第一个真实上升沿的时间应该是“从下降沿到上升沿的时间”加上“1个HBT”。这个操作就是为了补偿这个虚拟的时间差使得IR_TIME的值可以直接套用后续的解码规则表。同时PREV_BIT被默认设置为1对应这个虚拟的、值为1的前一个比特。3.3 核心解码规则与状态判断第一个上升沿之后的每个上升沿都会触发中断。此时程序读取定时器捕获值TBCH0H得到IR_TIME然后结合PREV_BIT的值根据下表进行判断规则条件 (时间间隔)前一个比特 (PREV_BIT)当前解码结果说明A2 HBT11比特值从1变为0在中间跳变。B2 HBT00比特值从0变为1在中间跳变。C3 HBT11, 0(两个比特)连续两个1中间无跳变第3个HBT出现跳变表示新比特开始。D3 HBT00, 1(两个比特)连续两个0中间无跳变。E4 HBT01, 0(两个比特)在连续两个0之后再遇到一个0形成“0,0,0”序列需要4HBT。代码中的if-else if链就是严格实现了这5条规则。例如规则A和D对应同一个判断分支解码为单个比特“1”规则C和E对应另一个分支解码为两个比特“1,0”。每次成功解码一个或两个比特后如果解码出“1”则执行IR_DATA IR_DATA | IR_DATA_MASK;。无论解码出0或1都需要更新PREV_BIT为最后一个解码出的比特值。IR_DATA_MASK右移一位解码一个比特或两位解码两个比特。3.4 错误处理、帧结束与缓冲区管理错误处理如果测量的IR_TIME不在任何预期的HBT范围内HBT_2L-HBT_2H,HBT_3L-HBT_3H,HBT_4L-HBT_4H则置位IR_ERROR TRUE。帧结束当IR_DATA_MASK右移14次后变为0x0000时表示14个比特全部接收完毕。在ISR的最后会根据IR_DATA_MASK和IR_ERROR决定下一步接收中且无错误重新使能输入捕获上升沿和定时器溢出中断等待下一个上升沿。帧结束或无错误准备接收新帧。这里有一个重要的缓冲区管理策略检查IR_NEW_DATA标志。如果为FALSE主程序已读取上一帧则将IR_DATA复制到MESSAGE并置位IR_NEW_DATA TRUE。如果IR_NEW_DATA为TRUE主程序还没处理完上一帧则丢弃当前新解码的帧。这是一种简单的“单缓冲”策略在遥控器按键速度不会快于主程序处理速度的应用中是可行的。如果担心丢包可以设计一个环形队列FIFO作为多级缓冲。定时器溢出中断作为看门狗。如果设置好时间上限例如远大于4个HBT在等待边沿时定时器溢出了说明信号丢失或出现严重错误如长按产生的连发码RC-5有其特定格式这里未处理ISR会重置整个接收状态机重新开始寻找起始位。4. 主程序逻辑、LIN消息映射与诊断输出4.1 主循环与命令映射主程序main()在初始化所有外设端口、SPI、定时器、LIN后进入一个while(1)死循环。其核心逻辑是定期检查**时间基准模块TBM**的溢出标志以此产生一个约130ms的周期性时基。在这个时基中断中检查IR_NEW_DATA标志。如果IR_NEW_DATA为真读取MESSAGE并通过一个庞大的switch-case语句进行映射。映射表是预先定义好的将特定的RC-5命令码映射到LIN20_Buf数组的特定位上。例如case 0x3011:对应RC-5码0x3011可能是音量-键被映射为LIN20_Buf[0] 0x08;控制机器人“慢速左转”。case 0x3151:对应另一个RC-5码映射为LIN20_Buf[0] 0x02;控制“快速左转”。LIN20_Buf[3]的最高位MSB用于模式选择0表示LIN主控模式1表示手动遥控模式。通过遥控器上的“频道”和“频道-”键来切换。自动清零机制主程序维护一个CLEAR_LIN_DATA计数器。如果大约130ms内计数器溢出没有收到新的有效遥控命令IR_NEW_DATA为假则自动将LIN20_Buf[0]、[1]、[2]清零[3]只保留模式位。这实现了“松手即停”的安全功能松开遥控键后机器人运动指令被清除各关节伺服电机停止。4.2 SPI诊断LED阵列这是一个非常巧妙的调试和诊断设计。项目使用MCU的SPI模块驱动两片74HC164串入并出移位寄存器级联后控制16个LED。电路连接MCU的SPSCK(PA5) 连接两片74HC164的时钟引脚。MCU的MOSI(PC1) 连接第一片74HC164的数据输入。第一片74HC164的最后一个输出QH连接第二片的数据输入。每个74HC164的输出通过限流电阻连接LED阴极LED阳极接VCC。因此74HC164输出低电平时LED亮。软件实现SPI_Transmit函数接收一个16位的LEDVALUE将其按位取反因为硬件是低电平点亮然后拆成高8位和低8位通过SPI数据寄存器SPDR依次发送出去。SPI模块会自动产生时钟将数据移入移位寄存器。LED 1-14分别对应RC-5码的14个比特LED亮比特1LED 15-16未使用。这个诊断工具的价值巨大。在开发阶段你可以直观地看到接收到的每一个RC-5码的二进制形态迅速判断解码是否正确。对于教学和故障排查它比逻辑分析仪更直观。5. 关键参数计算、配置与避坑指南5.1 定时器与HBT时间计算这是整个解码准确性的基础。RC-5协议的1 HBT 0.889ms。我们需要根据MCU的时钟频率计算出定时器计数多少个时钟周期等于1个HBT。项目提供了两套参数分别对应8MHz和9.8304MHz时钟。以8MHz为例定时器时钟源 系统时钟 / 4 2MHz (周期0.5us)。1 HBT 0.889ms 889us。1 HBT对应的定时器计数值 889us / 0.5us 1778 (十进制) ≈ 0x06F2。但是注意代码中的HBT_1被定义为0x07十进制7。为什么差这么多因为这里使用的是定时器的高字节(TBCH0H)。在输入捕获ISR中只读取了TBCH0H8位而TBCH0L虽然被读取用于解锁寄存器但其值并未参与计算。这意味着时间分辨率被降低了。实操要点与坑分辨率取舍只使用高8位意味着时间分辨率从0.5us降低到了 256 * 0.5us 128us。这对于检测~889us的HBT是否足够代码中通过定义HBT_2L、HBT_2H等一个范围如2 HBT对应0x08-0x0F来容忍这种误差。这是一种在8位MCU上节省计算资源和中断耗时的经典权衡。但在对时序要求极严的应用中可能需要使用16位值。时钟精度外部晶振的精度直接影响解码。如果使用内部RC振荡器其频率误差可能达到1-2%可能导致解码失败。务必使用高精度晶振。宏定义切换代码中通过注释来切换8MHz和9.8304MHz的配置。在实际项目中最好使用条件编译#ifdef。5.2 中断服务程序ISR的优化在8位MCU上ISR的效率至关重要。这个项目的ISR设计有几个可圈可点之处快速进出ISR一开始就停止定时器清除标志减少了中断嵌套的复杂性和时间误差。变量使用大量使用位操作和掩码避免乘除法。状态清晰通过几个布尔标志清晰地划分了接收状态寻起始位、接收中、错误、完成。避坑指南避免在ISR中做耗时操作例如这个ISR里没有调用SPI_Transmit来刷新LED也没有进行复杂的LIN消息处理。这些操作都放在了主循环中。ISR只负责最核心的时序测量和解码。注意共享变量IR_NEW_DATA和MESSAGE在ISR和主程序中被共享。在这个项目中主程序只在IR_NEW_DATA为真时读取MESSAGE然后立即将其置假。这是一个简单的“生产者-消费者”模型由于操作是原子的单字节读写且顺序固定在HC08这类架构上通常不会出问题。但在更复杂的系统或32位MCU上可能需要关中断保护或使用原子操作。栈空间确保有足够的栈空间处理中断。HC08的ISR使用TRAP_PROC指令有独立的栈处理。5.3 LIN通信配置代码中LIN的初始化LIN_Init()和发送LIN_PutMsg()是调用了飞思卡尔提供的LIN驱动API。这部分代码在原文的附录中没有完全展开但在实际项目中你需要正确配置LIN控制器的波特率通常与LIN主节点匹配如19200 bps。配置ID过滤器使本节点能响应ID 0x20的帧这里作为发送方也需要配置。理解LIN_PutMsg是非阻塞的它只是将数据放入硬件或驱动层的缓冲区由LIN控制器在调度时自动发送。6. 项目移植与扩展思考虽然这个项目基于特定的MCU和评估板但其核心思想具有普适性可以移植到任何带有输入捕获功能的微控制器上如STM32、AVR、PIC等。移植步骤替换硬件抽象层将针对MC68HC908EY16的寄存器操作如TBSC,TBCH0H替换为目标MCU的定时器输入捕获和中断配置代码。重算时间参数根据目标MCU的时钟频率和定时器预分频设置重新计算HBT_1、HBT_2L/H等阈值。最好将这些值定义为可配置的常量或通过宏计算。调整中断服务程序适应新MCU的中断向量表和ISR语法。注意新MCU的中断标志清除机制可能不同有些是读寄存器清除有些是写1清除。外设驱动替换SPI驱动和LIN驱动或改用其他通信方式如UART、CAN。扩展方向协议兼容RC-5只是红外协议的一种。你可以修改解码状态机使其支持NEC、Sony SIRC等更常见的协议。关键在于理解新协议的编码规则引导码、数据0/1的脉冲宽度、结束码并调整ISR中的判断逻辑。增加命令队列将MESSAGE从单一变量改为一个环形缓冲区避免在快速连续按键时丢失命令。增加信号滤波在输入捕获引脚前可以增加软件去抖或硬件RC滤波提高在嘈杂环境下的抗干扰能力。状态反馈当前项目是单向控制。可以扩展为双向通信让机器人通过LIN总线将自身状态如关节角度、电池电压发送回来再由MCU通过其他方式如另一个红外发射管反馈到遥控器或显示在诊断LED上。这个项目麻雀虽小五脏俱全涵盖了嵌入式开发中中断、定时器、通信协议解码、总线通信、诊断设计等多个核心知识点。吃透它你对如何用MCU处理实时异步事件的理解会上一个大台阶。