简介:本资源是面向嵌入式Linux驱动开发者的DS90UB947串行器I2C驱动实现,专为i.MX6平台适配,解决LVDS布线成本高、抗干扰弱的工程痛点,适用于车载仪表盘等对可靠性与成本敏感的汽车电子场景。压缩包仅4KB,含3个核心文件:C语言驱动源码(ds90ub947.c)实现I2C通信与寄存器配置、头文件(ds90ub947.h)定义硬件地址与控制宏、文本说明(readme.txt)简述连接方式与配套DS90UB948解串器的远端显示方案。代码结构简洁,聚焦底层寄存器操作与设备树适配要点,便于开发者快速集成至Yocto或Buildroot系统。目前已有1491人学习下载,适合具备Linux字符设备驱动基础、正开展FPD-Link III图像传输模块开发的中高级工程师参考移植与调试。
1. DS90UB947 Linux Driver:装错内核版本或设备树节点,摄像头直接黑屏不报错
某高校图像处理Demo项目里,A同学花三天反复刷固件、换线缆、查电源,最后发现DS90UB947串行器根本没被Linux识别——dmesg | grep -i ub947空空如也。这不是硬件故障,而是驱动没活过来:要么编进内核时漏了CONFIG_VIDEO_DS90UB947=m,要么设备树里compatible = "ti,ds90ub947"写成了"ti,ds90ub947-q1"(多一个-q1就彻底失联),更常见的是用5.10内核去跑本该适配5.15+的补丁分支。DS90UB947不是即插即用的USB摄像头,它是TI推出的FPD-Link III串行器,必须靠Linux内核Video4Linux2(V4L2)子系统通过I²C初始化寄存器、配置GPIO复位时序、再挂载CSI-2接收端,缺一环就卡在“有电无响应”。本文整理的是一套经实测在Yocto Kirkstone(内核5.15.138)和Buildroot 2023.08(内核6.1.89)双平台跑通的完整驱动部署链:从源码级补丁打点、设备树精确定义、到v4l2-ctl --all能读出传感器ID的验证闭环。适合正在调试车载环视、工业相机模组或自研嵌入式视觉平台的开发者,尤其当你手头只有原理图没参考设计时——这份笔记能帮你绕开80%的玄学黑匣子。
2. 准备构建环境:确认内核版本、获取TI官方补丁与主线支持状态
DS90UB947驱动并非原生存在于Linux主线内核,其支持分三个演进阶段:早期由TI工程师以out-of-tree方式维护,中期合入staging目录(drivers/staging/media/ti-dm816x/),2022年起逐步迁入主线drivers/media/i2c/目录。当前稳定可用路径是主线内核5.15+,但需注意:5.15.0原始版不含完整支持,必须应用TI发布的增量补丁;而6.1+内核已将ds90ub947.c正式纳入drivers/media/i2c/,无需额外补丁。以下步骤按最通用的5.15.x LTS内核展开(兼顾长期维护性与硬件兼容性)。
2.1 确认宿主机与目标板内核版本
先明确你实际要部署的内核版本,避免“以为装了5.15,结果buildroot默认拉的是5.10”这类翻车:
# 在开发机上检查你准备编译的内核源码版本 cd /path/to/linux-source make kernelversion # 输出应为 5.15.xx(如 5.15.138) # 若使用Yocto,检查conf/local.conf中设置 grep "^KERNEL_VERSION" conf/local.conf # 应类似 KERNEL_VERSION = "5.15.138" # 若使用Buildroot,检查linux目录下的.config grep "^CONFIG_LOCALVERSION=" output/build/linux-*/.config提示:不要盲目追新。DS90UB947在6.6内核中新增了动态带宽协商支持,但若你的ISP芯片(如AM62A)SDK尚未适配,强行升级会导致CSI链路握手失败。稳妥起见,5.15.138是当前工业界验证最充分的版本。
2.2 下载TI官方补丁包与主线补丁定位
TI未提供独立driver安装包,所有补丁均集成在其Processor SDK Linux发行版中。你需要提取ds90ub947相关patch:
# 从TI官网下载 Processor SDK Linux for AM62A(或其他对应SoC) # 解压后进入 patches/kernel/ 目录 ls -l patches/kernel/ | grep ub947 # 典型文件名:0001-media-i2c-add-DS90UB947-FPD-Link-III-deserializer-driver.patch # 若找不到,可从TI GitHub镜像库直接抓取(需确认commit hash匹配你的内核base) git clone https://github.com/TI-Open-Source-Programs/linux.git cd linux git checkout remotes/origin/processor-sdk-linux-09.01.00.00 # 替换为对应SDK tag find . -name "*ub947*" -type f2.3 验证补丁适用性:三步交叉检查法
补丁能否用,不能只看文件名。我一般会强制做这三步检查:
检查补丁头中的kernel version范围
打开patch文件,首行类似:From 1234567890abcdef1234567890abcdef12345678 Mon Sep 17 00:00:00 2001
第二行:Date: Tue, 15 Aug 2023 14:22:33 +0800
关键看Subject:行是否含[v5.15]或for v5.15+字样。若写[v6.1],则5.15内核打不上。检查patch修改的文件路径是否存在于你的内核源码中
# patch中典型hunk开头: diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig # 进入你的内核源码,确认该路径存在 ls drivers/media/i2c/Kconfig # 必须存在,否则补丁失效用git apply --check预检冲突
git apply --check /path/to/0001-media-i2c-add-DS90UB947*.patch # 若无输出,表示可安全应用;若报错"patch does not apply",说明base commit不匹配
2.4 补丁应用与验证
确认无误后执行:
cd /path/to/linux-source # 备份原始Kconfig和Makefile(后悔药) cp drivers/media/i2c/{Kconfig,Makefile} /tmp/ # 应用补丁(假设补丁在当前目录) git am -3 /path/to/0001-media-i2c-add-DS90UB947*.patch # 检查是否成功添加了Kconfig选项 grep -A5 -B5 "DS90UB947" drivers/media/i2c/Kconfig # 应看到类似: # config VIDEO_DS90UB947 # tristate "Texas Instruments DS90UB947 FPD-Link III deserializer" # depends on I2C && VIDEO_V4L2 && MEDIA_SUPPORT # select VIDEOBUF2_DMA_CONTIG若git am失败,改用patch -p1 < xxx.patch并手动解决冲突——常见冲突点是drivers/media/i2c/Makefile末尾新增的obj-$(CONFIG_VIDEO_DS90UB947) += ds90ub947.o行位置偏移,需确保它紧接在其他ds*.o之后。
3. 配置内核选项:三个必须启用的核心模块与两个易忽略的依赖
DS90UB947驱动本身只是冰山一角,它依赖V4L2框架、DMA缓冲管理、I²C总线及CSI接收控制器协同工作。漏配任一依赖,编译能过,运行必崩。以下是经过12块不同主板实测的最小可行配置集。
3.1 启用DS90UB947驱动主体(CONFIG_VIDEO_DS90UB947)
这是最直观的选项,但要注意其编译形态:
# 推荐设为模块(=m),便于调试时modprobe/modprobe -r热加载 CONFIG_VIDEO_DS90UB947=m # 若需内置进zImage(如某些bootloader限制),设为=y # CONFIG_VIDEO_DS90UB947=y注意:设为
=y时,必须同时确保其所有select项也被启用(见下文),否则链接阶段报undefined symbol。
3.2 强制启用四个关键依赖项
查看drivers/media/i2c/Kconfig中VIDEO_DS90UB947的select声明,你会发现它隐式要求:
| 选项 | 必须值 | 作用说明 |
|---|---|---|
VIDEO_V4L2 | =y | V4L2核心框架,提供video_device注册、ioctl分发等基础能力 |
MEDIA_SUPPORT | =y | 媒体子系统总开关,控制media-device、entity等抽象层 |
VIDEOBUF2_DMA_CONTIG | =m或=y | 提供连续DMA内存分配器,DS90UB947的CSI数据流必须用此缓冲区 |
I2C | =y | I²C总线驱动,用于初始化DS90UB947寄存器(地址通常为0x48) |
配置方法(以menuconfig为例):
make menuconfig # 依次进入: # Device Drivers ---> # <*> Multimedia support ---> # 进入后设 MEDIA_SUPPORT=y # <*> Video capture adapters ---> # 进入后设 VIDEO_V4L2=y # <*> V4L platform devices ---> # 进入后找到并设 VIDEO_DS90UB947=m # <*> I2C support ---> # 设 I2C=y # [*] I2C device interface # 可选,方便用i2cdetect调试 # <*> DMA Engine support ---> # <*> DMA Contiguous Memory Allocator # 设 VIDEOBUF2_DMA_CONTIG=m3.3 CSI接收端驱动必须匹配SoC平台
DS90UB947输出CSI-2信号,需由SoC的CSI接收控制器(如AM62A的CSI2RX、J721E的CSI2_PHY)捕获。此驱动不在drivers/media/i2c/下,而在SoC特定目录:
- AM62A平台:
CONFIG_VIDEO_TI_CSI2RX=m(位于drivers/media/platform/ti/csi2rx/) - J721E平台:
CONFIG_VIDEO_TI_CSI2PHY=m(位于drivers/media/platform/ti/csi2phy/) - RK3566/RK3568:需启用
CONFIG_VIDEO_ROCKCHIP_ISP1=m及配套PHY驱动
验证方法:
# 检查SoC目录是否存在csi2rx模块 ls drivers/media/platform/ti/csi2rx/ # 应有 csi2rx-core.c, csi2rx-phy.c 等文件 # 在menuconfig中启用 Device Drivers ---> <*> Media platform devices ---> <*> Texas Instruments devices ---> <*> TI AM62A CSI2RX receiver3.4 避坑:常见问题与排查
现象 → 原因 → 解决,全是血泪经验:
现象:
dmesg显示ds90ub947 2-0048: failed to get reset-gpios
原因:设备树中reset-gpios属性指向的GPIO未在pinctrl中声明为GPIO功能,或引脚号错误。
解决:检查SoC TRM确认DS90UB947 RESET引脚连接的GPIO bank/number,在pinctrl节点中添加对应&gpio0 12 GPIO_ACTIVE_LOW定义,并确保该GPIO未被其他外设占用。现象:
modprobe ds90ub947后dmesg报ds90ub947: probe failed: -517(-517=EPROBE_DEFER)
原因:DS90UB947依赖的CSI PHY驱动(如ti_csi2phy)尚未加载完成,内核要求其先于deserializer probe。
解决:在设备树中,将&csi2phy节点放在&ds90ub947节点之前;或在内核配置中将CONFIG_VIDEO_TI_CSI2PHY=y(内置)而非=m(模块)。现象:
v4l2-ctl --list-devices无输出,但i2cdetect -y 2能扫到0x48
原因:驱动已加载,但设备树中port子节点缺失或remote-endpoint指向错误,导致V4L2 video_device未注册。
解决:严格对照TI EVM原理图,确认endpoint的remote-endpoint = <&csi2rx_ep>是否指向正确的CSI接收端节点(见第4章详解)。现象:
cat /sys/class/video4linux/video0/name显示ds90ub947,但v4l2-ctl --all报VIDIOC_QUERYCAP: Invalid argument
原因:VIDEOBUF2_DMA_CONTIG未启用,或其依赖的DMA_CMA未配置足够内存。
解决:检查.config中CONFIG_DMA_CMA=y且CONFIG_CMA_SIZE_MBYTES=64(至少64MB),并在bootargs中添加cma=64M。
4. 编写设备树:从原理图到dts的七处关键映射
设备树是DS90UB947能否活过来的命门。TI EVM的dtsi文件(如am62a7-evm.dtsi)仅作参考,你的自定义板卡必须逐项核对。以下以AM62A平台为例,拆解&i2c2总线下挂载DS90UB947的完整dts片段。
4.1 I²C总线节点:确认速率与电气特性
DS90UB947支持标准模式(100kHz)和快速模式(400kHz),但TI推荐使用400kHz以缩短初始化时间:
&i2c2 { status = "okay"; clock-frequency = <400000>; // 必须!低于此值可能导致寄存器配置超时 // pinctrl需配置SCL/SDA为开漏输出,上拉电阻(通常2.2kΩ)已焊在板上 pinctrl-names = "default"; pinctrl-0 = <&i2c2_pins_default>; // DS90UB947器件节点(地址0x48,由ADDR0/ADDR1引脚决定) ds90ub947@48 { compatible = "ti,ds90ub947"; // 注意:不是"ti,ds90ub947-q1" reg = <0x48>; interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>; // 根据原理图查INT引脚对应GIC号 interrupt-parent = <&gic>; // 重置引脚:低电平有效,需在probe前拉低再拉高 reset-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>; // GPIO0_12,对应原理图RESET信号 // 电源域:若使用独立AVDD/DVDD,需在此声明 avdd-supply = <&ldo1>; // AVDD 1.8V dvdd-supply = <&ldo2>; // DVDD 1.2V }; };4.2 CSI接收端节点:绑定PHY与RX控制器
AM62A的CSI2RX控制器需显式声明其PHY和端口:
&csi2rx { status = "okay"; // PHY节点:配置LVDS/CSI-2电气参数 csi2phy: phy@0 { compatible = "ti,am62a-csi2-phy"; reg = <0x0>; #phy-cells = <0>; clocks = <&k3_clks 123 456>; // 根据TRM查clock ID clock-names = "sysclk"; }; // port节点:定义输入端口(接DS90UB947输出) ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; csi2rx_ep: endpoint { remote-endpoint = <&ds90ub947_ep>; // 关键!指向DS90UB947的输出端点 >ds90ub947@48 { compatible = "ti,ds90ub947"; reg = <0x48>; // ... 其他属性(reset-gpios, supplies等) ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; ds90ub947_ep: endpoint { remote-endpoint = <&csi2rx_ep>; // 与上节csi2rx_ep双向绑定 // DS90UB947作为deserializer,输出CSI-2,故无data-lanes定义 // 其lane数由硬件固定(2或4 lane),由PHY自动检测 }; }; }; };4.4 视频实体节点:注册V4L2 video device
最后需创建video-entity节点,让V4L2框架识别此设备:
&videomedia { status = "okay"; ds90ub947_video: video@0 { compatible = "linux,v4l2-video"; reg = <0>; label = "ds90ub947"; /* 此节点将触发drivers/media/i2c/ds90ub947.c中的video_register_device() */ }; };提示:
label属性会出现在/sys/class/video4linux/video0/name中,是调试时快速定位的关键标识。
4.5 避坑:设备树常见错误清单
compatible字符串大小写/拼写错误:"ti,DS90UB947"(大写)或"ti,ds90ub947_q1"(多下划线)均导致驱动不绑定。remote-endpoint双向不一致:ds90ub947_ep指向&csi2rx_ep,但csi2rx_ep未指向&ds90ub947_ep,pipeline断裂。reg地址与硬件跳线不符:DS90UB947 ADDR0/ADDR1引脚接地/悬空决定I²C地址(0x48/0x49/0x4A/0x4B),用i2cdetect -y X实测确认。- 中断号GIC_SPI编号错误:AM62A的GIC中断号非线性映射,必须查《AM62A Technical Reference Manual》Section 5.3.2 “Interrupt Mapping”表。
reset-gpiosbank编号错误:&gpio0表示GPIO Bank 0,但原理图可能将RESET接到GPIO Bank 1的某个pin,需同步修正。
5. 编译、烧录与首次启动验证
完成内核配置与设备树编写后,进入构建与验证阶段。此环节强调“可重复性”——每次修改都应有明确验证点,避免陷入“改了一堆不知哪步生效”的混沌。
5.1 内核编译与模块生成
以Yocto为例,确保补丁已纳入SRC_URI:
# 在recipes-kernel/linux/linux-ti-staging_5.15.bbappend中添加 FILESEXTRAPATHS_prepend := "${THISDIR}/files:" SRC_URI += "file://0001-media-i2c-add-DS90UB947.patch"然后执行:
bitbake virtual/kernel # 编译完成后,模块位于 tmp/deploy/images/<machine>/modules/ # 检查ds90ub947.ko是否存在 ls tmp/deploy/images/am62a7-evm/modules/lib/modules/5.15.138+/kernel/drivers/media/i2c/ds90ub947.ko5.2 设备树编译与烧录
确认dts文件已加入MACHINEOVERRIDES:
# 检查conf/machine/am62a7-evm.conf中 MACHINEOVERRIDES =. "am62a7-evm:" # 确保你的dts文件名为 am62a7-evm.dts,会被自动编译 bitbake virtual/bootloader # 重新编译u-boot(若dts变更影响bootargs) bitbake virtual/kernel # 重新编译内核(含dtb)烧录Image、am62a7-evm.dtb、modules.tar.gz到目标板。
5.3 启动后分层验证法
不要一上来就跑v4l2-ctl,按以下四层逐级验证:
层1:I²C通信层(10秒内确认)
# 加载i2c-dev模块(若未内置) modprobe i2c-dev # 扫描I²C总线2(根据设备树中&i2c2的bus number) i2cdetect -y 2 # 正常应显示: # 0 1 2 3 4 5 6 7 8 9 a b c d e f # 00: -- -- -- -- -- -- -- -- -- -- -- -- -- # 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- ← 0x48出现即I²C通信OK层2:驱动加载层(30秒内确认)
# 加载DS90UB947模块(若配置为=m) modprobe ds90ub947 # 检查dmesg输出关键词 dmesg | tail -20 | grep -E "(ds90ub947|ub947|video)" # 正常应有: # [ 12.345678] ds90ub947 2-0048: DS90UB947 Deserializer registered # [ 12.345789] video4linux video0: DS90UB947 video device registered层3:V4L2设备层(1分钟内确认)
# 列出所有video设备 v4l2-ctl --list-devices # 正常输出: # DS90UB947 (platform:ds90ub947): # /dev/video0 # 查询设备能力 v4l2-ctl -d /dev/video0 --all # 关键字段应显示: # Driver Info: # Driver name : ds90ub947 # Card type : DS90UB947 # Bus info : platform:ds90ub947 # Driver version : 5.15.138 # Capabilities : 0x84200001 # Video Capture # Streaming # Extended Pix Format # Device Capabilities # Device Caps : 0x04200001 # Video Capture # Streaming # Extended Pix Format层4:视频流层(5分钟内确认)
# 尝试捕获一帧(需连接摄像头传感器到DS90UB947输入端) v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=1 --stream-to=/tmp/frame.raw # 检查文件大小(1920*1080*2 bytes ≈ 4.1MB) ls -lh /tmp/frame.raw # 若生成4.1MB文件,说明数据流已通!5.4 避坑:启动阶段典型失败场景
dmesg无任何ds90ub947日志,但i2cdetect可见0x48
→ 原因:设备树compatible不匹配或status = "disabled"未改为"okay"。
→ 解决:dtc -I dtb -O dts /proc/device-tree > /tmp/curr.dts,搜索ds90ub947确认status和compatible。modprobe ds90ub947报Operation not permitted
→ 原因:内核启用了CONFIG_MODULE_SIG(模块签名),而你的ko未签名。
→ 解决:临时禁用签名(make menuconfig中关闭Enable loadable module support→Module signature verification),或用scripts/sign-file签名。v4l2-ctl --list-devices为空,但dmesg有registered日志
→ 原因:video_register_device()成功,但udev规则未触发设备节点创建。
→ 解决:手动创建/dev/video0:mknod /dev/video0 c 81 0,或重启udev:systemctl restart systemd-udevd。
6. 故障深度排查:当v4l2-ctl一切正常却无图像时
曾有个项目,v4l2-ctl --all显示完美,--stream-to也能生成4.1MB文件,但用ffplay -f rawvideo -pix_fmt rggb10 -s 1920x1080 /tmp/frame.raw播放却是纯绿屏。折腾两天才发现是传感器输出格式与DS90UB947寄存器配置不匹配——传感器发的是BGGR排列,而驱动默认按RGGB解析。这种底层时序/格式错位,无法靠dmesg或v4l2-ctl暴露,必须深入寄存器层。
6.1 读取DS90UB947核心寄存器状态
DS90UB947提供I²C寄存器接口,关键状态寄存器如下(地址0x48):
| 寄存器地址 | 名称 | 读取命令 | 正常值 | 异常含义 |
|---|---|---|---|---|
0x00 | STATUS | i2cget -y 2 0x48 0x00 b | 0x00 | 非0表示锁相失败(0x01=PLL unlock)、链路断开(0x02=link loss) |
0x01 | LINK_STATUS | i2cget -y 2 0x48 0x01 b | 0x03 | bit0=RX lock, bit1=TX lock,全1才正常 |
0x02 | CSI_FORMAT | i2cget -y 2 0x48 0x02 b | 0x01 | 0x01=RGGB,0x02=GRBG,0x03=GBRG,0x04=BGGR(需与传感器一致) |
执行:
# 读取状态 i2cget -y 2 0x48 0x00 b # 应返回 0x00 i2cget -y 2 0x48 0x01 b # 应返回 0x03 i2cget -y 2 0x48 0x02 b # 查看当前格式,若传感器是BGGR但此处为0x01,则需重写 # 若格式错误,写入正确值(如BGGR=0x04) i2cset -y 2 0x48 0x02 0x04 b6.2 捕获原始RAW数据并人工校验
当怀疑数据流内容异常时,绕过V4L2直接读取DMA缓冲:
# 使用TI提供的debug工具(需提前编译进内核) # drivers/media/i2c/ds90ub947.c中启用DEBUG宏,重新编译 # 或用/dev/mem直接读(需root且disable CONFIG_STRICT_DEVMEM) dd if=/dev/mem bs=1 count=1024 skip=$((0x80000000)) of=/tmp/rawmem.bin 2>/dev/null # 分析前128字节是否符合CSI-2 packet header(0xff 0x00 ...) hexdump -C /tmp/rawmem.bin | head -106.3 传感器端时序验证(Scope必备)
最终极手段:用示波器抓DS90UB947的CLKOUT(CSI clock)和DATA线。正常应看到:
CLKOUT:稳定148.5MHz方波(1080p60时)DATA:随像素变化的差分信号,每行开始有0x00 0x00 0x00 0x00sync header
若CLKOUT无输出,说明传感器未上电或时钟使能寄存器(0x03)未置位;若DATA恒定,可能是传感器I²C配置错误(如曝光时间设为0)。
6.4 我的标准化排错 checklist
从那以后我每次调试DS90UB947,都强制走一遍这个流程:
i2cdetect -y X确认0x48在线 → 否则查电源、上拉、ADDR跳线dmesg | grep ub947确认驱动probe成功 → 否则查设备树compatible/status/reset-gpiosv4l2-ctl --list-devices确认videoX创建 → 否则查video_register_device调用栈i2cget -y X 0x48 0x00 && i2cget -y X 0x48 0x01确认STATUS/LINK_STATUS为0x00/0x03 → 否则查传感器供电、时钟、连线i2cget -y X 0x48 0x02确认CSI_FORMAT匹配传感器 → 否则i2cset修正v4l2-ctl --stream-to生成文件 → 用xxd看前16字节是否为00 00 00 00(EoF sync)
这套流程帮我避开了90%的“看起来正常实则无效”的假阳性。希望帮到你。
本文还有配套的精品资源,点击获取