1. 项目概述
红外遥控,这个我们每天都会用到的技术,从电视遥控器到空调面板,背后其实是一套相当精巧的通信系统。在嵌入式开发领域,我们通常不会为红外功能单独设计一个硬件模块,而是复用已有的通用接口,比如UART(通用异步收发传输器)。德州仪器的AM261x系列处理器就提供了一个非常典型的案例:它的UART模块可以配置为CIR(Consumer Infrared,消费电子红外)模式,从而让一个标准的串口摇身一变,成为红外遥控的收发引擎。这不仅仅是简单的模式切换,更涉及到一套完整的、基于可变脉宽调制(PWM)的编码解码逻辑,以及如何通过软件灵活定义协议帧,去适配RC-5、SIRC等不同厂商的标准。今天,我就结合手册和实际调试经验,把UART的CIR模式从硬件接口到软件实现,掰开揉碎了讲清楚,希望能帮你绕过我当年踩过的那些坑。
2. CIR模式硬件接口与信号定义
2.1 引脚功能重映射
当UART模块切换到CIR模式时,其物理引脚的功能会发生根本性的变化,不再是传统的串行数据通信,而是服务于红外载波调制。这是理解后续所有软件配置的基础。
在AM261x中,每个UART模块(UART0到UART5)都有三个核心引脚在CIR模式下被重新定义:
- TX (UARTx_TXD): 从“发送数据”变为红外调制信号输出。在CIR模式下,这个引脚输出的是经过PWM调制的红外载波信号,直接驱动外部的红外发射管(IR LED)。手册里特别提到,这个引脚在CIR模式下的复位值是
0(低电平),确保红外发射管在初始化时不误触发。 - RX (UARTx_RXD): 从“接收数据”变为红外解调信号输入。这个引脚连接外部红外接收头(如HS0038B等)的输出端。接收头将接收到的红外光信号解调为数字电平信号后,通过此引脚送入UART模块进行解码。其复位状态为高阻态(HiZ)。
- SD (UARTx_RTSn): 这个引脚的功能最为关键,它从“请求发送”变为收发器配置/载波关断控制。它用于控制外部红外收发器(Transceiver)的开关状态。其输出电平是UART_ACREG[6]寄存器的SD_MOD位的反相值。通常,我们会配置SD_MOD=1,使得SD引脚默认输出低电平,从而使能外部红外发射电路;当需要关闭发射时,则设置SD_MOD=0,SD引脚输出高电平,关断发射。这一点在硬件设计时必须注意,防止上电瞬间或模式切换时产生误发射。
注意: 硬件设计时,务必查阅具体型号AM261x芯片的数据手册(Data Sheet)中的“Pin Attributes”和“Pin Multiplexing”表格。你需要确认你使用的UART模块的这三个引脚(TX、RX、SD)是否已经被正确复用到芯片的物理引脚上,并且没有与其他功能冲突。同时,要根据SD引脚的电平特性(推挽、开漏等)来设计外部红外发射管的驱动电路。
2.2 外部电路连接要点
仅仅理解引脚定义还不够,如何连接外部器件决定了系统的稳定性和可靠性。
发射端电路:
- TX引脚: 需要串联一个限流电阻(通常100-470欧姆)连接到NPN三极管或MOSFET的基极/栅极,由三极管/MOSFET来驱动红外发射管(IR LED)。绝对不能直接用TX引脚驱动LED,其驱动能力不足以提供红外发射所需的大电流(通常几十到上百毫安)。
- SD引脚: 连接到驱动三极管/MOSFET的使能端。当SD为低电平时,使能驱动电路;高电平时,彻底关闭驱动电路,实现硬件层面的发射关断,这对于降低功耗和避免干扰至关重要。
接收端电路:
- RX引脚: 直接连接一体化红外接收头(如VS1838B、TSOP38238)的数据输出引脚。接收头本身需要稳定的电源(通常是3.3V或5V)和良好的滤波电容。接收头的输出信号已经是解调好的数字电平(有信号时为低电平,空闲时为高电平),可以直接被RX引脚识别。
电平匹配: 确保红外接收头的输出电平与AM261x的I/O电压(例如1.8V或3.3V)兼容。如果不兼容,需要添加电平转换电路。
3. CIR协议编码原理与数据格式
CIR模式的精髓在于其编码的灵活性。它不像一些专用的红外编码芯片那样固化某一种协议,而是提供了一个基于时间单元t的底层PWM引擎,由软件来定义“1”和“0”的具体表现形式,从而模拟出各种标准协议。
3.1 载波调制与脉冲占空比
红外遥控为了抗干扰和增加传输距离,通常会将数字信号调制在一个高频载波上(常见为38kHz)。CIR模式在硬件层面实现了这个调制过程。
- 调制原理: 如图13-60所示,每一个有效的调制脉冲(代表一个数据位“1”),实际上是由一系列高频的“开/关”脉冲组成的。这个高频脉冲就是载波。
t代表了这个调制脉冲的总持续时间。在t时间内,载波以一定占空比进行开关,形成最终的调制波形。 - 占空比选择: CIR模式提供了四种可编程的载波脉冲占空比,通过配置
UART_MDR2[5-4](CIR_PULSE_MODE)位域来选择:00: 1/4 占空比01: 1/3 占空比10: 5/12 占空比11: 1/2 占空比 占空比的选择会影响发射功率和接收灵敏度。1/3或1/2占空比是最常用的,它们在发射效率和接收头解调效果之间取得了较好的平衡。较低的占空比(如1/4)可以降低功耗,但可能影响距离;较高的占空比(如1/2)功率大,但发热和功耗也相应增加。一个重要的硬件保证是,模块会确保每个调制脉冲都被完整发送,不会在传输中途被截断。
3.2 两种主流编码方式
在定义了“1”(一个t时长的调制脉冲)之后,“0”如何表示,就衍生出了两种主要的编码方式,这也是不同红外协议的核心区别。
脉冲宽度编码(Pulse Distance/Duration Encoding):
- 原理: “1”和“0”都用调制脉冲表示,但脉冲的宽度不同。例如,在NEC协议中,“1”是一个
t时长的脉冲后跟一个t时长的空闲(总时长2t),而“0”是一个t时长的脉冲后跟一个3t时长的空闲(总时长4t)。逻辑值由脉冲之间的“空间”长度来区分。 - 特点: 日本厂商(如索尼、NEC)偏爱这种方式。解码时,需要精确测量两个脉冲下降沿之间的时间间隔。
- 原理: “1”和“0”都用调制脉冲表示,但脉冲的宽度不同。例如,在NEC协议中,“1”是一个
双相编码(Biphase Encoding / Manchester Encoding):
- 原理: 每个位时间被平均分成两半。“1”用“前半个周期高电平(或调制脉冲),后半个周期低电平(空闲)”表示;“0”则用“前半个周期低电平(空闲),后半个周期高电平(调制脉冲)”表示。逻辑值由电平跳变的方向或位置来定义。RC-5协议就是典型的曼彻斯特编码。
- 特点: 欧洲厂商(如飞利浦)常用。这种编码自带时钟信息,抗干扰能力较强。
CIR模式的灵活性就体现在这里:它不关心你用的是哪种编码。它只认你写入FIFO的数据位。你写入一个“1”,它就输出一个持续时间为t的调制脉冲;你写入一个“0”,它就输出一段持续时间为T(T是t的整数倍,由软件控制发送多个“0”来实现)的空闲期。整个协议的帧结构(起始位、地址、命令、校验等)以及“0”和“1”的具体时间定义,完全由主机CPU的软件来构造和解析。
3.3 典型协议在CIR模式下的软件实现
手册中以RC-5和SIRC为例,说明了如何用CIR模式模拟它们。
模拟RC-5协议: RC-5采用曼彻斯特编码。它规定:
- 逻辑“1”: 在位时间中点发生从高到低的跳变。
- 逻辑“0”: 在位时间中点发生从低到高的跳变。 每个位时间固定为1.778ms。 在CIR模式下,我们可以用“01”这个2-bit序列来表示RC-5的“1”,用“10”来表示RC-5的“0”。因为“01”会产生一个
t脉冲(代表前半段)后跟一个t空闲(代表后半段),正好形成一个下降沿跳变。同理,“10”形成一个上升沿跳变。因此,要发送RC-5的位序列“0101”,主机软件需要向UART的TX FIFO写入8位数据10011001(即“10”,“01”,“10”,“01”的拼接)。这里t需要设置为RC-5位时间的一半,即0.889ms。通过调整UART的波特率除数(或使用专门的CIR时钟分频器,如果支持),可以精确产生这个t。
模拟SIRC协议(索尼): SIRC使用脉冲宽度编码。它规定:
- 逻辑“1”: 一个1.2ms的调制脉冲,后跟一个0.6ms的空闲(总时长1.8ms)。
- 逻辑“0”: 一个0.6ms的调制脉冲,后跟一个0.6ms的空闲(总时长1.2ms)。 在CIR模式下,这更直观:
- 发送“1”: 向FIFO写入一个“1”(产生一个
t时长的脉冲),然后软件延时或发送多个“0”来凑足剩余的0.6ms空闲。这里t设为1.2ms。 - 发送“0”: 向FIFO写入一个“1”(产生一个
t时长的脉冲),但这里的t需要设为0.6ms。这意味着在发送一帧SIRC数据时,t的时长可能需要动态变化。这通常通过改变UART的波特率(即改变t的基础时间单元)来实现,操作上更复杂一些。更常见的做法是固定一个较小的t(如0.1ms),然后用多个“1”和“0”的组合来拼出不同宽度的脉冲。例如,1.2ms的脉冲可以用12个连续的“1”来表示(如果t=0.1ms)。
实操心得: 在软件实现时,强烈建议将协议编码层与底层CIR驱动层分离。底层驱动只提供两个基本函数:
cir_send_pulse(uint32_t duration_us)和cir_send_space(uint32_t duration_us)。上层协议层(如RC-5、NEC)则根据协议规范,调用这两个函数组合出具体的帧。这样做代码结构清晰,易于调试和扩展支持新协议。对于需要动态改变t的协议(如SIRC),可以在cir_send_pulse函数内部临时修改UART的波特率分频器。
4. AM261x UART CIR模式配置详解
理解了原理,我们来看在AM261x上如何具体配置。这涉及到模式选择、时钟设置、中断管理等多个方面。
4.1 模式选择与基本配置流程
- 模块使能与复位: 首先,需要确保UART模块的时钟和电源域已使能(通过PRCM模块配置)。然后,可以通过设置
UART_SYSC[1] SOFTRESET位来对UART进行软件复位,使其进入一个确定的状态。 - 切换到CIR模式: 这是最关键的一步。通过配置
UART_MDR1[2:0] MODE_SELECT位域,将其设置为010b,即可将UART切换到CIR模式。在切换模式前,最好先关闭UART的发送和接收(通过UART_ECR寄存器)。 - 配置CIR特定参数:
- 设置载波占空比: 根据硬件设计和协议要求,配置
UART_MDR2[5:4] CIR_PULSE_MODE。 - 设置基本时间单元
t:t的时长由CIR功能的时钟频率决定。需要根据目标协议(如RC-5的0.889ms)来计算分频值。AM261x的UART在CIR模式下,其比特率(或称为t的周期)由专门的时钟分频寄存器控制(可能是UART_CIR_BAUD或类似寄存器,具体需查寄存器映射)。计算公式通常为:t = (CIR_CLK_DIVIDER + 1) / CIR_CLK_SRC_FREQ。你需要根据芯片时钟树,找到CIR模式下的时钟源(例如PER_PLL的某个分频),并计算合适的分频值。
- 设置载波占空比: 根据硬件设计和协议要求,配置
- 配置引脚复用: 通过芯片的Pad Configuration寄存器,将所用UART模块的TX、RX、SD引脚功能复用到CIR模式。
- 使能FIFO与中断: 根据数据传输方式(查询或中断),配置
UART_FCR寄存器使能TX/RX FIFO,并设置触发阈值。在UART_IER_CIR寄存器中使能所需的中断,例如THR中断(发送FIFO空)和RHR中断(接收FIFO数据就绪)。
4.2 时钟与功耗管理
AM261x为UART提供了多个时钟源,如图13-67和表13-70所示。在CIR模式下,主要关注两个时钟:
- VBUSP_CLK (UARTx_ICLK): 用于模块内部逻辑和寄存器访问,源可以是系统时钟等,频率较高(200/250MHz)。
- UART_CLK (UARTx_FCLK): 用于生成CIR的比特率时钟(即决定
t的时钟)。它可以从多个源选择,如外部晶振(XTALCLK,25MHz)、内部RC振荡器(10MHz)或PLL分频时钟。选择一個频率稳定且精度高的时钟源至关重要,因为t的精度直接决定了红外编码的准确性。通常选择外部晶振或高精度PLL时钟。
功耗管理: CIR模式支持模块级休眠。当满足以下条件时,可以进入休眠模式以降低功耗:
- RX输入线空闲。
- TX FIFO和TX移位寄存器为空。
- RX FIFO为空。
- 除了THR中断外,没有其他中断挂起。 通过设置
UART_MDR1[3] IR_SLEEP位为1来使能休眠。当RX线有变化或向TX FIFO写入数据时,模块会自动唤醒。这对于电池供电的遥控器设备非常有用。
4.3 中断与DMA配置
高效的数据处理离不开合理的中断或DMA使用。
中断管理: CIR模式使用自己的一套中断向量表(
UART_IER_CIR/UART_IIR_CIR)。最常用的中断是:- RHR中断 (IIR_CIR[0]): 当RX FIFO中的数据量达到预设的触发水平时产生,提示CPU读取数据。
- THR中断 (IIR_CIR[1]): 当TX FIFO中的数据量低于预设的触发水平时产生,提示CPU可以写入更多待发送数据。
- TX_STATUS中断 (IIR_CIR[5]):这个中断在CIR模式下特别重要。它表示一帧红外数据的最后一个比特已经送到了TX引脚。这意味着整个数据包(可能由多个FIFO写入操作组成)的硬件调制发送已经完成。你可以在这个中断服务程序里,进行发送完成后的处理,比如关闭红外发射管(通过SD引脚),或者准备下一帧数据。
DMA使用: 对于连续、大数据量的红外信号录制与回放(比如学习型遥控器),使用DMA可以极大减轻CPU负担。UART模块可以产生TX和RX DMA请求(
UARTx_DMA_REQ[1:0]),连接到EDMA(增强型直接内存访问)控制器。你可以配置DMA,当TX FIFO低于阈值时自动从内存搬运数据到FIFO,或者当RX FIFO高于阈值时自动从FIFO搬运数据到内存。结合TX_STATUS中断,可以高效地管理多帧数据传输。
5. 软件驱动设计与实战代码框架
理论说再多,不如看代码骨架。下面我给出一个基于AM261x SDK(假设使用TI的MCU+ SDK或类似框架)的CIR驱动层和RC-5协议层的简化实现框架。
// cir_driver.h typedef struct { uint32_t baseAddr; // UART模块基地址 uint32_t pulseWidthUs; // 基础脉冲时间t (微秒) uint32_t carrierDuty; // 载波占空比配置值 } CIR_Config; void CIR_init(const CIR_Config *config); void CIR_sendPulse(uint32_t durationUs); void CIR_sendSpace(uint32_t durationUs); void CIR_enableTx(void); void CIR_disableTx(void); bool CIR_isTxBusy(void); // cir_driver.c static uint32_t s_tUnitNs; // 计算出的一个t单元对应的纳秒数 void CIR_init(const CIR_Config *config) { uint32_t base = config->baseAddr; // 1. 禁用UART收发 HW_WR_REG32(base + UART_ECR_OFFSET, 0x0); // 2. 软件复位 HW_WR_REG32(base + UART_SYSC_OFFSET, 0x2); while((HW_RD_REG32(base + UART_SYSC_OFFSET) & 0x1) == 0); // 等待复位完成 // 3. 切换到CIR模式 HW_WR_FIELD32(base + UART_MDR1_OFFSET, UART_MDR1_MODE_SELECT, 0x2); // 4. 配置CIR脉冲模式(占空比) HW_WR_FIELD32(base + UART_MDR2_OFFSET, UART_MDR2_CIR_PULSE_MODE, config->carrierDuty); // 5. 配置CIR波特率(决定t) // 假设时钟源为25MHz,需要产生 config->pulseWidthUs 微秒的t // t = (CIR_BAUD_DIV + 1) / CLK_SRC_FREQ // CIR_BAUD_DIV = (CLK_SRC_FREQ * t) - 1 uint32_t clkFreqHz = 25000000; // 25MHz uint32_t tWidthNs = config->pulseWidthUs * 1000; uint32_t baudDiv = (clkFreqHz * tWidthNs) / 1000000000ULL - 1; HW_WR_REG32(base + UART_CIR_BAUD_OFFSET, baudDiv); s_tUnitNs = tWidthNs; // 保存供后续函数使用 // 6. 使能FIFO,设置触发阈值(例如TX空,RX达到8字节) HW_WR_REG32(base + UART_FCR_OFFSET, 0x07); // 使能FIFO,清除TX/RX FIFO HW_WR_FIELD32(base + UART_FCR_OFFSET, UART_FCR_TX_FIFO_TRIG, 0x0); // TX空触发 HW_WR_FIELD32(base + UART_FCR_OFFSET, UART_FCR_RX_FIFO_TRIG, 0x0); // RX 8字节触发 // 7. 使能CIR模式下的TX_STATUS和THR中断 uint32_t ierValue = (1 << 5) | (1 << 1); // 使能TX_STATUS和THR中断 HW_WR_REG32(base + UART_IER_CIR_OFFSET, ierValue); // 8. 最后,使能UART收发 HW_WR_REG32(base + UART_ECR_OFFSET, 0x3); } void CIR_sendPulse(uint32_t durationUs) { uint32_t numT = (durationUs * 1000 + s_tUnitNs/2) / s_tUnitNs; // 四舍五入计算需要多少个t // 向TX FIFO写入 (numT) 个 0xFF(每个字节8个‘1’位) // 注意处理numT大于255的情况,需要分多次写入 while (numT > 0) { uint8_t bytesToSend = (numT > 255) ? 255 : numT; // 等待TX FIFO有空间(可以通过查询或中断) while(!(HW_RD_REG32(base + UART_LSR_OFFSET) & 0x20)); // 查询THR空 for(int i=0; i<bytesToSend; i++) { HW_WR_REG32(base + UART_THR_OFFSET, 0xFF); } numT -= bytesToSend; } } void CIR_sendSpace(uint32_t durationUs) { // 发送空间意味着发送‘0’。在CIR模式下,发送一个‘0’意味着输出T时长的空闲。 // T是t的整数倍。我们需要通过发送多个‘0’位来实现。 // 但更高效的做法是:直接让CPU延时durationUs时间。 // 因为发送‘0’并不占用总线,只是等待。这里我们使用简单的延时循环。 // 注意:在发送长空间时,应关闭TX以避免噪声?这取决于硬件设计。 // 假设我们使用SD引脚控制发射管,这里可以先关闭发射。 CIR_disableTx(); // 使用高精度延时函数,例如基于CPU循环计数器的延时 delayMicroseconds(durationUs); CIR_enableTx(); } // rc5_protocol.c #define RC5_T_BIT_US 889 // RC-5 半位时间 0.889ms #define RC5_ONE_PAIR 0x01 // CIR模式用“01”表示RC-5的“1” #define RC5_ZERO_PAIR 0x02 // CIR模式用“10”表示RC-5的“0” void RC5_sendFrame(uint8_t address, uint8_t command, bool toggle) { uint16_t frame = 0; // 构建RC-5帧:2位起始位(1,1) + 1位Toggle + 5位地址 + 6位命令 frame = (0x3 << 12) | ((toggle & 0x1) << 11) | ((address & 0x1F) << 6) | (command & 0x3F); // 将14位帧转换为需要发送的CIR位序列(28个CIR位) uint8_t cirBuffer[4] = {0}; // 28位需要4字节 for (int i = 13; i >= 0; i--) { uint8_t rc5Bit = (frame >> i) & 0x1; // 每个RC-5位需要2个CIR位 uint8_t cirBits = (rc5Bit == 1) ? RC5_ONE_PAIR : RC5_ZERO_PAIR; // 将2个CIR位放入buffer,注意字节序和位序 // 此处简化处理,假设从高位开始发送 // 实际需要仔细处理位打包 } // 发送前导码(如果有,RC-5无前导码)和帧数据 // 使用CIR驱动函数,以RC5_T_BIT_US作为基础时间单元t // 这里需要重新初始化CIR驱动,将pulseWidthUs设置为RC5_T_BIT_US CIR_Config rc5Config = {UART0_BASE, RC5_T_BIT_US, CIR_DUTY_1_3}; CIR_init(&rc5Config); // 将cirBuffer中的数据通过CIR_sendPulse/Space组合发送出去 // 实际上,对于RC-5,更直接的方法是:根据帧的每一位,调用一次发送一个t脉冲和一个t空间的函数。 // 但我们已经将位序列转换成了CIR的“01”和“10”模式,可以直接写入FIFO。 // 假设我们已经将28个CIR位打包到了cirBuffer CIR_enableTx(); for(int i=0; i<4; i++) { // 等待FIFO有空位(简化处理) while(!(HW_RD_REG32(UART0_BASE + UART_LSR_OFFSET) & 0x20)); HW_WR_REG32(UART0_BASE + UART_THR_OFFSET, cirBuffer[i]); } // 等待TX_STATUS中断,表示发送完成 while(!CIR_isTxBusy()); // 假设这个函数检查发送状态 CIR_disableTx(); }6. 常见问题与调试技巧实录
红外遥控调试是个细致活,信号看不见摸不着,全靠逻辑分析仪或红外接收探头。下面是我总结的几个典型问题和排查方法。
6.1 问题排查速查表
| 现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 完全无发射 | 1. SD引脚配置错误,发射管未使能。 2. TX引脚未正确复用为CIR功能。 3. UART模块时钟未使能。 4. 未切换到CIR模式。 | 1. 用万用表测量SD引脚电平,确认在发送时应为低电平。检查UART_ACREG[6]配置。2. 检查Pad Configuration寄存器,确认PINMUX设置正确。 3. 检查PRCM模块,确认UART所在电源域和时钟域已开启。 4. 读取 UART_MDR1[2:0]寄存器,确认值为010b。 |
| 有发射但接收端无反应 | 1. 载波频率偏差太大。 2. 脉冲占空比不匹配。 3. 编码时序( t时长)不准确。4. 发射管驱动电流不足。 5. 接收头中心频率不匹配。 | 1. 用示波器或逻辑分析仪测量TX引脚波形,检查38kHz(或其它)载波频率是否准确。调整CIR时钟分频。 2. 测量调制脉冲的占空比,与接收头期望的(通常1/3)对比,调整 CIR_PULSE_MODE。3. 测量单个 t脉冲的宽度,与协议要求对比(如RC-5的889us)。4. 检查发射管限流电阻,确保峰值电流足够(通常>50mA)。 5. 确认接收头中心频率(如38kHz)与发射载波一致。 |
| 接收不稳定,时好时坏 | 1. 电源噪声大。 2. 软件延时精度不够。 3. 中断处理耗时过长,导致帧间间隔出错。 4. 环境光干扰(日光灯、太阳光)。 | 1. 在红外接收头电源引脚就近加10uF和0.1uF去耦电容。 2. 避免使用不精确的软件循环延时,改用硬件定时器。 3. 优化中断服务程序,或将耗时操作放到主循环。对于连续发送,确保帧间有足够的空闲时间(如SIRC的45ms帧间隔)。 4. 为接收头加装遮光罩,或选择抗光干扰强的型号。 |
| 能学习但不能控制 | 1. 发送的编码与学习到的编码不一致(位序反了)。 2. 协议判断错误(将NEC误判为RC-5)。 3. 地址码或客户码错误。 | 1. 用逻辑分析仪同时抓取学习时接收到的波形和发送时的波形,逐位对比。 2. 分析学习到的波形特征(引导码、位定义、帧长度),准确识别协议。 3. 许多设备有固定的地址码或客户码,需要从学习到的数据中正确提取并回放。 |
6.2 调试工具与技巧
- 必备工具:逻辑分析仪: 一个带红外解码功能的逻辑分析仪(如Saleae)是调试红外通信的神器。它可以直接捕获RX/TX引脚上的数字波形,并自动解析成NEC、RC-5等协议数据,一目了然。
- 红外探头: 一个简单的红外接收二极管(不加解调)连接到示波器,可以直观看到发射的红外光脉冲,确认是否有信号发出,以及大致波形。
- 手机摄像头检测法: 大部分手机摄像头对红外光敏感。在黑暗环境中,用手机摄像头对着工作的红外发射管,可以在屏幕上看到紫色的光点。这是一个快速验证发射管是否工作的土办法。
- 软件仿真与日志: 在编码层和驱动层加入详细的日志输出,记录下每个步骤准备发送的脉冲/空间宽度。与逻辑分析仪抓取的实际波形进行对比,可以快速定位是软件计算错误还是硬件时序问题。
- 注意帧间间隔: 手册中特别强调,CIR逻辑在连续发送字节时不会自动插入延迟。如果接收端要求帧之间必须有特定的间隔(如NEC协议的108ms重复码间隔),必须由软件主动控制。在发送完一帧后,调用
CIR_sendSpace()或简单的delay_ms()函数来满足间隔要求。
最后,关于协议兼容性,手册说得很对,它不可能涵盖所有红外协议。市面上还有NEC、Sharp、Samsung等多种协议。实现一个通用的红外学习/发射功能,核心在于设计一个灵活的“脉冲-空间”序列录制与回放引擎。将接收到的波形解析成一系列高电平(脉冲)和低电平(空间)的持续时间并存储下来;回放时,再按照这个时间序列精确地调用CIR_sendPulse和CIR_sendSpace。这样,无论什么奇葩协议,都能通吃。