目录
0 - 前言
在前面的文章里(ZYNQ7000 #0 - petalinux的使用与工程建立),我们已经使用 petalinux 成功的配置了一个可以在板子上运行的linux系统环境。这篇文章中我们加上HDMI向外输出图像信号的功能。
我的开发板是黑金的AX7010,其附带的资料对于这方面讲解的不是很清楚,我也是通过翻找其教程中提到的 “使用Debian系统”附带的Debian根文件系统解决的在使用 startx 唤起 X 桌面环境后黑屏的问题。究其主要原因还是 需要对 X 桌面环境进行设置,要求其使用FrameBuffer进行绘制。
AX7010这个开发板上虽然有HDMI-A的母头,但是其并没有板载HDMI芯片,而其对外输出HDMI信号的方式是利用PL端的IO对HDMI信号的时钟和数据进行模拟达到的。这里不过多的介绍关于其如何搭建PL端的方法,而重点关注如何在这样的环境中配置 X 桌面环境利用FrameBuffer进行图像绘制。
这里我们需要准备的是:
- 开发宿主机:ubuntu 16.04.4 LTS
- Petalinux:petalinux 2018.3
- Petalinux配套的Linux源码:linux-xlnx-xlnx_rebase_v4.14_2018.3
- Vivado工程生成的 .sdk 文件:linux_base.sdk (这个工程是黑金的例程)
- Digilent提供的IP
- 视频解码IP:RGB to DVI Video Encoder
- 动态时钟IP:Dynamic Clock Generator
- 视频解码IP RGB to DVI Video Encoder上用到的接口:TMDS
- Digilent提供的视频解码驱动(encoder)和时钟驱动(clk): (从黑金提供的2017版源码里找到的,可以从digilent的github上找到)
连接完后大致的框图如下:
1 - petalinux工程的建立与配置
petalinux工程的建立、从 .sdk 导入硬件工程配置、配置rootfs放置在SD卡 以及 配置使用外部linux源码的方法可以看这篇文章(ZYNQ7000 #0 - petalinux的使用与工程建立)
2 - 将解码驱动配置到linux内核中
如果你使用的是petalinux 2019.1或者更高的版本,因为其linux使用的内核为4.19,其内部有部分函数进行了修改,需要将digilent_encoder中的
drm_mode_connector_update_edid_property(connector, edid);
替换为
drm_connector_update_edid_property(connector, edid);
具体参考 https://forums.gentoo.org/viewtopic-t-1088330-start-0.html
首先按照上述准备工作中的路径将 clk-fixed-factor.c 时钟驱动文件, digilent_encoder.c 视频解码驱动文件放置到Linux源码路径下。
- 配置 clk-dglnt-dynclk
打开 linux-xlnx-xlnx_rebase_v4.14_2018.3/drivers/clk/Kconfig 将 clk-dglnt-dynclk.c 的配置项放入menuconfig中
config COMMON_CLK_DGLNT_DYNCLK
tristate "Digilent axi_dynclk Driver"
depends on ARCH_ZYNQ || MICROBLAZE
help
---help---
Support for the Digilent AXI Dynamic Clock core for Xilinx
FPGAs.
打开 linux-xlnx-xlnx_rebase_v4.14_2018.3/drivers/clk/Makefile 使得 clk-dglnt-dynclk.c 在menuconfig配置好后在linux编译时能编译。
obj-$(CONFIG_COMMON_CLK_DGLNT_DYNCLK) += clk-dglnt-dynclk.o
- 配置 digilent_encoder
打开 linux-xlnx-xlnx_rebase_v4.14_2018.3/drivers/gpu/drm/xilinx/Kconfig,将 digilent_encoder.c 的配置项放入menuconfig中
config DRM_DIGILENT_ENCODER
tristate "Digilent VGA/HDMI DRM Encoder Driver"
depends on DRM_XILINX
help
DRM slave encoder for Video-out on Digilent boards.
打开 linux-xlnx-xlnx_rebase_v4.14_2018.3/drivers/gpu/drm/xilinx/Makefile,使得 digilent_encoder.c 在menuconfig配置好后在linux编译时能编译。
obj-$(CONFIG_DRM_DIGILENT_ENCODER) += digilent_encoder.o
3 - petalinux配置linux内核
运行命令,配置内核
petalinux-config -c kernel
在 Device Drivers -> Graphics support,选择 Digilent VGA/HDMI DRM Encoder Driver 项按 y。
在 Device Drivers → Common Clock Framework 选项中选择 Digilent axi_dynclk Driver 按 y
保存退出
注意:这里不要修改默认的保存文件名 ( .config )
4 - 修改设备树
这里我是直接用的黑金写的设备树。
打开petalinux工程文件夹中 project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi,刚生成petalinux的工程文件夹中,这个dtsi文件是空的,将其修改为以下内容。
/include/ "system-conf.dtsi"
/ {
model = "Zynq ALINX Development Board";
compatible = "alinx,zynq", "xlnx,zynq-7000";
aliases {
ethernet0 = "&gem0";
serial0 = "&uart1";
};
usb_phy0: usb_phy@0 {
compatible = "ulpi-phy";
#phy-cells = <0>;
reg = <0xe0002000 0x1000>;
view-port = <0x0170>;
drv-vbus;
};
};
&i2c0 {
clock-frequency = <100000>;
};
&usb0 {
dr_mode = "host";
usb-phy = <&usb_phy0>;
};
&sdhci0 {
u-boot,dm-pre-reloc;
};
&uart1 {
u-boot,dm-pre-reloc;
};
&flash0 {
compatible = "micron,m25p80", "w25q256", "spi-flash";
};
&gem0 {
phy-handle = <ðernet_phy>;
ethernet_phy: ethernet-phy@1 {
reg = <1>;
device_type = "ethernet-phy";
};
};
&amba_pl {
hdmi_encoder_0:hdmi_encoder {
compatible = "digilent,drm-encoder";
digilent,edid-i2c = <&i2c0>;
};
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 = <&i2c0>;
planes {
xlnx,pixel-format = "rgb888";
plane0 {
dmas = <&axi_vdma_0 0>;
dma-names = "dma";
};
};
};
};
&axi_dynclk_0 {
compatible = "digilent,axi-dynclk";
#clock-cells = <0>;
clocks = <&clkc 15>;
};
&v_tc_0 {
compatible = "xlnx,v-tc-5.01.a";
};
这里面最重要的部分是 amba_pl 下的 hdmi_encoder 和 xilinx_drm部分的设置
5 - 编译打包
用下面命令编译uboot、内核、根文件系统、设备树等
petalinux-build
用下面命令打包生成 BOOT.BIN 文件
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --fpga --u-boot --force
将生成的 BOOT.BIN 及 image.ub 拷贝到 SD 卡的FAT分区,根文件系统已放置到EXT分区
6 - 文件修改
6.1 - 开机启动rc.local并唤起图形桌面环境
在/etc/目录下建立 rc.local 文件,赋予其 +x 属性,然后在rc.local中添加
#!/bin/bash
#ifconfig eth0 up #可选,唤起你的网卡
#dhclient & #可选,自带从dhcp获取ip地址
startx&
exit 0
这样,在linux启动后,会执行/etc/rc.local下的命令,即 startx
startx最终会调用/etc/X11/xinit/xinitrc下的命令
我们在X的配置文件(这里我用的是startx默认最终会选择的/etc/X11/xinit/xinitrc)修改如下
#!/bin/sh
# /etc/X11/xinit/xinitrc
#
# global xinitrc file, used by all X sessions started by xinit (startx)
# invoke global X session script
echo "LEE:xset turen off screensave DPMS"
xset s off #关闭 X Screen Save
xset dpms 0 0 0 #关闭 DPMS
xset -dpms
xhost + #允许任意host连接到x服务
export DISPLAY=:0.0 & /home/sements/developarea/qeyes/qeyes/qeyesArm64 & #DISPLAY变量 并 #后台执行自己的一个小程序
#. /etc/X11/Xsession
openbox-session #唤起 openbox
在Linux/Unix类操作系统上, DISPLAY用来设置将图形显示到何处. 直接登陆图形界面或者登陆命令行界面后使用startx启动图形, DISPLAY环境变量将自动设置为:0:0, 此时可以打开终端, 输出图形程序的名称(比如xclock)来启动程序, 图形将显示在本地窗口上
这里 xinitrc 中部分代码解释可以看这里: https://blog.csdn.net/sements/article/details/88123894
6.2 - 指定X系统所使用的framebuffer设备
为了让 X 界面系统启动时使用设定的framebuffer进行绘制,我们需要在相应配置文件中加入下面的语句:
Section "Device"
Identifier "myfb"
Driver "fbdev"
Option "fbdev" "/dev/fb0"
EndSection
有两种可选的加入位置。
-
第一种在目录(/etc/X11/xorg.conf.d 没有这个目录就创建即可)下已有的 配置文件(如:40-libinput.conf )中加入上面的语句。
-
(推荐)第二种就是在目录(/etc/X11/xorg.conf.d 没有这个目录就创建即可)新建一个 .conf 文件,这里叫它 99-fbdev.conf (前面的数字影响着 X 界面系统启动加载这些配置文件时的顺序),然后在这个文件中加入上面的语句。
X - 附录 问题
在没有正确的配置 X 使用 Framebuffer进行绘制时,使用 startx 开启图形界面时可能出现以下错误
- Failed to launch bus: Failed to connect to session bus
- 唤醒了 X 但是黑屏
- AddScreen/ScreenInit failed for driver 0
- failed to load driver: xilinx_drm