image-20221123145359081
image-20221123145359081
image-20221123145359081
image-20221123145359081
image-20221123145359081

1 概述

编写目的:介绍camera 模块在sunxi 平台上的开发流程。

适用范围:本文档目前适用于tina3.0 以上具备camera 的硬件平台。

2 模块介绍

2.1 模块功能介绍

用于接收并行或者mipi 接口的sensor 信号或者是bt656 格式的信号。

2.2 硬件介绍

目前Tina 系统的各平台camera 硬件接口、linux 内核版本以及camera 驱动框架如下表所示:

表2-1: 平台CSI 框架
平台支持接口是否具备ISP模块linux 内核版本camera 驱动框架
F35并口csi、mipi3.4VFE
R16并口csi3.4VFE
R18并口csi4.4VFE
R30并口csi4.4VFE
R40并口csi3.10VFE
R311mipi csi4.9VIN
MR133mipi csi4.9VIN
R818mipi csi4.9VIN
MR813mipi csi4.9VIN
R528并口csi5.4VIN
V536并口csi、mipi4.9VIN
V533并口csi、mipi4.9VIN
V831并口csi、mipi4.9VIN
V833并口csi、mipi4.9VIN
V851并口csi、mipi4.9VIN
V853并口csi、mipi4.9VIN

注意:

  1. 如果平台没有ISP 模块,那么将不支持RAW sensor(即sensor 只输出采集到的原始数据),文档中提到的RAW 等相关信息不用理会;
  2. 如果平台没有支持mipi 接口,文档中提到的mipi 相关信息忽略;
  3. 不同平台可能将使用不同的camera 驱动框架,这点注意区分;

2.3 源码结构介绍

2.3.1 linux3.4 VFE 框架

驱动路径位于linux-3.4/drivers/media/video/sunxi-vfe 下。

sunxi-vfe:.
│ bsp_common.c ;底层bsp共用的函数
│ bsp_common.h ;底层bsp共用函数头文件
│ config.c ;读取sys_config.fex的参数配置和isp参数
│ config.h ;读取sys_config.fex和isp参数函数的头文件
│ Kconfig
│ Makefile
│ platform_cfg.h ;区分各个平台的头文件
│ vfe.c ;v4l2驱动实现主体(包含视频接口和ISP部分)
│ vfe.h ;v4l2驱动头文件
│ vfe_os.c ;系统资源函数实现(pin,clock,memory)
│ vfe_os.h ;系统资源函数头文件
│ vfe_subdev.c ;sensor调用vfe资源函数
│ vfe_subdev.h ;sensor 调用vfe资源函数头文件
│
├─actuator
│ actuator.c ;vcm driver的一般行为
│ actuator.h ;vcm driver的头文件
│ dw9714_act.c ;具体vcm driver型号实现
│ Makefile
│
├─csi
│ bsp_csi.c ;底层csi bsp函数
│ bsp_csi.h ;底层csi bsp函数头文件
│ csi_reg.c ;csi硬件底层实现
│ csi_reg.h ;csi硬件底层实现头文件
│ csi_reg_i.h ;csi 寄存器资源头文件
│
├─device
│ camera.h ;camera公用结构体头文件
│ camera_cfg.h ;camera ioctl扩展命令头文件
│ Makefile
│ ov5640.c ;具体的sensor驱动
│
├─flash_light
│ flash.h ;led补光灯驱动头文件
│ flash_io.c ;led补光灯io控制实现
│ Makefile
│
├─lib
│ bsp_isp.h ;底层isp bsp函数头文件
│ bsp_isp_algo.h ;底层isp 算法bsp函数头文件
│ bsp_isp_comm.h ;底层isp共用函数头文件
│ isp_module_cfg.h ;isp里面各模块功能配置的头文件
│ libisp ;isp的函数库
│ lib_mipicsi2_v1 ;A31mipi库
│ lib_mipicsi2_v2 ;A80/A83mipi库
├─csi_cci
│ cci_helper.c ;cci 与设备相关初始化、注册以及通信等相关函数集实现
│ cci_helper.h ;cci 与设备相关初始化、注册以及通信等相关函数集实现头文件
│ bsp_cci.c ;cci 操作函数集实现
│ bsp_cci.h ;cci 操作函数集头文件
│ csi_cci_reg.c ;cci 底层实现
│ csi_cci_reg.h ;cci 底层实现头文件
│ csi_cci_reg_i.h ;cci寄存器资源头文件
│
├─mipi_csi
│ bsp_mipi_csi.c ;底层mipi bsp函数
│ bsp_mipi_csi.h ;底层mipi bsp函数头文件
│
└─utility
cfg_op.c ;读取ini文件的实现函数
cfg_op.h ;读取ini文件函数对应的头文件
2.3.2 linux3.10 VFE 框架

驱动路径位于linux-3.10/drivers/media/platform/sunxi-vfe 下。

sunxi-vfe:.
│ bsp_common.c ;底层bsp共用的函数
│ bsp_common.h ;底层bsp共用函数头文件
│ config.c ;读取sys_config.fex的参数配置和isp参数
│ config.h ;读取sys_config.fex和isp参数函数的头文件
│ Kconfig
│ Makefile
│ platform_cfg.h ;区分各个平台的头文件
│ vfe.c ;v4l2驱动实现主体(包含视频接口和ISP部分)
│ vfe.h ;v4l2驱动头文件
│ vfe_os.c ;系统资源函数实现(pin,clock,memory)
│ vfe_os.h ;系统资源函数头文件
│ vfe_subdev.c ;sensor调用vfe资源函数
│ vfe_subdev.h ;sensor 调用vfe资源函数头文件
│
├─actuator
│ actuator.c ;vcm driver的一般行为
│ actuator.h ;vcm driver的头文件
│ dw9714_act.c ;具体vcm driver型号实现
│ Makefile
│
├─csi
│ bsp_csi.c ;底层csi bsp函数
│ bsp_csi.h ;底层csi bsp函数头文件
│ csi_reg.c ;csi硬件底层实现
│ csi_reg.h ;csi硬件底层实现头文件
│ csi_reg_i.h ;csi 寄存器资源头文件
│
├─device
│ camera.h ;camera公用结构体头文件
│ camera_cfg.h ;camera ioctl扩展命令头文件
│ Makefile
│ ov5640.c ;具体的sensor驱动
│
├─flash_light
│ flash.h ;led补光灯驱动头文件
│ flash_io.c ;led补光灯io控制实现
│ Makefile
│
├─lib
│ bsp_isp.h ;底层isp bsp函数头文件
│ bsp_isp_algo.h ;底层isp 算法bsp函数头文件
│ bsp_isp_comm.h ;底层isp共用函数头文件
│ isp_module_cfg.h ;isp里面各模块功能配置的头文件
│ libisp ;isp的函数库
│ lib_mipicsi2_v1 ;A31mipi库
│ lib_mipicsi2_v2 ;A80/A83mipi库
├─csi_cci
│ cci_helper.c ;cci 与设备相关初始化、注册以及通信等相关函数集实现
│ cci_helper.h ;cci 与设备相关初始化、注册以及通信等相关函数集实现头文件
│ bsp_cci.c ;cci 操作函数集实现
│ bsp_cci.h ;cci 操作函数集头文件
│ csi_cci_reg.c ;cci 底层实现
│ csi_cci_reg.h ;cci 底层实现头文件
│ csi_cci_reg_i.h ;cci寄存器资源头文件
│
├─mipi_csi
│ bsp_mipi_csi.c ;底层mipi bsp函数
│ bsp_mipi_csi.h ;底层mipi bsp函数头文件
│
└─utility
cfg_op.c ;读取ini文件的实现函数
cfg_op.h ;读取ini文件函数对应的头文件
2.3.3 linux4.4 VFE 框架

驱动路径位于linux-4.4/drivers/media/platform/sunxi-vfe 下。

sunxi-vfe:.
│ bsp_common.c ;底层bsp共用的函数
│ bsp_common.h ;底层bsp共用函数头文件
│ config.c ;读取sys_config.fex的参数配置和isp参数
│ config.h ;读取sys_config.fex和isp参数函数的头文件
│ Kconfig
│ Makefile
│ platform_cfg.h ;区分各个平台的头文件
│ vfe.c ;v4l2驱动实现主体(包含视频接口和ISP部分)
│ vfe.h ;v4l2驱动头文件
│ vfe_os.c ;系统资源函数实现(pin,clock,memory)
│ vfe_os.h ;系统资源函数头文件
│ vfe_subdev.c ;sensor调用vfe资源函数
│ vfe_subdev.h ;sensor 调用vfe资源函数头文件
│
├─actuator
│ actuator.c ;vcm driver的一般行为
│ actuator.h ;vcm driver的头文件
│ dw9714_act.c ;具体vcm driver型号实现
│ Makefile
│
├─csi
│ bsp_csi.c ;底层csi bsp函数
│ bsp_csi.h ;底层csi bsp函数头文件
│ csi_reg.c ;csi硬件底层实现
│ csi_reg.h ;csi硬件底层实现头文件
│ csi_reg_i.h ;csi 寄存器资源头文件
│
├─device
│ camera.h ;camera公用结构体头文件
│ camera_cfg.h ;camera ioctl扩展命令头文件
│ Makefile
│ ov5640.c ;具体的sensor驱动
│
├─flash_light
│ flash.h ;led补光灯驱动头文件
│ flash_io.c ;led补光灯io控制实现
│ Makefile
│
├─lib
│ bsp_isp.h ;底层isp bsp函数头文件
│ bsp_isp_algo.h ;底层isp 算法bsp函数头文件
│ bsp_isp_comm.h ;底层isp共用函数头文件
│ isp_module_cfg.h ;isp里面各模块功能配置的头文件
│ libisp ;isp的函数库
│ lib_mipicsi2_v1 ;A31mipi库
│ lib_mipicsi2_v2 ;A80/A83mipi库
├─csi_cci
│ cci_helper.c ;cci 与设备相关初始化、注册以及通信等相关函数集实现
│ cci_helper.h ;cci 与设备相关初始化、注册以及通信等相关函数集实现头文件
│ bsp_cci.c ;cci 操作函数集实现
│ bsp_cci.h ;cci 操作函数集头文件
│ csi_cci_reg.c ;cci 底层实现
│ csi_cci_reg.h ;cci 底层实现头文件
│ csi_cci_reg_i.h ;cci寄存器资源头文件
│
├─mipi_csi
│ bsp_mipi_csi.c ;底层mipi bsp函数
│ bsp_mipi_csi.h ;底层mipi bsp函数头文件
│
└─utility
cfg_op.c ;读取ini文件的实现函数
cfg_op.h ;读取ini文件函数对应的头文件
2.3.4 linux4.4 VIN 框架

驱动路径位于linux-4.4/drivers/media/platform/sunxi-vin 下。

sunxi-vin:
│ vin.c ;v4l2驱动实现主体(包含视频接口和ISP部分)
│ vin.h ;v4l2驱动头文件
│ top_reg.c ;vin对各v4l2 subdev管理接口实现主体
│ top_reg.h ;管理接口头文件
│ top_reg_i.h ;vin模块接口层部分结构体
│
├─modules
│ ├─actuator
│ │ actuator.c ;vcm driver的一般行为
│ │ actuator.h ;vcm driver的头文件
│ │ dw9714_act.c ;具体vcm driver型号实现
│ │ Makefile
│ ├─flash
│ │ flash.h ;led补光灯驱动头文件
│ │ flash_io.c ;led补光灯io控制实现
│ ├─sensor
│ │ camera.h ;camera公用结构体头文件
│ │ camera_cfg.h ;camera ioctl扩展命令头文件
│ │ sensor_helper.c ;sensor公用操作接口函数文件
│ │ sensor_helper.h ;sensor公用操作接口函数头文件
│ │ Makefile
│ │ ov5640.c ;具体的sensor驱动
│
├─platform
│ platform_cfg.h ;平台相关的配置接口
│
├─utility
│ bsp_common.h ;底层公用的格式配置函数头文件
│ bsp_common.c ;底层公用的格式配置函数文件
│ cfg_op.h ;解析配置文件接口头文件
│ cfg_op.c ;解析配置文件接口函数实现主体
│ config.h ;解析设备树的函数头文件
│ config.c ;解析设备树的接口函数主体
│ sensor_info.h ;sensor列表信息头文件
│ sensor_info.c ;获取sensor列表信息函数主体
│ vin_io.h ;vin框架io操作接口头文件
│ vin_io.c ;vin框架io操作接口文件
│ vin_os.h ;vin框架系统操作接口头文件
│ vin_os.c ;vin框架系统操作接口文件
│ vin_supply.h ;vin框架设置时钟频率等接口头文件
│ vin_supply.c ;vin框架设置时钟频率等接口函数主体
│
├─vin-cci
│ cci_helper.c ;cci 与设备相关初始化、注册以及通信等相关函数集实现
│ cci_helper.h ;cci 与设备相关初始化、注册以及通信等相关函数集实现头文件
│ bsp_cci.c ;cci 操作函数集实现
│ bsp_cci.h ;cci 操作函数集头文件
│ csi_cci_reg.c ;cci 底层实现
│ csi_cci_reg.h ;cci 底层实现头文件
│ csi_cci_reg_i.h ;cci寄存器资源头文件
│ sunxi_cci.c ;cci 接口封装实现
│ sunxi_cci.h ;cci 接口封装头文件
│
├─vin-csi
│ bsp_csi.c ;csi 操作函数集实现
│ bsp_csi.h ;csi 操作函数集头文件
│ csi_reg.c ;csi 底层实现
│ csi_reg.h ;csi 底层实现头文件
│ csi_reg_i.h ;csi寄存器资源头文件
│ parser_reg.c ;csi 底层实现
│ parser_reg.h ;csi 底层实现头文件
│ parser_reg_i.h ;csi寄存器资源头文件
│ sunxi_csi.c ;csi 接口封装实现
│ sunxi_csi.h ;csi 接口封装头文件
│
├─vin-isp
│ bsp_isp.c ;isp操作函数集实现
│ bsp_isp.h ;isp操作函数集头文件
│ bsp_isp_comm.h ;isp结构体定义
│ isp_default_tbl.h ;isp默认配置列表
│ isp_platform_drv.h ;isp平台操作集头文件
│ isp_platform_drv.c ;isp平台操作集实现
│ sunxi_isp.h ;sunxi平台isp操作集头文件
│ sunxi_isp.c ;sunxi平台isp操作集实现
│
├─vin-mipi
│ bsp_mipi_csi.c ;底层mipi bsp函数
│ bsp_mipi_csi.h ;底层mipi bsp函数头文件
│ bsp_mipi_csi_v1.c ;sunxi平台底层mipi bsp接口函数
│ sunxi_mipi.c ;sunxi平台mipi实现
│ bsp_mipi_csi.h ;sunxi平台mipi实现头文件
│ combo_common.c ;combo common头文件
│ protocol.h ;protocol头文件
├─vin-stat
│ vin_h3a.c ;vin 3a操作函数
│ vin_h3a.h ;vin 3a操作函数头文件
│ vin_ispstat.c ;sunxi isp stat操作函数
│ vin_ispstat.h ;sunxi isp stat操作函数头文件
│
├─vin-video
│ dma_reg.c ;csi模块dma操作函数
│ dma_reg.h ;csi模块dma操作函数头文件
│ dma_reg_i.h ;csi dma寄存器资源头文件
│ vin_core.c ;vin video核心函数
│ vin_core.h ;vin video核心函数头文件
│ vin_video.c ;vin video设备接口函数
│ vin_video.h ;vin video设备接口函数头文件
├─vin-vipp
│ sunxi_scaler.c ;scaler 子设备操作函数集
│ sunxi_scaler.h ;caler 子设备操作函数头文件
│ vipp_reg.c ;vipp操作函数集
│ vipp_reg.h ;vipp操作函数集头文件
│ vipp_reg_i.h ;vipp操作函数集资源头文件
2.3.5 linux4.9 VIN 框架

驱动路径位于linux-4.9/drivers/media/platform/sunxi-vin 下。

sunxi-vin:
│ vin.c ;v4l2驱动实现主体(包含视频接口和ISP部分)
│ vin.h ;v4l2驱动头文件
│ top_reg.c ;vin对各v4l2 subdev管理接口实现主体
│ top_reg.h ;管理接口头文件
│ top_reg_i.h ;vin模块接口层部分结构体
├── modules
│ ├── actuator ;vcm driver
│ │ ├── actuator.c
│ │ ├── actuator.h
│ │ ├── dw9714_act.c
│ │ ├── Makefile
│ ├── flash ;闪光灯driver
│ │ ├── flash.c
│ │ └── flash.h
│ └── sensor ;sensor driver
│ ├── ar0144_mipi.c
│ ├── camera_cfg.h ;camera ioctl扩展命令头文件
│ ├── camera.h ;camera公用结构体头文件
│ ├── Makefile
│ ├── ov2775_mipi.c
│ ├── ov5640.c
│ ├── sensor-compat-ioctl32.c
│ ├── sensor_helper.c ;sensor公用操作接口函数文件
│ ├── sensor_helper.h
├── platform ;平台相关的配置接口
├── utility
│ ├── bsp_common.c
│ ├── bsp_common.h
│ ├── cfg_op.c
│ ├── cfg_op.h
│ ├── config.c
│ ├── config.h
│ ├── sensor_info.c
│ ├── sensor_info.h
│ ├── vin_io.h
│ ├── vin_os.c
│ ├── vin_os.h
│ ├── vin_supply.c
│ └── vin_supply.h
├── vin-cci
│ ├── sunxi_cci.c
│ └── sunxi_cci.h
├── vin-csi
│ ├── parser_reg.c
│ ├── parser_reg.h
│ ├── parser_reg_i.h
│ ├── sunxi_csi.c
│ └── sunxi_csi.h
├── vin-isp
│ ├── sunxi_isp.c
│ └── sunxi_isp.h
├── vin-mipi
│ ├── sunxi_mipi.c
│ └── sunxi_mipi.h
├── vin-stat
│ ├── vin_h3a.c
│ ├── vin_h3a.h
│ ├── vin_ispstat.c
│ └── vin_ispstat.h
├── vin_test
├── vin-video
│ ├── vin_core.c
│ ├── vin_core.h
│ ├── vin_video.c
│ └── vin_video.h
└── vin-vipp
├── sunxi_scaler.c
├── sunxi_scaler.h
├── vipp_reg.c
├── vipp_reg.h
└── vipp_reg_i.h
2.3.6 linux5.4 VIN 框架

驱动路径位于linux-5.4/drivers/media/platform/sunxi-vin 下。

sunxi-vin:
├── Kconfig
├── Makefile
├── modules
│ ├── actuator ;vcm driver
│ │ ├── actuator.c
│ │ ├── actuator.h
│ │ ├── dw9714_act.c
│ │ ├── Makefile
│ ├── flash ;flash driver
│ │ ├── flash.c
│ │ └── flash.h
│ ├── sensor ;cmos sensor driver
│ │ ├── camera_cfg.h
│ │ ├── camera.h
│ │ ├── gc0310_mipi.c
│ │ ├── Makefile
│ │ ├── ov2710_mipi.c
│ │ ├── ov5640.c
│ │ ├── sensor-compat-ioctl32.c
│ │ ├── sensor_helper.c
│ │ ├── sensor_helper.h
│ ├── sensor-list
│ │ ├── sensor_list.c
│ │ └── sensor_list.h
│ └── sensor_power ;sensor上下电接口函数
│ ├── Makefile
│ ├── sensor_power.c
│ └── sensor_power.h
├── platform
├── top_reg.c
├── top_reg.h
├── top_reg_i.h
├── utility ;驱动通用接口
│ ├── bsp_common.c
│ ├── bsp_common.h
│ ├── cfg_op.c
│ ├── cfg_op.h
│ ├── config.c
│ ├── config.h
│ ├── vin_io.h
│ ├── vin_os.c
│ ├── vin_os.h
│ ├── vin_supply.c
│ └── vin_supply.h
├── vin.c ;sunxi-vin驱动注册入口
├── vin-cci ;i2c操作相关接口
│ ├── bsp_cci.c
│ ├── bsp_cci.h
│ ├── cci_helper.c
│ ├── cci_helper.h
│ ├── Kconfig
│ ├── sunxi_cci.c
│ └── sunxi_cci.h
├── vin-csi ;csi操作相关接口
│ ├── sunxi_csi.c
│ └── sunxi_csi.h
├── vin.h
├── vin-isp ;isp驱动
│ ├── sunxi_isp.c
│ └── sunxi_isp.h
├── vin-mipi ;mipi驱动
│ ├── sunxi_mipi.c
│ └── sunxi_mipi.h
├── vin-stat
│ ├── vin_h3a.c
│ └── vin_h3a.h
├── vin-tdm
│ ├── vin_tdm.c
│ └── vin_tdm.h
├── vin-video ;video节点相关的接口定义
│ ├── vin_core.c
│ ├── vin_core.h
│ ├── vin_video.c
│ └── vin_video.h
└── vin-vipp
├── sunxi_scaler.c
├── sunxi_scaler.h

3 模块开发

3.1 模块体系结构描述

3.1.1 VFE 框架

• 使用过程中可简单的看成是vfe 模块+ device 模块+af driver + flash 控制模块的方式;

• vfe.c 是驱动的主要功能实现,包括注册/注销、参数读取、与v4l2 上层接口、与各device 的下层接口、中断处理、buffer 申请切换等;

• device 文件夹里面是各个sensor 的器件层实现,一般包括上下电、初始化,各分辨率切换,yuv sensor 包括绝大部分的v4l2 定义的ioctrl 命令的实现;而raw

sensor 的话大部分 ioctrl 命令在vfe 层调用isp 的库实现,少数如曝光/增益调节会透过vfe 层到实际器件层;

• actuator 文件夹内是各种vcm 的驱动;

• flash_light 文件夹内是闪光灯控制接口实现;

• csi 和mipi_csi 为对csi 接口和mipi 接口的控制文件;

• lib 文件夹为isp 的库文件;

• linux-3.0 前的版本相当于vivi.c+csi bsp 层

• linux-3.4 版本支持isp 驱动和双CSI

• linux-3.10 版本将mipi/csi/isp 模块化(由vfe.c 直接调用=>v4l2_subdev_ops), 支持device tree

image-20221122095738625

图3-1: VFE
3.1.2 VIN 框架

• 使用过程中可简单的看成是vin 模块+ device 模块+af driver + flash 控制模块的方式;

• vin.c 是驱动的主要功能实现,包括注册/注销、参数读取、与v4l2 上层接口、与各device 的下层接口、中断处理、buffer 申请切换等;

• modules/sensor 文件夹里面是各个sensor 的器件层实现,一般包括上下电、初始化,各分辨率切换,yuv sensor 包括绝大部分的v4l2 定义的ioctrl 命令的实

现;而raw sensor 的话大部分ioctrl 命令在vin 层调用isp 库实现,少数如曝光/增益调节会透过vin 层到实际器件层;

• modules/actuator 文件夹内是各种vcm 的驱动;

• modules/flash 文件夹内是闪光灯控制接口实现;

• vin-csi 和vin-mipi 为对csi 接口和mipi 接口的控制文件;

• vin-isp 文件夹为isp 的库操作文件;

• vin-video 文件夹内主要是video 设备操作文件;

image-20221122113602939

图3-2: VIN
3.1.3 Camera 通路框架

• VIN 支持灵活配置单/双路输入双ISP 多通路输出的规格

• 引入media 框架实现pipeline 管理

• 将libisp 移植到用户空间解决GPL 问题

• 将统计buffer 独立为v4l2 subdev

• 将的scaler(vipp)模块独立为v4l2 subdev

• 将video buffer 修改为mplane 方式,使用户层取图更方便

• 采用v4l2-event 实现事件管理

• 采用v4l2-controls 新特性

image-20221122113645959

图3-3: camera Input

3.2 驱动模块实现

3.2.1 硬件部分

检查硬件电源,gpio 是否和原理图一致并且正确连接;检查sys_config.fex 或者board.dts 是否正确配置,包括使用的电源名称和电压,详见CSI 板级配置章节说

明;如果是电源选择有多个源头的请确认板子上的连接正确,比如0ohm 电阻是否正确的焊接为0ohm,NC 的电阻是否有正确断开等等。带补光灯的也需要检查

灯和driver IC 和控制io 是否连好。

3.2.2 内核device 模块驱动

一般调试新模组的话建议以sdk 中的某个现成的驱动为基础修改:YUV 的并口模组以R40 平台(linux3.10) 的ov5640.c 为参考。

下面以ov5640.c 为例说明调试新模组需要注意的两点:

  1. 添加Makefile
[linux-3.10/drivers/media/platform/sunxi-vfe/device/Makefile]
添加
obj-m + = ov5640.o (详见1)
详注:
1.具体取决于使用的模组,如果是新模组则将驱动代码放置在该device目录下。
  1. 配置模组参数

配置参数在linux-3.10/drivers/media/platform/sunxi-vfe/device/ov5640.c 中,只需注意下面两个参数。

#define SENSOR_NAME "ov5640" (详见1)
#define I2C_ADDR 0x78 (详见2)
详注:
1.该参数为模组名,必须和sys_config.fex的csi0_dev0_mname或者board.dts的sensor0_mname保持一致。
2.I2C_ADDR可参考相应模组的datasheet,sys_config.fex的csi0_dev0_twi_addr与此值保持一致。
3.2.2.1 驱动宏定义
#define MCLK (24*1000*1000)

sensor 输入时钟频率,可查看模组厂提供的sensor datasheet,datasheet 中会有类似inputclock frequency: 6~27 MHz 信息,这个信息说明可提供给sensor 的

MCLK 可以在6 M 到27 M之间。其中MCLK 和使用的寄存器配置强相关,在模组厂提供寄存器配置时,可直接询问当前配置使用的MCLK 频率是多少。

#define VREF_POL V4L2_MBUS_VSYNC_ACTIVE_LOW
#define HREF_POL V4L2_MBUS_HSYNC_ACTIVE_HIGH
#define CLK_POL V4L2_MBUS_PCLK_SAMPLE_RISING

并口sensor 必须填写,MIPI sensor 无需填写,可在sensor 规格书找到,如下

image-20221122115215017

图3-4: timing

从上述的图像可得到以下信息:

  1. VSYNC 在低电平的时候,data pin 输出有效数据,所以VREF_POL 设置为V4L2_MBUS_VSYNC_ACTIVE_即低电平有效;

  2. HREF 在高电平的时候,data pin 输出有效数据,所以HREF_POL 设置为V4L2_MBUS_HSYNC_ACTIVE_即高电平有效;

  3. CLK_POL 则是表明SOC 是在sensor 输出的pclk 上升沿采集data pin 的数据还是下降沿采集数据,如果sensor 在pclk 上升沿改变data pin 的数据,那么SOC

应该在下降沿采集,CLK_POL 设置为V4L2_MBUS_PCLK_SAMPLE_FALLING;如果sensor在pclk 下降沿改变data pin 的数据,那么SOC 应该在上降沿采集,

CLK_POL 设置为V4L2_MBUS_PCLK_SAMPLE_RISING。

#define V4L2_IDENT_SENSOR 0x2770

一般填写sensor ID,用于sensor 检测。sensor ID 可在sensor 规格书的找到,如下

image-20221122115536252

图3-5: sensorid
#define I2C_ADDR 0x6c

sensor I2C 通讯地址,可在sensor 规格书找到,如下

image-20221122115814628

图3-6: sccbid
#define SENSOR_NAME OV5640

定义驱动名字,与系统其他文件填写的名字要一致,比如需要和sys_config.fex 中的sensorname 一致。

3.2.2.2 初始化代码
static struct regval_list sensor_default_regs[] = {}; /* 填写寄存器代码的公共部分*/
static struct regval_list sensor_XXX_regs[] = {}; /* 填写各模式的寄存器代码,不同的模式可以是分辨率、帧率等*/

上述部分的寄存器配置,公共部分可以忽略,直接在模式代码中配置sensor 即可,相应的寄存器配置,可让模组厂提供。

3.2.2.3 曝光增益接口函数
static int sensor_s_exp(struct v4l2_subdev *sd, unsigned int exp_val) /* 曝光函数*/
static int sensor_s_gain(struct v4l2_subdev *sd, unsigned int gain_val) /* 增益函数*/

AE 是同时控制曝光时间和增益的,所以需要在上面的函数中分别同时sensor 曝光和增益的寄存器。

image-20221122174724278

图3-7: expgain

根据规格书中的寄存器说明,在相应的函数配置即可。若设置exp/gain 无效,可能的原因有:

• sensor 寄存器打开了AE;

• 设置值超出了有效范围

具体可根据模组厂提供的配置设置,如若检查之后设置仍失效,可与模组厂沟通,确认配置是否正确。

3.2.2.4 上下电控制函数
static int sensor_power(struct v4l2_subdev *sd, int on)

控制sensor 上电、下电及进出待机状态,操作步骤须与规格书描述相同,注意power down 和reset pin 的电平变化。

image-20221122174813656

图3-8: powerup

驱动中,按照规格书的上电时序进行配置,而如果上电之后测量硬件并没有相应的电压,这时候
检查硬件和软件配置是否一致。关于csi 电源的配置,操作流程可如下:

  1. 先通过原理图确认sensor 模组的各路电源是连接到axp 的哪个ldo;
  2. 查看sys_config.fex 的regulator 配置,在相应的ldo 后增加相应的字段,比如“csi-vdd”等;
  3. 在sys_config.fex 的csi 部分,sensor 部分的电源后的字段再填写与上述一样的字段即可;
  4. 根据sensor 规格书的要求,填写相应的电压即可;

以上图为例,确认sensor 驱动中的上电时序。

static int sensor_power(struct v4l2_subdev *sd, int on)
{
    int ret;
    ret = 0;
    switch (on) {
    /* STBY_ON 和STBY_OFF 基本不使用,可忽略这两个选项的配置*/
    case STBY_ON:
        ...
        break;
    case STBY_OFF:
        ...
        break;
    /* 上电操作*/
    case PWR_ON:
        sensor_print("PWR_ON!\n");
        cci_lock(sd);
        /* 将PWDN、RESET 引脚设置为输出*/
        vin_gpio_set_status(sd, PWDN, 1);
        vin_gpio_set_status(sd, RESET, 1);
        /* 按照上图知道,上电前PWDN、RESET 信号为低,所以将其设置为低电平*/
        vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
        vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
        /* 延时*/
        usleep_range(1000, 1200);
        /* CAMERAVDD 为SOC 中的供电电源,部分板子可以忽略该电源,
        * 因为有些板子会通过一个vcc-pe 给上拉电阻等供电,所以需要
        * 使能该路电,有些是直接和iovdd 共用了,所以有部分会忽略该
        * 路电源配置.
        */
        vin_set_pmu_channel(sd, CAMERAVDD, ON);
        /* 将PWDN 设置为高电平*/
        vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
        /*AF上电*/
        vin_set_pmu_channel(sd, AFVDD, ON);
        /* AVDD 上电*/
        vin_set_pmu_channel(sd, AVDD, ON);
        /* 延时,延时时长为T1,T1 的大小在datasheet 的上电时序图下面有标注*/
        usleep_range(1000, 1200);
        /* DOVDD 上电*/
        vin_set_pmu_channel(sd, IOVDD, ON);
        /* 延时,按照上电时序中的标注的T2 时间延时*/
        usleep_range(1000, 1200);
        /* DVDD 上电*/
        vin_set_pmu_channel(sd, DVDD, ON);
        /* 延时,按照上电时序中的标注的T3 时间延时*/
        usleep_range(1000, 1200);
        /* 将PWDN 设置为低电平*/
        vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
        /* 设置MCLK 频率并使能*/
        vin_set_mclk_freq(sd, MCLK);
        vin_set_mclk(sd, ON);
        /* 延时,按照上电时序中的标注的T4 时间延时*/
        usleep_range(1000, 1200);
        /* 将RESET 设置为高电平*/
        vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
        /* 延时,按照上电时序中的标注的T6 时间延时*/
        usleep_range(10000, 12000);
        cci_unlock(sd);
        break;
    /* 掉电操作*/
        case PWR_OFF:
        sensor_print("PWR_OFF!\n");
        cci_lock(sd);
        /* 具体的掉电操作同样的按照datasheet 的power off 操作即可*/
        vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
        vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
        vin_set_mclk(sd, OFF);
        vin_set_pmu_channel(sd, AFVDD, OFF);
        vin_set_pmu_channel(sd, AVDD, OFF);
        vin_set_pmu_channel(sd, DVDD, OFF);
        vin_set_pmu_channel(sd, IOVDD, OFF);
        vin_set_pmu_channel(sd, CAMERAVDD, OFF);
        vin_gpio_set_status(sd, PWDN, 0);
        cci_unlock(sd);
        break;
    default:
    	return -EINVAL;
    }
    return 0;
}
3.2.2.5 检测函数
static int sensor_detect(struct v4l2_subdev *sd)

在开机加载驱动的时候,将会检测sensor ID,用于测试I2C 通讯是否正常和sensor 识别。

#define V4L2_IDENT_SENSOR 0x7750
    sensor_read(sd, 0x300A, &rdval);
    if (rdval != (V4L2_IDENT_SENSOR >> 8))
    	return -ENODEV;
    sensor_read(sd, 0x300B, &rdval);
    if (rdval != (V4L2_IDENT_SENSOR & 0xff))
    	return -ENODEV;

image-20221122175111168

图3-9: sensordetect
3.2.2.6 SENSOR 相关的IOCTL
static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)

用于应用层获取曝光增益,及进行与sensor 相关模块的驱动控制,如对焦,闪光等

case VIDIOC_VIN_SENSOR_EXP_GAIN:/*设置sensor的曝光增益*/
    ret = sensor_s_exp_gain(sd, (struct sensor_exp_gain *)arg);
    break;
case VIDIOC_VIN_SENSOR_CFG_REQ:/*获取sensor驱动的基础配置信息*/
    sensor_cfg_req(sd, (struct sensor_config *)arg);
    break;
case VIDIOC_VIN_ACT_SET_CODE:/*设置对焦马达配置参数,在配置AF模块时,需要此ioctl*/
	actuator_set_code(sd, (struct actuator_ctrl *)arg);
3.2.2.7 与CSI 的接口
static struct sensor_format_struct sensor_formats[] = {};
RAW sensor:
.desc = "Raw RGB Bayer",
.mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
.regs = sensor_fmt_raw,
.regs_size = ARRAY_SIZE(sensor_fmt_raw),
.bpp = 1
YUV sensor:
.desc = "YUYV 4:2:2",
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.regs = sensor_fmt_yuyv422_yuyv,
.regs_size = ARRAY_SIZE(sensor_fmt_yuyv422_yuyv),
.bpp = 2

其中,mbus_code 中BGGR 可以根据sensor raw data 输出顺序修改为GBRG/RGGB/-GRBG。若填错, 会导致色彩偏紫红和出现网格状纹理。10_1X10 表示10 bit

并口输出, 若是12 bit MIPI 输出, 则改为12_12X1。其他情况类推。对于DVP YUV sensor, 需根据yuv 输出顺序选择yuyv/vyuy/uyvy/yvyu 其中一种。

static int sensor_g_mbus_config(struct v4l2_subdev *sd,struct v4l2_mbus_config *cfg)
DVP sensor:
    cfg->type = V4L2_MBUS_PARALLEL;
    cfg->flags = V4L2_MBUS_MASTER | VREF_POL | HREF_POL | CLK_POL;
MIPI sensor:
    cfg->type = V4L2_MBUS_CSI2;
    cfg->flags = 0 | V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0;

其中,MIPI sensor 须根据实际使用的lane 数,修改V4L2_MBUS_CSI2_X_LANE 中的X值。如果使用LVDS 接口,需要将cfg->type 配置为V4L2_MBUS_SUBLVDS。

3.2.2.8 分辨率配置
static struct sensor_win_size sensor_win_sizes[] = {
{
    .width = VGA_WIDTH,
    .height = VGA_HEIGHT,
    .hoffset = 0,
    .voffset = 0,
    .hts = 928,
    .vts = 1720,
    .pclk = 48 * 1000 * 1000,
    .mipi_bps = 480 * 1000 * 1000,
    .fps_fixed = 30,
    .bin_factor = 1,
    .intg_min = 1 << 4,
    .intg_max = (1720) << 4,
    .gain_min = 1 << 4,
    .gain_max = 16 << 4,
    .regs = sensor_VGA_regs,
    .regs_size = ARRAY_SIZE(sensor_VGA_regs),
    .set_size = NULL,
},
{
    /* 定义图像输出的大小*/
    .width = VGA_WIDTH,
    .height = VGA_HEIGHT,
    /* 定义输入ISP 的偏移量,用于截取所需的Size,丢弃不需要的部分图像*/
    .hoffset = 0,
    .voffset = 0,
    /*
    定义行长(以pclk 为单位)、帧长(以hts 为单位) 和像素时钟频率。hts 又称line_length_pck,vts 又称frame_length_lines,与寄存器的值要一致。pclk(pixel clock)的值由PLL 寄存器计算得出。
    */
    .hts = 928,
    .vts = 1720,
    .pclk = 48 * 1000 * 1000,
    /* 定义MIPI 数据速率,MIPI sensor 必需,其他sensor 忽略*/
    /* mipi_bps = hts * vts * fps * raw bit / lane num */
    .mipi_bps = 480 * 1000 * 1000,
    /* 定义帧率,fps * hts * vts = pclk */
    .fps_fixed = 30,
    /*
    定义曝光行数最小值和最大值,增益最小值和最大值,以16 为1 倍。最值的设置应在sensor 规格和
    曝光函数限定的范围内,若超出会导致画面异常。此外,若AE table 中的最值超出这里的限制,会使得
    AE table 失效。
    */
    .intg_min = 1 << 4,
    .intg_max = (1720) << 4,
    .gain_min = 1 << 4,
    .gain_max = 16 << 4,
    /* (必需)说明这部分的配置对应哪个寄存器初始化代码*/
    .regs = sensor_VGA_regs,
    .regs_size = ARRAY_SIZE(sensor_VGA_regs),
    },
};

根据应用的需求,在这里配置驱动能输出的不同尺寸帧率组合,注意,一种分辨率、帧率配置为一个数组成员,不要混淆。

3.2.3 LVDS 接口须知

除了完成以上函数的实现,LVDS Sensor 驱动还需要完成combo 同步校验函数和combo 数据线映射函数。combo 校验码可以在sensor 规格书获取,combo 数

据线映射关系需要查看原理图设计进行配对,可参考imx274_slvds.c 完成开发。

image-20221122180028623

图3-10: SYNC_CODE
static void sensor_g_combo_sync_code(struct v4l2_subdev *sd,
struct combo_sync_code *sync)
{
    int i;
    for (i = 0; i < 12; i++) {
    sync->lane_sof[i].low_bit = 0x0000ab00;
    sync->lane_sof[i].high_bit = 0xFFFF0000;
    sync->lane_sol[i].low_bit = 0x00008000;
    sync->lane_sol[i].high_bit = 0xFFFF0000;
    sync->lane_eol[i].low_bit = 0x00009d00;
    sync->lane_eol[i].high_bit = 0xFFFF0000;
    sync->lane_eof[i].low_bit = 0x0000b600;
    sync->lane_eof[i].high_bit = 0xFFFF0000;
}
}
static void sensor_g_combo_lane_map(struct v4l2_subdev *sd,
	struct combo_lane_map *map)
	{
	struct sensor_info *info = to_state(sd);
    if (info->isp_wdr_mode == ISP_DOL_WDR_MODE) {
        map->lvds_lane0 = LVDS_MAPPING_A_D0_TO_LANE0;
        map->lvds_lane1 = LVDS_MAPPING_A_D1_TO_LANE1;
        map->lvds_lane2 = LVDS_MAPPING_B_D2_TO_LANE2;
        map->lvds_lane3 = LVDS_MAPPING_B_D0_TO_LANE3;
        map->lvds_lane4 = LVDS_MAPPING_B_D3_TO_LANE4;
        map->lvds_lane5 = LVDS_MAPPING_C_D2_TO_LANE5;
        map->lvds_lane6 = LVDS_LANE6_NO_USE;
        map->lvds_lane7 = LVDS_LANE7_NO_USE;
        map->lvds_lane8 = LVDS_LANE8_NO_USE;
        map->lvds_lane9 = LVDS_LANE9_NO_USE;
        map->lvds_lane10 = LVDS_LANE10_NO_USE;
        map->lvds_lane11 = LVDS_LANE11_NO_USE;
} else {
        map->lvds_lane0 = LVDS_MAPPING_A_D1_TO_LANE0;
        map->lvds_lane1 = LVDS_MAPPING_B_D2_TO_LANE1;
        map->lvds_lane2 = LVDS_MAPPING_B_D0_TO_LANE2;
        map->lvds_lane3 = LVDS_MAPPING_B_D3_TO_LANE3;
        map->lvds_lane4 = LVDS_LANE4_NO_USE;
        map->lvds_lane5 = LVDS_LANE5_NO_USE;
        map->lvds_lane6 = LVDS_LANE6_NO_USE;
        map->lvds_lane7 = LVDS_LANE7_NO_USE;
        map->lvds_lane8 = LVDS_LANE8_NO_USE;
        map->lvds_lane9 = LVDS_LANE9_NO_USE;
        map->lvds_lane10 = LVDS_LANE10_NO_USE;
        map->lvds_lane11 = LVDS_LANE11_NO_USE;
	}
}

3.2.4 内核代码注意事项

驱动中一般禁止使用mdelay 或者msleep 实现延时,例如使用msleep 实现10~20ms的延时,通常会因为系统调度而变成延时更长的时间,这种做法精度较差。

所以如果需要使用ms 级别延时,则使用usleep_range(a, b),比如原来mdelay(1)、mdelay(10) 可改为usleep_range(1000, 2000)、usleep_range(10000,

12000)。如果是长达30ms 或以上的延时可选择使用msleep();

中断过程中不能使用msleep 和usleep_range,除了特殊情况必须加延时之外,mdelay 一般也不可使用。

4 模块配置

4.1 Tina 配置

Tina 中主要是修改平台的modules.mk 配置,modules.mk 主要完成两个方面:

1.拷贝相关的ko 模块到小机rootfs 中

2.rootfs 启动时,按顺序自动加载相关的ko 模块。

由于内核框架的不一样,需要区分vfe 和vin 进行相应的配置。

4.1.1 vfe 框架

modules.mk 配置路径(以R40 平台的为例):

target/allwinner/r40-common/modules.mk

其中的r40-common 为R40 平台共有的配置文件目录,相应的修改对应平台的modules.mk即可。

define KernelPackage/sunxi-vfe
    SUBMENU:=$(VIDEO_MENU)
    TITLE:=sunxi-vfe support
    FILES:=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-core.ko
    FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-memops.ko
    FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-dma-contig.ko
    FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-v4l2.ko
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vfe/vfe_io.ko
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vfe/device/ov5640.ko (详见1)
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vfe/vfe_v4l2.ko
    AUTOLOAD:=$(call AutoLoad,90,videobuf2-core videobuf2-memops videobuf2-dma-contig
    videobuf2-v4l2 vfe_io ov5640 vfe_v4l2) (详见2)
endef
    define KernelPackage/sunxi-vfe/description
    	Kernel modules for sunxi-vfe support
endef
详注:
    1.由具体使用的模组确定,需要确定内核路径中这个驱动是否被编译出来。
    2.AUTOLOAD为小机rootfs挂载后自动加载的机制,vfe_v4l2.ko必须在最后加载,其它ko可以按照上面的相对顺序加载。必须修改相应的sensor ko才会开启自加载。
4.1.2 vin 框架

modules.mk 配置路径(以R30 平台的为例):

target/allwinner/r30-common/modules.mk

其中的r30-common 为R30 平台共有的配置文件目录,相应的修改对应平台的modules.mk即可。

define KernelPackage/sunxi-vin
    SUBMENU:=$(VIDEO_MENU)
    TITLE:=sunxi-vin support
    FILES:=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-core.ko
    FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-memops.ko
    FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-dma-contig.ko
    FILES+=$(LINUX_DIR)/drivers/media/v4l2-core/videobuf2-v4l2.ko
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/vin_io.ko
    /*对焦马达驱动加载*/
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/modules/actuator/actuator.ko
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/modules/actuator/dw9714_act.ko(详见
    3)
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/modules/sensor/ov5640.ko (详见1)
    FILES+=$(LINUX_DIR)/drivers/media/platform/sunxi-vin/vin_v4l2.ko
    AUTOLOAD:=$(call AutoLoad,90,videobuf2-core videobuf2-memops videobuf2-dma-contig
    videobuf2-v4l2 vin_io actuator dw9714_act ov5640 vin_v4l2) (详见2)
endef
    define KernelPackage/sunxi-vin/description
    	Kernel modules for sunxi-vin support
endef
详注:
    1.由具体使用的模组确定,需要确定内核路径中这个驱动是否被编译出来。
    2.AUTOLOAD为小机rootfs挂载后自动加载的机制,vin_v4l2.ko必须在最后加载,其它ko可以按照上面的相对顺序加载。
    3.对焦马达驱动加载顺序必须在sensor驱动加载之前,具体驱动型号根据模组规格书进行确认。

V 系列平台在完成modules.mk 配置后,还需要完成.ko 挂载脚本S00mpp 的配置,S00mpp

配置路径(以V853 平台为例):

target/allwinner/v853-perf1/busybox-init-base-files/etc/init.d

其中的v853-perf1 为V 系列平台共有的配置文件目录,相应的修改对应平台的S00mpp 即可。

#!/bin/sh
#
# Load mpp modules....
#
MODULES_DIR="/lib/modules/`uname -r`"
start() {
    printf "Load mpp modules\n"
    insmod $MODULES_DIR/videobuf2-core.ko
    insmod $MODULES_DIR/videobuf2-memops.ko
    insmod $MODULES_DIR/videobuf2-dma-contig.ko
    insmod $MODULES_DIR/videobuf2-v4l2.ko
    insmod $MODULES_DIR/vin_io.ko
    # insmod $MODULES_DIR/sensor_power.ko
    insmod $MODULES_DIR/gc4663_mipi.ko
    insmod $MODULES_DIR/vin_v4l2.ko
    insmod $MODULES_DIR/sunxi_aio.ko
    insmod $MODULES_DIR/sunxi_eise.ko
    # insmod $MODULES_DIR/vipcore.ko
}
stop() {
    printf "Unload mpp modules\n"
    # rmmod $MODULES_DIR/vipcore.ko
    rmmod $MODULES_DIR/sunxi_eise.ko
    rmmod $MODULES_DIR/sunxi_aio.ko
    rmmod $MODULES_DIR/vin_v4l2.ko
    rmmod $MODULES_DIR/gc4663_mipi.ko
    # rmmod $MODULES_DIR/sensor_power.ko
    rmmod $MODULES_DIR/vin_io.ko
    rmmod $MODULES_DIR/videobuf2-v4l2.ko
    rmmod $MODULES_DIR/videobuf2-dma-contig.ko
    rmmod $MODULES_DIR/videobuf2-memops.ko
    rmmod $MODULES_DIR/videobuf2-core.ko
}
case "$1" in
        start)
        start
        ;;
        stop)
        stop
        ;;
        restart|reload)
        stop
        start
        ;;
    *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
esac
exit $?

4.2 CSI 板级配置

Tina 平台根据不同的平台差异分别使用sys_config.fex 或board.dst 配置camera CSI,具体的对应关系如下表,下面将分别介绍sys_config.fex 和board.dts 中关

于camera CSI 配置。

表4-1: 平台配置方式对应表
平台CSI 使用的配置方式
F35sys_config.fex
R16sys_config.fex
R18sys_config.fex
R30sys_config.fex
R40sys_config.fex
R311sys_config.fex
MR133sys_config.fex
R818board.dts
MR813board.dts
R528board.dts
V536sys_config.fex
V533board.dts
V831board.dts
V833board.dts
V851board.dts
V853board.dts
4.2.1 sys_config.fex 平台配置

sys_config.fex 配置camera CSI,CSI sys_config.fex 部分对应的字段为:[csi0]。通过举例R40 平台说明在实际使用中应该如何配置:假如使用一个并口camera

模组需要配置[csi0] 的公用部分和[csi0] 的vip_dev0_(x) 部分,另外[csi0] 中vip_used 设置为1,[csi1]中vip_used 设置为0。

下面给出一个ov5640 模组的参考配置:其中[csi0] 为并口的配置。具体填写方法请参照以下说明:

/* 下面部分的CSI配置适用4.9内核之前的平台*/
;--------------------------------------------------------------------------------
;csi (COMS Sensor Interface) configuration
;csi(x)_dev(x)_used: 0:disable 1:enable
;csi(x)_dev(x)_isp_used 0:not use isp 1:use isp
;csi(x)_dev(x)_fmt: 0:yuv 1:bayer raw rgb
;csi(x)_dev(x)_stby_mode: 0:not shut down power at standby 1:shut down power at standby
;csi(x)_dev(x)_vflip: flip in vertical direction 0:disable 1:enable
;csi(x)_dev(x)_hflip: flip in horizontal direction 0:disable 1:enable
;csi(x)_dev(x)_iovdd: camera module io power handle string, pmu power supply
;csi(x)_dev(x)_iovdd_vol: camera module io power voltage, pmu power supply
;csi(x)_dev(x)_avdd: camera module analog power handle string, pmu power supply
;csi(x)_dev(x)_avdd_vol: camera module analog power voltage, pmu power supply
;csi(x)_dev(x)_dvdd: camera module core power handle string, pmu power supply
;csi(x)_dev(x)_dvdd_vol: camera module core power voltage, pmu power supply
;csi(x)_dev(x)_afvdd: camera module vcm power handle string, pmu power supply
;csi(x)_dev(x)_afvdd_vol: camera module vcm power voltage, pmu power supply
;fill voltage in uV, e.g. iovdd = 2.8V, csix_iovdd_vol = 2800000
;fill handle string as below:
;axp22_eldo3
;axp22_dldo4
;axp22_eldo2
;fill handle string "" when not using any pmu power supply
;--------------------------------------------------------------------------------
[csi0]
csi0_used = 1
csi0_sensor_list = 0
csi0_pck = port:PE00<2><default><default><default>
csi0_mck = port:PE01<2><default><default><default>
csi0_hsync = port:PE02<2><default><default><default>
csi0_vsync = port:PE03<2><default><default><default>
csi0_d0 = port:PE04<2><default><default><default>
csi0_d1 = port:PE05<2><default><default><default>
csi0_d2 = port:PE06<2><default><default><default>
csi0_d3 = port:PE07<2><default><default><default>
csi0_d4 = port:PE08<2><default><default><default>
csi0_d5 = port:PE09<2><default><default><default>
csi0_d6 = port:PE10<2><default><default><default>
csi0_d7 = port:PE11<2><default><default><default>
csi0_sck = port:PE12<2><default><default><default>
csi0_sda = port:PE13<2><default><default><default>
[csi0/csi0_dev0]
csi0_dev0_used = 1
csi0_dev0_mname = "ov5640" ;必须和sensor驱动中的SENSOR_NAME一致
csi0_dev0_twi_addr = 0x78 ;请参考实际模组的8bit ID填写
csi0_dev0_twi_id = 2
csi0_dev0_pos = "rear"
csi0_dev0_isp_used = 0 ;YUV格式填0,RAW格式填1
csi0_dev0_fmt = 0 ;YUV格式填0,RAW格式填1
csi0_dev0_stby_mode = 0
csi0_dev0_vflip = 0
csi0_dev0_hflip = 0
csi0_dev0_iovdd = "csi-iovcc" ;电源请参考实际原理图填写,同时参考
sys_config.fex 的regulator 配置,确认该字段有效
csi0_dev0_iovdd_vol = 2800000 ;电压值参考datasheet
csi0_dev0_avdd = "csi-avdd" ;电源请参考实际原理图填写,同时参考
sys_config.fex 的regulator 配置,确认该字段有效
csi0_dev0_avdd_vol = 2800000 ;电压值参考datasheet
csi0_dev0_dvdd = "csi-dvdd" ;电源请参考实际原理图填写,同时参考
sys_config.fex 的regulator 配置,确认该字段有效
csi0_dev0_dvdd_vol = 1500000 ;电压值参考datasheet
csi0_dev0_afvdd = "csi-afvcc" ;电源请参考实际原理图填写,同时参考
sys_config.fex 的regulator 配置,确认该字段有效
csi0_dev0_afvdd_vol = 2800000 ;电压值参考datasheet
csi0_dev0_power_en =
csi0_dev0_reset = port:PE14<1><0><1><0> ;io选取参照实际原理图
csi0_dev0_pwdn = port:PE15<1><0><1><0> ;io选取参照实际原理图
csi0_dev0_flash_used = 0
csi0_dev0_flash_type = 2
csi0_dev0_flash_en =
csi0_dev0_flash_mode =
csi0_dev0_flvdd = ""
csi0_dev0_flvdd_vol =
csi0_dev0_af_pwdn =
csi0_dev0_act_used = 0
csi0_dev0_act_name = "ad5820_act"
csi0_dev0_act_slave = 0x18
/* 下面部分的CSI配置适用4.9内核平台*/
;--------------------------------------------------------------------------------
;csi (COMS Sensor Interface) configuration
;csi(x)_dev(x)_used: 0:disable 1:enable
;csi(x)_dev(x)_isp_used 0:not use isp 1:use isp
;csi(x)_dev(x)_fmt: 0:yuv 1:bayer raw rgb
;csi(x)_dev(x)_stby_mode: 0:not shut down power at standby 1:shut down power at standby
;csi(x)_dev(x)_vflip: flip in vertical direction 0:disable 1:enable
;csi(x)_dev(x)_hflip: flip in horizontal direction 0:disable 1:enable
;csi(x)_dev(x)_iovdd: camera module io power handle string, pmu power supply
;csi(x)_dev(x)_iovdd_vol: camera module io power voltage, pmu power supply
;csi(x)_dev(x)_avdd: camera module analog power handle string, pmu power supply
;csi(x)_dev(x)_avdd_vol: camera module analog power voltage, pmu power supply
;csi(x)_dev(x)_dvdd: camera module core power handle string, pmu power supply
;csi(x)_dev(x)_dvdd_vol: camera module core power voltage, pmu power supply
;csi(x)_dev(x)_afvdd: camera module vcm power handle string, pmu power supply
;csi(x)_dev(x)_afvdd_vol: camera module vcm power voltage, pmu power supply
;fill voltage in uV, e.g. iovdd = 2.8V, csix_iovdd_vol = 2800000
;fill handle string as below:
;axp22_eldo3
;axp22_dldo4
;axp22_eldo2
;fill handle string "" when not using any pmu power supply
;--------------------------------------------------------------------------------
[vind0]
vind0_used = 1
[vind0/csi_cci0]
csi_cci0_used = 1 ;配置是否使用CCI,如果使用CCI,需要使能该配置并配置下面的CCI引脚
csi_cci0_sck = port:PE01<2><default><default><default>
csi_cci0_sda = port:PE02<2><default><default><default>
[vind0/flash0]
flash0_used = 0
flash0_type = 2
flash0_en =
flash0_mode =
flash0_flvdd = ""
flash0_flvdd_vol =
[vind0/actuator0]
actuator0_used = 0
actuator0_name = "ad5820_act"
actuator0_slave = 0x18
actuator0_af_pwdn =
actuator0_afvdd = "afvcc-csi"
actuator0_afvdd_vol = 2800000
[vind0/sensor0]
sensor0_used = 0
sensor0_mname = "gc8034_mipi"
sensor0_twi_cci_id = 0
sensor0_twi_addr = 0x6e
sensor0_pos = "rear"
sensor0_isp_used = 1
sensor0_fmt = 1
sensor0_stby_mode = 0
sensor0_vflip = 0
sensor0_hflip = 0
sensor0_cameravdd = ""
sensor0_cameravdd_vol = 3300000
sensor0_iovdd = "iovdd-csi"
sensor0_iovdd_vol = 1800000
sensor0_avdd = "avdd-csi-f"
sensor0_avdd_vol = 2800000
sensor0_dvdd = "dvdd-csi"
sensor0_dvdd_vol = 1200000
sensor0_power_en =
sensor0_reset = port:PE06<0><0><1><0>
sensor0_pwdn = port:PE05<0><0><1><0>
[vind0/sensor1]
sensor1_used = 1
sensor1_mname = "gc8034_mipi" ;必须要和驱动的SENSOR_NAME 一致
sensor1_twi_cci_id = 0 ;配置使用的TWI id,如果使用TWI,则不使用CCI
sensor1_twi_addr = 0x6e ;配置sensor的i2c地址
sensor1_pos = "front"
sensor1_isp_used = 1 ;配置是否使用isp
sensor1_fmt = 1
sensor1_stby_mode = 0
sensor1_vflip = 0
sensor1_hflip = 0
sensor1_cameravdd = ""
sensor1_cameravdd_vol = 3300000
sensor1_iovdd = "iovdd-csi"
sensor1_iovdd_vol = 1800000
sensor1_avdd = "avdd-csi-f"
sensor1_avdd_vol = 2800000
sensor1_dvdd = "dvdd-csi"
sensor1_dvdd_vol = 1200000
sensor1_power_en =
sensor1_reset = port:PE06<0><0><1><0>
sensor1_pwdn = port:PE05<0><0><1><0>
[vind0/vinc0] ;配置video0 的数据链路
vinc0_used = 1
vinc0_csi_sel = 0
vinc0_mipi_sel = 0
vinc0_isp_sel = 0
vinc0_rear_sensor_sel = 1 ;配置使用sensor1 输出图像数据到video0
vinc0_front_sensor_sel = 1 ;配置使用sensor1 输出图像数据到video0
vinc0_sensor_list = 0
[vind0/vinc1]
vinc1_used = 0
vinc1_csi_sel = 0
vinc1_mipi_sel = 0
vinc1_isp_sel = 0
vinc1_rear_sensor_sel = 1
vinc1_front_sensor_sel = 1
vinc1_sensor_list = 0

关于电源的配置,根据板子的原理图,了解需要sensor 驱动配置哪几路电,然后在sys_config.fex中进行配置:比如说sensor0 有个“CSI-IOVCC” 连接到AXP

的“LDO4”,那么,在sys_config.fex 中搜索LDO4 ,然后在其后面增加“csi-iovcc” ,这样,在sensor 端就可以使用该标号配置sensor0_iovdd。

regulator14 = "pmu1736_bldo2 none csi-iovdd"
sensor0_iovdd = "csi-iovdd"

同时关于mr133/R311 平台,sys_config.fex 中的vinc0_rear_sensor_sel 和vinc0_front_sensor_sel

配置决定着使用哪路sensor 输入数据,该配置与硬件连接相关,可参考本文档最后的其他注意事项章节。

4.2.2 board.dts 平台配置

当前MR813/R818/R528 平台的摄像头配置不再使用sys_config.fex 而使用board.dts,文件存放在tina/device/config/chips/mr813(R818、R528)/configs/

< 方案> 目录下,摄像头相关的配置如下:

    vind0:vind@0 {
        vind0_clk = <336000000>;
        vind0_isp = <327000000>;
        status = "okay";
        actuator0:actuator@0 {
        device_type = "actuator0";
        actuator0_name = "ad5820_act";/*必须要和驱动的SUNXI_ACT_NAME一致*/
        actuator0_slave = <0x18>;/*必须和驱动的SUNXI_ACT_ID一致*/
        actuator0_af_pwdn = <>;
        actuator0_afvdd = "afvcc-csi";
        actuator0_afvdd_vol = <2800000>;/*af模块的配电不在此处,在sensor配置中*/
        status = "disabled";/*使能开关,当使用AF功能时,status = "okay"*/
    };
    flash0:flash@0 {
        device_type = "flash0";
        flash0_type = <2>;
        flash0_en = <>;
        flash0_mode = <>;
        flash0_flvdd = "";
        flash0_flvdd_vol = <>;
        device_id = <0>;
        status = "disabled";
    };
    sensor0:sensor@0 {
        device_type = "sensor0";
        sensor0_mname = "imx278_mipi"; /* 必须要和驱动的 SENSOR_NAME 一致 */
        sensor0_twi_cci_id = <2>;
        sensor0_twi_addr = <0x20>;
        sensor0_mclk_id = <0>;
        sensor0_pos = "rear";
        sensor0_isp_used = <1>; /* R528 没有isp,该项需要配置为0 */
        sensor0_fmt = <1>;
        sensor0_stby_mode = <0>;
        sensor0_vflip = <0>;
        sensor0_hflip = <0>;
        /* sensor iovdd 连接的 ldo,根据硬件原理图的连接,
        * 确认是连接到 axp 哪个 ldo,假设 iovdd 连接到 aldo3,
        * 则 sensor0_iovdd-supply = <&reg_aldo3>,其他同理。
        */
        sensor0_iovdd-supply = <&reg_dldo2>;
        sensor0_iovdd_vol = <1800000>;
        sensor0_avdd-supply = <&reg_dldo3>;
        sensor0_avdd_vol = <2800000>;
        sensor0_dvdd-supply = <&reg_eldo2>;
        sensor0_afvdd-supply = <&reg_aldo3>;/*根据硬件原理图,确定配的哪路电*/
        sensor0_afvdd_vol = <2800000>;/*根据硬件原理图,确认工作电压*/
        sensor0_dvdd_vol = <1200000>;
        sensor0_power_en = <>;
        /* 根据板子实际连接,修改 reset、pwdn 的引脚即可 */
        /* GPIO 信息配置:pio 端口 组内序号 功能分配 内部电阻状态 驱动能力 输出电平状态 */
        sensor0_reset = <&pio PE 9 1 0 1 0>;
        sensor0_pwdn = <&pio PE 8 1 0 1 0>;
        status = "okay";
    };
    sensor1:sensor@1 {
        device_type = "sensor1";
        sensor1_mname = "imx386_mipi";
        sensor1_twi_cci_id = <3>;
        sensor1_twi_addr = <0x20>;
        sensor1_mclk_id = <1>;
        sensor1_pos = "front";
        sensor1_isp_used = <1>;
        sensor1_fmt = <1>;
        sensor1_stby_mode = <0>;
        sensor1_vflip = <0>;
        sensor1_hflip = <0>;
        sensor1_iovdd-supply = <&reg_dldo2>;
        sensor1_iovdd_vol = <1800000>;
        sensor1_avdd-supply = <&reg_dldo3>;
        sensor1_avdd_vol = <2800000>;
        sensor1_dvdd-supply = <&reg_eldo2>;
        sensor1_dvdd_vol = <1200000>;
        sensor0_power_en = <>;
        sensor1_reset = <&pio PE 7 1 0 1 0>;
        sensor1_pwdn = <&pio PE 6 1 0 1 0>;
        status = "okay";
    };
    /* 一个 vinc 代表一个 /dev/video 设备 */
    vinc0:vinc@0 {
        vinc0_csi_sel = <0>; /* 代表选择的 csi,MR813/R818 有两个 csi 接口
        */
        vinc0_mipi_sel = <0>; /* 代表选择的 mipi 接口,MR813/R818 有两个 mipi
        接口 */
        vinc0_isp_sel = <0>;
        vinc0_isp_tx_ch = <0>; /* 表示 ISP 的通道数,一般配置为 0 */
        vinc0_tdm_rx_sel = <0>; /* 与 isp_sel 保持一致即可 */
        vinc0_rear_sensor_sel = <0>; /* 该 video 可以选择从哪个 sensor 输入图像数据
        */
        vinc0_front_sensor_sel = <1>;
        vinc0_sensor_list = <0>;
        status = "okay";
    };
    vinc1:vinc@1 {
        vinc1_csi_sel = <0>;
        vinc1_mipi_sel = <0>; /* R528没有mipi,该项配置为0xff */
        vinc1_isp_sel = <0>; /* R528没有isp,该项配置为0 */
        vinc1_isp_tx_ch = <0>; /* R528没有isp,该项配置为0 */
        vinc1_tdm_rx_sel = <0>; /* R528没有isp,该项配置为0xff */
        vinc1_rear_sensor_sel = <0>;
        vinc1_front_sensor_sel = <1>;
        vinc1_sensor_list = <0>;
        status = "okay";
    };
    vinc2:vinc@2 {
        vinc2_csi_sel = <1>;
        vinc2_mipi_sel = <1>;
        vinc2_isp_sel = <1>;
        vinc2_isp_tx_ch = <0>;
        vinc2_tdm_rx_sel = <1>;
        vinc2_rear_sensor_sel = <1>;
        vinc2_front_sensor_sel = <1>;
        vinc2_sensor_list = <0>;
        status = "okay";
    };
    vinc3:vinc@3 {
        vinc3_csi_sel = <1>;
        vinc3_mipi_sel = <1>;
        vinc3_isp_sel = <1>;
        vinc3_isp_tx_ch = <0>;
        vinc3_tdm_rx_sel = <1>;
        vinc3_rear_sensor_sel = <1>;
        vinc3_front_sensor_sel = <1>;
        vinc3_sensor_list = <0>;
        status = "okay";
    };
    };
    /* 以下将配置两路 sensor 输入,产生 4 个 video 节点,内核配置 CONFIG_SUPPORT_ISP_TDM=n,此时
    * 不同 sensor 输出的节点不能同时使用,比如以下配置的 video0 不可以和 video2 video3 同时使用
    */
    vinc0:vinc@0 {
        vinc0_csi_sel = <0>;
        vinc0_mipi_sel = <0>;
        vinc0_isp_sel = <0>;
        vinc0_isp_tx_ch = <0>;
        vinc0_tdm_rx_sel = <0>;
        vinc0_rear_sensor_sel = <0>;
        vinc0_front_sensor_sel = <0>;
        vinc0_sensor_list = <0>;
        status = "okay";
    };
    vinc1:vinc@1 {
        vinc1_csi_sel = <0>;
        vinc1_mipi_sel = <0>;
        vinc1_isp_sel = <0>;
        vinc1_isp_tx_ch = <0>;
        vinc1_tdm_rx_sel = <0>;
        vinc1_rear_sensor_sel = <0>;
        vinc1_front_sensor_sel = <1>;
        vinc1_sensor_list = <0>;
        status = "okay";
    };
    vinc2:vinc@2 {
        vinc2_csi_sel = <1>;
        vinc2_mipi_sel = <1>;
        vinc2_isp_sel = <0>;
        vinc2_isp_tx_ch = <0>;
        vinc2_tdm_rx_sel = <0>;
        vinc2_rear_sensor_sel = <1>;
        vinc2_front_sensor_sel = <1>;
        vinc2_sensor_list = <0>;
        status = "okay";
    };
    vinc3:vinc@3 {
        vinc3_csi_sel = <1>;
        vinc3_mipi_sel = <1>;
        vinc3_isp_sel = <0>;
        vinc3_isp_tx_ch = <0>;
        vinc3_tdm_rx_sel = <0>;
        vinc3_rear_sensor_sel = <1>;
        vinc3_front_sensor_sel = <1>;
        vinc3_sensor_list = <0>;
        status = "okay";
    };
    /* 以下配置将可以从两路 sensor 同时输入,内核配置 CONFIG_SUPPORT_ISP_TDM=y,但是有个限制,
    * 只能先运行 video0,然后才可以运行 video2,关闭的时候也是如此,先关 video2,再关 video0
    */
    vinc0:vinc@0 {
        vinc0_csi_sel = <0>;
        vinc0_mipi_sel = <0>;
        vinc0_isp_sel = <0>;
        vinc0_isp_tx_ch = <0>;
        vinc0_tdm_rx_sel = <0>;
        vinc0_rear_sensor_sel = <0>;
        vinc0_front_sensor_sel = <0>;
        vinc0_sensor_list = <0>;
        status = "okay";
    };
    vinc1:vinc@1 {
        vinc1_csi_sel = <0>;
        vinc1_mipi_sel = <0>;
        vinc1_isp_sel = <0>;
        vinc1_isp_tx_ch = <0>;
        vinc1_tdm_rx_sel = <0>;
        vinc1_rear_sensor_sel = <0>;
        vinc1_front_sensor_sel = <1>;
        vinc1_sensor_list = <0>;
        status = "okay";
    };
    vinc2:vinc@2 {
        vinc2_csi_sel = <1>;
        vinc2_mipi_sel = <1>;
        vinc2_isp_sel = <1>;
        vinc2_isp_tx_ch = <0>;
        vinc2_tdm_rx_sel = <1>;
        vinc2_rear_sensor_sel = <1>;
        vinc2_front_sensor_sel = <1>;
        vinc2_sensor_list = <0>;
        status = "okay";
    };
    vinc3:vinc@3 {
        vinc3_csi_sel = <1>;
        vinc3_mipi_sel = <1>;
        vinc3_isp_sel = <1>;
        vinc3_isp_tx_ch = <0>;
        vinc3_tdm_rx_sel = <1>;
        vinc3_rear_sensor_sel = <1>;
        vinc3_front_sensor_sel = <1>;
        vinc3_sensor_list = <0>;
        status = "okay";
    };

修改该文件之后,需要重新编译固件再打包,才会更新到dts。同时,如果需要使用双摄,双摄分别使用到两个ISP,那么内核需要选上SUPPORT_ISP_TDM 配置。

4.3 menuconfig 配置说明

在命令行进入Tina 根目录,执行命令进入配置主界面:

source build/envsetup.sh (详见1)
lunch 方案编号(详见2)
make menuconfig (详见3)
详注:
1.加载环境变量及tina提供的命令;
2.输入编号,选择方案;
3.进入配置主界面(对一个shell而言,前两个命令只需要执行一次)

make menuconfig 配置路径:

Kernel modules
	└─>Video Support
        └─>kmod-sunxi-vfe(vfe框架的csi camera) (详见1)
        └─>kmod-sunxi-vin(vin框架的csi camera) (详见2)
        └─>kmod-sunxi-uvc(uvc camera) (详见3)
详注:
    1.平台使用vfe框架的csi camera选择该驱动;
    2.平台使用vin框架的csi camera选择该驱动;(该项与vfe框架,在同一个平台只会出现其中一个)
    3.usb camera选择该驱动;

在完成sensor 驱动编写,modules.mk 和板级配置后,通过make menuconfig 选上相应的驱动,camera 即可正常使用。下面以R40 平台介绍。首先,选择

Kernel modules 选项进入下一级配置,如下图所示:

image-20221123105646196

图4-1: menuconfig

然后,选择Video Support 选项,进入下一级配置,如下图所示:

image-20221123105728564

图4-2: video

最后,选择kmod-sunxi-vfe 选项,可选择<*> 表示编译包含到固件,也可以选择表示仅编译不包含在固件。如下图所示:

image-20221123105750877

图4-3: sunxi

4.4 如何增加ISP 效果配置

在完成ISP 调试之后,将会从ISP 调试工程师中得到相应的头文件配置,添加操作如下:

4.4.1 VFE 框架
  1. 将头文件添加到驱动的sunxi-vfe/isp_cfg/SENSOR_H 目录下;

  2. 在驱动sunxi-vfe/isp_cfg 目录下,有个isp_cfg.c 文件,这文件中有个isp_cfg_array 数组,在sensor 的ISP 配置文件最下面也有个相应的结构体,在

isp_cfg_array 数组中按照数组的结构,增加sensor 的name 和结构体即可,这样将会在ISP 匹配的时候,将会根据name 匹配到相应的配置;

4.4.2 VIN 框架
4.4.2.1 R 系列
  1. vin 框架的操作也是类似的,只是更换了位置。vin 的ISP 配置在tina/package/allwinner/libAWIspApi 目录下,其中R311、MR133 在src/isp520,而R818、

MR813 在src/isp522。在libisp/isp_cfg/SENSOR 目录下增加相应的头文件,然后在上一层目录的isp_ini_parse.c 文件增加头文件以及修改相应的isp_cfg_array cfg_arr 数组匹配即可。

  1. VIN 使用ISP,需要在camerademo 中make menuconfig 的时候,选择上Choosewhether to use VIN ISP (YES)。同时VIN 的需要注意,当自己开发camera

HAL 层时,需要自己运行camera ISP service,具体实现可参考camerademo 的实现。添加正确时,在运行camerademo 将会输出相应的sensor 配置信息,

比如:

[ISP]find imx278_mipi_2048_1152_60_0 [imx278_mipi_default_ini_mr813] isp config

上述表示正确查找到imx278_mipi 这个sensor 2048*1152 60fps 的ISP 配置,其他的sensorISP 配置移植正确也将会有类似的打印,输出信息分别是sensor name 、分辨率、帧率,确认这些信息一致即可。

4.4.2.2 V 系列
  1. V 系列ISP 库目录,V533、V83x 平台位于:softwinner/eyesee-mpp/middleware/sun8iw19p1/media/V536 平台位于:softwinner/eyesee-

mpp/middleware/v316/media/LIBRARY/libisp/,V85x 平台位于:external/eyesee-mpp/middleware/sun8iw21/media/LIBRARY/libisp/

  1. 修改libisp/isp_cfg/isp_ini_parse.c,将ISP 效果.h 包含进来,并修改struct isp_cfg_arraycfg_arr[] 结构体;其中参数定义:

(1)Sensor 模块名称:sensor_mipi
(2)ISP 效果头文件名称
(3)分辨率宽
(4)分辨率高
(5)帧率
(6)红外IR 模式标志位
(7)WDR 模式标志位
(8)ISP 参数结构体

image-20221123113519900

图4-4: sunxi

4.5 如何输出RAW 数据

在ioctl 的VIDIOC_S_FMT 命令,将其参数pixelformat 设置为RAW 格式即可,RAW 格式如下:

    V4L2_PIX_FMT_SBGGR8
    V4L2_PIX_FMT_SGBRG8
    V4L2_PIX_FMT_SGRBG8
    V4L2_PIX_FMT_SRGGB8
    V4L2_PIX_FMT_SBGGR10
    V4L2_PIX_FMT_SGBRG10
    V4L2_PIX_FMT_SGRBG10
    V4L2_PIX_FMT_SRGGB10
    V4L2_PIX_FMT_SBGGR12
    V4L2_PIX_FMT_SGBRG12
    V4L2_PIX_FMT_SGRBG12
    V4L2_PIX_FMT_SRGGB12

将pixelformat 设置为上述的其一即可输出RAW 数据, 而如何选择上述的操作, 这个根据sensor 驱动选择, 如果驱动中sensor_formats 的mbus_code 设置为

MEDIA_BUS_FMT_SBGGR10_1X10,则在输出RAW 数据时将pixelformat 设置为V4L2_PIX_FMT_SBGGR10

当前camerademo 已经支持输出RAW 数据,可参照本文档《camerademo 输出RAW 数据》章节。

4.6 如何计算实际曝光时间

该部分为使用到ISP 的RAW sensor 配置信息。曝光时间的计算和曝光控制寄存器、hts、pclk这些配置相关,这些配置都在sensor 驱动,ISP 将会根据sensor 驱

动中的设置计算相应的曝光时间,所以驱动中的配置必须正确,否则在调试ISP 效果可能会遇到其他的一些问题。

static struct sensor_win_size sensor_win_sizes[] = {
    ...
    .hts = 928,
    .vts = 1720,
    .pclk = 48 * 1000 * 1000,
    ...
}

在sensor 的驱动中有以上的一些配置,曝光时间在驱动中是以曝光行为计算单位的,即在sensor_s_exp() 函数中设置的参数为曝光行,部分sensor 是以16 为一

倍的,所以在计算实际的曝光行时,需要将上述函数参数除以16。

曝光时间= 曝光行× hts / pclk

一般的pclk 都是M 级别的,所以时间单位为us,部分sensor 的曝光行为参数的十六分之一,需要除以十六,同时,曝光行不能大于vts 的值,否则将会出现降

帧、没有正常输出图像等问题。

4.7 如何脱离isp tuning 工具微调图像亮度

在isp 配置文件中,有类似以下的信息:

.ae_cfg = {
	256, 555, 256, 555, 31, 22, 22, 25, 3, 130, 16, 60, 1, 2
},

上述ae_cfg 参数的倒数第4 个数值(130)即是控制图像亮度的阀门(期望亮度),该值越大,图像亮度越高。ae_cfg 一共14 个,分别对应着不同的环境亮度

(Lux)。如何确定AE 当前处于哪组ae_cfg 参数呢?修改isp 配置文件中的isp_log_param = 0x1,然后重新编译运行相机应用,留意应用中关于isp 的打印信息:

[ISP_DEBUG]: isp0 ae_target 92, pic_lum 0, weight_lum 0, delta_exp_idx 138, ae_delay 0, AE_TOLERANCE

5

从上述信息可以看到当前的目标亮度是92,这时可以查看isp 配置文件ae_cfg 中哪组的阀门处于92 这个范围,如果需要增加亮度,则提高相应的阀门;降低亮度

则降低阀门。相应的根据实际调试情况修改即可。调试之后,记得将isp_log_param 参数还原为0。

4.8 VIN 如何设置裁剪和缩放

裁剪修改sensor 驱动:在驱动有类似以下的配置

static struct sensor_win_size sensor_win_sizes[] = {
    {
        .width = VGA_WIDTH,
        .height = VGA_HEIGHT,
        .hoffset = 0,
        .voffset = 0,
        .hts = 878,
        .vts = 683,
        .pclk = 72 * 1000 * 1000,
        .mipi_bps = 720 * 1000 * 1000,
        .fps_fixed = 120,
        .bin_factor = 1,
        .intg_min = 1 << 4,
        .intg_max = (683) << 4,
        .gain_min = 1 << 4,
        .gain_max = 16 << 4,
        .regs = sensor_VGA_120fps_regs,
        .regs_size = ARRAY_SIZE(sensor_VGA_120fps_regs),
        .set_size = NULL,
    },
};

上述的width height 表示经过isp 输出之后的数据,如果需要裁剪,修改width、height、hoffset 和voffset。裁剪之后的输出width = sensor_output_src -

2hoffset height = sensor_height_src - 2voffset 注意,上述的hoffset voffset 必须为双数。

所以,假设sensor 输出的是640 × 480,我们想裁剪为320 × 240 的,则上述配置修改为:

static struct sensor_win_size sensor_win_sizes[] = {
    {
        .width = 320,
        .height = 240,
        .hoffset = 160,
        .voffset = 120,
        .hts = 878,
        .vts = 683,
        .pclk = 72 * 1000 * 1000,
        .mipi_bps = 720 * 1000 * 1000,
        .fps_fixed = 120,
        .bin_factor = 1,
        .intg_min = 1 << 4,
        .intg_max = (683) << 4,
        .gain_min = 1 << 4,
        .gain_max = 16 << 4,
        .regs = sensor_VGA_120fps_regs,
        .regs_size = ARRAY_SIZE(sensor_VGA_120fps_regs),
        .set_size = NULL,
    },
};

缩放配置:使用硬件缩放,可以在应用层通过VIDIOC_S_FMT 设置分辨率的时候,直接设置分辨率的大小为缩放的分辨率即可。

	fmt.fmt.pix_mp.width = 320;
	fmt.fmt.pix_mp.height = 240;

上述的操作,将会使用硬件完成相应的缩放输出。

5 模块调试常见问题

初次调试建议打开device 中的DEV_DBG_EN 为1,方便调试。

Camera 模块调试一般可以分为三步:

  1. 使用lsmod 命令查看驱动是否加载,查看/lib/modules/内核版本号目录下是否存在相应的ko,如果没有,确认modules.mk 是否修改正确,配置了开机自动

加载。如果存在相应的ko,可手动加载测试确认ko 是否正常,手动加载成功,则确认内核的版本是否一致,导致开机时没有找到相应的ko 从而没有加载。

  1. 使用ls /dev/v* 查看是否有video0/1 节点生成

  2. 在adb shell 中使用cat /proc/kmsg 命令,或者是使用串口查看内核的打印信息,查看不能正常加载的原因。一般情况下驱动加载不成功的原因有:一是读取

的sys_config.fex 文件中的配置信息与加载的驱动不匹配,二是probe 函数遇到某些错误没能正确的完成probe 的时候返回。

5.1 移植一款sensor 需要进行哪些操作

移植camera sensor,主要进行以下操作:

  1. 根据主板的原理图,确认与sensor 模组的接口是否一致,一致才可以保证配置和数据的正常接收。

  2. 根据产品的需求,让sensor 模组厂提供产品所需的分辨率、帧率的寄存器配置,这一步需要注意,提供的配置需要是和模组匹配的。比如模组的mipi 接口只

引出2lane,而提供的寄存器配置却是配置为4lane 输出的,那么该配置在该模组无法正常使用,让模组厂提供该模组可以正常使用的正确配置。注意,该寄存

器配置SOC 原厂没有,需要sensor 厂提供。

  1. 拿到寄存器配置之后,按照本文档《驱动模块实现》章节完成sensor 驱动的编写。

  2. 在完成驱动的编写之后,按照本文档《Tina 配置》章节完成modules.mk 的修改。

  3. 根据板子的原理图与模组的硬件连接,参照本文档《sys_config.fex 配置》或者《MR813/R818平台配置》章节完成sys_config.fex 或者board.dts 的修改。

  4. 完成上述操作之后,按照本文档《menuconfig 配置说明》章节,选上camera 驱动模块,按照《camera 功能测试》章节选上camera 的测试程序,测试驱动

移植是否正常。

5.2 I2C 通信出现问题

5.2.1 R16 R11 R40 等

I2C 出现问题内核一般会伴随打印” cci_write_aX_dX error! slave = 0xXX, addr = 0xXX,value = 0xXX“。

如果与此同时,内核出现打印“chip found is not an target chip.”,则说明在初始化camera前,读取camera 的ID 已经失败。此时,一般是如下几点出现问题。

a. 最先考虑应该是更换一个camera模组试试。
b. 电源
    检查sys_config.fex
    vip_dev0_iovdd = "axp22_eldo3"
    vip_dev0_iovdd_vol = 2800000
    vip_dev0_avdd = "axp22_dldo4"
    vip_dev0_avdd_vol = 2800000
    vip_dev0_dvdd = "axp22_eldo2"
    vip_dev0_dvdd_vol = 1500000
    一定要与原理图设计保持一致。必要时,需要用万用表测量camera模组的各路电压是否正常。
c. reset和power down脚
    检查sys_config.fex配置
    vip_dev0_reset = port:PH2<1><default><default><default>
    vip_dev0_pwdn = port:PH1<1><default><default><default>
    是否与原理图设计保持一致。必要时,需要用示波器测量reset,pwdn脚,在camera加载时,是否有动作。
d. mclk
    检查sys_config.fex配置
    vip_csi_mck = port:PE01<3><default><default><default>
    pin脚是否与原理图设计保持一致。必要时,在加载camera时,测量mclk,看是否有正确输出(一般是24MHz或27MHz
    )

如果已经能够正确通过camera 的id 读取,只是在使用过程当中,偶尔出现I2C 的读写错误,此时需要从打印里面,将报错的地址和读写值,结合camera 具体的

spec 来分析,到底是操作了camera 哪些寄存器带来的问题。

5.2.2 其他平台

出错时一般出现以下信息:

[ 5.556579] sunxi_i2c_do_xfer()1942 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0
x30)
[ 5.566234] sunxi_i2c_do_xfer()1942 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0
x30)
[ 5.575963] sunxi_i2c_do_xfer()1942 - [i2c1] incomplete xfer (status: 0x20, dev addr: 0
x30)
[ 5.585375] [VIN_DEV_I2C]sc031gs_mipi sensor read retry = 2
[ 5.591666] [sensorname_mipi] error, chip found is not an target chip.

出现上述错误打印时,可按以下操作逐步debug。

  1. 确认sys_config.fex 中配置的sensor I2C 地址是否正确(sensor datasheet 中标注,读地
    址为0x6d,写地址为0x6c,那么sys_config.fex 配置sensor I2C 地址为0x6c);
  2. 在完成以上操作之后,在senor 上电函数中,将掉电操作屏蔽,保持sensor 一直上电状态,
    方便debug;
  3. 确认I2C 地址正确之后,测量sensor 的各路电源电压是否正确且电压幅值达到datasheet
    标注的电压要求;
  4. 测量MCLK 的电压幅值与频率,是否正常;
  5. 测量senso r 的reset、pown 引脚电平配置是否正确,I2C 引脚SCK、SDA 是否已经硬件
    上拉;
  6. 确认I2C 接口使用正确并使能(CCI / TWI);
  7. 如果还是I2C 出错,协调硬件同事使用逻辑分析仪等仪器进行debug;
5.2.3 经典错误
5.2.3.1 I2C 没有硬件上拉
twi_start()450 - [i2c2] START can't sendout!
twi_start()450 - [i2c2] START can't sendout!
twi_start()450 - [i2c2] START can't sendout!
[VFE_DEV_I2C_ERR]cci_write_a16_d16 error! slave = 0x1e, addr = 0xa03e, value = 0x1

出现上述的问题是因为SDA、SCK 没有拉上,导致在进行I2C 通信时,发送开始信号失败,SDA、SCK 添加上拉即可。

5.2.3.2 没有使能I2C
[VFE]Sub device register "ov2775_mipi" i2c_addr = 0x6c start!
[VFE_ERR]request i2c adapter failed!
[VFE_ERR]vfe sensor register check error at input_num = 0

出现上述的错误,是因为使用twi 进行I2C 通信但没有使能twi 导致的错误,此时需要确认sys_config.fex 中,[twiX] 中的twiX_used 是否已经设置为1。

5.3 图像异常

5.3.1 运行camerademo 可以成功采集图像,但图像全黑(RAWsensor)

当camerademo 成功采集到图像时,最起码整条数据通路已经正常,而发现图像时全黑的,注意以下几点:

  1. 在编译camerademo 之前,是根据平台(MR813/R818/MR133/R311) 正确的选上了“Enable vin isp support”,选上之后,重新编译camerademo

(建议cd package/allwinner/-camerademo 目录后执行mm -B 编译);

  1. 通过上述操作之后, 执行新编译的camerademo 可执行程序, 运行过程应可看到类似”[ISP]create isp0 server thread¡‘信息,则正确运行isp,这时再查看新

抓取的图像数据;

  1. 执行运行camerademo 只会抓取5 张图像数据,由于isp 计算合适的图像曝光需要一定的帧数,所以可能存在前面几张图像黑的情况,修改camerademo 运行

参数,抓取多几张图像数据查看(20 张);

  1. 如果是没有移植isp 的环境,则可修改sensor 驱动中寄存器组中的曝光参数配置,增加初始化时曝光时间,从而使初始输出的图像亮度较合适;
5.3.2 camerademo 采集的图像颜色异常

运行camerademo 采集图像之后,发现拍摄得到的轮廓正确但颜色不对,比如红蓝互换、画面整体偏红或偏蓝等颜色异常的情况,出现这样的问题,首先考虑是

sensor 驱动中配置的RAW 数据RGB 顺序错误导致的。在sensor 驱动中有类似以下的配置:

static struct sensor_format_struct sensor_formats[] = {
    {
        .desc = "Raw RGB Bayer",
        .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
        .regs = sensor_fmt_raw,
        .regs_size = ARRAY_SIZE(sensor_fmt_raw),
        .bpp = 1
    },
};

以上配置表明sensor 输出的图像数据是RAW10,RGB 排列顺序是BGGR,出现颜色异常时,一般就是RGB 的排列顺序配置错误导致的,RGB 排列顺序一共有4 种

(MEDIA_BUS_FMT_SBGGR10_1X10/MEDIA_BUS_FMT_SGBRG10_1X10/MEDIA_BUS_FMT_SGRBG10_修改驱动中的mbus_code 为上述的4 种之一,确认哪一种

颜色比较正常,则驱动配置正确。

如果颜色还有细微的不够艳丽、准确等问题,需要进行isp 效果调试,改善图像色彩。上述是以10bit sensor 为例进行介绍,其他的8bit、12bit、14bit 类似,参

考上述即可。

5.4 调试camera 常见现象和功能检查

  1. insmod 之后首先看内核打印,看加载有无错误打印,部分驱动在加载驱动进行上下电时候会进行i2c 操作,如果此时报错的话就不需要再进入camera 了,先

检查是否io 或电源配置不对。或者是在复用模组时候有可能是另外一个模组将i2c 拉住了。

  1. 如果i2c 读写没有问题的话,一般就可以认为sensor 控制是ok 的,只需要根据sensor 的配置填好H/VREF、PCLK 的极性就能正常接收图像了。这个时候可以

在进入camera 应用之后用示波器测量sensor 的各个信号,看h/vref、pclk 极性、幅度是否正常(2.8V 的vpp)。

  1. 如果看到画面了,但是看起来是绿色和粉红色的,但是有轮廓,一般是YUYV 的顺序设置反了,可检查yuyv 那几个寄存器是否填写正确配置,其次,看是否是

在配置的其他地方有填写同一个寄存器的地方导致将yuyv fmt 的寄存器被改写。

  1. 如果画面颜色正常,但是看到有一些行是粉红或者绿色的,往往是sensor 信号质量不好所致,通常在比较长的排线中出现这个情况。在信号质量不好并且

yuyv 顺序不对的时候也会看见整个画面的是绿色的花屏。

  1. 当驱动能力不足的时候增强sensor 的io 驱动能力有可能解决这个问题。此时用示波器观察pclk 和数据线可能会发现:pclk 波形摆幅不够IOVDD 的幅度,或者

是data 输出波形摆幅有时候能高电平达到IOVDD 的幅度,有时候可能连一半都不够。

  1. 如果是两个模组复用数据线的话,不排除是另外一个sensor 在进入standby 时候没有将其数据线设置成高阻,也会影响到当前模组的信号摆幅,允许的话可

以剪断另一个模组来证实。

  1. 当画面都正常之后检查前置摄像头垂直方向是否正确,水平方向是否是镜像,后置水平垂直是否正确,不对的话可以调节sys_config.fex 中的hflip 和vflip 参数

来解决,但如果屏幕上看到的画面与人眼看到的画面是成90 度的话,只能是通过修改模组的方向来解决。

  1. 之后可以检查不同分辨率之间的切换是否ok,是否有切换不成功的问题;以及拍照时候是否图形正常,亮度颜色是否和预览一致;双摄像头的话需要检查前后

切换是否正常。

  1. 如果上述都没有问题的话,可认为驱动无大问题,接下来可以进行其他功能(awb/exp bias/-color effect 等其他功能的测试)。

  2. 测试对焦功能,单次点触屏幕,可正确对上不同距离的物体;不点屏幕时候可以自动对焦对上画面中心物体,点下拍照后拍出来的画面能清晰。

  3. 打开闪光灯功能,检查在单次对焦时候能打开灯,对完之后无论成功失败或者超时能够关闭,在点下拍照之后能打开,拍完之后能关闭。

  4. 如果加载模块后,发现dev/videoX 节点没有生成,请检查下面几点。

a. 模块加载的顺序
    一定要按照以下顺序加载模块
    insmod videobuf-core.ko
    insmod videobuf-dma-contig.ko
    ;如果有对应的vcm driver,在这里加载,如果没有,请省略。
    insmod actuator.ko
    insmod ad5820_act.ko
    ;以下是camera驱动和vfe驱动的加载,先安装一些公共资源。
    insmod vfe_os.ko
    insmod vfe_subdev.ko
    insmod cci.ko
    insmod ov5640.ko
    insmod gc0308.ko
    ;如果一个csi接两个camera,所有camera对应的ko都要在vfe_v4l2.ko之前加载。
    insmod vfe_v4l2.ko
b. sys_config.fex配置
    vip_used = 1 ;确保used为1
    vip_dev_qty = 2 ;确保csi接口上接的camera数量与ko加载情况相同
    vip_dev0_mname = "ov5640" ;确保camera型号与ko加载情况相同
    vip_dev0_twi_id = 1 ;确保camera使用的i2c总线id与配置一样
    vip_dev1_mname = "gc0308" ;确保camera型号与ko加载情况相同
    vip_dev1_twi_id = 1 ;确保camera使用的i2c总线id与配置一样

5.5 画面大体轮廓正常,颜色出现大片绿色和紫红色

一般可能是csi 采样到的yuyv 顺序出现错位。

确认camera 输出的yuyv 顺序的设置与camera 的spec 一致
若camera 输出的yuyv 顺序没有问题,则可能是由于走线问题,导致pclk 采样data 时发生错位,此时可以调整pclk 的采样沿。具体做法如下:

在对应的camara 驱动源码,如ov5640.c 里面,找到宏定义#define CLK_POL。此宏定义可以有两个值V4L2_MBUS_PCLK_SAMPLE_RISING 和

V4L2_MBUS_PCLK_SAMPLE_FALLING。若原来是其中一个值,则修改成另外一个值,便可将PCLK 的采样沿做反相。

5.6 画面大体轮廓正常,但出现不规则的绿色紫色条纹

一般可能是pclk 驱动能力不足,导致某个时刻采样data 时发生错位。

解决办法:

• 若pclk 走线上有串联电阻,尝试将电阻阻值减小。

• 增强pclk 的驱动能力,需要设置camera 的内部寄存器。

5.7 画面看起来像油画效果,过渡渐变的地方有一圈一圈

一般是CSI 的data 线没有接好,或短路,或断路。

5.8 出现[VFE_WARN] Nobody is waiting on thisvideo buffer

上层还回来所有的buffer,但是没有再来取buffer。

5.9 出现[VFE_WARN] Only three buffer left for csi

上层占用了大部分buffer,没有还回,驱动部分只有三个buffer 此时驱动不再进行buffer 切换,直到有buffer 还回为止。

5.10 sensor 的硬件接口注意事项

  1. 如果是使用并口的sensor 模组,会使用到720p@30fps 或更高速度的,必须在mclk/pclk/-
    data/vsync/hsync 上面串33ohm 电阻,5M 的sensor 一律串电阻;
  2. 使用Mipi 模组时候PCB layout 需要尽量保证clk/data 的差分对等长,过孔数相等,特征
    阻抗100ohm;
  3. 如果使用并口复用pin 的模组时候,不建议reset 脚的复用;
  4. 并口模组的排线长度加上pcb 板上走线长度不超过10cm,mipi 模组排线长度加上pcb 板上
    走线长度不超过20cm,超过此距离不保证能正常使用。
  5. 主控并口数据线有D11~D0 共12bit,并口的sensor 输出一般为8/10bit,原理图连接需
    要做高位对齐。

6 camera 功能测试

Tina 系统可以通过SDK 中的camerademo 包来验证camera sensor(usb camera)是否移植成功,如果可以正常捕获保存图像数据,则底层驱动、板子硬件正常。

6.1 camerademo 配置

在命令行中进入Tina 根目录,执行make menuconfig 进入配置主界面,并按以下配置路径操作:

Allwinner
	└─>camerademo

首先,选择Allwinner 选项进入下一级配置,如下图所示:
image-20221123141424322

图6-1: allwinner

然后,选择camerademo 选项,可选择<*> 表示直接编译包含在固件,也可以选择表示仅编译不包含在固件。当平台的camera 框架是VIN 且需要使用ISP 时,将

需要在camerademo 的选项处点击回车进行以下界面选择使能ISP。(该选项只能在VIN 框架中,使用RAW sensor时使用,在修改该选项之后,需要先单独mm -

B 编译该package)。

image-20221123141504379

图6-2: camerademo

image-20221123141522573

图6-3: vinisp

6.2 源码结构

camerademo 的源代码位于package/allwinner/camerademo/目录下:

|---src
| camerademo.c //camea测试的主流程代码
| camerademo.h //camera demo相关数据结构
| common.c //实现共用的函数,转换时间、保存文件、测试帧率等
| common.h //共用函数头文件
| convert.c //实现图像格式转换函数
| convert.h //图像格式转换函数头文件

6.3 camerademo 使用方法

在小机端加载成功后输入camerademo help,假如驱动产生的节点video0(测试默认以/dev/video0 作为设备对象)可以打开则会出现下面提示:

通过提示我们可以得到一些提示信息,了解到该程序的运行方式、功能,可以查询sensor 支持的分辨率、sensor 支持的格式以及设置获取照片的数量、数据保存

的格式、路径、添加水印、测试数据输出的帧率、从open 节点到数据流打通需要的时间等,help 打印信息如下图:

image-20221123142705806

图6-4: help

Camerademo 共有4 种运行模式:

  1. 默认方式:直接输入camerademo 即可,在这种运行模式下,将设置摄像头为640*480 的NV21 格式输出图像数据,并以BMP 和YUV 的格式保存在/tmp 目

录下,而当输入camerademodebug 将会输出更详细的debug 信息;

  1. 探测设置camerademo setting:将会在运行过程中根据具体camera 要求输入设置参数,当输入camerademo setting debug 的时候,将会输出详细的debug 信息;

  2. 快速设置:camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7],将会按照输入参数设置图像输出,同样,当输入camerademo argv[1]

argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] debug 时将会输出更详细的debug 信息。

  1. 选择camera 设置:camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] argv[8],将会按照输入参数设置图像输出,同样,当输入

camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] argv[8] debug 时将会输出更详细的debug 信息。

6.3.1 默认方式

当输入camerademo 之后,使用默认的参数运行,则会打印一下信息,如下图:

image-20221123142833252

图6-5: camerademouser

首先可以清楚的看到成功open video0 节点,并且知道照片数据的保存路径、捕获照片的数量以及当前设置:是否添加水印、输出格式、分辨率和从开启流传输到

第一帧数据达到时间间隔等信息。如果需要了解更多的详细信息,可以在运行程序的时候输入参数debug 即运行camerademo debug,将会打开demo 的debug

模式,输出更详细的信息,包括camera 的驱动类型,支持的输出格式以及对应的分辨率,申请buf 的信息,实际输出帧率等。

6.3.2 选择方式

在选择模式下有两种运行方式,一种是逐步选择,在camera 的探测过程,知道其支持的输出格式以及分辨率之后再设置camera 的相关参数;另一种是直接在运

行程序的时候带上相应参数,程序按照输入参数运行(其中还可以选择camera 索引,从而测试不同的camera)。

  1. 输入camerademo setting,则按照程序的打印提示输入相应选择信息即可。
    • 输入保存路径、照片数量、保存的格式等。

image-20221123142929515

图6-6: info

• 选择输出格式。

image-20221123142950450

图6-7: format

• 选择输出图像分辨率。

image-20221123143011933

图6-8: size

其它信息与默认设置一致,如需打印详细的信息,运行camerademo setting debug 即可。

  1. 第二种是设置参数:
    • 默认的video 0 节点:camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7]。
    输入参数代表意义如下:
argv[1]:camera输出格式---NV21 YUYV MJPEG等;
argv[2]:camera分辨率width;
argv[3]:camera分辨率height;
argv[4]:sensor输出帧率;
argv[5]:保存照片的格式:all---bmp和yuv格式都保存、bmp---仅以bmp格式保存、yuv---仅以yuv格式保存;
argv[6]:捕获照片的保存路径;
argv[7]:捕获照片的数量;

例如:camerademo NV21 640 480 30 yuv /tmp 2,将会输出640*480@30fps 的NV21格式照片以yuv 格式、不添加水印保存在/tmp 路径下,照片共2 张。

其它信息与默认设置一致,如需打印详细的信息,运行camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] debug 即可。

image-20221123143239114

图6-9: run1

• 选择其他的video 节点:camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] argv[8]。

输入参数代表意义如下:

argv[1]:camera输出格式---NV21 YUYV MJPEG等;
argv[2]:camera分辨率width;
argv[3]:camera分辨率height;
argv[4]:sensor输出帧率;
argv[5]:保存照片的格式:all---bmp和yuv格式都保存、bmp---仅以bmp格式保存、yuv---仅以yuv格式保存;
argv[6]:捕获照片的保存路径;
argv[7]:捕获照片的数量;
argv[8]:video节点索引;

例如:camerademo YUYV 640 480 30 yuv /tmp 1 1,将会打开/dev/video1 节点并输出640*480@30fps 的以yuv 格式、不添加水印保存在/tmp 路径下,照片共

1 张。

其它信息与默认设置一致,如需打印详细的信息,运行camerademo argv[1] argv[2] argv[3] argv[4] argv[5] argv[6] argv[7] argv[8] debug 即可。

image-20221123143328632

图6-10: run2
6.3.3 camerademo 保存RAW 数据

当需要使用camerademo 保存RAW 数据时,只需要将输出格式设置为RAW 格式即可。先确认sensor 驱动中的mbus_code 设置为多少位, 假设驱动中, 配置为

mbus_code =MEDIA_BUS_FMT_SGRBG10_1X10,那么可以确认sensor 输出是RAW10,camerademo 的输出格式设置为RAW10 即可。比如输入camerademo

RGGB10 1920 1080 30 bmp /tmp 5,以上命令输出配置sensor 输出RAW 数据并保存在/tmp 目录,命令的含义参考本章节的《选择方式》。

注意:RAW 数据文件的保存后缀是.raw 。

6.3.4 debug 信息解析

以下debug 信息将说明sensor 驱动的相关信息,拍摄到的照片保存位置、数量、保存的格式以及水印使用情况等:

image-20221123143422192

图6-11: debug1

以下debug 信息将说明驱动框架支持的格式以及sensor 支持的输出格式:

image-20221123143618199

图6-12: debug2

类似以下的信息代表这相应格式支持的分辨率信息:

image-20221123143642323

图6-13: debug3

以下信息将会提示将要设置到sensor 的格式和分辨率等信息:

image-20221123143703117

图6-14: debug4

以下信息将会提示设置格式的情况,buf 的相应信息等:

image-20221123143723798

图6-15: debug5

以下信息将提示当前拍照的照片索引以及从开启流传输到dqbuf 成功的时间间隔:

image-20221123143742621

图6-16: debug6

以下信息提示该sensor 的实际测量帧率信息:

image-20221123143807349

图6-17: debug7

以下信息提示从open 节点到可以得到第一帧数据的时间间隔,默认设置为测试拍照的相应设置:

image-20221123143825189

图6-18: debug8
6.3.5 文件保存格式

设置完毕之后,将会在所设路径(默认/tmp)下面保存图像数据,数据分别有两种格式,一种是YUV 格式,以source_ 格式.yuv 名称保存;一种是BMP 格式,以

bmp_ 格式.bmp 格式保存,如下图所示。

查看图像数据时,需要通过adb pull 命令将相应路径下的图像数据pull 到PC 端查看。

image-20221123143900168

图6-19: save

6.4 select timeout 了,如何操作?

在完成sensor 驱动的移植,驱动模块正常加载,I2C 正常通信,将会在/dev 目录下创建相应的video 节点,之后可以使用camerademo 进行捕获测试,如果出现

select timeout,end capture thread!,这个情况可按照以下操作进行debug。

  1. 先和模组厂确认,当前提供的寄存器配置是否可以正常输出图像数据。有些模组厂提供的寄存器配置还需要增加一个使能寄存器,这些可以在sensor

datasheet 上查询得到或者与模组厂沟通;

  1. 通过dmesg 命令, 查看在运行camerademo 的过程中内核是否有异常的打印。在MR813/R818 平台,内核出现tdm 相关字段的连续打印,则需要确认,

board.dts 中的isp配置是否正确,单摄的配置,isp_sel 和tdm_rx_sel 都需要配置为0;双摄的则需要先运行配置为isp0 的video 节点才能再运行isp1 的video

节点;

  1. 其他的按照是并口还是mipi 接口进行相应的debug;
6.4.1 DVP sensor
  1. 确定sensor 的出图data 配置正确,是8 位的、10 位的、12 位的?确认之后,检查驱动中的sensor_formats 和sys_config.fex 中的csi data pin 设置是否正

确;

  1. MCLK 的频率配置是否正确;

  2. sensor 驱动的sensor_g_mbus_config() 函数配置为DVP sensor,type 需要设置为V4L2_MBUS_PARALLEL;

  3. 确定输出的data 是高8 位、高10 位,确定硬件引脚配置没有问题;

  4. 示波器测量VSYNC、HSYNC 有没有波形输出,这两个标记着有一场数据、一行数据信号产
    生;

  5. 测量data 脚有没有波形,电压幅值是否正常;

  6. 如果没有波形,检查一下sensor 的寄存器配置,看看有没有软件复位的操作,如果有,在该寄存器配置后面加上”{REG_DLY,0xff}“进行相应的延时,防止在

软件复位的时候,sensor还没有准备好就I2C 配置寄存器;

  1. 如果上面都还是没有接收到数据,那么在sensor 的驱动文件,有以下配置,这三个宏定义的具体值。每个都有两种配置,将这三个宏的配置两两组合,共8 种

配置,都尝试一下;

#define VREF_POL V4L2_MBUS_VSYNC_ACTIVE_HIGH
#define HREF_POL V4L2_MBUS_HSYNC_ACTIVE_HIGH
#define CLK_POL V4L2_MBUS_PCLK_SAMPLE_RISING
6.4.2 mipi sensor

如果mipi sensor 没有正常出图,做以下debug 操作:

  1. mipi 接口和主控板子连接不要飞线,mipi 信号本身就是高频差分信号,布线时都要求高,飞线更会影响其信号质量,导致无法正常接收数据;
  2. 确认sensor 驱动设置的mipi 格式,同样是查看sensor_g_mbus_config() 函数(lane 和通道数);
  3. 示波器测量mipi 接口的data 线、时钟线,看看有没有数据输出;
  4. 检查一下寄存器配置方面有没有软件复位的,增加相应的延时;
  5. 和模组厂商确认sensor 驱动中对应分辨率的sensor_win_sizes 以下参数配置是否与寄存器组配合的,因为这些参数将会影响mipi 接收数据;

.hts = 3550, .vts = 1126, .pclk = 120 * 1000 * 1000, .mipi_bps = 480 * 1000 * 1000,

上面的hts,又称line_length_pck,VTS 又称frame_length_lines,与寄存器的值要一致,Pclk(Pixel clock) 的值由PLL 寄存器计算得出,可简单计算,pclk = hts ×

vts × fps;而mipi_bps 为mipi 数据速率,mipi_bps = hts × vts × fps ×(12bit/10bit/8bit)/ lane。

有些sensor 的datasheet 没有标注hts 和vts 的,但是他们有H Blanking 和Vertical blanking,他们的转换公式是:

hts = H Blanking + output_width

vts = Vertical blanking + output_height

Output_width 就是输出的一行的大小,output_height 就是输出的一列的大小。

gc 厂的sensor,vts = VB + win_height + 16;VB 和win_height 都是可以从寄存器中获取得到的,注意,win_height 是寄存器值,而不是输出的高。

6.4.3 其他注意事项
6.4.3.1 R311、MR133

sensor_sel

在vin 框架中,sys_config.fex 有以下配置:

[vind0/sensor0]
...
[vind0/sensor1]
...
[vind0/vinc0]
vinc0_used = 1
vinc0_csi_sel = 0
vinc0_mipi_sel = 0
vinc0_isp_sel = 0
vinc0_rear_sensor_sel = 0
vinc0_front_sensor_sel = 0
vinc0_sensor_list = 0

在这里主要是需要注意vinc0_rear_sensor_sel 和vinc0_front_sensor_sel 的配置,当它们都配置为0,表明vind0/vinc0 配置的video0 节点,使用的是

vind0/sensor0 节点中配置的sensor 输出图像数据;当它们配置为0、1,表明vind0/vinc0 配置的video0 节点,可以使用vind0/sensor0 和vind0/sensor1 两个

sensor 输出图像数据,可以通过ioctl 的VIDIOC_S_INPUT 的index 选择使用哪个sensor 的输出;当它们都配置为1 的时候,表明vind0/vinc0 配置的video0 节

点,使用的是vind0/sensor1 的输出。

mipi AB 配置

mipi 配置方面,还有一个需要注意的,该部分在R311、MR133 平台才有这种情况。一般情况,我们使用的是MCSIB 组的mipi 接口,这个按照一般配置使用即

可,MCSIA、MCSIB,这两个会在原理图上表明使用的是哪一组接口,如果单独使用MCSIA 组的mipi 接口,在sys_config.fex 中配置如下:

由于只是使用MCSIA,所以应该配置的是[vind0/sensor1] 组sensor,vinc0_rear_sensor_sel 和
vinc0_front_sensor_sel 都配置为1.
[vind0/vinc0]
vinc0_used = 1
vinc0_csi_sel = 0
vinc0_mipi_sel = 0
vinc0_isp_sel = 0
vinc0_rear_sensor_sel = 1
vinc0_front_sensor_sel = 1
vinc0_sensor_list = 0
Logo

更多推荐