4. 寻址机制:物理地址与逻辑地址
好,咱们今天聊聊CEC协议里最绕、也最核心的一块——寻址。
你想想看,一条HDMI线上挂着好几个设备,电视、机顶盒、音响、游戏机……它们之间要发消息,总得知道“谁是谁”吧?这就引出了CEC的两套地址系统:物理地址和逻辑地址。
我刚开始接触CEC时,也被这两套地址搞晕过。后来踩了几个坑才明白——物理地址是“硬件位置”,逻辑地址是“设备身份”。两者缺一不可。
4.1 物理地址:你的设备插在哪
物理地址,说白了就是告诉系统“你这台设备挂在HDMI树的哪个节点上”。
它由4个字节组成,每个字节取值范围0~15,用十六进制表示就是0.0.0.0到F.F.F.F。比如一台电视直连的机顶盒,物理地址可能是1.0.0.0。
怎么分配的呢?靠EDID通信。根设备(通常是电视)的物理地址是0.0.0.0。它给下游端口分配地址:端口1的设备是1.0.0.0,端口2是2.0.0.0。如果这个下游设备还有子端口,就继续往下分,比如2.1.0.0、2.2.0.0。
关键点:物理地址是硬件决定的,不能乱改。它反映了设备在HDMI拓扑中的实际位置。
我在项目中遇到过一个问题:某款电视的EDID里物理地址写错了,导致所有下游设备都拿不到正确的地址,CEC通信直接瘫痪。排查了两天才找到原因——嗯,从那以后我每次调试CEC,第一件事就是抓EDID看看物理地址对不对。
4.2 逻辑地址:你的设备是什么角色
物理地址告诉系统“你在哪”,逻辑地址告诉系统“你是谁”。
逻辑地址是一个0~15的数值,每个数值对应一种设备类型。比如:
| 逻辑地址 | 设备类型 | 典型设备 |
|---|---|---|
| 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 / Broadcast | 未注册/广播地址 |
逻辑地址的分配规则,我总结为三点:
- 设备类型决定候选地址池——比如电视只能选0或14,播放器只能选4、8、11。
- 通过“轮询”机制抢占地址——设备上电后,会发送
Polling Message试探某个地址是否被占用。如果没人应答,就占为己有。 - 地址冲突时,后上电的设备让路——如果两个同类型设备抢同一个地址,后上电的那个会主动放弃,换下一个候选地址。
个人经验:我建议你在固件里实现一个“地址重试机制”。曾经有个项目,机顶盒和游戏机都是Playback Device类型,同时上电时经常抢地址。后来我在代码里加了3次重试+随机延迟,问题就解决了。
4.3 逻辑地址分配规则:Device Type映射
刚才说了,设备类型决定了它能用哪些逻辑地址。但具体怎么映射的呢?
CEC协议里有一个Device Type字段,它出现在<Report Physical Address>消息中。设备上电后,会广播这个消息,告诉总线:“我是XX类型,我的物理地址是XX,我想申请逻辑地址XX。”
举个例子,一个机顶盒上电后:
// 伪代码:逻辑地址分配流程
1. 读取EDID,获取物理地址(比如1.0.0.0)
2. 根据设备类型(Tuner),确定候选地址池:[3, 6, 7, 10]
3. 从池中选第一个未占用的地址(比如3)
4. 发送Polling Message到地址3
5. 如果无应答 → 占用地址3,广播<Report Physical Address>
6. 如果有应答 → 换下一个地址(6),重复步骤4
7. 如果所有候选地址都被占用 → 进入“未注册”状态,使用地址15
这里有个细节:物理地址为0.0.0.0的设备(通常是电视),它的逻辑地址固定为0或14。其他设备的物理地址不能是0.0.0.0,否则会被当成根设备。
注意:我曾经见过一个产品,固件里把物理地址写死了0.0.0.0,结果插到电视上后,电视以为来了个“竞争对手”,两个设备疯狂抢地址,整个CEC总线直接卡死。这个坑,大家千万别踩。
4.4 广播地址与点对点地址
有了物理地址和逻辑地址,设备之间怎么通信呢?CEC消息的寻址方式有两种:
- 点对点地址(Unicast):指定发送方和接收方的逻辑地址。比如电视(地址0)发给机顶盒(地址3),消息格式就是
0x03 0x00 ...。 - 广播地址(Broadcast):发送方指定自己的地址,接收方地址填15(0xF)。所有设备都会收到这条消息,但只有相关设备会处理。
广播地址的典型应用场景:
- 设备上电时广播
<Report Physical Address>,告诉所有人“我来了” - 电视切换输入源时广播
<Active Source>,通知其他设备谁在“说话” - 遥控器按键广播
<User Control Pressed>,让目标设备响应
点对点地址的典型场景:
- 电视查询机顶盒的菜单状态:
<Give Menu Status>从电视发给机顶盒 - 机顶盒回复菜单状态:
<Menu Status>从机顶盒发回电视
我个人习惯把广播比作“喊话”,点对点比作“打电话”。喊话效率高,但容易吵;打电话精准,但需要知道对方号码。
4.5 知识体系总览
说了这么多,我画了一张图帮你理清思路:
这张图把物理地址、逻辑地址、通信方式串起来了。你仔细看看,其实逻辑并不复杂——物理地址是“你在哪”,逻辑地址是“你是谁”,通信方式决定“怎么喊话”。
4.6 避坑指南
最后,分享几个我实际项目中踩过的坑:
- 物理地址冲突:有些廉价HDMI切换器不按规范转发EDID,导致下游设备拿到错误的物理地址。我建议你在固件里加一个“物理地址校验”,如果发现地址异常(比如两个设备地址相同),主动触发一次地址重分配。
- 逻辑地址耗尽:如果总线上挂了5个同类型设备(比如5个机顶盒),但候选地址只有4个,最后一个设备会拿不到地址。解决办法是让设备支持“地址15(未注册)”模式,虽然功能受限,但至少不会死机。
- 广播风暴:多个设备同时广播
<Report Physical Address>,会导致总线拥堵。我曾经在一个项目里看到,电视和音响同时上电,互相广播了20多次才稳定下来。解决方案是加一个随机延迟(100~500ms),错开广播时机。
总结一下:寻址机制是CEC通信的基石。物理地址告诉你设备插在哪,逻辑地址告诉你设备是什么,广播和点对点决定了消息怎么传。把这三点搞明白,CEC协议你就掌握了至少一半。
对了,如果你在调试时发现设备“不听话”,先别急着怀疑协议栈。用CEC分析仪抓一下总线,看看物理地址和逻辑地址对不对——八成问题出在这里。