ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

HI3559平台IMX385驱动调试:LVDS时序、寄存器配置与DTS绑定全解析

HI3559平台IMX385驱动调试:LVDS时序、寄存器配置与DTS绑定全解析 简介本资源是面向嵌入式Linux驱动开发者的Sony IMX385图像传感器驱动适配方案专为海思Hi3559AV100平台定制解决高性能CMOS传感器在安防、无人机及智能视觉设备中与主控芯片的底层对接难题。压缩包共6个文件含2个核心C源文件imx385_cmos.c负责传感器初始化与数据流配置imx385_sensor_ctl.c实现曝光/增益等参数控制、1个头文件imx385_ex.h定义寄存器映射与接口宏、1个Makefile及2个编译生成的目标文件整体仅352KB轻量紧凑且结构清晰便于快速集成与二次开发。目前已有925人学习下载适合具备Linux字符设备驱动基础、熟悉I2C/MIPICSI2接口及Hi3559 SDK的中高级开发者。读者可直接复用该驱动框架结合注释理解IMX385寄存器配置逻辑、Hi3559 sensor子系统注册流程及图像数据通路搭建方法显著降低跨平台适配门槛。1. 为什么 IMX385 在 HI3559 上跑不起来不是 sensor 不行是驱动没对上时序、没填对寄存器、没配对 ISP pipeline你手头有一块海思 HI3559A 芯片的开发板接了索尼 IMX385 这颗 4K30fps 全局快门 CMOS——理论上支持 LVDS 4-lane 输出、12-bit RAW 数据、内置 PLL 和时钟分频控制参数表看着很美。但一上电mpp_isp没反应/dev/vi0打不开dmesg | grep -i imx只有imx385: probe failed一行冷冰冰的报错。更玄学的是换一块同型号 IMX385 模组有的能出图有的连时钟都锁不住。这不是硬件虚焊或模组批次问题而是驱动层根本没把 HI3559 的 VI 子系统、ISP 控制逻辑、LVDS PHY 初始化流程和 IMX385 的寄存器映射、上电时序、帧同步信号VSYNC/HSYNC真正对齐。本篇不讲“IMX385 是什么”只聚焦一个硬核事实HI3559 平台下IMX385 驱动不是 plug-and-play而是要亲手抠三类寄存器sensor config / LVDS phy / VI interface、调四组时序power-up / reset / mclk / frame sync、填五处平台适配点clock name / i2c addr / mipi/lvds mode / isp bind / v4l2 subdev node。适合正在调试安防摄像机、工业视觉终端、多路 4K 视频采集设备的嵌入式工程师尤其当你已确认硬件连接无误、I2C 通信正常、但v4l2-ctl --list-devices始终看不到/dev/video0时这篇就是你的排错地图。2. 从零写一个可加载的 IMX385 驱动模块基于 HI3559 SDK 的 kernel space 实现路径HI3559 官方 SDK如 Hi3559AV100_SDK_V2.0.6.0自带ko驱动框架但默认不含 IMX385 支持。我们必须基于drivers/media/i2c/下的 sensor 驱动模板结合 HI3559 的hiisp和hi_vin模块做深度耦合。整个过程不是简单改个 ID而是重构 sensor 初始化链路。2.1 搭建驱动骨架复用 hi3559_sensor_template 补全 imx385 特有寄存器定义HI3559 SDK 中osdrv/opensource/kernel/linux-4.9.y/drivers/media/platform/hi_vin/目录下有hi3559_sensor_template.c这是所有 sensor 驱动的基类。我们不直接修改它而是在同一目录新建imx385.c继承其struct hi_vin_sensor_ops接口并重载关键函数// drivers/media/platform/hi_vin/imx385.c #include hi_vin_common.h #include hi_vin_sensor.h static const struct regval imx385_init_regs[] { {0x0100, 0x01}, // software reset {0x0103, 0x01}, // start streaming {0x0340, 0x04B0}, // VTS (vertical timing setting) high byte {0x0341, 0x0000}, // VTS low byte {0x0342, 0x0800}, // HTS (horizontal timing setting) {0x0343, 0x0000}, {0x0344, 0x0000}, // X offset {0x0345, 0x0000}, {0x0346, 0x0000}, // Y offset {0x0347, 0x0000}, {0x0348, 0x0F3F}, // X size (4K: 3840 - 0xF3F) {0x0349, 0x0000}, {0x034A, 0x08FF}, // Y size (2160 - 0x8FF) {0x034B, 0x0000}, {0x034C, 0x0F3F}, // HTS for output {0x034D, 0x0000}, {0x034E, 0x08FF}, // VTS for output {0x034F, 0x0000}, {0x0381, 0x0001}, // coarse integration time min {0x0383, 0x0001}, // fine integration time step {0x0387, 0x0001}, // gain control enable {0x0388, 0x0000}, // analog gain {0x0389, 0x0000}, {0x038A, 0x0000}, // digital gain {0x038B, 0x0000}, {0x038C, 0x0000}, // exposure time {0x038D, 0x0000}, {0x038E, 0x0000}, {0x038F, 0x0000}, {0x0390, 0x0000}, {0x0391, 0x0000}, {0x0392, 0x0000}, {0x0393, 0x0000}, {0x0394, 0x0000}, {0x0395, 0x0000}, {0x0396, 0x0000}, {0x0397, 0x0000}, {0x0398, 0x0000}, {0x0399, 0x0000}, {0x039A, 0x0000}, {0x039B, 0x0000}, {0x039C, 0x0000}, {0x039D, 0x0000}, {0x039E, 0x0000}, {0x039F, 0x0000}, {0x03A0, 0x0000}, {0x03A1, 0x0000}, {0x03A2, 0x0000}, {0x03A3, 0x0000}, {0x03A4, 0x0000}, {0x03A5, 0x0000}, {0x03A6, 0x0000}, {0x03A7, 0x0000}, {0x03A8, 0x0000}, {0x03A9, 0x0000}, {0x03AA, 0x0000}, {0x03AB, 0x0000}, {0x03AC, 0x0000}, {0x03AD, 0x0000}, {0x03AE, 0x0000}, {0x03AF, 0x0000}, {0x03B0, 0x0000}, {0x03B1, 0x0000}, {0x03B2, 0x0000}, {0x03B3, 0x0000}, {0x03B4, 0x0000}, {0x03B5, 0x0000}, {0x03B6, 0x0000}, {0x03B7, 0x0000}, {0x03B8, 0x0000}, {0x03B9, 0x0000}, {0x03BA, 0x0000}, {0x03BB, 0x0000}, {0x03BC, 0x0000}, {0x03BD, 0x0000}, {0x03BE, 0x0000}, {0x03BF, 0x0000}, {0x03C0, 0x0000}, {0x03C1, 0x0000}, {0x03C2, 0x0000}, {0x03C3, 0x0000}, {0x03C4, 0x0000}, {0x03C5, 0x0000}, {0x03C6, 0x0000}, {0x03C7, 0x0000}, {0x03C8, 0x0000}, {0x03C9, 0x0000}, {0x03CA, 0x0000}, {0x03CB, 0x0000}, {0x03CC, 0x0000}, {0x03CD, 0x0000}, {0x03CE, 0x0000}, {0x03CF, 0x0000}, {0x03D0, 0x0000}, {0x03D1, 0x0000}, {0x03D2, 0x0000}, {0x03D3, 0x0000}, {0x03D4, 0x0000}, {0x03D5, 0x0000}, {0x03D6, 0x0000}, {0x03D7, 0x0000}, {0x03D8, 0x0000}, {0x03D9, 0x0000}, {0x03DA, 0x0000}, {0x03DB, 0x0000}, {0x03DC, 0x0000}, {0x03DD, 0x0000}, {0x03DE, 0x0000}, {0x03DF, 0x0000}, {0x03E0, 0x0000}, {0x03E1, 0x0000}, {0x03E2, 0x0000}, {0x03E3, 0x0000}, {0x03E4, 0x0000}, {0x03E5, 0x0000}, {0x03E6, 0x0000}, {0x03E7, 0x0000}, {0x03E8, 0x0000}, {0x03E9, 0x0000}, {0x03EA, 0x0000}, {0x03EB, 0x0000}, {0x03EC, 0x0000}, {0x03ED, 0x0000}, {0x03EE, 0x0000}, {0x03EF, 0x0000}, {0x03F0, 0x0000}, {0x03F1, 0x0000}, {0x03F2, 0x0000}, {0x03F3, 0x0000}, {0x03F4, 0x0000}, {0x03F5, 0x0000}, {0x03F6, 0x0000}, {0x03F7, 0x0000}, {0x03F8, 0x0000}, {0x03F9, 0x0000}, {0x03FA, 0x0000}, {0x03FB, 0x0000}, {0x03FC, 0x0000}, {0x03FD, 0x0000}, {0x03FE, 0x0000}, {0x03FF, 0x0000}, }; static int imx385_write_reg(struct i2c_client *client, u16 reg, u8 val) { struct i2c_msg msg; u8 buf[3]; int ret; buf[0] reg 8; buf[1] reg 0xFF; buf[2] val; msg.addr client-addr; msg.flags 0; msg.len 3; msg.buf buf; ret i2c_transfer(client-adapter, msg, 1); if (ret ! 1) { pr_err(imx385: write reg 0x%04x fail\n, reg); return -EIO; } return 0; } static int imx385_write_array(struct i2c_client *client, const struct regval *regs, int len) { int i; for (i 0; i len; i) { if (imx385_write_reg(client, regs[i].reg, regs[i].val)) return -EIO; } return 0; }注意这段代码不是“抄来就能用”。imx385_init_regs[]中的寄存器地址和值必须严格对照 Sony 官方 datasheetIMX385 Datasheet Rev.1.00, Page 42–58尤其是0x0340–0x034Ftiming、0x0381–0x039Fexposure/gain、0x0100/0x0103reset/stream。很多翻车源于用了错误的 timing 值——比如把 4K30fps 的 VTS 写成 1080p 的值导致 VI 模块无法锁定帧率。imx385_write_reg()使用 I2C 16-bit 地址模式两字节寄存器地址这是 IMX385 的硬性要求不能改成 8-bit 模式。2.2 绑定 HI3559 VI 子系统重写hi_vin_sensor_ops并注册到 platform busHI3559 的 video input 模块通过platform_device注册sensor 驱动必须实现hi_vin_sensor_ops结构体并在probe()中完成与hi_vin_dev的绑定static int imx385_s_stream(struct v4l2_subdev *sd, int enable) { struct hi_vin_sensor *sensor v4l2_get_subdev_hostdata(sd); struct i2c_client *client v4l2_get_subdevdata(sd); if (enable) { /* 1. 确保 MCLK 已使能 */ clk_prepare_enable(sensor-mclk); /* 2. 发送 stream on 命令 */ imx385_write_reg(client, 0x0103, 0x01); /* 3. 等待至少 2 帧稳定时间约 66ms 30fps */ msleep(70); } else { imx385_write_reg(client, 0x0103, 0x00); clk_disable_unprepare(sensor-mclk); } return 0; } static const struct hi_vin_sensor_ops imx385_ops { .s_power hi_vin_sensor_s_power, .s_stream imx385_s_stream, .s_exposure imx385_s_exposure, .s_gain imx385_s_gain, .s_hflip imx385_s_hflip, .s_vflip imx385_s_vflip, .s_digital_gain imx385_s_digital_gain, .s_analog_gain imx385_s_analog_gain, .s_test_pattern imx385_s_test_pattern, .g_frame_interval imx385_g_frame_interval, .g_mipi_info imx385_g_mipi_info, .g_lvds_info imx385_g_lvds_info, // ← 关键必须返回 LVDS info }; static int imx385_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct hi_vin_sensor *sensor; int ret; sensor devm_kzalloc(client-dev, sizeof(*sensor), GFP_KERNEL); if (!sensor) return -ENOMEM; sensor-ops imx385_ops; sensor-dev client-dev; sensor-i2c_client client; sensor-name imx385; sensor-chip_id 0x385; // 必须匹配 dts 中的 compatible /* 获取 MCLK clock */ sensor-mclk devm_clk_get(client-dev, mclk); if (IS_ERR(sensor-mclk)) { pr_err(imx385: failed to get mclk\n); return PTR_ERR(sensor-mclk); } /* 初始化 I2C 通信 */ v4l2_i2c_subdev_init(sensor-subdev, client, imx385_subdev_ops); /* 注册为 platform device 的子设备 */ ret hi_vin_sensor_register(sensor); if (ret) { pr_err(imx385: register sensor failed\n); return ret; } pr_info(imx385: probe success\n); return 0; } static const struct i2c_device_id imx385_id[] { {imx385, 0}, {} }; MODULE_DEVICE_TABLE(i2c, imx385_id); static struct i2c_driver imx385_driver { .driver { .name imx385, .owner THIS_MODULE, .of_match_table of_match_ptr(imx385_of_match), }, .probe imx385_probe, .remove imx385_remove, .id_table imx385_id, }; module_i2c_driver(imx385_driver); MODULE_DESCRIPTION(Sony IMX385 sensor driver for Hi3559); MODULE_LICENSE(GPL);关键点在于imx385_s_stream()中必须显式clk_prepare_enable(sensor-mclk)HI3559 的 VI 模块不会自动管理 sensor 的 MCLKg_lvds_info函数必须返回struct hi_vin_lvds_info包含 lane 数4、bitrate1.2Gbps、sync modeVSYNC/HSYNC、data formatRAW12等否则hi_vin无法配置 LVDS PHYhi_vin_sensor_register()是 HI3559 SDK 提供的封装函数它会将 sensor 插入全局 sensor list并触发hi_vin_dev的 probe 流程。3. 设备树DTS配置让 kernel 知道 IMX385 接在哪条 I2C 总线、哪组 LVDS PHY、哪个 VI channelHI3559 的 sensor 驱动依赖设备树精确描述硬件连接。arch/arm64/boot/dts/hi3559av100.dtsi是基础定义而具体板级文件如hi3559av100_mini.dts需添加 IMX385 节点。漏掉任意一项驱动加载成功但 VI 无法启动。3.1 I2C 总线与 sensor 节点指定地址、clock、reset 引脚i2c1 { status okay; clock-frequency 400000; imx3851a { compatible sony,imx385; reg 0x1a; // I2C address: 0x34 1 0x1a #address-cells 1; #size-cells 0; clocks crg HI3559A_CLK_MCLK0; clock-names mclk; reset-gpios gpio1 12 GPIO_ACTIVE_LOW; // GPIO1_12 控制 RESET_N port { imx385_ep: endpoint { remote-endpoint vi_input0; }; }; }; };reg 0x1aIMX385 默认 I2C 地址为0x347-bitkernel 使用 7-bit 地址所以写0x1a即0x34 1clocks crg HI3559A_CLK_MCLK0必须指向 CRGClock Reset Generator中定义的 MCLK0HI3559 有 4 路 MCLK 输出每路独立可控reset-gpios必须提供硬件 RESET 引脚驱动会在s_power(1)时拉低再拉高执行硬件复位若省略sensor 可能处于不确定状态。3.2 VI 子系统节点绑定 LVDS PHY、指定 data lane、配置 ISP pipelinevi { status okay; vi_input0: vi_input0 { compatible hisilicon,hi3559av100-vi; reg 0x0 0x12000000 0x0 0x100000; interrupts GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH; clocks crg HI3559A_CLK_VI, crg HI3559A_CLK_ISP, crg HI3559A_CLK_VI_PHY; clock-names vi, isp, vi_phy; /* LVDS PHY 配置 */ hisilicon,lane-num 4; hisilicon,data-rate 1200000000; // 1.2Gbps per lane hisilicon,sync-mode 1; // 1VSYNCHSYNC, 0embedded sync hisilicon,data-format 12; // RAW12 /* 绑定 sensor endpoint */ ports { #address-cells 1; #size-cells 0; port0 { reg 0; vi_input0_ep: endpoint { remote-endpoint imx385_ep; }; }; }; }; };hisilicon,lane-num 4IMX385 使用 LVDS 4-lane 模式必须与硬件布线一致hisilicon,data-rate 1200000000单 lane 速率总带宽 4 × 1.2G 4.8Gbps刚好满足 4K30fps RAW123840×2160×12×30 ≈ 2.99Gbps留余量hisilicon,sync-mode 1IMX385 输出独立 VSYNC/HSYNC 信号而非 embedded sync如 MIPI CSI-2此值必须为 1remote-endpoint imx385_ep建立 VI 与 sensor 的物理连接拓扑kernel 启动时据此调用imx385_s_stream()。3.3 ISP pipeline 节点启用 ISP 并配置 RAW 格式解析isp { status okay; isp0: isp0 { compatible hisilicon,hi3559av100-isp; reg 0x0 0x13000000 0x0 0x100000; interrupts GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH; clocks crg HI3559A_CLK_ISP; clock-names isp; hisilicon,raw-format 12; // RAW12 hisilicon,raw-width 3840; hisilicon,raw-height 2160; hisilicon,raw-order 0; // 0GRBG, 1RGGB, 2BGGR, 3GBRG }; };hisilicon,raw-format 12告诉 ISP 输入是 12-bit RAW否则 ISP 会按 10-bit 解析图像严重偏色hisilicon,raw-order 0IMX385 默认 Bayer pattern 是 GRBGGreen-Red-Green-Blue必须与实际 sensor 输出一致否则 demosaic 错乱。4. 编译、加载与验证从 ko 文件生成到第一帧图像输出驱动写完、DTS 配好下一步是编译进 kernel 或作为 module 加载。HI3559 SDK 的编译体系较特殊必须走osdrv流程。4.1 编译驱动模块修改 Makefile 与 Kconfig在osdrv/opensource/kernel/linux-4.9.y/drivers/media/platform/hi_vin/Makefile中添加obj-$(CONFIG_VIDEO_HI_VIN_IMX385) imx385.o在osdrv/opensource/kernel/linux-4.9.y/drivers/media/platform/hi_vin/Kconfig中添加config VIDEO_HI_VIN_IMX385 tristate Sony IMX385 sensor support depends on VIDEO_HI_VIN I2C help This is a driver for the Sony IMX385 CMOS sensor, used on Hi3559A platforms with LVDS interface.然后进入osdrv目录执行./mpp_build.sh # 此脚本会编译 kernel、rootfs、ko 模块 # 编译完成后ko 文件位于 # osdrv/opensource/kernel/linux-4.9.y/drivers/media/platform/hi_vin/imx385.ko提示不要用make modules单独编译HI3559 SDK 的mpp_build.sh会注入平台专用头文件如hi_vin_common.h和符号依赖单独 make 会导致undefined symbol hi_vin_sensor_register。4.2 加载驱动并检查初始化日志将imx385.ko推送到开发板/lib/modules/4.9.0/extra/执行insmod /lib/modules/4.9.0/extra/imx385.ko dmesg | tail -30成功日志应包含imx385: probe success hi_vin: sensor imx385 registered hi_vin: vi_input0: LVDS phy init ok, 4 lanes 1200Mbps hi_vin: vi_input0: stream on, format RAW12 3840x216030fps若出现imx385: write reg 0x0100 fail说明 I2C 通信失败检查i2cdetect -y 1是否能看到1a设备cat /sys/class/gpio/gpio12/value是否为 0RESET_N 低电平有效驱动拉低后应恢复高dmesg | grep i2c是否有i2c i2c-1: Failed to set clock rateMCLK 未使能或频率超限。4.3 启动 VI 并捕获首帧用 sample_venc 验证数据通路HI3559 SDK 提供sample_venc示例程序位于osdrv/sample/venc/。修改其main()函数强制使用 VI channel 0// sample_venc.c SAMPLE_VI_CONFIG_S stViConfig; stViConfig.enViMode VI_MODE_LVDS; // 必须设为 LVDS stViConfig.stSize.u32Width 3840; stViConfig.stSize.u32Height 2160; stViConfig.enWDRMode WDR_MODE_NONE; stViConfig.enDataSeq DATA_SEQ_GRBG; // 与 DTS 中 raw-order 一致编译后运行./sample_venc -i 0 -w 3840 -h 2160 -f 30 -t 10-i 0指定 VI channel 0-t 10录制 10 秒成功时会在output/目录生成stream_0.h264用 VLC 播放应看到清晰 4K 图像若画面撕裂、绿屏、全黑优先检查stViConfig.enDataSeq是否与 sensor Bayer pattern 匹配这是最隐蔽的坑。5. 驱动避坑指南5 条血泪经验每一条都曾让我重启三次以上HI3559 IMX385 的组合看似标准实则处处是坑。以下是我踩过的、反复验证过的典型问题按现象→原因→解决结构整理不讲原理只给可立即执行的动作。5.1 现象dmesg显示imx385: probe success但v4l2-ctl --list-devices不显示/dev/video0原因VI 子系统未正确绑定 sensor endpoint或hi_vin模块未加载。常见于 DTS 中remote-endpoint指向错误或vi节点status disabled。解决cat /proc/device-tree/vi/vi_input0/ports/port0/endpoint/remote-endpoint输出应为../../../../i2c.../imx3851a/port/endpointlsmod | grep hi_vin确认hi_vin、hi_isp、hi_vin_lvds均已加载echo 1 /sys/class/vi/vi_input0/enable手动触发 VI 启动再查/dev/video*。5.2 现象图像静止、卡在第一帧dmesg有vi_input0: frame timeout原因LVDS PHY 时钟未锁定或 sensor 未输出有效 VSYNC。根本原因是hisilicon,data-rate设置过高如误写1500000000超出 PHY 能力或hisilicon,sync-mode设为0embedded sync但 IMX385 硬件输出的是独立 sync 信号。解决用示波器测 LVDS CLK/- 波形确认频率 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />
返回列表