简介:gv7704驱动.rar 面向在 Hisi3531A 平台上接入 SDI 视频输入并完成设备查询控制的嵌入式开发者与系统集成人员,提供一套已在该芯片架构下编译通过的驱动参考实现,可服务于高清摄像机接入、实时编码处理与广播级视频采集等场景。压缩包共 10 个文件,约 90KB,以 7 个 C 源文件、2 个头文件和 1 个 Makefile 为主,分别承载驱动读写、SPI 与 GPIO 交互、测试入口、结构声明及编译组织,便于梳理从硬件访问到上层接口的调用关系。已有 697 人学习,说明其对海思多媒体平台驱动移植具有参考价值。读者可据此理解 SDI 输入链路的状态查询、参数获取与设备控制思路,掌握视频流参数、工作模式与输入状态等查询入口,并借鉴 Makefile 的适配方式,为同类采集卡或接口设备的驱动调试、二次开发与故障排查提供可复用的代码骨架。
1. 从一块认不出的采集板说起:gv7704驱动到底补的是哪一环
四通道 SDI 采集板插上主机,设备管理器里只多出一个带黄色感叹号的未知设备;换到 Linux 下,lspci干净得像什么都没插,dmesg里连一行报错都懒得给。这类场景里缺的往往不是上层应用代码,而是 gv7704驱动 这一层。gv7704 是一颗四通道 3G-SDI 收发芯片,四路 BNC 进来之后,均衡、时钟恢复、解串、音频解嵌都在它内部完成,最终把并行视频数据和时钟交给下游桥片或 FPGA。没有驱动去释放复位、拉起参考时钟、按顺序写寄存器、打开通道并使能输出,下游拿到的就是一片不会动的死数据。
这个 rar 包解决的就是“芯片认不出、通道起不来、出帧不稳”这三件事。它适合手里已经有同类采集板、需要把 SDI 输入接进自己系统的工程师,也适合刚接触 SDI 链路、想弄明白配置顺序为什么不能乱的人。后面按“包里有什么 → 硬件先量什么 → 两个平台怎么装 → 哪里会翻车 → 怎么验证”走一遍。
2. 拆开这个 rar:四通道 3G-SDI 链路里驱动到底管什么
很多人拿到驱动包的第一反应是找.inf或者Makefile,找到就双击、就make,然后把报错截图发出来问为什么。真正省时间的顺序是先看清包里的分层,再对上自己板子的硬件链路。同一颗芯片,参考时钟从 27MHz 晶振来还是从 FPGA 分频来,复位脚是 GPIO 控的还是 RC 延时自己起来的,控制口走 SPI 还是走 I2C 桥——这几种组合下,驱动里要改的地方完全不同。所以这一章不讲怎么装,先讲清楚这颗芯片在系统里处于什么位置,以及驱动实际介入的是哪几个时间点。
2.1 包内文件的分层结构
四通道 SDI 这类驱动包,结构通常比普通外设驱动复杂,因为它横跨“配置总线”和“视频数据流”两条路径。拆包后不要急着按文件名排序看,按下面的层次归类,心里就有数了。
| 文件/目录类型 | 典型内容 | 落地时先看哪个 |
|---|---|---|
| 硬件抽象层 | SPI/I2C 读写封装、GPIO 复位、寄存器读写宏 | 先看,决定你能不能在自己的板子上跑通 |
| 初始化表 | 按输入格式分组的寄存器序列,或结构体数组 | 次看,决定支持哪些分辨率与帧率 |
| 平台驱动 | Linux 侧的模块入口、probe、设备树匹配;Windows 侧的 inf 与端口驱动 | 再看,决定安装方式 |
| 时钟与 PLL 配置 | 参考时钟选择、分频系数、输出时钟使能 | 重点看,出帧不稳多半在这 |
| 中断与状态 | 锁定状态、丢失信号、FIFO 溢出等状态位读取 | 调试时看 |
| 示例与工具 | 设备树片段、回读脚本、测试图样配置 | 验证时看 |
提示:包里的初始化表往往对应某一版芯片 revision。同一型号不同 revision 的默认值可能有差异,直接照抄初始化表是常见翻车点,先回读芯片 ID 和 revision 再决定用哪一组。
这里有个容易被忽略的细节:初始化表通常不是“从头到尾写一遍”就完事。四通道芯片的寄存器分全局区和通道区,全局区负责参考时钟、PLL、总使能,通道区负责每一路的均衡增益、电缆驱动幅度、音频嵌入开关。全局配置必须在通道配置之前完成,而且中间要留出 PLL 锁定时间。我在一块板上见过有人把整张表连续写完,中间不加延时,结果四路里只有第一路能锁上——不是表错了,是 PLL 还没稳就去配通道了。
2.2 从上电到出帧:驱动介入的四个时间点
把这颗芯片的工作过程拆成时间轴,驱动的职责就清楚了。四个时间点里任何一个顺序错,现象都是“没图像”,但原因完全不同。
第一个时间点是复位释放。芯片的复位脚如果是 GPIO 控的,驱动 probe 里第一件事就是拉低、延时、拉高,延时长度按手册要求来,通常不是随便给个 10ms 就行。如果板子上是 RC 延时自复位,驱动里就不能再去控这个脚,否则会和外部电路打架。这一步做错的典型现象是:寄存器能读能写,但读出来的全是默认值,写进去不生效。
第二个时间点是参考时钟稳定。SDI 链路对参考时钟的抖动很敏感,驱动在配置 PLL 之前要确认时钟已经起来。Linux 侧一般通过clk_prepare_enable()拿时钟,Windows 侧则由端口驱动在上电序列里保证。这一步没做对的現象是通道能锁定但帧率飘,或者隔几分钟丢一次锁。
第三个时间点是寄存器初始化。全局区先写,等 PLL 锁定状态位置起,再写通道区。通道区里均衡和电缆驱动的参数跟线缆长度、输入幅度有关,长距离线缆要用更高的均衡增益,但增益给太大会把噪声一起放大。
第四个时间点是通道使能与锁定轮询。四路通道使能之后,驱动要轮询每一路的锁定状态位,锁定失败要能上报,而不是闷头认为成功。很多“设备装好了但采集软件看不到信号”的问题,根子就在这一步:驱动把使能写了,但没检查锁定,上层自然拿不到有效帧。
2.3 先量硬件再装驱动
装驱动之前花十分钟量几个点,能省掉后面几小时的对抓。这不是玄学,是顺序问题。
先量电源。这类芯片一般要几路不同电压,而且有上电顺序要求。用示波器看几路电压的上升时序,如果某一路明显滞后或者有台阶,先把电源理清楚,别急着怀疑驱动。
再量参考时钟。探头放到时钟引脚上,看频率对不对、幅度够不够、波形是不是干净。我遇到过一块板子,晶振焊盘虚焊,示波器上看频率是对的但幅度只有一半,芯片偶尔能工作,重启十次成一次,查了两天才发现是焊接问题。
然后量配置总线。SPI 的话看片选、时钟、数据三根线的波形,重点看片选拉低到第一个时钟沿之间有没有足够的建立时间。有些桥片的 SPI 控制器默认时序偏快,寄存器写进去像是成功了,实际芯片没采样到。
最后看 BNC 端接。75 欧端接电阻有没有漏贴、贴错阻值,会直接导致输入幅度不对,均衡再怎么调也救不回来。
3. Windows 侧落地:inf 安装、签名与设备枚举顺序
Windows 平台的麻烦不在驱动逻辑,在安装链路。同一个包,在一台机器上双击 inf 就装上了,换一台就提示“找不到兼容的驱动程序”,或者装完了设备管理器里显示正常但采集软件打开就报错。这一章按“解包 → 安装 → 枚举 → 排错”的顺序走,每一步都留下可回退的动作。
3.1 解包前先确认驱动模型
先看 inf 里的硬件 ID 段。驱动包支持的设备 ID 和你手上板子的 ID 必须对得上,对不上时 Windows 不会报“驱动错误”,只会报“未知设备”,这个提示很有误导性。把板子插上,在设备管理器里看未知设备的硬件 ID,和 inf 里[Manufacturer]、[Models]段的 ID 逐字符比一遍,注意大小写和斜杠方向。
还要确认驱动模型。老一些的包是 WDM 模型,新一些的是 AVStream 或 KMDF。模型不同,安装方式和依赖的运行库不一样。包里有.sys、.inf、.cat三个文件是基本配置,.cat是签名文件,缺了它在新版系统上会直接拒绝加载。
| 检查项 | 怎么看 | 不对时的现象 |
|---|---|---|
| 硬件 ID 匹配 | inf 的 Models 段 vs 设备管理器硬件 ID | 未知设备,装不上 |
| 签名文件 | 目录里有没有.cat,签名是否过期 | 安装被拒绝,提示签名问题 |
| 依赖服务 | inf 里AddService段引用的服务名 | 装完设备有感叹号,错误码 10 或 31 |
| 架构匹配 | x64 驱动不能装在 arm64 上 | 安装程序直接退出 |
注意:不要为了让驱动装上就随手关掉系统的签名强制。临时测试可以走测试签名模式,但产品环境必须用正式签名,否则换一台机器就复现一次。
3.2 用 pnputil 安装并保留回滚点
图形界面双击 inf 适合一次性验证,真正部署我一般走命令行,因为可以回滚、可以批量、可以脚本化。
:: 以管理员身份运行,先把驱动加入驱动存储区,不立即绑定设备 pnputil /add-driver "D:\gv7704\driver\gv7704.inf" /install :: 列出已安装的第三方驱动包,记下 oemXX.inf 编号,回滚时要用 pnputil /enum-drivers :: 卸载时按 oem 编号删除,不要直接删文件 pnputil /delete-driver oem42.inf /uninstall /force :: 触发一次硬件重新扫描,让新驱动绑定到设备 pnputil /scan-devices这段命令的逻辑是“先入库、再绑定”。/add-driver只把驱动包放进系统驱动存储区,/install才会尝试匹配当前设备。分成两步的好处是:匹配失败时驱动包还在库里,改完 inf 重新加一次就行,不用反复插拔设备。/enum-drivers输出的oemXX.inf编号是回滚的唯一凭据,装之前记下来,出问题用/delete-driver按编号删,比在设备管理器里点“卸载设备”干净得多,后者经常留下残留的服务注册项。
参数上有一点要留意:/force会跳过一些兼容性检查,只在明确知道旧驱动残留导致冲突时用。默认不加,加了之后如果系统里还有别的设备共用同一个服务名,可能把无关设备一起弄挂。
3.3 枚举异常时的排查顺序
装完之后设备管理器里显示正常,不代表链路通了。按下面的顺序看,能快速定位到是哪一层的问题。
第一层看设备状态。设备管理器里右键属性,看“设备状态”里有没有错误码。错误码 10 是设备无法启动,多数是资源冲突或初始化失败;错误码 12 是资源不足,四通道设备容易在中断或内存映射上撞车;错误码 31 是驱动加载失败,回去看签名和服务依赖。
第二层看系统日志。事件查看器里筛“系统”日志,来源选驱动服务名,看加载阶段有没有报错。这一步经常能直接看到“初始化超时”或者“无法映射资源”这类明确信息。
第三层看采集链路。设备枚举成功但采集软件没画面,用系统自带的相机应用或者一个最小采集程序打开设备,看能不能出帧。能出帧说明驱动到系统这一层是通的,问题在信号侧;不能出帧说明驱动内部还有没走完的初始化。
第四层才回头查信号。这时候再去看 SDI 输入有没有锁定、线缆有没有问题,顺序不要倒过来——先确认软件链路通,再查硬件,否则容易在硬件上白花时间。
4. Linux 侧落地:模块编译、设备树节点与时钟配置
Linux 侧的自由度大,坑也多。包里的模块源码能不能编过,取决于内核版本、头文件、配置开关三者是否对齐;编过了能不能加载,取决于设备树节点写得对不对;加载了能不能出帧,取决于时钟和中断有没有配对。这一章按编译、设备树、加载三段走。
4.1 对齐内核头与编译开关
不要直接make。先看包里的Makefile和Kconfig,确认它依赖哪些内核配置项。四通道视频类驱动通常依赖 V4L2 子设备框架、媒体控制器、SPI 或 I2C 子系统,这几个没开,编到一半就会报一堆未定义符号。
# 1. 确认内核头文件版本和当前运行内核一致,不一致时先装对应的头文件包 uname -r ls -l /lib/modules/$(uname -r)/build # 2. 看包里 Makefile 的 KDIR 变量默认指向哪里,必要时显式覆盖 grep -n "KDIR\|PWD\|obj-m" Makefile # 3. 编译,用 -j 加速,但先单线程跑一遍看完整报错 make -C /lib/modules/$(uname -r)/build M=$PWD modules # 4. 编译产物检查,确认 .ko 生成了再谈加载 modinfo ./gv7704.ko第一段uname -r和/lib/modules/.../build必须指向同一个内核版本。升级过内核但没重启的机器上,这两个经常不一致,编出来的模块加载时会报invalid module format。第二段看KDIR,有些包的 Makefile 把路径写死成作者自己的内核目录,直接编会去一个不存在的路径找头文件,显式传KDIR=覆盖掉就行。
第三段编译时先不加-j,因为并行编译会把多个错误混在一起,第一个错误才是根因。等单线程能过,再加-j提速。第四段用modinfo看模块的依赖和参数,输出里depends:一栏如果为空但你用了 SPI 接口,说明编译时没链接到 SPI 子系统,加载后会报符号错误。
内核配置这一块,用zcat /proc/config.gz | grep或者看/boot/config-$(uname -r),确认CONFIG_MEDIA_SUPPORT、CONFIG_VIDEO_V4L2_SUBDEV_API、CONFIG_SPI这几项是y或m。如果是m,对应的模块要先modprobe进来,否则你的驱动 probe 时会因为找不到父设备而失败,报错信息通常只写-EPROBE_DEFER,看起来像驱动自己的问题,其实是依赖没就绪。
4.2 设备树里必须写对的五个属性
设备树节点是 Linux 侧最容易写错的地方,错一个属性,probe 就进不去,而且报错信息往往很含糊。下面这五个属性是必须确认的。
&spi1 { status = "okay"; /* 片选号、频率要和硬件原理图对上,频率过高会导致寄存器写入不稳定 */ cs-gpios = <&gpio2 14 GPIO_ACTIVE_LOW>; sdi_rx: gv7704@0 { compatible = "vendor,gv7704"; /* 必须和驱动 of_match_table 完全一致 */ reg = <0>; /* 片选索引,多片时依次递增 */ spi-max-frequency = <10000000>; /* 先保守给 10MHz,跑通再往上提 */ reset-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; clocks = <&clk_27m>; /* 参考时钟,名字要和 clock-names 对应 */ clock-names = "refclk"; interrupt-parent = <&gpio3>; interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; status = "okay"; }; };逐条说。compatible是最容易错的一个,驱动里of_match_table写的是vendor,gv7704,设备树里写成vendor,gv7704-rx就匹配不上,probe 根本不会调用,dmesg里只有一行“设备未绑定驱动”。这个字符串以包里 dts 示例为准,不要自己发明。
reg是片选索引,不是寄存器地址。一片芯片挂在一个片选上就写 0,两片写 0 和 1,和cs-gpios的顺序要对应。
spi-max-frequency建议先给保守值。寄存器写入对时序敏感,频率给到控制器上限,短线上可能没事,长线或者经过电平转换的板子上就会偶发写失败。先 10MHz 跑通,再逐步往上提,每提一次都做一遍回读校验。
reset-gpios的极性要按原理图来。有的板子复位是低有效,有的是高有效,写反了要么芯片一直复位、要么永远不释放。上电后先量一下这个引脚的电平,和驱动里的配置对一遍。
clocks和clock-names必须成对且名字一致,驱动里clk_get(dev, "refclk")取的就是这个名字。少了这个,驱动在 PLL 配置阶段拿不到时钟句柄,大概率直接返回失败。
4.3 加载后读 dmesg 的四个关键行
模块加载完不要只看lsmod里有没有它,要看dmesg。四通道驱动的加载过程会打好几行日志,这四行能定位到具体卡在哪一步。
# 清理旧模块,避免版本混用 sudo rmmod gv7704 2>/dev/null sudo insmod ./gv7704.ko dmesg | tail -n 40 # 关注这几类关键字,按出现顺序看 dmesg | grep -i "gv7704\|probe\|refclk\|lock"要看的第一行是 probe 入口。如果连这行都没有,说明设备树没匹配上,回去查compatible。第二行是时钟获取,出现failed to get clock就是clock-names或时钟提供者的问题。第三行是寄存器初始化完成,通常会打印芯片 ID 和 revision,这个值要和你手册上的一致,不一致说明总线读写有问题。第四行是通道锁定状态,四路逐路打印,哪一路没锁上,问题就在那一路的输入侧或者通道配置。
中断这一块单独说一句。四通道芯片常见做法是四路共用一个中断线,驱动里要读状态寄存器判断是哪一路触发。如果设备树里中断类型写成边沿触发而硬件是电平保持,会出现中断只来一次后续全丢的现象,表现是采集软件跑几分钟后卡死。
5. 避坑与排查:五条把板子跑挂的血泪记录
下面这五条都是实际调试里踩出来的,写的是现象、原因、解决三段。放在一起是因为它们的共同点是:报错信息指向的位置和真正的问题位置不一致,照着报错查会走偏。
5.1 电源、时钟与复位相关的三条
第一条:寄存器能读不能写,读出来全是默认值。
现象是驱动 probe 阶段打印的芯片 ID 正确,但后续写配置寄存器再回读,值没变。原因多半在复位或写保护:复位脚没释放干净,芯片处于部分复位状态,读通道正常但写通道被锁;或者芯片有个全局写保护位,初始化表里漏了打开这一步。解决顺序是先量复位脚电平,确认驱动拉高之后引脚真的变高(有些板子有外部下拉,GPIO 驱动能力不够拉不上去);再看初始化表里有没有使能写的控制位;最后用示波器看 SPI 写时序,确认片选和数据的关系正确。
第二条:四路里只有一路能锁上,其余三路锁定状态位一直为低。
现象是通道 0 正常出图,通道 1 到 3 的锁定位读出来是 0,但输入信号用示波器量是有的。原因通常是全局 PLL 配置只覆盖了单通道场景,四通道需要额外的时钟分发使能;另一种可能是通道使能寄存器的位定义是按通道分组的,初始化表只写了通道 0 那一组。解决办法是回读全局时钟配置区,确认四个通道的时钟分发位都置起来了,再逐通道检查使能位。
第三条:跑一段时间后丢锁,重启就恢复,过一会儿又丢。
现象是采集正常几分钟到几十分钟后画面卡住,dmesg里有锁定丢失的日志,重新加载模块就好。原因是参考时钟抖动偏大或者电源纹波超标,芯片在临界状态,温度上来之后漂出锁定窗口。这类问题最容易被当成“驱动不稳定”,其实是硬件余量不够。解决方式是用示波器看参考时钟的相位噪声,以及带载情况下电源的纹波;软件侧可以临时加锁定状态轮询和自动重配,但这只是遮症状,硬件余量还是要补。
5.2 总线与枚举相关的两条
第四条:Windows 下装完驱动设备正常,采集软件打开设备报错。
现象是设备管理器里设备图标正常,没有感叹号,但采集程序调用打开接口时返回失败。原因是驱动装上了但服务没有真正启动,或者 inf 里的服务启动类型写成了手动。设备管理器显示的“正常”只代表驱动绑定成功,不代表设备已经初始化完成。解决方式是看事件查看器里服务启动阶段的日志,以及在设备属性的“驱动程序”页里确认服务状态;必要时把启动类型改成自动,再重新插拔设备。
第五条:Linux 下模块加载成功,但dmesg里没有任何 probe 日志。
现象是lsmod能看到模块,insmod也没报错,但设备树节点像是被忽略了。原因通常是compatible不匹配,或者设备树没有真正编译进启动用的那份 dtb。改了 dts 但没重新编译、或者编译了但启动用的是另一份 dtb,都会出现这个现象。解决办法是先确认/proc/device-tree下有没有你的节点,没有就是 dtb 没更新;有节点但没 probe,就去比对compatible字符串,逐字符比,包括厂商前缀。
6. 验证收尾:寄存器回读与彩条自检的两步法
驱动跑起来只是第一步,能不能稳定出帧要看验证。我一般用两步:先做寄存器回读比对,再做彩条自检。回读比对解决“配置到底写进去没有”,彩条自检解决“数据通路到底通不通”,两步都过了,才敢说这条链路是好的。
回读脚本不用复杂,把初始化表写成键值对,逐条写进去再读出来比。
import spidev # 地址和期望值来自包内初始化表,这里只放结构示意 INIT_TABLE = [ (0x0000, 0x01), # 全局使能 (0x0004, 0x1B), # 参考时钟分频 (0x0100, 0x80), # 通道0 均衡增益 ] spi = spidev.SpiDev() spi.open(1, 0) # 总线号、片选号要和设备树一致 spi.max_speed_hz = 10_000_000 # 先保守,稳定后再提 def write_reg(addr, val): spi.xfer2([0x80 | (addr >> 8), addr & 0xFF, val]) def read_reg(addr): resp = spi.xfer2([(addr >> 8) & 0x7F, addr & 0xFF, 0x00]) return resp[2] for addr, val in INIT_TABLE: write_reg(addr, val) failed = [] for addr, val in INIT_TABLE: got = read_reg(addr) if got != val: failed.append((hex(addr), hex(val), hex(got))) print("mismatch:", failed if failed else "none")这段代码的关键在0x80这个读写标志位,写操作置位、读操作清零,具体位定义以手册为准,不同 revision 可能不同。max_speed_hz先给 10MHz 是有意的,跑通之后再往上提,每提一次重跑一遍比对脚本。输出的mismatch列表为空,说明配置链路是干净的;有值不一致,先怀疑时序和片选,再怀疑初始化表版本不对。
彩条自检更直接:让芯片内部生成测试图样,或者从信号源送 100% 彩条进来,用采集端抓一帧看颜色和位置。四路都要抓,不要只验一路——四通道芯片的问题经常是某一路的通道配置漏了。抓到的图如果颜色对但边缘有横纹,多半是时钟相位问题;如果整幅偏色,先查数据位序有没有接反。
从那以后我每次上电都强制走一遍回读比对,宁可多花两分钟,也不想再经历一次“以为装好了、现场却黑屏”的尴尬。希望帮到你。
本文还有配套的精品资源,点击获取