简介这份PPT系统讲解基于以太网的可见光通信技术面向通信工程、物联网及光通信方向的初学者和研究人员可用于课程学习、毕业设计或技术入门。内容从可见光通信的基本概念出发完整梳理了基于以太网的LED可见光通信系统架构包括计算机A/B、信号接口处理电路、以太网介质转换模块、光发射与光接收模块等核心环节并介绍了OFDM技术在可见光通信中的应用。信号接口电路的电平耦合与隔离作用、MLT-3编码与单极性NRZ电平转换、LED光源的伏安特性/P-I特性/调制特性以及光发射机与光接收机的设计要点均有展开方便读者建立系统级认知。资源为1个PPTX演示文稿大小1.13MB已有119人学习适合对照PPT逐页理解以太网与可见光通信结合的关键流程。1. 基于以太网的可见光通信系统到底在解决什么问题把以太网跑在 LED 上听起来像是一个“能不能行”的极客实验但它背后其实是一个很实在的工程命题可见光通信VLC的物理层是一路单向、连续、带宽受限的光载波而以太网的 MAC 层是双工、异步、带载波侦听的标准协议栈。两者之间没有一个现成的 PHY 芯片能直接对接这让不少第一次接触 VLC 的工程师误以为“把网线换成 LED”就行结果上电后抓包抓不到任何有效帧。本文要讲的就是这条链路上真正需要动手的部分帧怎么裁、时钟怎么同步、电路怎么搭、STM32 侧怎么把裸光链路包装成一个可以被 TCP/IP 认识的标准以太网口。适合正在做可见光通信硬件电路、以太网接口调试或者想用低成本方案打通“最后一米”光链路的嵌入式工程师。搞清楚这条路径之后你会发现难点不在光而在如何让一个没有时钟恢复机制的异步协议在连续光信道上稳定传输。2. 为什么以太网 PHY 不能直接驱动 LED以及帧裁剪的边界2.1 物理层本质冲突异步协议 vs 连续时钟以太网从 10BASE-T 到 1000BASE-T物理层都是自带时钟恢复机制的。接收端从数据流里提取时钟发送端和接收端之间没有独立的时钟线。这条规则在双绞线上工作了三十年但到了可见光通信这里就出了问题LED 驱动电路和光电二极管接收链路本质上是一条连续的光信道它不关心你有没有做载波侦听也不认识前导码和 SFD。信道里只有“亮”和“暗”两种状态你给它以太网的差分 Manchester 编码它也能亮灭但问题是接收端的采样时钟必须和发送端严格同步否则连续传 64 字节之后累计的时钟漂移就足以让 CRC 校验崩溃。我一般会在设计最开始就做一个决定链路速率直接压到 LED 的调制带宽以内而不是硬扛标准以太网的 10Mbps。可见光通信硬件电路里常用的 LED 是白光照明灯珠它的 3dB 调制带宽通常只有 3MHz 到 5MHz靠预加重电路能勉强推到 10MHz但代价是接收端要加均衡器。大多数人做这个系统是为了把数据传通而不是做高速率研究所以把物理层速率定在 1Mbps 以下是最务实的起点。这个决定意味着不能走标准 PHY 芯片的 MII 接口直接接光器件因为 MII 的 TX_CLK 已经被 PHY 锁死在 25MHz 或 2.5MHz你没法让它按你的意愿降速。2.2 最小帧裁剪保留 18 字节丢掉 46 字节填充以太网最小帧是 64 字节从目的地址到 FCS 算起。但 64 字节的数据里真正必须传输的只有目的 MAC6 字节、源 MAC6 字节、长度/类型2 字节和净荷里的有效数据。46 字节的填充字段是 CSMA/CD 时代的产物规定最小帧长是为了在冲突域里能检测到碰撞这一点在有确定链路调度的可见光通信系统里完全没有必要。所以常见做法是自己定义一个精简帧格式把标准以太网帧的填充字段和 FCS 一起裁掉让有效传输负载降到 128 字节以下。这样处理的直接收益是链路利用率大幅提高。算一笔账如果物理层速率定在 500kbps用标准以太网最小帧一次传 46 字节应用数据帧间隔、前导码、SFD 加进来的总开销要占到帧长的 20% 以上裁掉填充和重算 CRC 之后同样传 46 字节只需要 18 字节的头部加 4 字节的帧校验效率提升是立竿见影的。2.2.1 自定义精简帧格式的字段设计我习惯在裁剪后的帧头里保留三个关键字段帧长度2 字节、序列号1 字节、帧类型1 字节再加上原本的以太网目的地址和源地址。帧长度字段必须放在最前面这样接收端可以在一帧数据到达尾部之前就知道该收多少字节避免把下一帧的同步头当成数据收进来。序列号字段是纯自定义的但强烈建议加上因为可见光信道里遮断、抖动、突发干扰都可能丢帧序列号是接收端判断丢帧最廉价的依据。裁剪后的帧结构如下字段长度字节说明帧长度2从帧长度字段之后到 FCS 之前的总字节数序列号1发送计数器低 8 位用于检测丢帧目的 MAC6保留标准以太网目的地址源 MAC6保留标准以太网源地址帧类型2沿用 0x0800 表示 IPv40x0806 表示 ARP载荷0~256从原始以太网帧提取的有效数据FCS4对上述所有字段做 CRC32这里有个参数需要特别说明载荷最大长度按 256 字节设是综合考虑 LED 带宽和链路误码率之后折中的结果。载荷越长单帧传输时间越长光信道里背景光干扰和抖动造成误码的概率就越大重传代价也越高。标准以太网的最大帧是 1518 字节如果你想在可见光链路上跑 TCP 分段后的 1460 字节大包就必须在接收端做重组。这样带来的复杂度远高于直接把 MTU 降到 256所以我倾向于把整个链路的 MTU 设置为 300 字节以内让 IP 层自己分片。这个思路和车载以太网里的 SOME/IP 大包处理更像属于适配层策略而不是物理层能力问题。2.3 接收端重组与缓冲策略接收端拿到裁剪帧后需要把它们还原成完整的以太网帧再交给上层的 TCP/IP 协议栈。因为裁剪后的帧没有前导码和 SFD接收端判断帧边界依靠的是自定义的同步头也就是在前一帧结束到下一帧开始之间插入一段固定的曼彻斯特编码序列。重组逻辑放在一个环形缓冲区里完成当帧类型字段是 IP 分片时缓冲区需要记住分片偏移等待同一 IP 报文的所有分片到齐后再组包。我见过不少人在这一步直接掉进一个坑把自定义帧里的目的 MAC 排除在重组范围外导致多个裁剪帧拼出来的以太网帧没有目的地址协议栈直接把包丢了。重组时一定要把裁剪时删掉的 802.3 头重新拼回去包括前导码不用恢复但目的 MAC、源 MAC、长度/类型必须重建。建议在重组模块里做一次 MAC 地址合法性校验目的地址全 0 或广播地址处理方式完全不同这个校验能帮你挡住至少一半的解析错误。3. 光链路编码与时钟同步的工程做法3.1 为什么不能用裸 NRZ 直接连 LED很多第一次做可见光通信硬件电路的人会直接把 UART 的 TX 引脚接到 LED 驱动管上用最简单的 NRZ 编码传数据。这种方案在实验室台面上能跑通但稳定性很差核心原因是 LED 光链路是单端、非平衡的而且接收端的光电二极管输出信号幅度随环境光漂移。当数据流里出现连续的长 1 或长 0 时接收端的直流工作点会被拉偏判决门限跟着漂移等数据恢复变化时已经来不及了。以太网本身就给出了答案100BASE-TX 用的是 4B5B 编码加 MLT-310BASE-T 用 Manchester。Manchester 编码的特点是每个比特中间必然有一次跳变跳变沿里既带数据也带时钟接收端可以用数字 PLL 从这个跳变沿里恢复出采样时钟。对于可见光链路来说Manchester 编码还有一个额外的好处编码后的信号永远是 50% 占空比的交替亮灭消除了直流漂移对 LED 亮度的影响。代价是波特率翻倍500kbps 的用户速率需要 1MHz 的开关频率这对大多数 LED 来说还在带宽承受范围内。3.2 Manchester 编码的 STM32 实现要点用 STM32 做 Manchester 编码时我一般不推荐用定时器输出比较做逐 bit 翻转。原因很简单中断频率太高主频全被占满。一个更省 CPU 的做法是做成查表编码把每个字节的 Manchester 编码结果预存成 16 bit 的查找表发送时查表直接拼出待发送的比特流再通过 SPI 或 DMA 把编码后的数据推给移位寄存器。这样数据速率能推到 1Mbps 以下而 CPU 占用率几乎为零。/* 生成Manchester编码查找表LSB先行 */ uint16_t manchester_encode_table[256]; void manchester_table_init(void) { for (int i 0; i 256; i) { uint16_t encoded 0; for (int bit 7; bit 0; bit--) { /* 0 - 01, 1 - 10保证每个bit中间都有跳变 */ if ((i bit) 0x01) { encoded (encoded 2) | 0x02; /* 10 */ } else { encoded (encoded 2) | 0x01; /* 01 */ } } manchester_encode_table[i] encoded; } }查表编码的逻辑很简单对一个字节的每一位0 编码成“01”表示从低到高的跳变1 编码成“10”表示从高到低的跳变。注意这里的“01”和“10”是双 bit 表示发送顺序是高位在前接收端在解码时要从接收到的比特流里每隔两个 bit 采样一次中心位置根据中心位置的电平高低直接还原出原始 bit不需要恢复出精确的跳变沿位置。这个 16 bit 的表项里隐含的设计是接收端只需要一个比数据速率高两倍以上的采样时钟就能可靠解码对时钟精度要求降低了一个数量级。发送端要把待发送的数据包按字节送入编码器时还有一个顺序问题CRC 计算和编码必须用同一个字节序。标准以太网 FCS 是按位反序处理的如果你先算 CRC 再编码编码后的 bit 顺序会被 Manchester 表打乱。最简单的办法是让 Manchester 编码器不管字节序保持字节为单位连续发接收端解码后按同一个字节序重组再统一做 CRC 校验。这样发送端和接收端的字节序约定保持一致CRC 按这个约定计算即可不用在编码层面做 bit 反序。3.3 接收端时钟恢复与采样窗口接收端的解码算法我用的是过采样加滑动窗口判决而不是复杂的数字 PLL。理由是 Manchester 编码本身携带跳变沿信息只要采样时钟的频率误差控制在 -2% 到 2% 以内一个 bit 周期内最多累计 2% 的时钟偏差16 个 bit 之后累计偏差还在一个采样周期的范围内出错的概率极低。具体做法是接收端用 8 倍波特率的采样时钟连续采样光电二极管输出检测到下降沿或上升沿后启动一个 16 bit 的滑动窗口窗口内统计每 2 个采样点对应的电平值。如果窗口内的电平序列依次呈现出“01”或“10”的跳变模式就认定这个是有效数据位。这个方法的优势在于不依赖精确的边沿锁定即使 LED 的上升沿和下降沿不对称白光 LED 的下降沿通常比上升沿慢只要采样点落在电平稳定区内解码仍然正确。有一个参数需要重点调采样窗口的对齐位置。我一般把判决点设置在 bit 周期的 25% 和 75% 两个位置各采一次取两次采样的异或值作为跳变方向。这样做的好处是对 LED 驱动电路的过冲不敏感因为过冲通常发生在跳变沿之后的几个纳秒内而 25% 和 75% 两个位置的采样点已经避开了瞬态区域。4. 可见光通信硬件电路的驱动与接收设计4.1 LED 驱动级的带宽补偿可见光通信的发送端电路看起来和一个普通的 LED 照明驱动很像但有一个关键差异你需要在直流偏置点上叠加一个高速开关信号。很多文章会说用三极管或 MOS 管做开关这没错但高速开关光通信对驱动管的开关时间有硬性要求。9013 这类小功率三极管的开关时间在几百纳秒量级用来做 1Mbps 的 Manchester 编码信号驱动上升沿和下降沿会被拉宽到将近一个 bit 周期接收端采样时很容易采到中间电平。我一般会先用一个高速比较器把 STM32 输出的 3.3V 逻辑电平整形再驱动 MOS 管开关 LED。比较器选型看两个参数传播延迟要小于 20ns输出上升时间要小于 10ns。TI 的 TLV3501 或类似规格的比较器在这个场景里够用。LED 驱动级的核心设计是预加重电路简单说就是在开关跳变沿之后的短暂时间内注入一个更大的电流脉冲加快 LED 的响应速度。做法是在 LED 驱动管栅极并联一个 100pF 到 470pF 的电容和 10Ω 电阻串联支路这个支路会在电平跳变时额外提供瞬间电流让 LED 的照度变化更快达到稳定。预加重参数的具体数值需要根据实际 LED 的响应时间调整。一个可复现的调试方法是发送一个 1010 交替的测试序列用示波器接光电二极管输出看 20% 到 80% 的上升时间。如果上升时间超过 100ns就加大并联电容每次按 1.5 倍递增直到上升时间收窄到 20ns 以内。注意不要加得过大否则会在脉冲顶部形成过冲过冲会在接收端造成误判。4.2 光电接收前端的跨阻放大与比较判决接收端的光电二极管输出的是微弱的电流信号典型值在几十纳安到几微安之间必须先经过跨阻放大器TIA转成电压信号。常见的做法是用运算放大器搭 TIA反馈电阻 Rf 决定增益反馈电容 Cf 用来补偿带宽。这里有一个参数权衡要仔细算Rf 太大会让增益高但带宽低太小的 Rf 则信号幅度不足以驱动后级比较器。我用过的典型值是 Rf 100kΩCf 2pF对应的 -3dB 带宽大约是 800kHz刚好覆盖 1Mbps 的 Manchester 信号需求。如果环境光比较强光电二极管的直流光电流会直接把放大器输出偏置到饱和解决办法是加一个高通的隔直电容把信号耦合到后级时滤掉直流分量。推荐在 TIA 输出和第二级放大器之间串 10nF 电容并联 1kΩ 电阻到地这个时间常数对应的高通截止频率是 16kHz既能抑制环境光的慢变化又不会损伤 500kHz 以上的数据信号。后级判决电路用比较器实现参考电压不能直接接固定电压源而应取接收信号自身的平均值。可以用一个 RC 低通滤波从信号里提取平均电平时间常数设置在 1ms 左右然后把信号和这个自适应的参考电压分别接到比较器的两个输入端。这样即使环境光缓慢变化导致信号幅度漂移比较器也能自动调整判决门限。4.3 STM32 定时器引脚与中断资源分配整个系统的主控我用 STM32F407它自带的以太网 MAC 其实可以复用但物理层被我们改造成了光链路所以 MAC 的 MII 接口实际只用来做 DMA 缓冲管理不接 PHY 芯片。UART 或者 SPI 外设才是真正和光编码器交互的通道。一个比较稳妥的引脚分配是PA9 作为发送数据输出接比较器输入PA10 作为接收数据输入接 TIA 输出经过比较器整形后的信号。接收方向的数据由定时器输入捕获中断来采样每捕获到一个上升沿就读取当前计数值算出和上一个上升沿的间隔再根据间隔恢复出 bit。Tim4 的输入捕获通道用映射到 PB6这样可以不占用 UART 的 RX 引脚留给调试串口用。/* TIM4 输入捕获初始化用于接收端曼彻斯特信号采样 */ static void tim4_input_capture_init(void) { GPIO_InitTypeDef gpio {0}; TIM_ICInitTypeDef ic {0}; __HAL_RCC_TIM4_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); gpio.Pin GPIO_PIN_6; gpio.Mode GPIO_MODE_AF_PP; gpio.Pull GPIO_PULLDOWN; gpio.Speed GPIO_SPEED_FREQ_HIGH; gpio.Alternate GPIO_AF2_TIM4; HAL_GPIO_Init(GPIOB, gpio); ic.ICPolarity TIM_INPUTCHANNELPOLARITY_RISING; ic.ICSelection TIM_ICSELECTION_DIRECTTI; ic.ICPrescaler TIM_ICPSC_DIV1; ic.ICFilter 0x0F; HAL_TIM_IC_ConfigChannel(htim4, ic, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim4, TIM_CHANNEL_1); }这段初始化里 ICFilter 设为 0x0F 是为了过滤掉比较器输出上的窄脉冲毛刺。曼彻斯特编码理论上每个 bit 周期只有一个跳变但光电接收链路会引入毛刺这些毛刺表现为极窄的脉冲滤波器的数字采样宽度可以把它滤掉。但要注意滤波会引入额外的传播延迟延迟时间约为 4 到 8 个采样时钟周期如果后续做时钟恢复时要把这个延迟也考虑进去否则解码窗口会偏。接收解码的主循环里每个捕获中断对应一个曼彻斯特跳变沿把相邻沿的间隔换算成 bit 序列填进 DMA 接收缓冲区。当识别到同步头连续 16 个跳变沿间隔一致时把之后的 bit 流暂存直到收齐帧长度字段指定的字节数。5. 让 Linux 认识这条光链路驱动封装与协议栈联动5.1 把光链路挂成标准以太网设备的两个路径STM32 这一侧把光链路数据收上来之后还只是完成了物理层和链路层的一半工作真正要跑 TCP/IP需要让上层系统认为这是一个标准网卡。这里有两个工程上常见的选择一是 STM32 本身跑 LwIP把精简帧重组出的完整以太网帧直接交给 LwIP 的 netif 接口二是 STM32 通过 SPI 或 USB 连接一块标准以太网 MAC 芯片比如 W5500 或 ENC28J60让 MAC 芯片生成标准以太网帧头VLC 工作在物理层和 MAC 之间的适配层。我推荐第二种方案理由是协议栈的通用性。W5500 内部有完整的 TCP/IP offload它的 RX 缓冲里收到的是标准以太网帧上层应用不用关心底层是双绞线还是光链路。唯一要处理的是让 W5500 的输出数据在进入光链路之前经过帧裁剪和 Manchester 编码。你可以把 W5500 配置为 loopback 模式把它的 TX 输出接到 STM32 的编码器光链路接收端解码后再经过重组模块把帧恢复出来通过 STM32 的 SPI 回传给另一个 W5500 或同一片 W5500 的 RX 缓冲。5.2 以太网帧序列号与丢包检测光链路不像双绞线那样稳定偶尔被遮挡、快速移动、强环境光干扰都会造成丢帧。在适配层加入以太网帧序列号机制之后接收端就能精确统计丢帧率。注意这里的序列号和前面自定义帧格式里的序列号含义不同那个是帧序列号用于光链路内部的丢帧检测这个序列号可以理解成逻辑链路层的连续计数两个层面要区分开。在 Linux 侧你最终看到的设备是一个标准网口掉包表现为 TCP 重传、ICMP 回显丢包、或者 UDP 的 RTP 流中断。排查时先用 ping 测通再统计 ICMP 请求和应答的序列号是否连续如果连续丢包且丢包间隔有规律多半是光链路接收端的解码窗口对齐出了问题而不是随机干扰。如果丢包是随机的且单包丢失优先检查 LED 驱动有没有过热导致光功率下降以及光电二极管的接收角是否对得准。5.3 ethtool 与断线重协商即使物理层是光链路上层标准的 ethtool 指令依然能用因为 netdev 层的接口是统一的。调试时常用的一组命令如下# 查看光链路上跑的协议和速率 ethtool eth0 # 查看网卡统计里的丢弃和错误帧计数 ethtool -S eth0 # 手动触发链路 down/up验证重协商逻辑 sudo ip link set eth0 down sudo ip link set eth0 up # 这里需要用 Linux 自带回环测试确认数据通路完整性 sudo ethtool --test eth0 offlineethtool --test 里的 offline 测试会驱动 MAC 芯片发送回环帧对可见光通信硬件电路来说这个测试要慎用因为它默认是 MAC 内部回环绕过了光路测不到 LED 和光电二极管的问题。我一般会在适配层驱动里自己加一个光链路的自环模式接收端把解码后的数据原封不动送回编码器再利用 STM32 的串口打印回环数据的 CRC 校验结果这样才真正覆盖了光路。ethtool -S输出的 rx_crc_errors 和 rx_frame_errors 是定位物理层的首要指标。如果这两个计数器持续增长说明干扰源在帧传输过程中破坏了数据完整性如果计数器不涨但应用层丢包说明问题出在上层协议栈或缓冲区管理器和光链路无关。这套排查思路和标准以太网完全一致可以让熟悉以太网调试的同事快速上手。6. 用眼图、误码率测试和长跑验证链路稳定性可见光通信系统做完第一版能通信之后真正决定能不能交付的不是“能通”而是“能稳定通多久”。我建议在上层调应用之前先花一下午把误码率测试跑透。这里给出一套从粗到细的验证流程每一步都有明确的通过标准。先用 STM32 的 DMA 循环发送一个伪随机序列PRBS-7 即可接收端把解码数据逐字节比对统计误码率。注意发送端和接收端的 PRBS 生成器必须用同一个多项式初始化否则误码率会被错误同步掩盖。通过标准可以按链路速率来定500kbps 下连续 100 万字节无误码说明误比特率低于 1E-6这个量级的数据可以支撑 UDP 实时传输TCP 会因为重传机制稳定吃掉零星丢包。误码率测试跑通之后再测同步头捕获的稳定性。最直接的方法是人为制造链路中断比如用手在 LED 和光电二极管之间快速挥过模拟遮挡场景。观察接收端在中断期间是否产生假帧、恢复后能否在一个同步头周期内重新锁定。我一般会在适配层驱动里把每次丢失同步的事件记录成一个计数器通过串口或者光链路本身的反向信道读取如果在 100 次遮挡测试中重新同步时间超过 10ms 的次数多于 3 次说明同步头的长度或判决阈值需要重新调整。最后做一次连续 72 小时的长跑测试记录每一小时的数据吞吐量和错误帧数。这个测试能暴露两个隐蔽问题一个是 LED 长时间点亮后的温漂会导致光功率下降接收端比较器的自适应参考电平会慢慢偏离最优值另一个是 STM32 的 DMA 缓冲区在高负载下可能溢出导致重组模块静默丢帧。针对温漂问题我上市之前通常会把接收端的参考电平跟踪时间常数从 1ms 改成 10ms牺牲一点对快速环境光变化的响应速度换来长时间运行的稳定性。长跑测试期间建议开一个定时任务每 10 分钟记录一次 ethtool -S 的计数快照对比相邻快照的增量如果 rx_error 增量始终为零而 throughput 有周期性掉坑优先看是不是应用层的发送窗口太小而不是把锅甩给光链路。做完这三个阶段的测试这套基于以太网的可见光通信系统才算真正达到可交付状态。误码率、重同步时间、长跑稳定性这三个数字比任何示波器的漂亮波形都更能说明问题。调试到最后你会发现这套系统的瓶颈不在于光而在于适配层的处理逻辑把帧裁剪、曼彻斯特编码、缓冲管理这三件小事做扎实了链路自然就稳了。本文还有配套的精品资源点击获取