3. CEC协议栈详解
好,咱们今天来啃一块硬骨头——CEC协议栈。说实话,这玩意儿刚接触的时候,我也觉得挺绕的。但搞懂了之后你会发现,它其实就是一套“客厅里的指挥系统”。
CEC的全称是Consumer Electronics Control,消费电子控制。说白了,就是让HDMI线上的设备能互相“说话”。你按一下电视遥控器,功放自动开机、蓝光机开始播放、电视切换到对应输入源——背后全是CEC在干活。
我个人习惯把CEC协议栈分成三层来看:物理层、数据链路层、应用层。今天咱们重点讲数据链路层和应用层,也就是帧结构、寻址机制、逻辑地址分配,还有常用的命令集。
3.1 CEC帧结构:数据是怎么打包的?
CEC通信用的是单线双向串行总线,速率很慢,只有400bps左右。你别嫌慢,控制指令就那么几个字节,够用了。
一个完整的CEC帧长这样:
起始位 + 10位数据块 + 10位数据块 + ... + 10位数据块
每个10位数据块的结构是:
- 1位起始位:逻辑0,表示开始传输
- 8位数据:真正的信息内容
- 1位EOM:End of Message,1表示这是最后一个数据块
- 1位ACK:应答位,接收方拉低表示收到
嗯,这里要注意:CEC的ACK机制是“线与”的。也就是说,总线上任何一个设备都可以拉低ACK位。如果没人应答,那ACK位就是高电平,发送方就知道“没人理我”。
关键点:CEC帧的最小单位是“数据块”,不是字节。每个数据块10位,其中有效数据只有8位。别搞混了。
我刚开始做CEC调试的时候,用逻辑分析仪抓波形,对着协议手册一个一个数位,数得眼睛都快瞎了。后来发现,其实大部分逻辑分析仪都有CEC解码插件,直接帮你解析好了。嗯,工具要用对。
3.2 CEC寻址机制:设备之间怎么找到对方?
CEC用的是逻辑地址,不是物理地址。每个设备在CEC总线上有一个唯一的逻辑地址,范围是0到15,一共16个地址。
但注意,不是所有地址都能随便用。CEC协议规定了每个地址对应什么类型的设备:
| 逻辑地址 | 设备类型 | 说明 |
|---|---|---|
| 0 | TV(电视) | 通常作为根设备 |
| 1 | Recording Device 1 | 比如机顶盒、录像机 |
| 2 | Recording Device 2 | 第二个录像设备 |
| 3 | Tuner 1 | 调谐器/电视棒 |
| 4 | Playback Device 1 | 蓝光机、游戏机 |
| 5 | Audio System | 功放、回音壁 |
| 6 | Tuner 2 | 第二个调谐器 |
| 7 | Tuner 3 | 第三个调谐器 |
| 8 | Playback Device 2 | 第二个播放设备 |
| 9 | Recording Device 3 | 第三个录像设备 |
| 10 | Tuner 4 | 第四个调谐器 |
| 11 | Playback Device 3 | 第三个播放设备 |
| 12 | Reserved | 保留 |
| 13 | Reserved | 保留 |
| 14 | TV(备用) | 某些电视用这个 |
| 15 | Unregistered | 未注册/广播地址 |
你想想看,一个客厅里最多也就五六台设备,16个地址完全够用。但问题来了——设备怎么知道自己该用哪个地址?
答案是:通过物理地址和逻辑地址的映射。HDMI有一个EDID(扩展显示标识数据)机制,里面包含了物理地址信息。CEC设备上电后,会先读取EDID,然后根据物理地址和自身设备类型,向总线申请一个逻辑地址。
避坑指南:我曾经遇到过一台蓝光机,死活拿不到逻辑地址。查了半天,发现是EDID里的物理地址写错了。HDMI线缆的DDC通道有问题,导致EDID读取失败。所以,CEC调不通的时候,先检查物理层和EDID。
3.3 CEC命令集:设备之间说什么?
CEC的命令集大概有70多条,但常用的也就十几条。我把它们分成几类:
3.3.1 系统控制类
- Standby:让设备进入待机。电视按关机键,所有CEC设备一起关。
- Active Source:声明自己是当前活跃源。比如你打开蓝光机,它就会广播这个消息。
- Set Stream Path:切换HDMI输入源。电视收到这个命令,会自动切到对应端口。
3.3.2 设备控制类
- User Control Pressed:模拟用户按键。比如音量+、方向键、确认键。
- User Control Released:按键释放。这两个命令通常成对出现。
- Give Device Power Status:查询设备电源状态。返回是“开”、“关”还是“正在启动”。
3.3.3 菜单控制类
- Menu Request:请求设备显示菜单。
- Menu Status:报告菜单状态(显示中/已关闭)。
- Give OSD Name:查询设备的OSD名称。比如电视上显示“蓝光播放器”。
每个命令的格式是固定的:
发起方地址 + 目标方地址 + 操作码 + 操作数
举个例子,电视(地址0)想让功放(地址5)静音:
0x05 0x44 0x42
// 0x05: 目标地址是5(功放)
// 0x44: 发起方地址是0(电视),加上命令标志
// 0x42: 操作码,表示"User Control Pressed"
// 后面还要跟操作数,表示具体按键
看着有点乱是吧?别急,我教你一个记忆方法:CEC命令的头部字节,高4位是目标地址,低4位是源地址。比如0x05,高4位是0,低4位是5,表示“从地址0发给地址5”。
实战经验:我在做CEC兼容性测试时发现,不同品牌的设备对命令的响应速度差异很大。索尼的电视响应很快,但某些品牌的功放会有200ms以上的延迟。所以,发送命令后一定要加超时处理,别死等。
3.4 逻辑地址分配流程:设备是怎么“注册”的?
这个流程我建议你用一张图来理解:
流程其实不复杂:
- 上电:设备启动,读取EDID获取物理地址。
- 探测:设备向总线发送Polling Message,探测自己想要的逻辑地址是否被占用。
- 判断:如果收到ACK,说明地址被占用了,换下一个地址再试。
- 注册:如果没收到ACK,说明地址空闲,设备就占用这个地址。
- 广播:设备发送Report Physical Address命令,告诉所有设备“我是谁,我在哪”。
这里有个细节:Polling Message其实就是一个只有头部没有数据的帧。发送方把目标地址设为自己想占用的地址,如果没人应答,就说明地址空闲。
注意:CEC协议规定,设备在分配地址时,必须按照优先级顺序尝试。比如电视优先尝试地址0,功放优先尝试地址5。如果优先级最高的地址被占了,才尝试次优先级的。这样做是为了避免地址冲突。
我记得有一次调试,发现一台回音壁总是拿不到地址。抓日志一看,它一直在尝试地址5,但功放已经占了。回音壁的固件写死了只尝试地址5,没有做fallback。这就是典型的固件bug。
3.5 实战中的常见问题
最后,我总结几个CEC调试中经常踩的坑:
- 物理层问题:CEC信号是5V的,但有些设备只支持3.3V。电平不匹配会导致通信失败。我建议用示波器量一下CEC引脚的电压。
- 总线冲突:CEC是单线总线,多个设备同时发送会冲突。协议有仲裁机制,但实现不好的设备会“死锁”。
- 命令超时:CEC协议规定,设备必须在100ms内响应命令。但有些老设备响应很慢,需要加超时重试。
- 逻辑地址冲突:如果两个设备用了同一个逻辑地址,总线会乱套。这时候需要断电重启,让设备重新分配地址。
我的调试工具清单:逻辑分析仪(带CEC解码)、USB转CEC适配器(比如Pulse-Eight的)、HDMI CEC分析仪。如果你只是做应用层开发,用USB转CEC适配器就够了。但要是做底层驱动,逻辑分析仪是必须的。
好了,CEC协议栈的核心内容就这些。帧结构、寻址机制、逻辑地址分配、命令集——这四个东西搞明白了,CEC对你来说就没有秘密了。下一节咱们会讲CEC的物理层实现和电气特性,到时候再深入聊。
可可精品资料库