CC253x/CC254x定时器红外收发原理与实战配置详解 1. 项目概述与核心价值如果你正在用CC253x或CC254x这类芯片做物联网节点、智能家居遥控器或者任何需要红外收发功能的小玩意儿那你肯定绕不开它的定时器。芯片手册里关于Timer1和Timer3在红外模式下的那几页我当年第一次看的时候也是一头雾水一堆寄存器位和框图感觉懂了又好像没完全懂。直到真正动手调了几个项目把红外发射和学习的代码从头到尾写了一遍才把这些碎片化的信息串成了完整的知识链条。简单来说CC253x/CC254x的红外功能其核心就是让两个定时器“打配合”。Timer3这个8位定时器主要干的是“打拍子”的活负责产生那个38kHz或者其它频率的载波你可以把它想象成一个节奏稳定的节拍器。而Timer1这个16位的大家伙则负责“唱旋律”它根据具体的红外协议比如NEC、RC5生成高低电平变化的编码信号。最关键的是这两个定时器的输出在硬件上通过一个与门AND Gate合并最终生成一个被载波调制好的、干净的红外波形直接从IO脚推出去驱动红外LED。反过来在学习接收模式下这套硬件又能化身为一对“耳朵”Timer3监听原始的、带着载波的红外信号用来测算载波频率Timer1则接收已经解调好的数字波形精准地捕获其上升沿和下降沿从而还原出原始的编码数据。这种硬件集成的方案其价值在于把最耗CPU时间的波形生成和精确计时工作完全甩给了专用外设。你的主CPU只需要在合适的时机比如一个完整的红外帧发送或接收完毕去处理一下数据就行中间那些微秒级的时序控制完全不用操心极大地解放了CPU也保证了系统的实时性和低功耗。这对于电池供电的无线设备来说简直是福音。下面我就结合实际的寄存器操作和代码片段把这套机制掰开揉碎了讲清楚。2. 硬件架构与工作模式深度解析要玩转红外功能首先得在脑子里把芯片内部的硬件连接图画出来。手册里的框图是静态的我们需要理解的是数据流和控制流。2.1 红外生成模式的信号通路在红外生成模式下整个信号链是这样的Timer3通道1它被配置为输出比较模式生成一个固定占空比比如33%的方波这个方波的频率就是红外载波频率通常是38kHz。Timer1通道1它也被配置为输出比较模式但生成的是红外编码信号。这个信号在“有脉冲”Mark时为高电平“无脉冲”Space时为低电平。与门AND Gate这是关键所在。Timer3通道1的输出载波和Timer1通道1的输出编码同时送入一个硬件与门。与门的逻辑是“两者都为高输出才为高”。最终输出与门的输出直接连接到芯片的某个特定IO引脚例如P1.1作为IR Out。这样一来只有当Timer1输出高电平表示发送红外脉冲时Timer3的载波方波才能通过形成一段被调制了的38kHz脉冲群当Timer1输出低电平时与门输出恒为低红外LED熄灭。这就完美实现了幅移键控ASK调制。这里有个非常重要的细节为了避免输出信号出现毛刺glitchTimer1和Timer3的计数器在启动时是同步的。手册里提到“The timing of the Timer 3 channel 1 output and Timer 1 channel 1 output signals is synchronized”。这种同步通常由硬件内部保证确保了编码信号的边沿与载波信号的边沿对齐不会产生短暂的错误脉冲。2.2 红外学习模式的信号通路切换到学习模式时信号流方向反了过来前端电路红外PIN二极管接收到红外信号经过一个外部解调电路通常是一个三极管或专用解调IC。这个电路的作用是滤除38kHz载波将ASK调制信号还原成数字电平即编码信号。同时从PIN二极管出来的原始调制信号也会直接接入另一路。Timer3通道1配置为输入捕获模式其输入引脚连接的是未经解调的原始红外信号。它的任务是测量输入脉冲的周期从而计算出载波的频率。这对于学习不同设备的红外码因为载波频率可能有36kHz, 38kHz, 40kHz等至关重要。Timer1通道2配置为输入捕获模式其输入引脚连接的是解调后的数字编码信号。它的任务是精确记录编码信号每一个上升沿和下降沿到来的时刻即捕获当时的计数器值从而计算出脉冲宽度Mark的时长和间隔宽度Space的时长。注意手册示意图中学习模式用的是Timer1 Ch2但实际使用中Timer1的任何通道0-4理论上都可以配置为捕获模式。选择哪个通道取决于你的具体引脚分配和电路设计。通道2可能因其引脚位置或与DMA触发器的关联而被推荐。2.3 核心控制位IRGEN模式切换的钥匙是一个叫做IRCTL.IRGEN的寄存器位。这个位在Timer1的寄存器区域内。当IRGEN 0Timer1和Timer3独立工作它们的输出走正常的IO引脚功能。当IRGEN 1硬件内部进行重路由。Timer1通道1的输出不再直接通向其IO引脚而是作为一路输入送往前文提到的那个与门与Timer3通道1的输出进行逻辑与操作最终结果从IR Out引脚输出。同时Timer1的时钟Tick输入可能会与Timer3联动以实现同步。这个设计非常巧妙通过一个比特位的切换就完全改变了两个外设的功能组合和物理连接实现了“一发一收”两种截然不同的硬件配置。3. 寄存器配置与实操步骤详解理论懂了接下来就是动手配置。我会以最常见的场景为例生成38kHz载波调制的NEC协议红外码以及学习同样的红外码。3.1 红外信号生成配置以NEC协议为例假设系统时钟为32MHz我们目标是生成38kHz载波载波占空比33%并调制一个NEC协议的引导码9ms Mark, 4.5ms Space。第一步配置Timer3产生38kHz载波计算参数38kHz的周期 T 1 / 38000 ≈ 26.3us。系统Tick频率取决于CLKCONCMD.TICKSPD假设我们设为16MHz分频后。Timer3的时钟源为此Tick频率再经过预分频。为方便计算我们选择预分频DIV1即不分频Timer3时钟为16MHz。Timer3工作在模数模式Modulo周期由T3CC0决定。T3CC0 时钟频率 / 目标频率 16,000,000 / 38,000 ≈ 421。我们取421。要产生33%占空比高电平时间 周期 * 0.33 ≈ 139。所以设置T3CC1 139。手册例子中T3CC0 3 × T3CC1即对应33%占空比因为T3CC1是翻转点从0到T3CC1输出一种状态T3CC1到T3CC0输出另一种状态。寄存器配置// 配置P1.1为外设功能Timer3 Ch1输出 P1SEL | 0x02; // 假设Timer3 Ch1复用在该引脚 P1DIR | 0x02; // 设置为输出 // 配置Timer3为模数模式不分频 T3CTL 0x02; // MODE[1:0]10 (模数模式), DIV[2:0]000 (/1) // 设置周期 T3CC0 421; // 设置比较值用于输出翻转实现33%占空比 T3CC1 139; // 配置通道1为输出比较模式设置“比较时翻转输出”模式 T3CCTL1 0x1C; // MODE1 (比较模式), CMP[2:0]010 (翻转输出), IM1 (使能中断可选) // 启动Timer3 T3CTL | 0x10; // START1此时Timer3通道1输出引脚上应该能看到一个38kHz、占空比约33%的方波。第二步配置Timer1生成NEC编码波形NEC协议的一位数据由560us的Mark载波脉冲和560us或1690us的Space静默组成。我们需要Timer1产生一个控制信号在需要发送Mark时为高发送Space时为低。计算参数Timer1时钟同样假设为16MHz不分频。560us需要的计数器增量 0.00056 * 16,000,000 8960。这超出了8位范围但Timer1是16位定时器最大值65535完全够用。寄存器配置// 首先确保IRGEN位为0直到我们完成配置避免意外输出 IRCTL ~0x01; // 配置Timer1为模数模式用于控制编码周期 T1CTL 0x02; // MODE[1:0]10 (模数模式), DIV[1:0]00 (/1) // 假设我们使用DMA或CPU来动态更新比较值以生成波形。 // 这里以生成一个固定的9ms Mark, 4.5ms Space引导码为例 // 9ms Mark: 计数器值 0.009 * 16,000,000 144,000 65535说明需要分频。 // 将Timer1预分频设为/8则时钟为2MHz。 T1CTL 0x0A; // MODE10, DIV01 (/8) - 2MHz // 9ms Mark 需要计数值 0.009 * 2,000,000 18,000 // 4.5ms Space 需要计数值 0.0045 * 2,000,000 9,000 // 但Timer1在模数模式下一个完整的周期MarkSpace由T1CC0定义。 // 对于引导码我们可以将整个13.5ms作为一个周期用T1CC1控制Mark的结束点。 // 总周期 T1CC0 18,000 9,000 27,000 // Mark结束点 T1CC1 18,000 T1CC0H (27000 8); T1CC0L (27000 0xFF); T1CC1H (18000 8); T1CC1L (18000 0xFF); // 配置Timer1通道1为输出比较模式并设置为“比较时置位计数到0时清零”模式。 // 这能确保在每个周期开始时输出高Mark当计数器等于T1CC1时比较事件发生但我们的动作是“清除输出”这里需要仔细选择模式。 // 查看手册对于模数模式模式011是“Set output on compare-up, clear on 0”。这不符合要求。 // 模式101是“Clear when equal T1CC0, set when equal T1CC1”。这看起来符合当计数器等于T1CC1时置位开始Mark等于T1CC0时清零结束Mark开始Space。但注意计数器是从0到T1CC0。所以 // - 计数器从0开始不等于T1CC1输出取决于初始化通常为0。 // - 当计数器增加到等于T1CC1时输出置位变高Mark开始。 // - 当计数器增加到等于T1CC0时输出清零变低Mark结束Space开始。 // - 计数器归零进入下一个周期。 // 这正是我们需要的 T1CCTL1 0x2C; // MODE1, CMP[2:0]101 (Clear when equal T1CC0, set when equal T1CC1), IM1 (可选)第三步联动输出与发送启用红外生成模式将Timer1和Timer3的输出通过硬件与门合并。IRCTL | 0x01; // 设置IRGEN位为1一旦设置此位Timer1通道1的物理输出将被重定向与Timer3通道1的输出进行与操作后从IR Out引脚输出。启动Timer1T1CTL | 0x01; // 确保Timer1处于运行模式MODE已设START位实质在T1CTL[4]注意Timer1的START控制可能在其他位置或通过模式选择隐含启动。对于Timer1设置模数模式并写入T1CNTL通常会启动。更稳妥是使用T1CTL.CLR位启动。 // 通常写入T1CNTL会复位并启动计数器。或者直接设置T1CTL.MODE为非00即启动。 // 根据手册写入T1CNTL会清零计数器。所以我们可以 T1CNTL 0x00; // 写入任何值都会清零并启动计数器发送完整数据帧上面的配置只发送了一个固定的引导码。要发送完整的NEC码地址、命令、重复码需要在每个位周期1.125ms或2.25ms动态更新T1CC0和T1CC1的值。这通常通过DMA直接存储器访问或Timer1中断来实现。你可以预先计算好整个帧所有高低电平切换点对应的计数器值存入一个数组然后让DMA自动搬运到T1CC1寄存器或者在中斷服务程序里手动更新。实操心得在调试红外发射时最头疼的就是波形不对。强烈建议使用逻辑分析仪或示波器抓取IR Out引脚的实际波形。首先单独测试Timer3的输出确保38kHz方波正确。然后单独测试Timer1的输出在IRGEN0时确保编码时序正确。最后再开启IRGEN观察调制后的波形。这样分层调试问题定位会快很多。3.2 红外信号学习配置学习模式的目标是测量未知红外信号的载波频率和脉冲宽度。第一步硬件连接与引脚配置将红外接收头或PIN二极管加解调电路的输出解调后的数字信号连接到Timer1的某个捕获通道引脚例如P0.3假设对应Timer1 Ch2。将红外接收头的原始输出未解调的载波信号连接到Timer3的捕获通道引脚例如P0.4假设对应Timer3 Ch1。配置这些引脚为外设输入功能并禁用上拉电阻以避免干扰。// 配置Timer1 Ch2 (P0.3) 为外设输入用于捕获解调后的编码 P0SEL | 0x08; // P0.3 用作外设 P0INP ~0x08; // 根据需要设置输入模式通常为三态 // 配置Timer3 Ch1 (P0.4) 为外设输入用于捕获载波 P0SEL | 0x10; // P0.4 用作外设 P0INP ~0x10;第二步配置Timer3进行载波频率检测目标测量输入脉冲的周期。配置Timer3为自由运行模式这样计数器会从0x00到0xFF循环提供一个时间基准。T3CTL 0x00; // MODE00 (自由运行), DIV根据需要的分辨率设置例如/1配置Timer3通道1为输入捕获模式捕获上升沿或下降沿或双边沿。捕获到边沿时当前的计数器值T3CNT会被锁存到T3CC1寄存器并产生中断。T3CCTL1 0x04; // MODE0 (捕获模式), CAP[1:0]01 (上升沿捕获), IM1 (使能中断)在中断服务程序中读取两次连续上升沿捕获到的T3CC1值。两者的差值考虑计数器溢出就是载波一个周期对应的计数值。根据Timer3的时钟频率即可计算出载波频率。#pragma vector T3_VECTOR __interrupt void T3_ISR(void) { if(T3CCTL1 0x01) { // 检查CH1IF标志简化判断实际需查TIMIF寄存器 static uint16_t last_capture 0; uint16_t current_capture T3CC1; uint16_t period_ticks current_capture - last_capture; // 注意处理溢出 last_capture current_capture; // 计算频率: Freq Timer3_Clock / period_ticks // 将频率值存入变量用于后续平均或判断 TIMIF ~0x04; // 清除T3CH1IF中断标志具体位需查手册 } }第三步配置Timer1进行脉冲宽度学习目标测量解调后信号即编码的Mark和Space宽度。配置Timer1为自由运行模式提供高精度的时间基准。由于Timer1是16位其溢出周期更长适合测量较长的脉冲如NEC的9ms引导码。T1CTL 0x01; // MODE01 (自由运行), DIV根据需求设置配置Timer1通道2为输入捕获模式捕获双边沿。这样无论是Mark开始上升沿还是Space开始下降沿都能被记录。T1CCTL2 0x0C; // MODE0, CAP[1:0]11 (双边沿捕获), IM1在中断服务程序中记录每次捕获的计数器值T1CC2。连续两次捕获值之间的差值就是上一个电平Mark或Space的持续时间。通过判断是上升沿还是下降沿可以读取IO口状态或通过标志位区分但手册中捕获模式本身不直接提供边沿方向通常需要软件记录前一个状态可以重建出完整的红外编码序列。#pragma vector T1_VECTOR __interrupt void T1_ISR(void) { if(T1STAT 0x04) { // 检查CH2IF标志 static uint16_t last_capture 0; static uint8_t last_level 0; // 假设0为低Space1为高Mark uint16_t current_capture T1CC2L | (T1CC2H 8); uint16_t pulse_width_ticks current_capture - last_capture; // 处理溢出 last_capture current_capture; // 读取当前输入引脚电平判断是Mark开始还是Space开始 uint8_t current_level (P0 0x08) ? 1 : 0; // 读取P0.3 if(current_level ! last_level) { if(current_level 1) { // 当前是上升沿说明刚结束的是一个Space // 记录Space宽度: pulse_width_ticks } else { // 当前是下降沿说明刚结束的是一个Mark // 记录Mark宽度: pulse_width_ticks } last_level current_level; } T1STAT ~0x04; // 清除CH2IF标志 } }注意事项在学习模式下两个定时器都在高速运行和捕获中断可能非常频繁尤其是38kHz载波。这会给CPU带来较大负担。因此在实际产品中学习功能通常是在一个专门的“学习模式”下启用此时系统可以暂时专注于处理红外信号。对于载波频率检测也可以采用多次采样求平均的方法来提高准确性。4. 关键参数计算与模式选择背后的逻辑为什么这么配置参数怎么来的这里藏着很多容易踩坑的细节。4.1 时钟树与分频选择CC253x/CC254x的定时器时钟源是系统时钟分频后的“Tick”频率。CLKCONCMD.TICKSPD寄存器控制着这个基础频率。Timer1和Timer3的DIV分频器在此基础上进行二次分频。选择分频比的核心是在分辨率、溢出周期和中断频率之间取得平衡。高分辨率需求如果你需要测量非常窄的脉冲例如某些高速红外协议或者生成非常精确的载波频率那么应该选择较小的分频值甚至不分频让定时器跑在更高的频率下这样每个计数单位代表的时间更短精度更高。长周期需求如果你需要测量或生成很长的脉冲如NEC的几十毫秒而定时器的位数有限Timer3只有8位最大255Timer1为16位最大65535那么就需要通过增大分频值来降低定时器时钟频率从而让计数器在溢出前能覆盖更长的时间。例如用16位Timer1测量9ms脉冲若系统Tick为16MHz不分频时计数值为144,000已超过65535必须分频/8或更高才能测量。中断频率在输入捕获模式下每次捕获都会产生中断。如果信号边沿很密集如载波信号高中断频率会拖垮CPU。此时可以考虑使用DMA将捕获值自动搬运到内存或者适当提高分频比以降低中断频率但会牺牲时间分辨率。计算公式总结定时器时钟频率 CLKCONCMD.TICKSPD/TxCTL.DIV定时器计数周期 1 / 定时器时钟频率实际时间 计数值 * 定时器计数周期生成波形的频率 定时器时钟频率 /TxCC0(模数模式下)4.2 输出比较模式的选择在生成红外编码时Timer1通道1的输出比较模式选择至关重要。手册中提供了多种模式Set on compare, Clear on compare, Toggle, 以及几种在上下计数模式下的特殊行为。对于简单的脉冲生成模式101Clear when equal T1CC0, set when equal T1CC1和模式110Set when equal T1CC0, clear when equal T1CC1非常有用。模式101计数器从0开始在等于T1CC1时输出置高在等于T1CC0时输出清零。这完美适配了“在一个周期内先高电平一段时间后低电平一段时间”的需求只需设置T1CC1为高电平结束点T1CC0为周期结束点。模式110逻辑相反在等于T1CC0时置高等于T1CC1时清零。可以根据编码逻辑灵活选择。为什么不用简单的Toggle模式因为Toggle模式每次比较事件就翻转一次输出要生成一个固定占空比的脉冲需要精确控制两次比较事件一次开、一次关并且需要动态更新比较值控制逻辑更复杂。而模式101/110只需要在周期开始时设置好T1CC0和T1CC1硬件就能自动生成一个完整的脉冲更适合用DMA进行流式数据发送。4.3 模数模式 vs 上下计数模式模数模式计数器从0线性增加到TxCC0然后归零重启。这是最常用的模式用于生成固定周期的PWM或测量周期信号。在红外生成中Timer3常用此模式生成固定频率载波。上下计数模式计数器从0增加到TxCC0再减少到0如此往复。此模式可以生成中心对齐的PWM在某些电机控制中能减少谐波。在红外应用中此模式的一个妙用是配合输出比较模式011或100可以简化某些对称波形的生成但在标准的ASK红外调制中不常用。5. 常见问题排查与实战经验调红外功能十有八九会碰到波形不对、收不到、学不准的问题。下面是我踩过的一些坑和解决办法。5.1 问题一红外发射无输出或波形异常检查IRGEN位这是最容易被忽略的确认IRCTL.IRGEN是否已设置为1。如果为0Timer1和Timer3的输出是独立的不会进行与操作也就没有调制效果。检查引脚复用确认你使用的IO引脚通常是P1.1作为IR Out是否已经正确配置为外设功能PxSEL寄存器而不是普通的GPIO。检查Timer同步确保Timer1和Timer3在启动前都已正确配置并且先启动载波Timer3再启动编码Timer1最后再打开IRGEN。顺序混乱可能导致初始同步问题。测量各点波形用示波器分别测量Timer3通道1输出引脚在IRGEN0时应有38kHz方波。Timer1通道1输出引脚在IRGEN0时应有你期望的编码波形如9ms高4.5ms低。IR Out引脚在IRGEN1时应看到被38kHz调制了的编码波形。在Mark期间是密集的38kHz脉冲群在Space期间是平坦的低电平。驱动能力不足红外LED需要一定的驱动电流通常20-50mA。芯片IO口的拉电流可能不够需要增加一个三极管如NPN型2N3904或MOSFET来驱动LED。记得串联一个限流电阻如10-100欧姆。5.2 问题二红外学习不到信号或数据错误解调电路问题大部分商用红外接收头如VS1838B内部已经集成了解调电路输出就是干净的数字信号。如果你用的是单独的PIN二极管那么外部的解调电路通常是一个三极管放大器加带通滤波至关重要电路没调好信号质量就差导致捕获不稳定。输入引脚配置确保用于捕获的IO口配置为输入模式并且禁用内部上拉电阻PxINP寄存器因为外部电路可能有自己的偏置内部上拉会产生冲突。捕获边沿选择对于解调后的编码信号使用双边沿捕获是最可靠的因为它能记录下每个电平变化的精确时刻。对于载波信号通常使用上升沿捕获来测量周期。中断服务程序效率学习模式下的中断非常频繁。确保你的中断服务程序ISR尽可能短小精悍只做最基本的读取捕获值和保存到缓冲区的工作。复杂的计算如换算成时间单位应该放到主循环中。避免在ISR内调用耗时函数或进行浮点运算。计数器溢出处理在自由运行模式下计数器会循环溢出。计算两个捕获值之间的时间差时必须考虑溢出。使用16位或32位变量来记录扩展的计数值。static uint32_t extended_timer1_count 0; static uint16_t last_t1cnt 0; // 在定时器溢出中断或主循环中定期处理 if(T1STAT 0x80) { // 溢出标志 extended_timer1_count 65536UL; // Timer1是16位溢出加65536 T1STAT ~0x80; } // 在捕获中断中 uint16_t current_capture T1CC2; uint32_t full_capture extended_timer1_count current_capture; // ... 计算与上一次full_capture的差值抗干扰与软件滤波红外信号容易受到环境光干扰。可以在软件中加入简单的滤波算法比如连续捕获到N个周期接近的载波频率才认为是一个有效的信号头或者对脉冲宽度进行范围判断剔除明显过短或过长的噪声脉冲。5.3 问题三功耗考虑红外LED是耗电大户。在非发射状态下务必确保IR Out引脚处于高阻态或输出低电平。手册特别指出“The IR output pin should be placed in the high-impedance state or pulled down during reset to avoid unnecessary power consumption”。在你的代码初始化阶段和进入低功耗模式前检查并设置好相关引脚的状态。6. 进阶应用与DMA技巧对于连续发送一长串复杂的红外码比如空调的复杂指令或者高速学习频繁的CPU中断更新比较值或读取捕获值会成为瓶颈。这时DMA直接存储器访问就是你的神器。6.1 使用DMA辅助红外发射Timer1有三个DMA触发源T1_CH0,T1_CH1,T1_CH2分别在通道0、1、2的比较事件时触发。我们可以利用这个机制实现“无人值守”的红外波形发送。准备数据缓冲区在内存中创建一个数组里面按顺序存放你要发送的每一个脉冲/间隔结束点对应的T1CC1比较值如果你使用模式101则T1CC0固定为周期值T1CC1动态变化。配置DMA通道将DMA通道的源地址指向这个数组目标地址指向T1CC1H和T1CC1L寄存器注意是16位传输。设置传输模式为“单次触发每次传输一个字16位”。配置DMA触发源选择T1_CH1作为触发源。这意味着每次Timer1通道1发生比较匹配即完成当前脉冲/间隔就会自动触发DMA将下一个比较值从内存搬运到T1CC1寄存器。启动流程初始化Timer1和DMA启动Timer1。当第一个比较事件发生时DMA自动装载第二个比较值如此循环直到DMA传输完数组中所有数据可以产生一个中断通知CPU发送完成。这样CPU只需要在开始时启动发送结束时处理一下完成中断中间所有的时序控制全部由Timer1硬件和DMA自动完成CPU可以休眠或处理其他任务极大地提高了系统效率。6.2 使用DMA辅助红外学习同样在输入捕获模式下DMA可以在每次捕获事件发生时自动将T1CCn捕获到的计数值搬运到内存中的一个大型缓冲区。这避免了频繁的捕获中断尤其适用于学习高速或长时间的红外信号。配置DMA通道以T1_CH2如果使用通道2捕获为触发源目标地址为内存数组源地址为T1CC2L注意可能是8位访问需要根据数据手册确认。这样每个边沿到来时计数值就被自动保存CPU可以定期或在缓冲区满后再对这批数据进行处理解码。7. 不同红外协议适配要点不同的红外协议NEC, RC-5, RC-6, Sony SIRC等主要区别在载波频率、编码方式脉宽调制PWM或脉冲位置调制PPM和数据帧结构。我们的硬件框架是通用的适配不同协议主要在于软件对定时器参数和数据处理逻辑的调整。NEC协议38kHz载波PWM编码。引导码9ms Mark 4.5ms Space后跟32位数据地址、命令及反码。每位数据560us Mark 560us/1690us Space。适配要点Timer3载波设为38kHz。Timer1用于生成引导码和位数据需要动态更新T1CC0/T1CC1来产生不同宽度的Space。RC-5协议36kHz载波双相编码曼彻斯特编码。每位中间都有跳变。适配要点载波频率改为36kHz。编码生成更复杂因为每个位周期内需要一次电平翻转可能需要Timer1工作在Toggle模式或者使用两个比较通道配合。学习兼容在学习时Timer3的载波频率检测结果可以用来判断是哪种协议38k vs 36k。Timer1捕获到的脉冲宽度模式则用于分析是PWM还是PPM编码进而调用相应的解码算法。最后分享一个我个人的调试习惯在项目初期我会用一个已知能正常工作的红外遥控器比如电视机的先确保我的学习电路和代码能正确解码它的信号。然后再用我自己的发射代码去模拟这个遥控器的信号看能否控制设备。用已知正确的信号作为“金标准”能帮你快速定位问题是出在发射端还是接收端或者是协议理解有误。红外通信虽然是个老技术但把这些硬件定时器的细节吃透在资源受限的嵌入式设备上实现稳定可靠的收发依然是一件很有成就感的事情。