JetsonXavierNX摄像头驱动开发指南
参考手册:NVIDIA Jetson Linux Developer Guide(下文中所指的参考手册均是指该手册)
Jetson资料下载中心:Jetson Download Center | NVIDIA Developer
Jetson Xavier NX 信息:
- Linux jetson-desktop 4.9.253-tegra
- L4T 32.7.2
platform代码:
- t234:Jetson AGX Orin
- t194:Jetson Xavier NX 系列、Jetson AGX Xavier 系列
- t210:jetson nano、jetson tx1
内核定制化
下载编译内核
从Jetson Linux | NVIDIA Developer处下载最新的(或者自己选L4T版本号)内核源码L4T Driver Package (BSP) Sources
注意:
- 目前(2022/6/4)L4T 34.1之后,暂时只支持 Jetson AGX 系列和 Jetson Xavier 系列,其余暂不支持
- 下载的是源码包(L4T Driver Package (BSP) Sources)
解压TBZ2文件:
1 2 3
| tar -xjf public_sources.tbz2 cd Linux_for_Tegra/source/public tar –xjf kernel_src.tbz2
|
安装工具链:
注意:
- 如果在jetson nano上则不需要该步骤
- 官方推荐
Linaro gcc 7.3.1 2018.05 aarch64
参考连接:本文最开始说明的参考手册中Kernel Customization -> Jetson Linux Toolchain
下载地址:http://releases.linaro.org/components/toolchain/binaries/7.3-2018.05/aarch64-linux-gnu/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu.tar.xz
解压:
1 2 3
| mkdir $HOME/l4t-gcc cd $HOME/l4t-gcc tar xf gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu.tar.xz
|
编译内核:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| sudo apt install build-essential bc
TEGRA_KERNEL_OUT=<outdir>
export CROSS_COMPILE=$HOME/l4t-gcc/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- export LOCALVERSION=-tegra
cd <kernel_source> mkdir -p $TEGRA_KERNEL_OUT make ARCH=arm64 O=$TEGRA_KERNEL_OUT tegra_defconfig
make ARCH=arm64 O=$TEGRA_KERNEL_OUT -j<n> make ARCH=arm64 O=$TEGRA_KERNEL_OUT dtbs
sudo make ARCH=arm64 O=$TEGRA_KERNEL_OUT modules_install INSTALL_MOD_PATH=<modules_install_path>
cd <modules_install_path> tar --owner root --group root -cjf kernel_supplements.tbz2 lib/modules
|
编译生成的内核以及设备树在:
- 内核:
TEGRA_KERNEL_OUT/arch/arm64/boot/Image
- 设备树:
TEGRA_KERNEL_OUT/arch/arm64/boot/dts/
编译内核模块
找到内核的头文件目录:
编译:
1 2 3 4
| cd <path_to_module_source> make ARCH=arm64 –C <kernel_directory> M=$(pwd)
<tool_chain_path>/aarch64-linux-gnu-strip -–strip-unneeded <path-of-kernel-module.ko>
|
摄像头驱动开发
官方提供两种开发方式:
- Camera Core Library接口
- 直接通过V4L2访问
对于Camera Core Library来说,Linux Driver Package (L4T)框架上的引用和内核模式的V4L2驱动之间的关系如下所示:
对于直接通过V4L2访问和方式来说,其驱动于应用之间的关系为:
对于这种方式,需要:
- 在内核中添加设备树节点
- 开发对应的V4L2驱动,对于该驱动官方有两种版本,建议用最新的2.0。以下为可以参考的Sony IMX185驱动代码
- Version 1.0 driver: imx185_v1.c
- Version 2.0 driver: imx185.c
设备树
位置
参考手册中:Bootloader -> U-Boot Customization -> Environment Configuration -> exlinux.conf
在L4T中,一般最后的设备树启动文件*.dtb
在根文件系统的/boot/dtb/
下,同时可以通过以下命令找到具体位置:
1 2 3
| cat /sys/firmware/devicetree/base/nvidia,dtsfilename cat /proc/device-tree/nvidia,dtsfilename
|
- 默认位置:
- Jetson Nano:
XXXX/hardware/nvidia/platform/t210/porg/kernel-dts/tegra210-p3448-0000-p3449-0000-b00.dts
- Jetson Xavier NX:
XXXX/hardware/nvidia/platform/t19x/jakku/kernel-dts/tegra194-p3668-all-p3509-0000.dts
同时通过/boot/extlinux/extlinux.conf
配置文件(参见EXTLINUX - Syslinux Wiki)可以使得uboot通过dtb overlays的方式选取不同的设备树或者启动参数来启动系统,对Linux驱动编写与调试方便很多
如下所示为本文中对应的脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| TIMEOUT 30 DEFAULT primary
MENU TITLE L4T boot options
LABEL primary MENU LABEL primary kernel LINUX /boot/Image INITRD /boot/initrd FDT /boot/tegra194-p3668-all-p3509-0000-ice.dtb APPEND ${cbootargs} root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyTCU0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0
LABEL backup MENU LABEL backup dtb LINUX /boot/Image INITRD /boot/initrd APPEND ${cbootargs} quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyTCU0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0
|
其中:
- TIMEOUT:默认等待选择时间
- DEFAULT:默认启动方式
- LINUX:Linux内核存放位置
- FDT:设备树文件(不指定则用默认位置,即/boot/dtb/下文件)
- FDTOVERLAYS:xxx.dtbo文件,用于补充或者覆盖设备树文件中的一些配置(uboot会根据该文件自动修改设备树)
通过上述命令编译设备树后,其生成的*.dtb
文件会出现在output/arch/arm64/boot/dts
下,
调试
设备树目录:/proc/device-tree
(软连接)和/sys/firmware/devicetree/base/
(实际位置)
1 2 3 4 5 6
| fdtdump /boot/tegra210-p3448-0000-p3449-0000-a02-fe-pi-audio.dtbo
dtc -I dtb -O dts <filename>
hexdump -C /proc/device-tree/host1x/i2c@546c0000/tca9546@70/i2c@2/reg
|
基础含义
在设备树文件<top>/hardware/nvidia/platform/t19x/common/kernel-dts/t19x-common-modules/tegra194-camera-imx185-a00.dtsi
中找到tegra-camera-platform
节点
在tegra-camera-platform
节点中创建一个模块表(module table),每个模块必须包含其基本信息和该节点内设备的定义
如下所示为一种典型的节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| tegra-camera-platform { compatible = "nvidia, tegra-camera-platform"; modules { module0 { badge = "imx185_bottom_liimx185"; position = "bottom"; orientation = "0"; drivernode0 { pcl_id = "v4l2_sensor"; devname = "imx185 30-001a"; proc-device-tree = "/proc/device-tree/i2c@3180000/tca9546@70/i2c@0/imx185_a@1a"; }; }; }; };
|
属性含义:
- badge:标识此模块的唯一名称。
- 名称必须由三部分组成,用下划线隔开:
- 模组的摄像头板卡ID (camera_board_id)
- 模块的位置,例如后置(rear)或前置(front)
- 模块的零件号,可在模块数据表中找到。如果零件号不可用,请使用唯一标识符。只有零件号的最后六个字符是有效的
- 例如,imx185_rear_liimx185代表一个模块,该模块带有一个后置imx185摄像头,零件号为“llimx185”
- 如果系统有多个相同的模块,则每个模块必须具有不同的位置,从而使模块名称唯一
- position:相机位置。支持的值取决于系统中的摄像机数量
- 在双摄像头系统中:rear 和 front
- 在三摄像头系统中:bottom、top 和 center
- 在六摄像头系统中:bottomleft、bottomright、centerleft、centerright、topleft 和 topright
- orientation:传感器方向;表示传感器方向的数字索引。索引和方向之间的关系是任意的,但通常在双摄像头系统中,0 是“后向”,1 是“前向”
CBoot
在L4T 32.7.2
中的bootloader存在问题,在修改上述/boot/extlinux/extlinux.conf
配置文件后,boot阶段并不会生效,如下为对应的log:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| [0008.443] I> ########## Fixed storage boot ########## [0008.448] I> Loading kernel-bootctrl from partition [0008.453] I> Loading partition kernel-bootctrl at 0xa42e0000 from device(0x1) [0008.467] W> tegrabl_get_kernel_bootctrl: magic number(0x00000000) is invalid [0008.468] W> tegrabl_get_kernel_bootctrl: use default dummy boot control data [0008.474] I> Already published: 00010003 [0008.477] I> Look for boot partition [0008.481] I> Fallback: assuming 0th partition is boot partition [0008.487] I> Detect filesystem [0008.514] I> Loading extlinux.conf ... [0008.514] I> Loading extlinux.conf binary from rootfs ... [0008.514] I> rootfs path: /sdmmc_user/boot/extlinux/extlinux.conf [0008.557] I> ext4_read_data_from_extent:298: Total file read should not be larger than file stat size [0008.558] E> file /sdmmc_user/boot/extlinux/extlinux.conf read failed!! [0008.558] W> Failed to load extlinux.conf binary from rootfs (err=202113305) [0008.559] E> Failed to find/load /boot/extlinux/extlinux.conf [0008.560] I> Loading kernel ... [0008.563] I> No kernel binary path [0008.566] I> Continue to load from partition ... [0008.571] W> No valid slot number is found in scratch register
|
主要是其中的[0008.557] I> ext4_read_data_from_extent:298: Total file read should not be larger than file stat size
,导致读取extlinux.conf失败
根据Cboot in 32.7.2 fails to read extlinux.conf中所述,需要手动修改并编译bootloader
下载编译CBoot
从Jetson Linux R32.7.2 Release Page | NVIDIA Developer处下载CBoot Source T194
(Jetson Xavier NX)
1 2 3 4 5 6 7 8 9 10 11 12 13
| mkdir cboot tar -xjf cboot_src_t194.tbz2 -C cboot cd cboot
export CROSS_COMPILE=$HOME/l4t-gcc/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
export TEGRA_TOP=$PWD export TOP=$PWD
make -C ./bootloader/partner/t18x/cboot PROJECT=t194 TOOLCHAIN_PREFIX="${CROSS_COMPILE}" DEBUG=2 BUILDROOT="${PWD}"/out NV_TARGET_BOARD=t194ref NV_BUILD_SYSTEM_TYPE=l4t NOECHO=@
cp lk.bin cboot_t194.bin
|
如果系统的python版本为python3,则需要注意:
- 修改
./bootloader/partner/t18x/cboot/build/get_branch_name.py
中第24行,即print prj.getAttribute("revision")
改为print(prj.getAttribute("revision"))
- 修改
./bootloader/partner/t18x/cboot/scripts/add_version_info.py
中第43行,即f.write(struct.pack('%ds' % (length), version_string))
改为 f.write(struct.pack('%ds' % (length), version_string.encode('utf-8')))
- 编译中间会报错缺少
.repo/manifest.xml
文件,可以忽略
修改CBoot
修改CBoot,解决启动后读取extlinux.conf失败的问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
@@ -244,6 +244,9 @@ uint8_t *buf_ptr = NULL; off_t total_bytes_read = 0; int err = 0; + uint32_t file_len_in_blocks = 0; + uint32_t total_blocks_read; + uint32_t block_size; LTRACE_ENTRY; @@ -254,6 +257,15 @@ } buf_ptr = (uint8_t *)buf; + block_size = E2FS_BLOCK_SIZE(ext2->super_blk); + + if (len != 0UL) { + file_len_in_blocks = len/block_size; + if ((len % block_size) != 0U) { + file_len_in_blocks++; + } + LTRACEF("file_len_in_blocks: %u\n", file_len_in_blocks); + } /* Extract extents info */ extent_header = (struct ext4_extent_header *)inode->e2di_blocks; @@ -284,21 +296,37 @@ err = ext4_read_extent(ext2, (struct ext4_extent_header *)buf2, buf_ptr, &bytes_read); total_bytes_read += bytes_read; buf_ptr += bytes_read; - if ((len != 0) && (total_bytes_read > len)) { - TRACEF("Total file read should not be larger than file stat size\n"); - err = ERR_NOT_VALID; - goto fail; - } + + total_blocks_read = total_bytes_read/block_size; + if ((total_bytes_read % block_size) != 0U) { + total_blocks_read++; + } + + /* Check if extra block isn't read */ + if ((len != 0) && (total_blocks_read > file_len_in_blocks)) { + TRACEF("More blocks are read (%u) than file block count (%u)\n", + total_blocks_read, file_len_in_blocks); + err = ERR_NOT_VALID; + goto fail; + } } } else { /* Read leaf node */ err = ext4_read_extent(ext2, extent_header, buf_ptr, &bytes_read); total_bytes_read += bytes_read; - if ((len != 0) && (total_bytes_read > len)) { - TRACEF("Total file read should not be larger than file stat size\n"); - err = ERR_NOT_VALID; - goto fail; - } + + total_blocks_read = total_bytes_read/block_size; + if ((total_bytes_read % block_size) != 0U) { + total_blocks_read++; + } + + /* Check if extra block isn't read */ + if ((len != 0) && (total_blocks_read > file_len_in_blocks)) { + TRACEF("More blocks are read (%u) than file block count (%u)\n", + total_blocks_read, file_len_in_blocks); + err = ERR_NOT_VALID; + goto fail; + } } LTRACEF("err %d, bytes_read %lu\n", err, total_bytes_read);
|
重新烧录
找到SDK MANGER下的对应目录,一般是形如~/nvidia/nvidia_sdk/JetPack_4.6.2_Linux_JETSON_XAVIER_NX_TARGETS/Linux_for_Tegra
,将上述编译生成的cboot_t194.bin
放入并替换Linux_for_Tegra/bootloader/cboot_t194.bin
1 2 3
| cd bootloader cp ~/cboot_t194.bin ./ chmod +x cboot_t194.bin
|
之后可以通过SDK MANGER重新烧录整个系统
或者在Linux_for_Tegra
下执行以下命令单独烧写CBoot:
1
| sudo ./flash.sh -r -k cpu-bootloader jetson-xavier-nx-devkit-emmc mmcblk0p1
|
- -r:跳过构建并重用现有的 system.img
- -k :flash.cfg 中指定的分区名称或编号
- jetson-xavier-nx-devkit-emmc:即同文件夹下的jetson-xavier-nx-devkit-emmc.conf配置文件
注意:烧录时需要板卡通过micro usb和PC相连,且板卡处于recovery模式(一般在关机时短接底板上的rec和gnd,上电后松开即可)
测试
参考链接:linux/v4l2 – Gateworks
参考手册中:
- Multimedia -> Accelerated GStreamer -> Camera Capture with GStreamer-1.0
- Camera Development -> Sensor Software Driver Programming -> Device Registration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| sudo apt install v4l-utils
v4l2-ctl --list-devices v4l2-ctl --list-formats-ext
v4l2-ctl -d /dev/video1 --list-formats-ext
v4l2-ctl -d /dev/video1 --list-ctrls v4l2-ctl -d /dev/video1 --all
dmesg | grep -i imx219
sudo service nvargus-daemon restart
nvgstcapture
gst-launch-1.0 nvarguscamerasrc sensor-id=0 sensor-mode=3 ! 'video/x-raw(memory:NVMM),width=3820, height=2464, framerate=21/1, format=NV12' ! nvvidconv flip-method=0 ! 'video/x-raw,width=960, height=616' ! nvvidconv ! nvegltransform ! nveglglessink -e
v4l2-compliance -d /dev/video<n>
v4l2-ctl --set-fmt-video=width=1920,height=1080,pixelformat=RG12 --stream-mmap --set-ctrl=sensor_mode=0 --stream-count=100 -d /dev/video<n>
gst-launch-1.0 nvarguscamerasrc sensor-id=0 ! 'video/x-raw(memory:NVMM), width=(int)1920, height=(int)1080,format=(string)NV12, framerate=(fraction)30/1' ! nvv4l2h264enc bitrate=8000000 ! h264parse ! qtmux ! filesink location=filename_h264.mp4 -e
gst-launch-1.0 nvarguscamerasrc sensor-id=1 ! 'video/x-raw(memory:NVMM), width=(int)1920, height=(int)1080,format=(string)NV12, framerate=(fraction)30/1' ! nvv4l2h264enc bitrate=8000000 ! h264parse ! qtmux ! filesink location=filename_h264_1.mp4 -e
gst-launch-1.0 nvarguscamerasrc sensor-id=2 ! 'video/x-raw(memory:NVMM), width=(int)1920, height=(int)1080,format=(string)NV12, framerate=(fraction)30/1' ! nvv4l2h264enc bitrate=8000000 ! h264parse ! qtmux ! filesink location=filename_h264_2.mp4 -e
v4l2-ctl --device /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 --set-ctrl bypass_mode=0 --stream-mmap --stream-to=frame.raw --stream-count=1 convert -size 1920x1080 -depth 16 uyvy:frame.raw frame.png
v4l2-ctl --device /dev/video1 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 --set-ctrl bypass_mode=0 --stream-mmap --stream-to=frame.raw --stream-count=1 convert -size 1920x1080 -depth 16 uyvy:frame.raw frame.png
v4l2-ctl --device /dev/video0 --stream-mmap --stream-to=frame.raw --stream-count=1 convert -size 640x480 -depth 16 uyvy:frame.raw frame.png
v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 --set-ctrl bypass_mode=0 --stream-mmap --stream-count=100
|