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 未注册/广播地址

逻辑地址的分配规则,我总结为三点:

  1. 设备类型决定候选地址池——比如电视只能选0或14,播放器只能选4、8、11。
  2. 通过“轮询”机制抢占地址——设备上电后,会发送Polling Message试探某个地址是否被占用。如果没人应答,就占为己有。
  3. 地址冲突时,后上电的设备让路——如果两个同类型设备抢同一个地址,后上电的那个会主动放弃,换下一个候选地址。

个人经验:我建议你在固件里实现一个“地址重试机制”。曾经有个项目,机顶盒和游戏机都是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 知识体系总览

说了这么多,我画了一张图帮你理清思路:

CEC寻址机制知识体系 物理地址(硬件位置) 逻辑地址(设备身份) 格式:4字节,0.0.0.0 ~ F.F.F.F 分配:通过EDID,从根设备向下级联 特点:反映HDMI拓扑结构,不可更改 格式:1字节,0~15,共16个地址 分配:Device Type映射 + 轮询抢占 特点:反映设备功能角色,可动态分配 点对点地址 广播地址 指定收发双方逻辑地址 接收方地址为0xF(15) 物理地址 + 逻辑地址 = 完整的CEC设备标识

这张图把物理地址、逻辑地址、通信方式串起来了。你仔细看看,其实逻辑并不复杂——物理地址是“你在哪”,逻辑地址是“你是谁”,通信方式决定“怎么喊话”。

4.6 避坑指南

最后,分享几个我实际项目中踩过的坑:

  • 物理地址冲突:有些廉价HDMI切换器不按规范转发EDID,导致下游设备拿到错误的物理地址。我建议你在固件里加一个“物理地址校验”,如果发现地址异常(比如两个设备地址相同),主动触发一次地址重分配。
  • 逻辑地址耗尽:如果总线上挂了5个同类型设备(比如5个机顶盒),但候选地址只有4个,最后一个设备会拿不到地址。解决办法是让设备支持“地址15(未注册)”模式,虽然功能受限,但至少不会死机。
  • 广播风暴:多个设备同时广播<Report Physical Address>,会导致总线拥堵。我曾经在一个项目里看到,电视和音响同时上电,互相广播了20多次才稳定下来。解决方案是加一个随机延迟(100~500ms),错开广播时机。

总结一下:寻址机制是CEC通信的基石。物理地址告诉你设备插在哪,逻辑地址告诉你设备是什么,广播和点对点决定了消息怎么传。把这三点搞明白,CEC协议你就掌握了至少一半。

对了,如果你在调试时发现设备“不听话”,先别急着怀疑协议栈。用CEC分析仪抓一下总线,看看物理地址和逻辑地址对不对——八成问题出在这里。

可可精品资料库