基于正点原子ZYNQ7020的RTSP摄像头视频流获取

news/2024/10/3 10:45:11

硬件描述

硬件需求

  • ZYNQ7020 系列
  • LCD 显示屏 800*600
    img
  • rtsp 网口摄像头

接线方式

img

  • USB_UART 通过 type-c 线连接电脑,电脑需要安装 CH340 的串口驱动
  • ps 端网口和摄像头的网口连接
  • 开发板和摄像头的电源线连接
  • LCD 显示屏的排线连接 pl 端排线接口
  • SD 卡包含镜像等必须文件

开发环境需求

参考文档 3_【正点原子】启明星 ZYNQ 之嵌入式 Linux 开发指南_V3.2
参考文献 https://wdjxrrf0as.feishu.cn/docx/DNAUd22pWoPSrbxUbTMcwKk6n9e?from=from_copylink

  • Ubuntu18 的虚拟机,见文档第一章
    • 第二章是 linux 基本操作,可以了解
  • MobaXterm,tftp,ssh,见第四章
  • Petalinux2020.2,见第五章
    • 5.5 的 vitis 非必须
  • Petalinux 离线编译依赖,见 6.3
  • Vivado2020.2

参考资料

链接:https://pan.baidu.com/s/1Tj_UvPEQUGVyvZW5Sf7CiA?pwd=xoec
提取码:xoec

参考依赖库

  • 交叉编译完毕的 ffmpeg 和 opencv 库

  • 使用时需要将 ffmpeg 中的 tmp/lib 下的文件放到开发板的 linux 系统的/usr/local/lib 和/usr/lib 下

  • 使用时需要将 opencv 中的 install_arm/lib 下的文件放到开发板的 linux 系统的/usr/local/lib 和/usr/lib 下

    • 上述操作使用 scp 命令,可以参考下面的

      scp lib/* root@192.168.110.50:/usr/lib/
      scp lib/* root@192.168.110.50:/usr/local/lib/
      

系统制作

目标实现 PS 端读取 RTSP 摄像头,发送到 PL 端的 LCD 显示屏

Vivado 配置

参考例程

  • 参照例程《 3_【正点原子】启明星 ZYNQ 之嵌入式 Linux 开发指南_V3.2 》的 LCD 驱动实验,使用其 Vivado 工程进行配置
    • 该例程配置了 LCD 的显示驱动,配置 AXI-Stream 相关 IP 核等,但是没有配置网口相关内容
  • 参照例程《 2_【正点原子】启明星 ZYNQ 之嵌入式 Vitis 开发指南 v1_2 》的 lwip 的 echo server 实验,参考其配置网口相关芯片

具体流程

  • 复制并打开 LCD 的 Vivado 工程,将其放在英文路径,项目路径在 【新资料-Vivado_2020.2-持续更新中】正点原子启明星(V2)ZYNQ开发板资料盘(A盘)\4_Source_Code\3_Embedded_Linux\Linux驱动例程\25_lcd_disp\vivado_prj\Navigator_7020,将其另存为一个新的项目
    img
    img
  • 在 Flow Navigator 中,点击 IP INTEGRATOR 下的 Open Block Design,双击 ZYNQ 的组件
    img
  • 配置网口引脚,并点击 OK,随后按 Crtl+S 保存修改,可以看到 Console 输出提示
    img
    img
  • 右击 Source 下的后缀为 bd 的文件,生成 HDL 文件
    img
    所有的选项都是默认的,直接点下一步
  • 生成 bitstream,会提示没有 Synthesis,直接下一步,会自动执行 Run Synthesis 和 Run Implementation
    img
  • 导出硬件描述文件
    img
    img
    img
    导出的 xsa 硬件描述文件,大小应该差不多是这个大小,如果小于他可能是没有包含 bit 流文件,会导致后续生成的镜像少文件,附上 xsa 文件

Petalinux 镜像制作

参考例程

  • 参照例程《 3_【正点原子】启明星 ZYNQ 之嵌入式 Linux 开发指南_V3.2 》的 LCD 驱动实验,Linux 网络设备驱动实验和搭建驱动开发使用的 ZYNQ 镜像

编译 Petalinux

  • 参考 20.1,创建 Petalinux 工程,创建完毕之后先不执行 20.1.1,先根据 6.3 配置 petalinux 离线编译的 yocto 等,随后根据教程一直执行到 20.2,20.3 暂时不执行

  • 创建工程,导入硬件描述文件

    petalinux-create -t project --template zynq -n ALIENTEK-ZYNQ-camera
    cd ALIENTEK-ZYNQ-camera/
    petalinux-config --get-hw-description ../xsa_7020/camera/
    

    img

    • 其中,我的硬件描述文件和工程目录的相对位置为

      img

      • 执行 petalinux-config 时是在 ALIENTEK-ZYNQ-camera 下,../的意思是当前目录的上级目录,上级目录中有 xsa_7020,在../xsa_7020/camera/下存放了 xsa 后缀的文件
  • 配置串口和 sd 卡启动

    • 进入“Subsystem AUTO Hardware Settings ---> Serial Settings”选项,将“FSBL”和“DTG”两项使用的串口改成“uart_0”
      img
  • 按四次“ESC”键返回顶层配置界面,进入“Image Packaging Configuration---> Root filesystem type (INITRD)”配置选项,将其更改为“EXT4(SD/eMMC/SATA/USB)”

    img

  • 配置“Yocto Settings --->Local sstate feeds settings--->local sstate feeds url”,取消“Enable Network sstate feeds”使能,此处参考 6.3,自行解压文件,配置路径

    img
    img

  • 配置“Yocto Settings --->Add pre-mirror url”,删除原来的内容,添加 downloads 包文件路径,添加格式为“file://<路径>”,使能“Enable BB NO NETWORK”,此处参考 6.3,自行解压文件,配置路径

    img
    img

  • 进入 Petalinux 工程,编辑工程下的 project-spec/meta-user/conf/petalinuxbsp.conf 文件,在文件末尾添加如下内容

     PREMIRRORS_prepend = " \git://.*/.* file:///opt/tmp/downloads \n \ gitsm://.*/.* file:///opt/tmp/downloads \n \ ftp://.*/.* file:///opt/tmp/downloads \n \ http://.*/.* file:///opt/tmp/downloads \n \ https://.*/.* file:///opt/tmp/downloads \n"
    

    img

    file:///opt/tmp/downloads 要和上一步配的一致

  • 生成 BOOT.BIN,在项目根目录下执行

     petalinux-build -c bootloader
    

    img

     petalinux-build -c u-boot
    

    img

     petalinux-package --boot --fsbl --u-boot --dtb no --force
    

    img

  • 生成 boot.scr,位置在 images/linux

    petalinux-build
    

    img

    • 参照 20.1.2,修改 boot.scr:

    img

    # Generate boot.scr:
    # mkimage -c none -A arm -T script -d boot.cmd.default boot.scr
    #
    ################for boot_target in ${boot_targets};
    doif test "${boot_target}" = "jtag" ; thenbootm 0x00200000 0x04000000 0x00100000exit;fiif test "${boot_target}" = "mmc0" || test "${boot_target}" = "mmc1" ; thenif test -e ${devtype} ${devnum}:${distro_bootpart} /image.ub; thenfatload ${devtype} ${devnum}:${distro_bootpart} 0x10000000 image.ub;bootm 0x10000000;exit;fiif test -e ${devtype} ${devnum}:${distro_bootpart} /zImage; thenfatload ${devtype} ${devnum}:${distro_bootpart} 0x00200000 zImage;;fiif test -e ${devtype} ${devnum}:${distro_bootpart} /system.dtb; thenfatload ${devtype} ${devnum}:${distro_bootpart} 0x00100000 system.dtb;fiif test -e ${devtype} ${devnum}:${distro_bootpart} /rootfs.cpio.gz.u-boot; thenfatload ${devtype} ${devnum}:${distro_bootpart} 0x04000000 rootfs.cpio.gz.u-boot;bootm 0x00200000 0x04000000 0x00100000exit;fiif test -e ${devtype} ${devnum}:${distro_bootpart} /system.bit; then fatload ${devtype} ${devnum}:${distro_bootpart} 0x00800000 system.bit; fpga loadb 0 ${fileaddr} ${filesize} fibootz 0x00200000 - 0x00100000exit;fiif test "${boot_target}" = "xspi0" || test "${boot_target}" = "qspi" || test "${boot_target}" = "qspi0"; thensf probe 0 0 0;if test "image.ub" = "image.ub"; thensf read 0x10000000 0x1000000 0xF00000;bootm 0x10000000;exit;fiif test "image.ub" = "uImage"; thensf read 0x00200000 0x1000000 0x500000;sf read 0x04000000 0x1580000 0xA00000bootm 0x00200000 0x04000000 0x00100000exit;fiexit;fiif test "${boot_target}" = "nand" || test "${boot_target}" = "nand0"; thennand infoif test "image.ub" = "image.ub"; thennand read 0x10000000 0x1000000 0x6400000;bootm 0x10000000;exit;fiif test "image.ub" = "uImage"; thennand read 0x00200000 0x1000000 0x3200000;nand read 0x04000000 0x4600000  0x3200000;bootm 0x00200000 0x04000000 0x00100000exit;fifi
    done
    
    • 使用 mkimage 生成 boot.scr,但是会提示没有这个工具

      mkimage -c none -A arm -T script -d boot.cmd.default boot.scr
      

    img

    • mkimage 可以先全局搜索,这个会在编译过程中生成,如下所示,找到和该路径相似的路径,将内容拷贝到/usr/bin 下,回到之前的目录再执行即可

      sudo find / -name "*mkimage*"
      

    img

    cd /home/workspace/ALIENTEK-ZYNQ-camera/build/tmp/work/zynq_generic-xilinx-linux-gnueabi/linux-xlnx/5.4+gitAUTOINC+62ea514294-r0/recipe-sysroot-native/usr/bin/sudo cp mkimage uboot-mkimage /usr/bin/cd /home/workspace/ALIENTEK-ZYNQ-camera/images/linux/mkimage -c none -A arm -T script -d boot.cmd.default boot.scr
    

    img

    img

  • 编译完成后会在 components/plnx_workspace/device-tree/device-tree/ 下生成设备树文件,设备树文件会参与接下来的内核编译,设备树文件都是根据 xsa 硬件描述文件生成的,我们只需要进行修改即可

    img

    • system-conf.dtsi:这是一个包含特定于系统配置的定义和指令的设备树包含文件(. dtsi) 。它通常由 PetaLinux 工具根据项目配置自动生成。这个文件可能包括对其他 .dtsi 文件的引用,系统级别的配置,以及特定于板卡的设置

    • zynq-7000.dtsi:这个文件定义了 Zynq-7000 SoC 的核心硬件资源和配置。它是一个通用的设备树包含文件,用于描述 Zynq-7000 SoC 的基本硬件结构,包括处理器、内存、中断控制器等。这个文件通常是不需要修改的,因为它描述的是 SoC 的固定硬件特性。

    • system-top.dts:这是主设备树源文件,它包括(通过 #include 指令)system-conf.dtsi 和其他必要的 .dtsi 文件。system-top.dts 是描述整个系统硬件配置的顶层文件,它最终被编译成 .dtb 文件,操作系统在启动时使用这个 .dtb 文件来了解硬件配置。可以看到是 dts 引入了其他的设备树文件才能正确编译出 dtb 文件

      /** CAUTION: This file is automatically generated by Xilinx.* Version:  * Today is: Wed Jun 12 03:11:33 2024*//dts-v1/;
      #include "zynq-7000.dtsi"
      #include "pl.dtsi"
      #include "pcw.dtsi"
      / {chosen {bootargs = "earlycon";stdout-path = "serial0:115200n8";};aliases {ethernet0 = &gem0;i2c0 = &i2c1;serial0 = &uart0;spi0 = &qspi;};memory {device_type = "memory";reg = <0x0 0x40000000>;};
      };
      #include "system-user.dtsi"
      
    • pl.dtsi:这个文件包含了与 PL(Programmable Logic,可编程逻辑)部分相关的设备树定义。在使用 Zynq-7000 SoC 的系统中,PL 部分是 FPGA,pl.dtsi 通常描述了在 FPGA 中实现的自定义硬件模块(例如,自定义 IP 核)的配置和接口。

    • pcw.dtsi:这个文件包含了处理器配置向导(Processor Configuration Wizard,PCW)生成的配置信息。它描述了处理器子系统(PS)的配置,包括时钟、外设和接口的设置。这个文件是由 Xilinx 工具根据你的硬件设计自动生成的。

编译根文件系统

  • 回到项目根目录下,配置根文件系统,这里不做修改直接退出

    petalinux-config -c rootfs
    
  • 编译根文件系统,生成 rootfs.tar.gz

    petalinux-build -c rootfs
    

    img

内核编译

参考例程

  • 参照例程《 3_【正点原子】启明星 ZYNQ 之嵌入式 Linux 开发指南_V3.2 》的 LCD 驱动实验,Linux 网络设备驱动实验和搭建驱动开发使用的 ZYNQ 镜像
  • 使用镜像为官方的 2020.2 的内核源码开发板资料盘(A 盘)\4_SourceCode\3_Embedded_Linux\资源文件\Kernel\ linux-xlnx-xlnx_rebase_v5.4_2020.2.tar.gz

添加设备树文件

  • 参照 20.3.1 将设备树文件复制到指定目录

    cd /home/workspace/ALIENTEK-ZYNQ-camera/components/plnx_workspace/device-tree/device-tree/
    cp pcw.dtsi pl.dtsi zynq-7000.dtsi system-conf.dtsi system-top.dts /home/workspace/kernel/kernel-ethnet-camera/arch/arm/boot/dts/
    cd /home/workspace/ALIENTEK-ZYNQ-camera/project-spec/meta-user/recipes-bsp/device-tree/files/
    cp system-user.dtsi /home/workspace/kernel/kernel-ethnet-camera/arch/arm/boot/dts/
    
  • 修改 system-user.dtsi,修改 system-conf.dtsi

    • system-user.dtsi 如下所示

      ///include/ "system-conf.dtsi" 
      #include <dt-bindings/gpio/gpio.h> 
      #include <dt-bindings/input/input.h> 
      #include <dt-bindings/media/xilinx-vip.h> 
      #include <dt-bindings/phy/phy.h> / { model = "Alientek Navigator Zynq Development Board"; compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000"; chosen { bootargs = "console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait"; stdout-path = "serial0:115200n8"; }; }; &uart0 { u-boot,dm-pre-reloc; status = "okay"; 
      }; &sdhci0 { u-boot,dm-pre-reloc;status = "okay"; 
      }; &gem0 { local-mac-address = [00 0a 35 00 8b 87]; phy-handle = <&ethernet_phy>; ethernet_phy: ethernet-phy@7 {  /* yt8521 */ reg = <0x7>; device_type = "ethernet-phy"; }; 
      };
      
    • 虽然这里注释掉了 system-conf.dtsi,理论上不会引用这个文件,但是保险起见还是修改一下,主要修改网口的内容,这个文件里的 gem0 的值是错的,参考上述的内容

      img
      img

    • 设备树的文件节点可以理解为追加和覆盖,在 pl.dtsi 和 pcw.dtsi 以及 zynq-7000.dtsi 中都定义了一些设备树的节点,以网口为例,后追加的信息,会覆盖前面已经有的信息,比如 gem0 的 local-mac-address 属性,后写入的覆盖之前的

  • 修改 arch/arm/boot/dts 目录下 Makefile 文件,将添加后的设备树顶层文件放到 Makefile 中

    img

    • vim 编辑器支持查找功能便于快速定位这一行

添加 LCD 显示驱动

  • 参照 49.5.1 创建 LCD 节点的第五点,前面的可以不用看,修改内核目录下的 arch/arm/boot/dts/system-user.dtsi,这里直接给完整的内容,内容包含前置的修改的 system-user.dtsi

    #include <dt-bindings/gpio/gpio.h>
    #include <dt-bindings/input/input.h>
    #include <dt-bindings/media/xilinx-vip.h>
    #include <dt-bindings/phy/phy.h>/ {model = "Alientek Zynq Development Board";compatible = "xlnx,zynq-zc702", "xlnx,zynq-7000";        chosen {bootargs = "console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait";stdout-path = "serial0:115200n8";};#if 0backlight_lcd: backlight {compatible = "pwm-backlight";pwms = <&pwm_0 0 5000000>;brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>;default-brightness-level = <10>;};
    #endif
    };&uart0 {u-boot,dm-pre-reloc;status = "okay";
    };&sdhci0 {u-boot,dm-pre-reloc;status = "okay";
    };&gem0 {local-mac-address = [00 0a 35 00 8b 87];phy-handle = <&ethernet_phy>;ethernet_phy: ethernet-phy@7 {  /* yt8521 */reg = <0x7>;device_type = "ethernet-phy";};
    };&qspi {u-boot,dm-pre-reloc;flash@0 { /* 16 MB */compatible = "w25q256", "jedec,spi-nor";reg = <0x0>;spi-max-frequency = <50000000>;#address-cells = <1>;#size-cells = <1>;partition@0x000000 {label = "boot";reg = <0x000000 0x600000>;};partition@0x600000 {label = "bootenv";reg = <0x600000 0x020000>;};partition@0x620000 {label = "image.ub";reg = <0x620000 0x700000>;};partition@0xD20000 {label = "boot.scr";reg = <0xD20000 0x020000>;};partition@0xD40000 {label = "space";reg = <0xD40000 0x000000>;};};
    };&pwm_0 {compatible = "digilent,axi-pwm";#pwm-cells = <2>;clock-names = "pwm";npwm = <1>;
    };/* RGB LCD */
    &lcd_vtc {compatible = "xlnx,v-tc-5.01.a";
    };&lcd_vdma {dma-ranges = <0x00000000 0x00000000 0x40000000>;        // 1GB
    };&amba_pl {xlnx_vdmafb_lcd {compatible = "xilinx,vdmafb";status = "okay";vtc = <&lcd_vtc>;clocks = <&clk_wiz_0 0>;clock-names = "lcd_pclk";dmas = <&lcd_vdma 0>;dma-names = "lcd_vdma";pwms = <&pwm_0 0 5000000>;reset-gpio = <&gpio0 54 GPIO_ACTIVE_LOW>;lcdID-gpio = <&gpio1 0 0 GPIO_ACTIVE_LOW &gpio1 1 0 GPIO_ACTIVE_LOW &gpio1 2 0 GPIO_ACTIVE_LOW>;display-timings {timing_4342: timing0 {clock-frequency = <9000000>;hactive = <480>;vactive = <272>;hback-porch = <40>;hfront-porch = <5>;hsync-len = <1>;vback-porch = <8>;vfront-porch = <8>;vsync-len = <1>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <0>;};timing_4384: timing1 {clock-frequency = <31000000>;hactive = <800>;vactive = <480>;hback-porch = <88>;hfront-porch = <40>;hsync-len = <48>;vback-porch = <32>;vfront-porch = <13>;vsync-len = <3>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <0>;};timing_7084: timing2 {clock-frequency = <40330000>;hactive = <800>;vactive = <480>;hback-porch = <88>;hfront-porch = <40>;hsync-len = <128>;vback-porch = <33>;vfront-porch = <10>;vsync-len = <2>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <0>;};timing_7016: timing3 {clock-frequency = <51200000>;hactive = <1024>;vactive = <600>;hback-porch = <140>;hfront-porch = <160>;hsync-len = <20>;vback-porch = <20>;vfront-porch = <12>;vsync-len = <3>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <0>;};timing_1018: timing4 {clock-frequency = <71100000>;hactive = <1280>;vactive = <800>;hback-porch = <80>;hfront-porch = <70>;hsync-len = <10>;vback-porch = <10>;vfront-porch = <10>;vsync-len = <3>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <0>;};};};};
    
  • 参照 49.6.1 添加 LCD 驱动程序到内核,上传 atk_vdmafb.c,xilinx_drm_drv.h、xilinx_vtc.c 和 xilinx_vtc.h 到 drivers/gpu/drm/xlnx

    cd drivers/gpu/drm/xlnx/
    

    img

  • 打开 drivers/gpu/drm/xlnx 目录下的 Makefile 文件,在文件末尾添加如下内容:

    obj-$(CONFIG_FB_XILINX_VDMA) += xilinx_vtc.o
    obj-$(CONFIG_FB_XILINX_VDMA) += atk_vdmafb.o
    
  • 修改 Kconfig 文件,打开 drivers/gpu/drm/xlnx 目录下的 Kconfig 文件,在文件末尾添加如下内容:

    config FB_XILINX_VDMA tristate "Xilinx LCD Framebuffer driver support By Alientek" depends on FB && ARCH_ZYNQ && XILINX_DMA select FB_SYS_FILLRECT select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT select FB_MODE_HELPERS select VIDEOMODE_HELPERS help LCD framebuffer driver based on vdma,vtc,dyclk IP core.
    

添加 PWM 驱动

  • 将 pwm 的驱动文件拷贝到 drivers/pwm

    img

  • 打开 drivers/pwm 目录下的 Makefile 文件,在文件末尾添加如下内容

    obj-$(CONFIG_PWM_DGLNT)  += pwm-dglnt.o
    
  • 打开 drivers/pwm 目录下的 Kconfig 文件,在文件末尾前添加如下内容,注意相对位置

    config PWM_DGLNT tristate "Digilent AXI PWM driver support" depends on ARCH_ZYNQ || ARCH_ZYNQMP help Simple Driver for Digilent AXI_PWM IP Core. To compile this driver as a module, choose M here: the module will be called pwm-dglnt.c
    

    img

开始编译

  • 配置 defconf,可能会出错

    make xilinx_zynq_defconfig
    

    img

    • 参考第十章,10.6 及以前的配置,配置好交叉编译环境,其中后面的路径是你交叉编译环境的,配置完成后需要重启

      echo "alias esdk='source /opt/pkg/petalinux/2020.2/environment-setup-cortexa9t2hf-neon-xilinx-linux-gnueabi'" >> ~/.bashrc
      
    • 先运行 esdk,然后在执行就可以了

    img

  • 配置内核,先参照 49.6.3

    make menuconfig
    
    • Device Drivers ---> Graphics support ---> <*> Xilinx LCD Framebuffer driver support By Alientek

    img

    • Device Drivers ---> Common Clock Framework ---> <*> Xilinx Clocking Wizard

    img

    • Device Drivers --->[] Pulse-Width Modulation (PWM) Support ---> <> Digilent AXI PWM driver support

    img

    img

  • 再参照 54.4.1 打开 Device Drivers ---> [ * ]Network device support ---> -- PHY Device support and infrastructure ---> <> Xilinx GMII2RGMII converter driver

    img

  • 至此配置完成,按 esc 退出,退出时记得选 yes 保存配置

    img

  • 启动编译

    make -j8
    make dtbs
    
    • 内核编译完成后会在 arch/arm/boot 目录下生成内核镜像文件 zImage,在 arch/arm/boot/dts 目录下生成设备树镜像文件 system-top.dtb

    • 命令“make dtbs”将。dts 编译成。dtb,编译完成后就可以使用新的设备树镜像文件,放到 sd 卡时需要将 system-top.dtb 修改为 system.dtb

    img

制作启动 SD 卡

参考例程

  • 参照例程《 3_【正点原子】启明星 ZYNQ 之嵌入式 Linux 开发指南_V3.2 》的 Petalinux 设计流程实战,其中 6.2.10 制作 SD 启动卡

SD 卡分区配置

  • 将 SD 卡插入 Ubuntu 系统,查看设备上挂载的存储设备和分区信息

    sudo fdisk -l
    

    img

    • 可以根据 sd 卡的容量判断设备 sda1 就是刚刚插入 ubuntu 系统的 sd 卡
  • 查看当前文件目录的挂载情况

    df -h
    

    img

    • 现在 SD 卡还没有分区还没有挂载所以看不见
  • 使用 fdisk 工具来对指定的设备进行分区操作(如果你前面显示的 sd 卡是 sda1,下面试试使用 sda,不然可能会报错)

    sudo fdisk /dev/sda
    

    img

    • 先将之前的分区删除,输入 d

      img

    • 按 n 新建分区,输入 p 创建主分区并回车,输入 1 使用分区号 1 并回车,回车使用第一个扇区 2048,输入 +100 使第一个分区大小 100M

      img

    • 设置分区类型,先按 t 再按 c

      img

    • 设置为引导分区,输入 a 回车

      img

    • 按 n 创建第二个分区,一路回车即可

      img

    • 输入 p 检查分区情况,输入 w 写入 sd 卡

      img

    • 格式化 sd 卡的分区

      sudo mkfs.vfat -F 32 -n boot /dev/sda1
      sudo mkfs.ext4 -L rootfs /dev/sda2
      

      img

制作 SD 卡

  • 我们需要的文件包含启动文件 BOOT.BIN,boot.scr,system.bit,zImage,system.dtb,需要放在第一个分区,rootfs.tar.gz 是根文件系统放在第二个分区

  • 挂载刚刚创建的 sd 卡分区

    • 先在/mnt 创建文件夹用于挂载分区,然后再使用 mount 挂载

      sudo mkdir -p /mnt/sda1
      sudo mkdir -p /mnt/sda2
      
      sudo mount /dev/sda1 /mnt/sda1
      sudo mount /dev/sda2 /mnt/sda2
      
    • 使用 df 命令可以查看分区是否挂载成功

      img

  • 拷贝文件到 SD 卡的分区中,system.bit、BOOT.BIN 和 boot.scr 在上文 4.2.2 中编译的 Petalinux 的 image/linux 中;zImage 和 system-top.dtb 在上文 4.3.5 内核编译的内核源码路径,arch/arm/boot 目录下生成内核镜像文件 zImage,在 arch/arm/boot/dts 目录下生成设备树镜像文件 system-top.dtb

    cd /home/workspace/ALIENTEK-ZYNQ-camera/images/linux/
    sudo cp BOOT.BIN boot.scr system.bit /mnt/sda1/
    

    img

    cd /home/workspace/kernel/kernel-ethnet-camera/arch/arm/boot/
    sudo cp zImage dts/system-top.dtb /mnt/sda1/
    

    img

    sudo mv /mnt/sda1/system-top.dtb /mnt/sda1/system.dtb
    

    img

  • 将根文件系统解压到 SD 卡的第二个分区,rootfs.tar.gz 也在项目目录下的

    cd /home/workspace/ALIENTEK-ZYNQ-camera/images/linux/
    sudo tar -xzf rootfs.tar.gz -C /mnt/sda2/
    

    img

  • 至此,sd 卡制作成功,卸载 SD 卡准备启动

    sudo umount /dev/sda*
    

验证是否成功

  • 开发板的拨码开关选为 OFF,为 SD 卡启动模式,然后按照前文 1.2 连接线,LCD 显示了字符,进入之后网口设置 ip 和网段可以 ping 视为成功
    img
  • 此处附本次实验的编译成功的文件

PS 端程序编写

开发环境配置

  • 基本的 x86 架构的 C++编译环境配置:ubuntu 下安装 C/C++ 开发环境_ubuntu 安装 c++开发环境-CSDN 博客

  • 配置 arm 的编译环境

    sudo apt-get update
    sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
    
    • 和博客中相比只需要修改一下 gcc 和 g++的路径即可,默认安装都是在/usr/bin 下

      img

    • 注意每一个 toolchain 都要有一个对应的 cmake 配置项,切换编译环境时要将对应的项上移

      img

    • 编译成功会提示你的编译结果在哪里

      img
      img

  • 本项目的代码依赖于 opencv 模块,需要有交叉编译版本的 opencv

    • OpenCv 简易编译,官网下载 SourceCode,在目标目录下解压,进入目录后创建 build 和 install

      cd /home/workspace/opencv-4.6.0
      mkdir install_arm
      mkdir build_arm && cd build_arm
      
    • 使用 cmake 并指定工具链文件来配置 OpenCV

      cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/linux/arm-gnueabi.toolchain.cmake \-DCMAKE_INSTALL_PREFIX=/home/workspace/opencv-4.6.0/install_arm \-D WITH_FFMPEG=ON \-D FFMPEG_INCLUDE_DIR=/home/workspace/ffmpeg-4.4.4/tmp/include \-D FFMPEG_LIB_DIR=/home/workspace/ffmpeg-4.4.4/tmp/lib \-D CMAKE_BUILD_TYPE=RELEASE \-D BUILD_EXAMPLES=OFF \.. 
      
    • 编译 opencv

      make -j4 && make install
      
    • CMakeList 配置

      cmake_minimum_required(VERSION 3.3)
      project(Camera)set(CMAKE_CXX_STANDARD 14)
      set(OpenCV_DIR "/home/workspace/opencv-4.6.0/install_arm/lib/cmake/opencv4")
      find_package(OpenCV REQUIRED)
      # 添加 OpenCV 的头文件路径
      include_directories(${OpenCV_INCLUDE_DIRS})# 添加库文件搜索路径
      link_directories(${OpenCV_LIBRARY_DIRS})add_executable(Camera main.cpp)# 链接OpenCV库到您的项目
      target_link_libraries(Camera ${OpenCV_LIBS})
      
  • 上述的交叉编译能够编译出二进制文件,但是无法在 petalinux 运行,原因是缺少 FFMPEG 库,可以参考OpenCv+FFmpeg交叉编译,编译出 3.3 的参考依赖库然后移植

摄像头源码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <unistd.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>static void display_demo_1 (unsigned char *frame, unsigned int width, unsigned int height, unsigned int stride)
{unsigned int xcoi, ycoi;unsigned char wRed, wBlue, wGreen;unsigned int iPixelAddr = 0;for(ycoi = 0; ycoi < height; ycoi++) {for(xcoi = 0; xcoi < (width * 3); xcoi += 3) {if (((xcoi / 4) & 0x20) ^ (ycoi & 0x20)) {wRed = 255;wGreen = 255;wBlue = 255;} else {wRed = 0;wGreen = 0;wBlue = 0;}frame[xcoi + iPixelAddr + 0] = wRed;frame[xcoi + iPixelAddr + 1] = wGreen;frame[xcoi + iPixelAddr + 2] = wBlue;}iPixelAddr += stride;}
}static void display_demo_2 (unsigned char *frame, unsigned int width, unsigned int height, unsigned int stride)
{unsigned int xcoi, ycoi;unsigned int iPixelAddr = 0;unsigned char wRed, wBlue, wGreen;unsigned int xInt;xInt = width * 3 / 8;for(ycoi = 0; ycoi < height; ycoi++) {for(xcoi = 0; xcoi < (width*3); xcoi+=3) {if (xcoi < xInt) {                                   //White colorwRed = 255;wGreen = 255;wBlue = 255;}else if ((xcoi >= xInt) && (xcoi < xInt*2)) {         //YELLOW colorwRed = 255;wGreen = 255;wBlue = 0;}else if ((xcoi >= xInt * 2) && (xcoi < xInt * 3)) {        //CYAN colorwRed = 0;wGreen = 255;wBlue = 255;}else if ((xcoi >= xInt * 3) && (xcoi < xInt * 4)) {        //GREEN colorwRed = 0;wGreen = 255;wBlue = 0;}else if ((xcoi >= xInt * 4) && (xcoi < xInt * 5)) {        //MAGENTA colorwRed = 255;wGreen = 0;wBlue = 255;}else if ((xcoi >= xInt * 5) && (xcoi < xInt * 6)) {        //RED colorwRed = 255;wGreen = 0;wBlue = 0;}else if ((xcoi >= xInt * 6) && (xcoi < xInt * 7)) {        //BLUE colorwRed = 0;wGreen = 0;wBlue = 255;}else {                                                //BLACK colorwRed = 0;wGreen = 0;wBlue = 0;}frame[xcoi+iPixelAddr + 0] = wRed;frame[xcoi+iPixelAddr + 1] = wGreen;frame[xcoi+iPixelAddr + 2] = wBlue;}iPixelAddr += stride;}
}
static void write_frame_to_lcd(unsigned char* frameBuffer, const cv::Mat& frame, unsigned int lcdWidth, unsigned int lcdHeight, unsigned int lcdStride) {// 缩放图像以适应 LCD 尺寸cv::Mat resizedFrame;cv::resize(frame, resizedFrame, cv::Size(lcdWidth, lcdHeight));unsigned int xcoi, ycoi;unsigned int iPixelAddr = 0;for (ycoi = 0; ycoi < lcdHeight; ycoi++) {for (xcoi = 0; xcoi < lcdWidth; xcoi++) {// 获取缩放后的图像中的像素值cv::Vec3b pixel = resizedFrame.at<cv::Vec3b>(ycoi, xcoi);// RGB 值unsigned char wRed = pixel[2];unsigned char wGreen = pixel[1];unsigned char wBlue = pixel[0];// 写入 LCD 帧缓冲区frameBuffer[iPixelAddr + (xcoi * 3) + 0] = wRed;frameBuffer[iPixelAddr + (xcoi * 3) + 1] = wGreen;frameBuffer[iPixelAddr + (xcoi * 3) + 2] = wBlue;}iPixelAddr += lcdStride;}
}int main(int argc, char **argv) {struct fb_var_screeninfo fb_var = {0};struct fb_fix_screeninfo fb_fix = {0};unsigned int screensize;unsigned char *base;int fd;/* 打开LCD */fd = open("/dev/fb0", O_RDWR);if (fd < 0) {printf("Error: Failed to open /dev/fb0 device.\n");return fd;}/* 获取framebuffer设备的参数信息 */ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);/* mmap映射 */screensize = fb_var.yres * fb_fix.line_length;base = (unsigned char *)mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if ((unsigned char *)-1 == base) {close(fd);return -1;}memset(base, 0x00, screensize);       // 显存清零cv::VideoCapture capture("rtsp://192.168.110.13:554/bk_livestream/1");if (!capture.isOpened()) {std::cerr << "ERROR! Unable to open URL\n";return -1;}cv::Mat frame;cv::Mat frameRGB;cv::VideoWriter writer;int frame_width = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_WIDTH));int frame_height = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_HEIGHT));//下面的代码用于写入视频文件//writer.open("/home/root/output.avi", cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 10,cv::Size(frame_width, frame_height));/*if (!writer.isOpened()) {std::cerr << "Could not open the output video file for write\n";return -1;}*/int frameNumber = 0;while (true) {//LCD的显示例程//display_demo_1(base, fb_var.xres, fb_var.yres, fb_fix.line_length);if (!capture.read(frame)) {std::cerr << "No frame\n";break;}if (!frame.empty()) {std::cout << "Frame " << ++frameNumber;std::cout << " - Size: " << frame.size();std::cout << " - Type: " << frame.type();std::cout << std::endl;//写入视频文件的代码//writer.write(frame); // Write the frame without checking return value} else {std::cerr << "Empty frame\n";}if (frameNumber == 100*3){break;}cv::cvtColor(frame, frameRGB, cv::COLOR_YUV2RGB);// 再次检查转换后的图像是否为空if (frameRGB.empty()) {std::cout << "Conversion failed: Resulting frame is empty." << std::endl;return -1;}write_frame_to_lcd(base, frameRGB, 800, 480, 800 * 3);//LCD的显示例程//display_demo_2(base, fb_var.xres, fb_var.yres, fb_fix.line_length);}capture.release();//writer.release();std::cout << "Video capture and write ended." << std::endl;/* 关闭设备 释放内存 */memset(base, 0x00, screensize);munmap(base, screensize);close(fd);return 0;
}

启动程序

  • RTSP 视频依赖于以太网,因此开发板启动后要配置好 IP,开发板的 IP 要和摄像头在同一个网段

  • 务必保证 3.3 的依赖库按照要求移植到开发板

  • 使用 scp 命令将编译好的二进制程序放到开发板

    scp Camera root@192.168.110.50:/home/root/Camera 
    
  • 然后在开发板的/home/root 目录下启动程序,启动之前修改一下程序权限

    chmod 777 Camera
    ./Camera
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/44622.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

移动平台开发综合实践 安卓android大作业-雷net在线对战

转自我的安卓课程作业 图片应该是没了,懒得搞了目录移动平台开发综合实践 大作业-雷net在线对战一、实验目的二、实验环境三、实验内容与实验要求3.1实验内容:雷net在线对战3.1.1棋盘3.1.2棋子3.1.3终端卡3.2原理分析3.3具体实验要求四、实验过程与分析4.1实验记录4.1.1 Sock…

解析Html Canvas的卓越性能与高效渲染策略

一、什么是Canvas 想必学习前端的同学们对Canvas 都不陌生,它是 HTML5 新增的“画布”元素,可以使用JavaScript来绘制图形。 Canvas元素是在HTML5中新增的标签用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图(bitmap)。Canvas 由…

怎么控制多个存储设备的访问权限?数据安全存储方案来了

数据安全存储是指将数据以安全的方式存储在存储系统中,以确保数据的机密性、完整性和可用性。要控制数据安全存储的权限以保障安全,可以采取以下措施:访问控制列表(ACLs):使用ACLs来定义对存储数据的访问权限。ACLs允许你为每个文件或目录指定哪些用户或组有权访问它们,…

GPT-4o 只是对话式 Al 的冰山一角,背后隐藏了哪些新机会?(内含福利) | 编码人声

「编码人声」是由「RTE开发者社区」策划的一档播客节目,关注行业发展变革、开发者职涯发展、技术突破以及创业创新,由开发者来分享开发者眼中的工作与生活。 听友福利 欢迎在小宇宙播客评论区留言,分享你对 GPT-4o 的看法,或者对最有潜力的对话式 AI 场景的预测。我们将抽出…

一个开源的快速准确地将 PDF 转换为 markdown工具

大家好,今天给大家分享的是一个开源的快速准确地将 PDF 转换为 markdown工具。 Marker是一款功能强大的PDF转换工具,它能够将PDF文件快速、准确地转换为Markdown格式。这款工具特别适合处理书籍和科学论文,支持所有语言的转换,并且能够去除页眉、页脚等干扰元素,格式化表格…

简单聊聊Unicode

接下来我们简单讲讲,Unicode 的原理,例如,Unicode 的设计思路,Unicode 和 UTF 的关系等。接下来我们简单讲讲,Unicode 的原理,例如,Unicode 的设计思路,Unicode 和 UTF 的关系等。 ‍ 简单的字符编码模型 一个字符,在计算机中如何存储,是涉及到很多部分的。例如,一个…

有手就行!10分钟上手实现文生图!

Fooocus 一款基于 Gradio 实现的图像生成软件,提供高质量的文本生成图像功能,完全离线、开源、免费。大家好,我是 Java陈序员。 今天,给大家介绍一个文生图工具,无需繁杂的配置,一键安装启动,十分钟就能实现文生图!关注微信公众号:【Java陈序员】,获取开源项目分享、…

盘点常见的内外网文件传输方法 看看哪个最好用!

内外网文件传输在企业里面是很常见的一种业务场景,因为很多企业,包括政府机构、金融、医疗、能源等等行业组织,都会进行网络隔离,来保护内部的数据安全性。常见的内外网文件传输的方法主要包括以下几种: 1、移动介质:通过U盘、移动硬盘等移动存储设备在内外网之间进行文件…