wordpress建站教程书推荐wordpress菜单高级应用
wordpress建站教程书推荐,wordpress菜单高级应用,北大青鸟,郴州网络推广案例米联客MZ7035FD开发板HDMI调试实录#xff1a;当Digilent驱动遇上PCA9548复用器
最近在折腾一块米联客的MZ7035FD开发板#xff0c;想用它来驱动一个HDMI显示器#xff0c;跑个图形界面。本以为照着之前的经验#xff0c;把Digilent的驱动挂上去#xff0c;设备树节点配好…米联客MZ7035FD开发板HDMI调试实录当Digilent驱动遇上PCA9548复用器最近在折腾一块米联客的MZ7035FD开发板想用它来驱动一个HDMI显示器跑个图形界面。本以为照着之前的经验把Digilent的驱动挂上去设备树节点配好就能点亮没想到一脚踩进了I2C复用器的“坑”里。这块板子的设计有点意思HDMI接口的DDC通道就是用来读取显示器EDID信息的那组I2C信号并没有直接连到ZYNQ的PL端引脚而是中间经过了一个PCA9548芯片——一个I2C总线复用器。这就意味着你没法像往常那样简单地把一个I2C控制器节点直接丢给显示驱动。整个过程更像是在Linux设备树的迷宫里为你的显示信号找到那条正确的“专属通道”。如果你也在用类似的ZYNQ平台尤其是遇到FPGAPL端模拟HDMI输出并且I2C拓扑结构比较复杂的情况这篇记录或许能帮你省下不少查资料和调试的时间。我会从硬件连接原理开始掰开揉碎了讲清楚设备树该怎么写驱动如何配合以及最终让系统自动识别显示器分辨率的关键所在。这不是一篇照本宣科的理论文章而是一次实打实的工程问题解决过程复盘。1. 理解问题核心为什么I2C复用器让事情变复杂了在标准的HDMI显示方案里事情通常很简单。FPGA逻辑PL生成视频流同时提供一个I2C主控制器这个控制器的两根线SCL和SDA直接连接到HDMI接口的DDC引脚上。上电后Linux内核里的显示驱动比如我们用的Digilentdrm-encoder通过这个I2C总线去读取显示器里的一块小芯片EEPROM中存储的EDID数据。EDID里面包含了显示器支持的所有分辨率、刷新率等关键信息。驱动拿到这些信息就能自动配置出最合适的显示模式。但PCA9548的出现改变了这个“直连”的游戏规则。这个芯片本质上是一个I2C总线开关它有一个上游端口连接主控制器和多个下游通道。主控制器通过向PCA9548发送特定的命令来选择接通哪一个下游通道。在我们的板子上HDMI的DDC线只是接在了它的某一个下游通道上比如通道4。这就引出了设备树描述时的核心矛盾对Linux内核的I2C子系统而言PCA9548本身是一个需要被识别和管理的I2C设备。同时PCA9548的每一个下游通道在逻辑上又可以看作一个独立的I2C总线。我们的显示驱动需要的参数是一个I2C总线控制器adapter的句柄它要通过这个总线去访问HDMI显示器。所以目标不再是找到“一个I2C设备”而是要在设备树中清晰地描述出“从主I2C控制器 - PCA9548设备 - 特定下游通道”这条路径并让这条路径的终点那个通道能够作为一个标准的I2C总线被其他驱动引用。这个过程涉及到设备树中几个关键概念的理解和运用i2c-mux驱动内核中用于支持I2C复用器的框架。子总线Child Bus在设备树中PCA9548节点下的每个通道节点都可以声明自己是一个新的I2C总线。句柄Phandle引用如何跨节点引用另一个节点这是设备树连接不同设备的关键语法。搞明白这些我们才能写出正确的设备树让内核在启动时自动构建出正确的设备拓扑。2. 硬件连接与驱动准备搭建调试环境在动笔写代码之前我们必须先确认硬件是怎么连的。以米联客MZ7035FD为例我们需要关注三个部分1. FPGA工程中的IP核与引脚分配在Vivado或类似的开发环境中你需要实例化以下核心IPDigilent AXI DynCLK用于生成可编程的像素时钟。Digilent HDMI Encoder (axi2vid)将AXI-Stream视频流转换为HDMI TMDS信号。Xilinx AXI VDMA用于视频数据的DMA传输。Xilinx Video Timing Controller (VTC)产生视频时序信号。最关键的是引脚约束。你需要将axi2vid输出的TMDS差分对TMDS_clk_p/n,TMDS_data_p/n[2:0]分配到板载HDMI连接器的对应物理引脚。同时用于DDC的hdmi_ddc_scl_io和hdmi_ddc_sda_io信号通常是单端信号也需要正确分配。这里一个常见的坑是电平标准务必根据你的板子原理图确认是LVCMOS18还是LVCMOS33。一个典型的XDC约束片段如下# 电平标准设置 set_property IOSTANDARD LVDS [get_ports TMDS_clk_p] set_property IOSTANDARD LVDS [get_ports {TMDS_data_p[0]}] set_property IOSTANDARD LVCMOS18 [get_ports hdmi_ddc_scl_io] set_property IOSTANDARD LVCMOS18 [get_ports hdmi_ddc_sda_io] # 引脚位置锁定 set_property PACKAGE_PIN J14 [get_ports TMDS_clk_p] set_property PACKAGE_PIN A15 [get_ports hdmi_ddc_scl_io] set_property PACKAGE_PIN A14 [get_ports hdmi_ddc_sda_io]2. 确认PCA9548的硬件地址与通道查看开发板原理图找到PCA9548芯片。它的I2C从地址由A0, A1, A2引脚的电平决定。如果这三个引脚都接地那么它的7位地址就是0x70写地址0xE0读地址0xE1。同时确认HDMI的DDC线具体连接到了PCA9548的哪一个通道上假设是通道3对应寄存器值0x08或通道4对应0x10。这个通道号就是我们后续在设备树中标识子总线的关键。3. 内核驱动配置在编译Linux内核时确保以下驱动被编译进内核y或作为模块mI2C multiplexer support(CONFIG_I2C_MUX)PCA954x and PCA954x I2C mux/switch drivers(CONFIG_I2C_MUX_PCA954x)Digilent DRM encoder support(这通常需要你手动将Digilent提供的驱动补丁打到内核源码中或者使用他们提供的内核分支)Xilinx DRM/KMS driver(CONFIG_DRM_XLNX)你可以通过make menuconfig在相应菜单下找到这些选项。一个快速的检查方法是在内核源码目录下运行grep -r CONFIG_I2C_MUX_PCA954x .config grep -r CONFIG_DRM_XLNX .config如果返回y或m说明配置正确。3. 设备树深度解析从参考到实战设备树Device Tree是描述硬件拓扑结构的核心。面对PCA9548我们不能想当然地写最好的老师是内核源码里已有的例子和官方文档。3.1 借鉴官方示例与绑定文档Linux内核源码的Documentation/devicetree/bindings/目录是我们的圣经。针对这个案例重点看两个文件i2c/i2c-mux.txt描述了I2C复用器的通用绑定规则。i2c/i2c-mux-pca954x.txt描述了NXP PCA954x系列芯片的具体属性。同时在arch/arm/boot/dts/或arch/arm64/boot/dts/目录下搜索包含pca954的.dts文件能找到大量现成的参考。比如之前提到的imx6q-b850v3.dts它就清晰地展示了如何将复用器的子通道引用给HDMI节点i2c2 { pca9547_ddc: mux70 { compatible nxp,pca9547; reg 0x70; #address-cells 1; #size-cells 0; mux2_i2c1: i2c0 { #address-cells 1; #size-cells 0; reg 0x0; }; }; }; hdmi { ddc-i2c-bus mux2_ic1; // 关键这里引用了子通道 };这个结构告诉我们mux70节点下的i2c0子节点定义了一个新的I2C总线并且它可以通过标签mux2_i2c1被其他节点引用。3.2 Digilent Encoder驱动的需求Digilent的编码器驱动文档通常在其Linux内核仓库的Documentation/devicetree/bindings/drm/xilinx/digilent_encoder.txt中明确指出它需要一个可选的属性来指定EDID-I2C总线Optional properties: - digilent,edid-i2c: The I2C device connected to the DDC bus on the video connector. This is used to obtain the supported resolutions of an attached monitor.注意它要求的是一个“I2C device connected to the DDC bus”。在设备树语境下这通常指的是一个I2C总线控制器节点而不是一个具体的从设备地址。所以我们要提供给它的就是PCA9548下游那个代表HDMI DDC通道的子总线节点。3.3 为MZ7035FD编写设备树覆盖.dtsi假设我们的硬件连接是ZYNQ PS端的I2C0控制器连接到了PCA9548的上游PCA9548的地址是0x70HDMI DDC连接在它的第4个通道上reg 4。那么在Petalinux工程中我们通常在project-spec/meta-user/recipes-bsp/device-tree/files/目录下创建或修改一个system-user.dtsi文件。内容如下我加入了详细的注释/include/ system-conf.dtsi / { /* 可在此处添加板级覆盖 */ }; /* 覆盖PS端的I2C0控制器节点 */ i2c0 { clock-frequency 100000; /* I2C总线速度100kHz */ status okay; /* 在I2C0总线上添加PCA9548复用器设备 */ i2c_mux_hdmi: i2cmux70 { compatible nxp,pca9548; reg 0x70; /* 芯片的I2C从地址 */ #address-cells 1; /* 子节点使用1个cell表示地址 */ #size-cells 0; /* 子节点无大小字段 */ status okay; /* 通道4连接至HDMI接口的DDC线路 */ i2c_hdmi_ddc: i2c4 { #address-cells 1; #size-cells 0; reg 4; /* 对应PCA9548的通道选择寄存器值 */ /* 这个节点现在代表了一个新的I2C总线可以挂载设备 但这里我们不需要挂设备显示器EDID芯片由驱动自动探测 */ }; }; }; /* 覆盖PLFPGA逻辑部分的amba_pl总线添加显示相关节点 */ amba_pl { /* Digilent HDMI编码器 */ hdmi_encoder_0: hdmi_encoder { compatible digilent,drm-encoder; /* 最关键的一行将EDID-I2C指向我们刚刚定义的子总线 */ digilent,edid-i2c i2c_hdmi_ddc; }; /* Xilinx DRM显示驱动主节点 */ xilinx_drm { compatible xlnx,drm; xlnx,vtc v_tc_0; /* 视频时序控制器 */ xlnx,connector-type HDMIA; xlnx,encoder-slave hdmi_encoder_0; /* 使用的编码器 */ clocks axi_dynclk_0; /* 像素时钟源 */ /* 注意这里属性名可能是dglnt,edid-i2c需根据驱动源码确认 */ dglnt,edid-i2c i2c_hdmi_ddc; /* 再次指定EDID-I2C总线 */ planes { xlnx,pixel-format rgb888; plane0 { dmas axi_vdma_0 0; dma-names dma; }; }; }; }; /* 确保其他相关PL IP节点状态正常 */ axi_dynclk_0 { compatible digilent,axi-dynclk; #clock-cells 0; clocks clkc 15; /* 连接到PS的时钟 */ status okay; }; v_tc_0 { compatible xlnx,v-tc-5.01.a; status okay; }; axi_vdma_0 { status okay; };注意xilinx_drm节点中使用的属性名可能是dglnt,edid-i2c而不是digilent,edid-i2c这取决于你使用的内核版本和驱动补丁。务必查看你实际使用的驱动源码通常是drivers/gpu/drm/xilinx/xlnx_drv.c或类似文件中的of_match_table来确认正确的属性字符串。写错了驱动就读取不到正确的总线。3.4 关键点梳理与验证写完设备树编译并更新到开发板后如何验证配置是否正确检查I2C总线拓扑系统启动后在串口终端执行i2cdetect -l你应该能看到类似下面的输出i2c-0 unknown i2c-0-mux (chan_id 4) N/A这表示多了一个编号为i2c-0-mux (chan_id 4)的总线它正是我们定义的子总线。探测EDID在子总线生效的前提下Digilent驱动在初始化时会通过我们指定的i2c_hdmi_ddc总线去地址0x50读取EDID。你可以通过dmesg | grep -i edid或dmesg | grep -i hdmi来查看内核日志确认是否成功读取到有效的EDID数据。检查DRM设备运行cat /sys/class/drm/card0-*/status或使用modetest工具可以查看显示接口的状态和 supported modes。如果EDID读取失败驱动可能会回退到一组默认的分辨率或者显示完全不工作。这时候就需要回头检查设备树引用是否正确、I2C物理连接是否可靠、以及PCA9548驱动是否正常加载。4. 系统集成与图形环境启动设备树搞定只是第一步要让整个图形界面跑起来还需要系统层面的配置。这里假设你使用的是基于Petalinux或Yocto构建的带有X Window System如Xorg的根文件系统。4.1 配置FrameBuffer与Xorg为了让Xorg使用我们通过DRM驱动创建出来的显示设备需要创建一个Xorg的配置文件。通常放在/etc/X11/xorg.conf.d/目录下例如99-fbdev.conf。但更现代的做法是让Xorg自动探测DRM驱动modesetting驱动。对于我们的情况由于使用了xilinx-drm驱动内核会创建/dev/dri/card0设备节点modesetting驱动通常能自动识别。不过为了确保万无一失你可以创建一个简单的配置来指定使用modesetting驱动并关联正确的输出# /etc/X11/xorg.conf.d/99-xlnx-drm.conf Section Device Identifier Xilinx DRM Driver modesetting Option PageFlip true # 如果需要可以指定BusID通过lspci或查看/sys/class/drm获取 # BusID PCI:0:0:0 EndSection4.2 自动启动图形界面如果你希望系统启动后自动进入图形登录管理器如LightDM或直接启动某个窗口管理器如Matchbox需要配置好对应的服务。使用LightDM确保安装了lightdm和lightdm-gtk-greeter并启用服务systemctl enable lightdm.service直接启动X Session可以编辑用户家目录下的.xinitrc文件指定要启动的窗口管理器如xfce4-session,openbox等然后通过startx命令启动。若要开机自动执行可以将其添加到~/.bash_profile或由系统服务调用。一个关键的细节在通过脚本或服务启动X之前必须正确设置DISPLAY环境变量。通常X Server会运行在:0显示端口。同时为了允许从非控制台启动的客户端比如通过SSH登录后启动的GUI程序连接可能需要运行xhost 注意这降低了安全性仅用于调试。export DISPLAY:0.0 xhost local: # 然后启动你的窗口管理器或桌面环境4.3 调试与问题排查当屏幕没亮起来时按顺序排查硬件信号用示波器或逻辑分析仪检查HDMI的TMDS差分对和DDC线上是否有信号活动。这是最根本的一步。内核日志dmesg是最好朋友。关注以下关键词pca954x看复用器驱动是否成功探测。xlnx-drm或digilent看显示驱动是否加载是否报告错误。EDID看是否成功读取。[drm]DRM子系统的通用信息。I2C工具使用i2cdetect工具扫描总线确认PCA9548设备地址0x70和HDMI EDID设备在子总线的地址0x50是否都能被看到。# 扫描I2C0总线查看PCA9548 i2cdetect -y 0 # 扫描新出现的子总线假设是i2c-1 i2cdetect -y 1DRM调试信息可以尝试在启动内核时添加drm.debug0x0e参数来打开更详细的DRM内核日志。整个调试过程就像侦探破案设备树是蓝图内核日志是线索硬件信号是物证。耐心地沿着数据流的方向从FPGA输出到PHY芯片再到I2C总线拓扑最后到驱动加载和用户空间配置每一步都确认无误那块漆黑的屏幕终将被点亮。