平台参考:原厂 V1.0.3,RV1106/RV1103/RV1103B/RV1106B; Linux 5.10+。介绍相机驱动、传感器接入、图像获取与调试;完整技术章节见下文。具体 ArmSoM 板卡、BSP/SDK 与内核组合尚未验证,须核对原理图、接口、电源及时钟、驱动和 IQ 文件。示例及测试结果来自原厂,不是 ArmSoM 实测;本文未执行硬件命令。中文使用中文原图,英俄德使用英文原图。
1. Sensor点亮相关
1.1 Sensor ID识别不到, I2C通讯失败
Sensor ID 如果未识别到,这与 RKISP 或 RKCIF 没有任何关系,仅仅是 Sensor 上电时序未满足要求。请按以下顺序排查:
- Sensor 的 7-bits i2c slave id 是否正确, 是否误写成8-bits。
- mclk 是否有输出, 电压幅度是否正确。mclk 一般是 24Mhz,也有27Mhz。
- Sensor 的上电时序是否满足要求,主要包括 avdd, dovdd, dvdd, power down, reset 等。
1.1.1 什么是7bits地址
8bits 中的最低位 (LSB) 表示 R/W,高 7bits 即是我们需要的 i2c slave id。
1.1.2 开机后,测量不到 24M mclk 和 VDD 电源
在 Sensor 驱动的实现中,一般是只有在需要时才开启 mclk 及电源,因此开机后 mclk 及电源默认是关闭的。调试时,可以将驱动中的 power_off() 函数的实现注释掉,这样不会下电,方便测量。
1.1.3 仍然测量不到 24M mclk
使用示波器时,检查示波器的带宽是否足够,建议至少 48M 以上的带宽。
- Sensor 没有正确打开 mclk,请参考如 drivers/media/i2c/ov5695.c 中对 mclk 的操作。
- 该 gpio 被其它模块占用了,这种情况时,一般 kernel log 会有相应的提示。还可以通过 io 命令去查看 pin-ctrl 寄存器设置是否正确。
1.1.4 检查 Sensor 的上电时序是否满足要求
Sensor 的 Datasheet 中一般会详细描述每路电源的上电顺序及间隔要求,请通过示波器检查是否满足。有一些 Sensor 的电源 vdd 在上电时是没有时间先后要求的,如 ov5695 ,它的驱动中可能是用regulator_bulk 来管理电源;但有一些是有先后要求的,如 ov2685.c ,它在驱动中是用多个regulator 去分别控制,具体如 avdd_regulator , dovdd_regulator 。请根据实际情况选择。
1.2 Sensor 驱动中的 exp_def 、 hts_def 、 vts_def 默认值是多
少
如果有 Sensor 原厂的联系方式,请联系原厂获取。否则,请从 Datasheet 中查找到对应的寄存器,并从寄存器列表中找到初始化时配置的值即可。以 ov2685.c 为例:
#define OV2685_REG_VTS 0x380e
...
{0x380e, 0x05},
{0x380f, 0x0e},
...
.vts_def = 0x050e,
0x380e 与 0x380f 是 VTS 对应的寄存器,在初始化时配置的值是 0x050e,那么 vts_def 就是 0x050e。exp 与 hts 采用默认值,可直接从 Datasheet 中查找。如果不期望应用程序去调节曝光、帧率时,可以不必要用到 exp, hts, vts。一般 RAW 格式的 Sensor需要这三个参数。
1.3 link_freq 与 pixel_rate 值应该是多少
link_freq 指的是 MIPI clk 的实际频率。注意不是 24M 的 mclk ,而是 MIPI dn/dp clk。优先通过原厂窗口查问,或查找 Datasheet 是否有相关的参数。一般情况下, link_freq 实际值不会小于如下公式的计算结果,单位是(Hz)
link_freq = width * height * fps * bits_per_pixel / lanes / 2
如果实在不知道 link_freq 的实际值,可以用示波器测量。pixel_rate 指的是每秒传输的像素个数,在 link_freq 确定下之后,可用以下公式计算:
pclk = link_freq * 2 * lanes / bits_per_pixel
1.4 怎么才算点亮 Sensor
首先需要能认到 Sensor id,即 i2c 的读写不能有异常。这时用 media-ctl -p -d /dev/media0 应该能够看到Sensor 的具体信息,如名称、分辨率等。如下所示:
# media-ctl -p -d /dev/media0 | tail -n 30
-> "rkcif_tools_id2":0 []
pad11: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 [ENABLED]
- entity 58: rockchip-csi2-dphy0 (2 pads, 2 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev1
pad0: Sink
[fmt:SBGGR10_1X10/2688x1520@10000/300000 field:none]
<- "m00_b_sc450ai 4-0030":0 [ENABLED]
pad1: Source
-> "rockchip-mipi-csi2":0 [ENABLED]
- entity 63: m00_b_sc450ai 4-0030 (1 pad, 1 link)
type V4L2 subdev subtype Sensor flags 0
device node name /dev/v4l-subdev2
pad0: Source
[fmt:SBGGR10_1X10/2688x1520@10000/300000 field:none]
-> "rockchip-csi2-dphy0":0 [ENABLED]
若 media-ctl 或 v4l2-ctl 没有编译打包在系统中,需要使用 dmesg 打印kernel log,查找相关sensor 是否成功Detect 成功。其次,上层抓图时,MIPI 要能输出数据,且不报 MIPI / ISP 相关错误,应用层能接收到帧。
1.5 如何设置 Sensor 裁剪输出
有时需要将一个 Sensor 从比较大的分辨率裁剪到小一些的分辨率输出,我们可以通过修改 Sensor 驱动与 IQ 文件的方式实现。修改 Sensor 驱动文件,主要配置 xxxx_get_selection 接口实现。以 sc530ai Sensor 为例,修改 Sensor 分辨率从 2880x1616 到裁剪后 2560x1440 输出,修改代码如下:
+++ b/drivers/media/i2c/sc530ai.c
@@ -1593,8 +1593,8 @@ static int sc530ai_open(struct v4l2_subdev *sd, struct
v4l2_subdev_fh *fh)
}
#endif
-#define DST_WIDTH 2880
-#define DST_HEIGHT 1616
+#define DST_WIDTH 2560
+#define DST_HEIGHT 1440
修改对应 Sensor 的 IQ 文件中的分辨率为裁剪输出后的分辨率。IQ 文件修改分辨率的步骤详⻅第 4 章节如何修改 Sensor IQ 文件中的分辨率。
1.6 i2ctransfer 工具的使用
Sensor 驱动作为 I2C 的设备,在调试过程中,难免会读取或者写入 Sensor 的寄存器值,这里介绍i2ctransfer 工具的常用方法。
# i2ctransfer -f -y 4 w3@0x32 0x43 0x24 0x18
4:代表 IIC 的总线号(对应的可为0,1,2,3...)
w:代表写
3:代表写 3 Byte
0x32:代表 IIC 设备地址
后边三个数据代表要写的数据,这⾥0x4324假如为16bit的寄存器地址,0x18为要写⼊的值。
# i2ctransfer -f -y 4 w1@0x30 0x08 r3
4:代表 IIC 的总线号(对应的可为0,1,2,3...)
w:表⽰写
1:表⽰写 1 Byte
0x30:代表 IIC 设备地址
r:代表读取
3:代表读取 3 Byte
这句代表的意思是从0x30地址上偏移0x08之后读取3个Byte,r后数字表⽰读取的个数。
其他 i2c-tools 的使用可参考博客文章:https://blog.csdn.net/qq_42952079/article/details/125217208。注意: 不同平台Linux 的位数不同(32位、64位),使用的 i2ctransfer 的版本不同。若没有此工具可自行下载或找开发人员获取。
1.7 Sensor AVL 列表
RGB Sensor AVL 位于https://redmine.rockchip.com.cn/projects/rockchip_camera_module_support_list/camera支持列表中显示了 Sensor 模组的详细信息。如果是其它非 RGB Sensor,如 YUV Sensor,可以直接查看 kernel 源码的 drivers/media/i2c/ 目录,其中驱动作者是Rockchip 的,驱动都是有调试过的。
1.8 Sensor 驱动调试参考文档
RV1106 / RV1103 SDK 中调试新的 Sensor,可参考如下 驱动开发文档:/docs/zh/isp/<Rockchip_Driver_Guide_VI_CN_v1.1.5.pdf>。
2. MIPI / ISP 异常相关
Sensor 调试初期,比较经常碰到的几类问题是:
- 没有收到帧数据,也没看到 ISP/MIPI 有报错
- 看到 log 不停打印 MIPI 错误
- ISP 报 PIC_SIZE_ERROR
- 偶现 MIPI 错误
- MIPI 不停报错,直至死机
2.1 MIPI 需要设置哪些参数
在 Sensor 与 ISP 之间 MIPI 通讯需要设置4个参数,请务必确认4个 MIPI 参数的正确性。Sensor 输出的分辨率大小Sensor 输出的图像格式,是 YUV 或 RGB RAW,8-bits、10-bits、或12-bitsSensor 的 MIPI 实际输出 link_freqSensor 使用了几个 MIPI lane,这需要在 dts 中2个位置都配置正确
2.2 没有收到帧数据,也没有看到 ISP/MIPI 有报错
- 确认 kernel log 中有没有关于 MIPI 的报错,比如用 dmesg | grep MIPI 看看有没有出错信息。
- 确认 kernel log 中有没有出现 Sensor 的 I2C 读写失败,如果 Sensor 在配置寄存器时失败了,Sensor 也可能没有正确初始化并使能输出。
- 实际量测下 MIPI 的 clk 及 data 线上有没有信息输出。如果没有,建议从 Sensor 初始化及硬件方面分析。
- 实际量测有 MIPI 信号输出,但没报错也收不到数据请再次检查2.1 MIPI需要设置哪些参数,请确认 I2C 通讯没有错,Sensor 的寄存器初始化列表有全部写到 Sensor 中,在 Sensor 驱动中,最后使能 MIPI 输出的是 s_stream() ,请确认在这个函数前,特别是s_power() ,不要让 MIPI 信号输出。这是因为在 s_stream() 前,MIPI 控制器还未实际准备好接收数据,如果在 s_stream() 前输出数据,可能导致 MIPI 协议头 SOT 信号丢失,也可以将 Camera Sensor 端 clock lane 由 continue 模式切换到 no continues。
2.3 命令正确,select timeout 报错
经常会出现抓 Raw 数据没数据返回,串口也没报错,抓图的时候报 select timeout 的错误。此现象可以按照如下排查:
- 查看DPHY的状态根据TRM手册,查看dphy的stopstate 来判断是否有收到数,例如查看RK3588 CSI0:io -4 -l 0x100 0xfdd30000需要连续读取10次上述寄存器,正常有识别到 MIPI 信号的话,对应的stopstate会在0/1之间变化。
- 测量 MIPI 信号使用示波器测量是否有 MIPI 通道信号的输出。
- 确认 sensor 寄存器确认 sensor mipi ou t的寄存器是正常的,可以使用i2c工具读取,推荐使用 i2ctransfer。
- 确定芯片的睡眠和复位引脚电平是否正常。
2.4 MIPI报错
2.4.1 MIPI错误信息详细表
针对RK3288/RK3399/RK3368,错误信息表如下:
| 错误位(Bit) | 简称 | 描述 |
|---|---|---|
| 25 | ADD_DATA_OVFLW | additional data fifo overflow occurred |
| 24 | FRAME_END | 正常收到一帧,不是错误 |
| 23 | ERR_CS | checksum error |
| 22 | ERR_ECC1 | 1-bit ecc error |
| 21 | ERR_ECC2 | 2-bit ecc error |
| 20 | ERR_PROTOCOL | packet start detected within current packet |
| 19:16 | ERR_CONTROL | PPI interface control error occured, one bit per lane |
| 15:12 | ERR_EOT_SYNC | MIPI EOT(End Of Transmission) sync, one bit per lane |
| 11:8 | ERR_SOT_SYNC | MIPI SOT(Start Of Transmission) sync, one bit per lane |
| 7:4 | ERR_SOT | MIPI SOT(Start Of Transmission), one bit per lane |
| 3:0 | SYNC_FIFO_OVFLW | synchronization fifo overflow occurred, one bit per lane |
针对RK3326/PX30/RK1808,3个错误信息表如下:
| ERR1 错误位(Bit) |
简称 | 描述 |
|---|---|---|
| 28 | ERR_ECC | ECC ERROR |
| 27:24 | ERR_CRC | CRC ERROR |
| 23:20 | ERR_FRAME_DATA | Frame 传输完毕,但至少包含一个CRC错误 |
| 19:16 | ERR_F_SEQ | Frame Number 不连续不符合预期 |
| 15:12 | ERR_F_BNDRY | Frame start与Frame end没有匹配 |
| 11:8 | ERR_SOT_SYNC | MIPI PHY SOT(Start Of Transmission) sync error |
| 7:4 | ERR_EOT_SYNC | MIPI PHY EOT(End Of Transmission) sync error |
| ERR2 错误位(Bit) | 简称 | 描述 |
|---|---|---|
| 19:16 | ERR_CONTROL | |
| 15:12 | ERR_ID | |
| 11:8 | ERR_ECC_CORRECTED | |
| 7:4 | ERR_SOTHS | PHY SOTHS error |
| 3:0 | ERR_ESC | PHY ESC error |
常⻅的错误分析如下小章节。
2.4.2 如何处理 SOT/SOT_SYNC 错误
SOT(Start of Transmission)和 SOT_SYNC(Start of Transmission Sync)是在 MIPI(MobileIndustry Processor Interface)接口中可能出现的错误类型之一。SOT 信号需要符合 MIPI_D-PHY_Specification。如果需要深入分析,请直接从网上搜索该 pdf 文档,并建议重要参考:High-Speed Data TransmissionStart-of-Transmission SequenceHS Data Transmission BurstHigh-Speed Clock TransmissionGlobal Operation Timing Parameters但一般来讲,Sensor 如果有在其它平台调通过,那么不符合 MIPI 协议的可能性比较小,建议客戶:首先向 Sensor 厂家确认该 Sensor 是否有实际成功使用过 MIPI 接口传输数据,再次确认 link_freq 是否正确。因为 SOT 时序中的 Ths-settle 需要在 MIPI 接收端配置正确,所以link_freq 很关键,如果使用了多 lane,看 Sensor 原厂有没有办法修改成 1 lane 传输。
检查物理连接:确保 MIPI 接口的物理连接良好。检查线缆、接头和连接器是否松动、损坏或有不良接触。物理连接问题可能导致数据传输错误和通信中断。验证电源供应:检查 MIPI 接口的供电稳定性。确保电源线路连接正常,供电电压水平符合规范要求。电源问题可能导致通信错误和协议异常。调整时序参数:MIPI 接口的时序参数对通信稳定性至关重要。尝试调整时钟频率、数据线延迟等参数,以获得更稳定的通信。这可能需要参考设备规格和厂商建议,进行适当的优化和调整。检查协议设置:确保 MIPI 接口的协议设置正确,并与设备之间的通信协议匹配。这包括时钟频率、数据线延迟、通信模式等方面的设置。参考 MIPI 接口的规范和相关文档,确保协议配置符合要求。分析错误日志:查看系统或设备的错误日志,以了解更多关于 SOT 和 SOT_SYNC 错误的详细信息。错误日志可能包含有关错误类型、位置和时间戳等信息,有助于定位问题。调试工具和设备:使用 MIPI 接口调试工具和设备,如逻辑分析仪、协议分析仪或信号发生器,来监测和分析 MIPI 接口的信号和通信过程。这些工具可以提供更深入的调试能力,帮助定位和解决SOT 和 SOT_SYNC 错误。需要注意的是,SOT 和 SOT_SYNC 错误可能由多种原因引起,包括物理连接问题、电源供应问题、协议设置不正确等。因此,解决问题的方法可能因具体情况而异。在排查过程中,综合考虑硬件、软件和通信方面的因素,并进行逐步排查和验证,有助于定位和解决这些错误。
2.4.3 如何处理 CRC/CheckSum(CS) 、ECC/ECC1/ECC2 错误
出现了 ECC 错误,CS 检验错误,说明数据在传输时不完整。建议:优先排查硬件信号。如果使用了多lane,看 Sensor 原厂有没有办法修改成 1 lane 传输。因为多 lane 之间没有同步好,也有可能出现ECC 错误。检查物理连接:确保 MIPI 接口的物理连接正常,包括线缆、接头和连接器。检查是否存在松动、损坏或不良接触等问题。验证电源供应:确保 MIPI 接口的供电稳定。检查电源线路是否正常连接,电压水平是否符合规范要求。检查时序配置:MIPI 接口的正确操作需要正确配置时序参数,如时钟频率、数据线延迟等。请确保时序配置与设备要求一致,并且在正常范围内。检查协议设置:MIPI 接口使用不同的协议,如 MIPI D-PHY 或 MIPI C-PHY。确保协议设置正确,并与设备之间的通信协议匹配。分析错误日志:查看系统或设备的错误日志,以了解更多关于 ECC 错误的详细信息。错误日志可能包含有关错误类型、位置和时间戳等信息,有助于定位问题。调试工具和设备:使用 MIPI 接口调试工具和设备,如逻辑分析仪、协议分析仪或信号发生器,来监测和分析 MIPI 接口的信号和通信过程。这些工具可以提供更深入的调试能力,帮助定位 ECC 错误的原因。咨询设备厂商或技术支持团队:如果以上步骤无法解决问题,可以咨询 MIPI 接口相关的设备厂商或技术支持团队,寻求他们的帮助和建议。他们通常具有更深入的了解和专业知识,可以提供针对特定设备和应用的解决方案。需要注意的是,ECC 错误可能由多种原因引起,包括硬件故障、信号干扰、配置错误等。因此,解决问题的方法可能因具体情况而异。在排查过程中,综合考虑硬件、软件和通信方面的因素,并进行逐步排查和验证,有助于定位和解决 ECC 错误。
2.4.4 如何处理 ERR_PROTOCOL/ERR_F_BNDRY 错误
该错误说明没有收到预期的 EOT/SOT。SOT,EOT 应该成对匹配出现。建议实测波形检查。检查协议设置:确保 MIPI 接口的协议设置正确,并与设备之间的通信协议匹配。这包括时钟频率、数据线延迟、通信模式等方面的设置。参考 MIPI 接口的规范和相关文档,确保协议配置符合要求。验证电源供应:检查 MIPI 接口的供电稳定性。确保电源线路连接正常,供电电压水平符合规范要求。电源问题可能导致通信错误和协议异常。检查物理连接:确保 MIPI 接口的物理连接良好。检查线缆、接头和连接器是否松动、损坏或有不良接触。物理连接问题可能导致数据传输错误和通信中断。调整时序参数:MIPI 接口的时序参数对通信稳定性至关重要。尝试调整时钟频率、数据线延迟等参数,以获得更稳定的通信。这可能需要参考设备规格和厂商建议,进行适当的优化和调整。分析错误日志:查看系统或设备的错误日志,以了解更多关于 ERR_PROTOCOL 和 ERR_F_BNDRY错误的详细信息。错误日志可能包含有关错误类型、位置和时间戳等信息,有助于定位问题。调试工具和设备:使用 MIPI 接口调试工具和设备,如逻辑分析仪、协议分析仪或信号发生器,来监测和分析 MIPI 接口的信号和通信过程。这些工具可以提供更深入的调试能力,帮助定位和解决ERR_PROTOCOL 和 ERR_F_BNDRY 错误。需要注意的是,ERR_PROTOCOL 和 ERR_F_BNDRY 错误可能由多种原因引起,包括协议不匹配、物理连接问题、时序参数设置不正确等。因此,解决问题的方法可能因具体情况而异。在排查过程中,综合考虑硬件、软件和通信方面的因素,并进行逐步排查和验证,有助于定位和解决这些错误。
2.4.5 能正常收帧,但偶现 MIPI 错误
如果是 MIPI 错误,参考前面的错误描述。与信号相关建议从硬件信号上分析。特别地,如果 MIPI 错误只在刚开始抓图时有,有可能是 Sensor 在上电的过程中 MIPI 信号有输出但并不符合协议,从而报错。这种情况下,可以尝试按如下流程修改:将完整的 Sensor 寄存器的初始化放到 s_power() 中。因为此时 MIPI 接收端尚未开始接收数据,会忽略所有数据。在 s_power() 函数的最后,关闭sensor的输出,即相当于调用了 stop_stream()在 start_stream() 与 stop_stream() 中,仅打开或关闭 MIPI 的输出。
2.4.6 报很多 MIPI 错误甚至死机
这可能是2.4.5 能正常收帧,但偶现MIPI错误的更坏的情况。碰到过这样的现象,其原因是 MIPI 信号不符合要求,而且 MIPI 接收端某些错误是电平中断,导致中断⻛暴并最终死机。可以尝试按2.4.5 能正常收帧,但偶现MIPI错误的方法看是否有效。
2.4.7 如何处理 ISP PIC_SIZE_ERROR
Picture size error 是 ISP 级的错误,它提示未接收到预期的行数,列数。因此从各级的分辨率大小检查。如果前级(即MIPI)有报错,应该先解决 MIPI 错误。请从如下几点检查:DDR 频率是否太小。当 DDR 频率太低时,响应速度不够时,也会出现该错误。尝试将 DDR 定频到最高频率看还会不会出错:echo performance > /sys/class/devfreq/dmc/governor整个 ISP 链路中,有没有出现后级比前级的分辨率还大的情况。可以用 media-ctl -p -d/dev/media0 去查看拓扑结构。分辨率应该要满足 Sensor == MIPI_DPHY >= isp_sd input >= isp_sd output。如果您没有手动修改过,默认应该是满足这个条件的。Sensor 的输出分辨率大小是否正确。尝试在驱动代码中将分辨率强制改小。比如ov7251.c中默认分辨率是640x480,
static const struct ov7251_mode supported_modes[] = {
{
.width = 640,
.height = 480,
将 width, height 都改小些,比如 320x240,寄存器的配置不用改。这是为了确认 Sensor 的配置大小会不会超过实际输出的大小。
static const struct ov7251_mode supported_modes[] = {
{
.width = 320,
.height = 240,
3. 获取图像相关
这部分主要涉及与抓图相关的常⻅问题。
3.1 有哪些方式可以抓图
RKISP 及 RKCIF 驱动支持 v4l2 接口,获取图像可以使用:v4l-utils 包中的 v4l2-ctl 工具获取图像。在调试过程中,建议首先先使用该工具检验能否成功出图。v4l2-ctl 抓图保存成文件,它不能解析图像并显示出来。如需要解析,Ubuntu/Debian环境下可以使用 mplayer,Windows 下可以使用如 7yuv 等工具。对 v4l2-ctl, mplayer 工具的详细说明,请参考《Rockchip_Developer_Guide_Linux_Camera_CN.pdf》。v4l2-ctl也自带有详细的 v4l2-ctl –help 文档。使用 rocki t多媒体库中提供的 demo bin 二进制程序取图保存。
常⻅命令有:
# 取 raw 图
rk_mpi_vi_test -w 1920 -h 1080 -d 0 -c 0 -m 0 -l 10 -n /dev/video0 -f 131076
# 从 mainpath 通道取 yuv 图,并保存在⽂件中。⽂件⽬录 /data/test_0_0_0.bin,可使⽤
7yuv等⼯具打开。⽂件命名根据 dev id, pipe id, chn id相关。
rk_mpi_vi_test -w 1920 -h 1080 -d 0 -c 0 -m 0 -l 10 -o 1
# 从 mainpath 通道取 yuv 图,并并编码,写⽂件。⽂件保存在/data/venc_0.bin,⽂件命名跟
chn id 有关。
rk_mpi_vi_test -w 1920 -h 1080 -d 0 -c 0 -m 1 -l 10 -o 1
...
其他 demo 可参考 rockit 多媒体开发文档,查看demo help帮助命令来使用。实时预览。开发中,需要实时预览取流效果时,可使用 SDK中提供的 simple demo。
# 使⽤ simple_vi_bind_venc_rtsp demo,通过rtsp 实时预览(使⽤vlc 或 potplayer⼯具配
置板端IP).
simple_vi_bind_venc_rtsp -I 0 -w 1920 -h 1080 (rtsp://ip/live/0)
3.2 抓到的图颜色不对,亮度也明显偏暗或偏亮
需要根据 Sensor 分情况:
- Sensor 是 RAW RGB 的输出,如 RGGB、BGGR 等,需要 3A 正常跑起来。可以参考4 3A相关3A 确认正常在跑时,请再次检查解析/显示图像时使用的格式是否正确,uv分量有没有弄反。
- Sensor 是 yuv 输出,或 RGB如RGB565、RGB888,此时 ISP 处于bypass 状态,
如果颜色不对,请确认sensor的输出格式有没有配置错误,uv分量有没有弄反。确认无误时,建议联系Sensor原厂如果亮度明显不对,请联系 Sensor 原厂
3.3 什么是 ISP 的拓扑结构(topology, 链路结构),如何使用
media-ctl 命令
RKISP 或 RKCIF 可以接多个的 Sensor,分时复用;同时 RKISP 还有多级的裁剪功能。因此用链接的方式将各个节点连接,并可通过 media-ctl 分别配置参数。关于 media-ctl 的使用,在《Rockchip_Developer_Guide_Linux_Camera_CN.pdf》文档中有较完整的描述。
3.3.1 一个 ISP 怎样接多个 Sensor
可以接多个 Sensor ,但只能分时复用。通过配置 dts ,将多个 Sensor 链接到 MIPI DPHY 后,可通过media-ctl 切换Sensor。
3.4 抓取 RAW 图是否与原图完全一致
当 ISP 以 bypass 模式获取 Sensor RAW 图(如RGGB, BGGR)时,需要 8 bit 对⻬,不足8 bit 会低位填充0,即如果是 8bit, 16bit 的原图,应用获取到的是原图,没有填充如果是 10bit, 12bit 的原图,会每个像素低位补0到16bit只有 MP 对应的 video 设备可以出 RAW 图,SP 不支持 RAW 图输出的。
3.5 ISP 怎样双路( MP, SP )同时输出
RKISP 有 SP, MP 两路输出,即 Sensor 出来一张图像,SP,MP 可以分别对该图像做裁剪、格式转换,并可同时输出。SP, MP 具有不同的视频处理能力,详细请参考《Rockchip_Developer_Guide_Linux_Camera_CN.pdf》。只有当 SP, MP 都输出 RGB 或 YUV 时才可以同时输出。如果 MP 输出 RAW 图,那么 SP 不可以出图。
3.6 ISP 是否具有放大功能
硬件上有该功能,但不建议使用,驱动中也是默认关闭该功能。
3.7 ISP 是否具有旋转功能
没有。如果需要使用旋转功能,建议:如果是 flip, mirror,首先查看 Sensor 是不是有该功能,如果有,直接使用。这样效率最高如果无法使用 Sensor flip, mirror,考虑使用 RGA 模块,它的代码及 demo 位于external/linux-rga/目录,且有相关文档位于 docs/目录下
3.8 怎样抓灰度 (GREY) 图
只要 ISP 可以输出 YUV、或者 Sensor输出是 Y8灰度图时,应用程序总是可以使用V4L2_PIX_FMT_GREY(FourCC为GREY) 格式直接获取图像。
3.9 如何区分MP、 SP、BP
可通过 media-ctl -p -d /dev/media0 (如有多个 media 设备,也尝试下/dev/media1,/dev/media2) 去查看拓扑结构,如下截取部分输出:
# media-ctl -p -d /dev/media0
...
- entity 2: rkisp1_mainpath (1 pad, 1 link) //表⽰该entity是
MP(MainPath)
type Node subtype V4L flags 0
device node name /dev/video1 //对应的设备节点
是/dev/video1
pad0: Sink
<- "rkisp1-isp-subdev":2 [ENABLED]
- entity 3: rkisp1_selfpath (1 pad, 1 link) //表⽰该entity是
SP(SelfPath)
type Node subtype V4L flags 0
device node name /dev/video2 //对应的设备节点
是/dev/video2
pad0: Sink
<- "rkisp1-isp-subdev":2 [ENABLED]
...
少数情况下如果没有media-ctl命令,可以通过/sys/节点查找,如:
# grep '' /sys/class/video4linux/video*/name
/sys/class/video4linux/video0/name:stream_cif
/sys/class/video4linux/video1/name:rkisp1_mainpath # MP节点对应/dev/video1
/sys/class/video4linux/video2/name:rkisp1_selfpath # SP节点对应/dev/video2
/sys/class/video4linux/video3/name:rkisp1_rawpath
/sys/class/video4linux/video4/name:rkisp1_dmapath
/sys/class/video4linux/video5/name:rkisp1-statistics
/sys/class/video4linux/video6/name:rkisp1-input-params
RV1106 / RV1103 平台下, 还有BP、MP的下采样通道、BP的下采样通道。
# media-ctl -p -d /dev/media1
...
- entity 6: rkisp_mainpath (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video11
pad0: Sink
<- "rkisp-isp-subdev":2 [ENABLED]
- entity 12: rkisp_selfpath (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video12
pad0: Sink
<- "rkisp-isp-subdev":2 [ENABLED]
- entity 18: rkisp_bypasspath (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video13
pad0: Sink
<- "rkisp-isp-subdev":2 [ENABLED]
- entity 24: rkisp_mainpath_4x4sampling (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video14
pad0: Sink
<- "rkisp-isp-subdev":2 [ENABLED]
- entity 30: rkisp_bypasspath_4x4sampling (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video15
pad0: Sink
<- "rkisp-isp-subdev":2 [ENABLED]
...
3.10 图像分屏问题
现象:当 MIPI 通道受到干扰时,便会出现分屏问题,复现也很简单,只需要对 MIPI 通道的数据线或时钟线进行干扰即可分屏,并且每次分屏的位置都不同。解决办法:这里存在两种情况启动时分屏,当设备启动时分屏,这样的情况是因为获取图片之前没有对设备进行复位(可能软复位和硬复位都要执行),RN6725V1 的复位如下图所示:

ret = rn6752_write(client, 0x80, 0x31);
usleep_range(200, 500);
ret |= rn6752_write(client, 0x80, 0x30);
if (ret)
{
dev_err(&client->dev, "rn6752 soft reset failed\n");
return ret;
}
运行时分屏,当图像正常运行后,受到硬件部分的干扰也会导致分屏,例如:接在 CSI0 会出现分屏,而接在 CSI1 不会分屏,是因为他的 CSI0 通道经过了 VICP 模块,CSI1 直接接到了 ISP 模块上。这是因为从 VICP 没有开启图像异常检测功能导致的,这样的现象只需要增加 CIF 通道的异常检测功能即可,具体的操作⻅ VICAP 异常复位的设置。
3.11 如何提高ISP频率
RV1106/RV1103 平台上,为了解决某些场景下出流慢,ISP 处理速度慢,系统卡顿等情况时,需要提高ISP 频率,可使用如下操作:
# cat /proc/clk/summary | grep isp
clk_core_isp3p2 1 1 0 339428572 0 0
50000
aclk_isp3p2 1 1 0 339428572 0 0
50000
hclk_isp3p2 1 1 0 148500000 0 0
50000
isp0clk_vicap 2 2 0 0 0 0
50000
set clk rate:echo [clk_name] [rate(Hz)] > /proc/clk/rate如下:
# echo clk_core_isp3p2 420000000 > /proc/clk/rate
# cat /proc/clk/summary | grep isp
aclk_isp3p2 1 1 0 339428572 0
0 50000
hclk_isp3p2 1 1 0 148500000 0
0 50000
clk_core_isp3p2 1 1 0 420000000 0
0 50000
isp0clk_vicap 2 2 0 0 0
0 50000
4. 3A相关
如果 Sensor 需要 3A tunning,如 Sensor 输出格式 RGGB, BGGR 等这样的 RAW BAYER RGB 格式,那么需要 RKISP 提供图像处理。根据 camera_engine_rkaiq 版本的不同,3A 处理方式有差别。建议尽量将 camera_engine_rkaiq 升级到最新的版本。请首先确认该模组是否在支持列表中,已经在支持列表中的,media/isp/camera_engine_rkaiq/rkaiq/iqfiles/ 目录下会有一份对应的json文件否则请向业务窗口发起模组调试申请
4.1 如何确认 camera_engine_rkaiq 的版本
从源码中查看
#grep RK_AIQ_VERSION_REAL media/isp/camera_engine_rkaiq/rkaiq/RkAiqVersion.h
#define RK_AIQ_VERSION_REAL_V "v5.0x5.0"
4.1.1 如何确认 camera_engine_rkaiq 所需要的 rkisp kernel 驱动的版本号
camera_engine_rkisp对kernel驱动版本有要求,需要保证rkisp驱动足够新。从kernel源码中查看 ISP 驱动版本
# grep RKISP_DRIVER_VERSION drivers/media/platform/rockchip/isp/version.h
从kernel log 中查看 ISP 驱动版本
# dmesg | grep "version"
dmesg | grep "version"
[ 0.848252] udevd[65]: starting version 3.2.7
[ 3.889404] imx415 4-001a: driver version: 00.01.08
[ 3.967388] os04a10 4-0036: driver version: 00.01.05
[ 4.084418] sc4336 4-0030-3: driver version: 00.01.01
[ 4.114867] sc3336 4-0030-1: driver version: 00.01.01
[ 4.152066] sc530ai 4-0030: driver version: 00.01.01
[ 4.180572] sc200ai 4-0030-6: driver version: 00.01.09
[ 4.237776] rkcif rkcif-mipi-lvds: rkcif driver version: v00.02.00
[ 4.260419] rkisp rkisp-vir0: rkisp driver version: v02.05.00
4.2 如何升级 camera_engine_rkaiq
包含有三部分
- camera_engine_rkaiq 本身位于SDK 的 media/isp/camera_engine_rkaiq 目录,直接通过 git 或 repo 工具可以更新。可以仅更新该目录而不影响其它 SDK 中的目录。
- kernel 根据 camera_engine_rkaiq 的需要相应升级在 media/isp/camera_engine_rkaiq 目录下通过查看 git log ,可以找到它所需要的kernel rkisp驱动的版本号。例如:
# git log
commit 3d71d22e1e1cc080cd299b914e4e8daac2a58329
Author: ZhongYichong <zyc@rock-chips.com>
Date: Sun Feb 18 10:17:18 2024 +0800
release v5.0x5.0
cherry-pick:
6227d46 Revert "fastboot: remove rk_aiq_uapi2_sysctl_preInit_tb_info"
f8efdd6 Revert "fastboot: _first_awb_cfg use pointer replace struct"
4.3 如何确认 3A 是否正常在工作
通过抓取图像,查看图像的色彩及曝光是否正常。同时,通过查看后台是否有rkisp_3A_server进程在执行,如下:
# ps -ef | grep rkisp_3A_server
706 root 9176 S /usr/bin/rkisp_3A_server --mmedia=/dev/media1
746 root 2408 S grep rkisp_3A_server
# pidof rkisp_3A_server
706
可以看到进程号 706 即是 rkisp_3A_server。
4.3.1 没有看到 rkisp_3A_server 进程
首先先确认/usr/bin/rkisp_3A_server可执行文件是否存在,如不存在,请检查camera_engine_rkaiq 版本及编译。查看 /var/log/syslog 中是否有 rkisp_3A 相关的错误,如有,看具体错误是什么,是否 Sensor 模组对应的iq 文件(xxx.json)没有找到,或不匹配。在 shell 中执行 rkisp_3A_server –mmedia=/dev/media0 (如有多个/dev/media设备,选择/dev/video对应的那一个),从另一个shell中抓图。获取rkisp_3A_server对应的错误信息
4.3.2 rkisp_3A_server 是如何启动的
Linux SDK中,rkisp_3A_server由脚本/etc/init.d/S40rkisp_3A 启动并在后台执行。如果/etc/init.d/S40rkisp_3A文件未找到,检查camera_engine_rkisp的版本及buildroot package编译脚本。
4.3.3 如何确定 Sensor IQ 配置文件文件名及路径
Sensor IQ文件由三部分组成,Sensor Type, 比如sc200ai。Module Name, 在dts中定义,比如rv1106g2 rk evb板上,该名称为”CMK-OT2115-PC1”rockchip,camera-module-name = “CMK-OT2115-PC1”;Module Lens Name, 在dts中定义,比如以下的”30IRC-F16”:rockchip,camera-module-lens-name = “30IRC-F16”;那么上例中的iq文件名为:sc200ai_CMK-OT2115-PC1_30IRC-F16.json, 存放在/etc/iqfiles/目录下。注意大小写有区分。
4.4 怎样手动曝光
需要手动曝光的情况下,rkisp_3A_server 进程必须先退出。然后可参考 rkisp_demo.cpp 程序或librkisp_api.so 的源码。
4.5 如何打开 librkaiq 的 log
通过设置环境变量 persist_camera_engine_log,其对应的位表示如下:
bits: 23-20 19-16 15-12 11-8 7-4 3-0
module: [xcore] [ISP] [AF] [AWB] [AEC] [NO]
0: error
1: warning
2: info
3: verbose
4: debug
例如,打开 ISP 及 AWB 的 debug log:
# /etc/init.d/S40rkisp_3A stop
# export persist_camera_engine_log=0x040400
# /usr/bin/rkisp_3A_server &
4.6 如何修改 Sensor IQ 文件中的分辨率
这里假如您手上已经有一份对应 Sensor的 IQ 效果文件, 需要修改该文件中的分辨率得到另一份不同分辨率的 IQ文件,可按如下步骤:修改 sensor_calib 字段的宽、高为期望的宽高。搜索原 IQ 文件中原始分辨率的关键字,全部替换为期望的目标分辨率大小。LSC 模块数组的修改搜索 lsc_v2,修改数组 lsc_sect_size_x、lsc_sect_size_ylsc_sect_size_x 数组每个元素为 dst_width / 16,一共有16个元素;lsc_sect_size_y数组每个元素为dst_height / 16,一共有16个元素;注意:由于存在多个场景(白天、夜晚、hdr),iq文件中所有lsc_v2中的lsc_sect_size_x、lsc_sect_size_y均要修改。
5. 应用开发相关
C 语言参考 demoRK 提供的 Linux SDK 中包含 rkisp_demo 工具及源码rkisp_demo 是一个简单的工具,可以用于获取图像。类似于 v4l2-ctl 工具,rkisp_demo 也不能显示图像,它主要是提供源码供参考。源码位于 /media/isp/camera_engine_rkaiq/rkisp_demo目录下。RK 提供的 IPC SDK 中包含 sample demo 源码,simple demo源码sample demo 是 RK 基于 rockit 多媒体库以及rkaiq库基础上开发的样例程序。根据不同模块或功能分别提供有相关应用程序。源码位于 /media/samples/example 目录下:
.
├── audio
│ ├── Makefile
│ ├── sample_ai_aenc.c
│ └── sample_ai.c
├── avs
│ ├── Makefile
│ ├── sample_avs.c
│ └── sample_multi_vi_avs.c
├── common
│ ├── fillimage.c
│ ├── isp2.x
│ ├── isp3.x
│ ├── lib
│ ├── loadbmp.c
│ ├── loadbmp.h
│ ├── Makefile
│ ├── sample_comm_aenc.c
│ ├── sample_comm_ai.c
│ ├── sample_comm_ao.c
│ ├── sample_comm_avs.c
│ ├── sample_comm.c
│ ├── sample_comm.h
│ ├── sample_comm_iva.c
│ ├── sample_comm_ivs.c
│ ├── sample_comm.o
│ ├── sample_comm_rgn.c
│ ├── sample_comm_tde.c
│ ├── sample_comm_venc.c
│ ├── sample_comm_vi.c
│ ├── sample_comm_vo.c
│ ├── sample_comm_vpss.c
├── demo
│ ├── Makefile
│ ├── sample_demo_aiisp.c
│ ├── sample_demo_dual_aiisp.c
│ ├── sample_demo_dual_camera.c
│ ├── sample_demo_dual_camera_wrap.c
│ ├── sample_demo_multi_camera_eptz.c
│ ├── sample_demo_vi_avs_venc.c
│ ├── sample_demo_vi_venc.c
│ └── sample_rv1103_dual_memory_opt.c
├── Makefile
├── out
│ ├── bin
│ └── install_to_userdata
├── test
│ ├── Makefile
│ ├── sample_ai_aenc_adec_ao_stresstest.c
│ ├── sample_avs_stresstest.c
│ ├── sample_demo_aiisp_stresstest.c
│ ├── sample_demo_dual_aiisp_stresstest.c
│ ├── sample_demo_vi_avs_venc_stresstest.c
│ ├── sample_demo_vi_venc_stresstest.c
│ ├── sample_isp_stresstest.c
│ ├── sample_mulit_isp_stresstest.c
│ ├── sample_rgn_stresstest.c
│ ├── sample_venc_stresstest.c
│ ├── sample_vpss_stresstest.c
│ └── source
├── venc
│ ├── Makefile
│ ├── sample_multi_vi_avs_osd_venc.c
│ └── sample_vi_vpss_osd_venc.c
├── vi
│ ├── Makefile
│ ├── sample_multi_vi.c
│ ├── sample_vi.c
│ └── sample_vi_eis.c
└── vo
├── Makefile
└── sample_vi_vo.c
其中 demo 目录提供应用程序样例,test 目录提供压力测试应用程序样例。其他目录根据功能模块分为 common、VI、VO、VENC、AUDIO、AVS等。
6. 快速启动相关
6.1 dts 修改
- 根据具体设备板子硬件原路图,正确配置 Sensor 相关的链接关系、上下电引脚、 电源域配置。
- 正确配置 rkisp_thunderboot 快启内存分配,根据 Sensor 实际分辨率与 VICAP 离线帧 Buf 个数配置 ramdisk_r 、ramdisk_c 大小与偏移。RV1106 / RV1103:
&rkisp_thunderboot {
/* reg's offset MUST match with RTOS */
/*
* vicap, capture raw10, ceil(w*10/8/256)*256*h *4(buf num)
* e.g. 2304x1296: 0xf30000
*/
// 2560 x 1440: 0x1248000
reg = <0x00860000 0x1248000>;
&ramdisk_r {
reg = <0x1aa8000 (10 * 0x00100000)>;
};
&ramdisk_c {
reg = <0x24a8000 (5 * 0x00100000)>;
};
RV1103B / RV1106B:
&rkisp_thunderboot {
/* reg's offset MUST match with RTOS */
/*
* vicap, capture raw10, ceil(w*10/8/256)*256*h *3(buf num) +
sizeof(rkisp_thunderboot_resmem_head)
* In RV1103B the size of rkisp_thunderboot_resmem_head is 0x9000, and it
is 0x6000 in RV1103/RV1106
* e.g. 2688x1520: 0xf96000(raw picture size) + 0x9000 = 0xf9f000
*/
reg = <0x00860000 0xf9f000>;
};
&ramdisk_r {
reg = <0x17ff000 (10 * 0x00100000)>;
};
&ramdisk_c {
reg = <0x21ff000 (4 * 0x00100000)>;
};
6.2 kernel Sensor 驱动
- 按照电池 IPC ⻔铃的《Rockchip_RV1106_RV1103_Quick_Start_Linux_Battery_IPC_Doorbell_CN.md》指导文档, 修改当前 Sensor 驱动,添加支持快启方案的代码。注意:快启中起流接口中不配置 Sensor 寄存器序列,仅写入起流寄存器。
static int __sc401ai_start_stream(struct sc401ai *sc401ai)
{
int ret;
if (!sc401ai->is_thunderboot) {
ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list);
if (ret)
return ret;
/* In case these controls are set before streaming */
ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler);
if (ret)
return ret;
}
// 仅写⼊起流寄存器。
return sc401ai_write_reg(sc401ai->client,
SC401AI_REG_CTRL_MODE,
SC401AI_REG_VALUE_08BIT,
SC401AI_MODE_STREAMING);
}
- Sensor 驱动中需要正常配置曝光、增益、VBLANK 接口,否则会出现画面异常的情况。
static int sc401ai_set_ctrl(struct v4l2_ctrl *ctrl)
{
......
switch (ctrl->id) {
case V4L2_CID_EXPOSURE: // 曝光
if (sc401ai->cur_mode->hdr_mode == NO_HDR) {
val = ctrl->val << 1;
/* 4 least significant bits of expsoure are fractional part */
ret = sc401ai_write_reg(sc401ai->client,
SC401AI_REG_EXPOSURE_H,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_EXP_H(val));
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_REG_EXPOSURE_M,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_EXP_M(val));
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_REG_EXPOSURE_L,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_EXP_L(val));
}
break;
case V4L2_CID_ANALOGUE_GAIN: //模拟Gain
if (sc401ai->cur_mode->hdr_mode == NO_HDR)
ret = sc401ai_set_gain_reg(sc401ai, ctrl->val);
break;
case V4L2_CID_VBLANK: // VBLANK, 影响帧率
ret = sc401ai_write_reg(sc401ai->client,
SC401AI_REG_VTS_H,
SC401AI_REG_VALUE_08BIT,
(ctrl->val + sc401ai->cur_mode->height)
>> 8);
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_REG_VTS_L,
SC401AI_REG_VALUE_08BIT,
(ctrl->val + sc401ai->cur_mode->height)
& 0xff);
if (!ret)
sc401ai->cur_vts = ctrl->val + sc401ai->cur_mode->height;
sc401ai_modify_fps_info(sc401ai);
break;
case V4L2_CID_TEST_PATTERN: // pattern 测试模式
ret = sc401ai_enable_test_pattern(sc401ai, ctrl->val);
break;
case V4L2_CID_HFLIP: // ⽔平镜像
ret = sc401ai_read_reg(sc401ai->client, SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT, &val);
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_MIRROR(val, ctrl->val));
break;
case V4L2_CID_VFLIP: // 垂直翻转
ret = sc401ai_read_reg(sc401ai->client, SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT, &val);
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_FLIP(val, ctrl->val));
break;
......
}
pm_runtime_put(&client->dev);
return ret;
}
6.3 mcu rtt Sensor 驱动相关
rtt Sensor 驱动开发过程中需要注意以下几点:
- rtt Sensor 驱动中小分辨率、大分辨率的寄存器配置不能配置 mipi enable 或控制 Sensor 起流的寄存器。若有配置会导致 rtt 阶段 AE 收敛异常。切到 kernel 按大图分辨率取流时无法取流。
static const uint8_t g_sc401ai_2560x1440_30fps_reg_table[] = {
......
0x3, 0x36, 0xf9, 0x14,
// 0x3, 0x01, 0x00, 0x01, // 0x0100 寄存器起流控制寄存器不能配置
0x3a,
}
- rtt Sensor 驱动中的曝光、gain值 计算按照 kernel Sensor 驱动中的方式。
6.4 Sensor iq 文件
使用正确的 iq 文件,iq 文件 json 格式转 bin 格式的方式如下:
./media/isp/release_camera_engine_rkaiq_rv1106_arm-rockchip830-linux-
uclibcgnueabihf/host/j2s4b json⽂件 bin⽂件
修改 iq 文件后快速编译方式:iq bin文件可直接放在 output/out/media_out/isp_iqfiles 下,
cd output/out/media_out/isp_iqfiles
j2s4b 工具转换:
../host/j2s4b mis2032_CMK-OT2115-PC1_30IRC-F16.json mis2032_CMK-OT2115-
PC1_30IRC-F16.bin
编译 meta 分区、烧录 meta 分区固件:
cd -
./build.sh meta
或将 iq bin文件推到板端,可以参考 sdk 的文档,更新meta分区参数 sensor_iq_bin 参数重启生效。快启取流前面帧图像绿色问题考虑 awb 或曝光异常、可开启 json 文件中的 earlierAwbAct 功能;
"earlierAwbAct ":{
"enable": 1,
......
同时关闭CAC 模块
"cac_v11": {
"SettingPara": {
"enable": 0,
}
6.5 问题处理
6.5.1 编译提示 “Not found main camera sensor config,…”
编译固件时,遇到找不到主摄配置的问题,如下:
[build_meta.sh:error] Not found main camera sensor config, please add
[support_sensors] in build_meta.sh
解决方法:首先检查 iq 文件是否存在,build_meta.sh 脚本中确定已添加支持的 Sensor。板级配置中已修改 eport RK_CAMERA_SENSOR_IQFILES为当前 Sensor 的 iq 文件。将对应 Sensor 的 iq bin 文件直接放在 output/out/media_out/isp_iqfiles下,然后编译 kernel、编译固件。重新烧录boot 分区。
6.5.2 光敏配置问题
rtt 运行过程中遇到如下错误:
Meta: ok
## Verified-boot: 0
iq_bin_mode=0
scene(day) frmRate: mode=0, val=25, keep fps
scene(night) frmRate: mode=1, val=25, update fps=15
load iq bin file finished!
gc5603-sensor_0 cur resulotion, width 1480, height 832, fps 60!
gc5603-sensor_0 find dst resulotion, width 2880, height 1616, fps 15!
[FASTAE]: fps/hts/vts: RTT=[60, 3223, 875], MainAIQ=[15, 3200, 3500]
[AELIB]: fastae: 3.0.5
[FASTAE]: ADC run failed! can't find rk_adc0 device!
Illegal instruction
一般是meta中光敏配置错误。光敏类型有:软光敏、模拟光敏、数字光敏。
typedef enum {
ALS_TYPE_NONE = 0, // 软光敏
ALS_TYPE_ANALOG = 1, // 模拟光敏
ALS_TYPE_DIGITAL = 2, // 数字光敏
} eALS_TYPE;
procject/make_meta/sensor_init/xxx_init.c 中:根据实际项目中所选择的光敏类型配置正常的类型。
.als_type = ALS_TYPE_ANALOG,
.als_value = FIX2INT16(10.0),
注意:设置模拟光敏时,即 ALS_TYPE_ANALOG,相关 sensor 的 board config 中需要打开配置:SDK/sysdrv/source/mcu/rt-thread/bsp/rockchip/xxx_mcu/board/:CONFIG_RT_USING_SARADC=y设置为软光敏时,即 ALS_TYPE_NONE,相关 sensor 的 board config 中则不需要打开配置:#CONFIG_RT_USING_SARADC is not set
6.5.3 kernel 崩溃
当rtt 启动后,切换到 kernel 启动时崩溃,报 rk_csirx_irq1_handler 异常,原因是 host 驱动注册好了,rtt 运行阶段产生mipi 报错,这个时候会进 err1 的中断,但此时差不多 rtt 结束,所以 rtt 把 host时钟关掉,导致中断中访问寄存器报错。
[ 0.274655] [<b037486c>] (rk_csirx_irq1_handler) from [<b022e2a7>]
(__handle_irq_event_percpu+0x25/0x7e)
[ 0.275490] [<b022e2a7>] (__handle_irq_event_percpu) from [<b022e30f>]
(handle_irq_event_percpu+0xf/0x30)
[ 0.276334] [<b022e30f>] (handle_irq_event_percpu) from [<b022e34b>]
(handle_irq_event+0x1b/0x28)
[ 0.277121] [<b022e34b>] (handle_irq_event) from [<b02300d9>]
(handle_fasteoi_irq+0x57/0x90)
[ 0.277864] [<b02300d9>] (handle_fasteoi_irq) from [<b022df6f>]
(__handle_domain_irq+0x4b/0x64)
[ 0.278630] [<b022df6f>] (__handle_domain_irq) from [<b02ee983>]
(gic_handle_irq+0x41/0x4e)
[ 0.279375] [<b02ee983>] (gic_handle_irq) from [<b0208d13>]
(__irq_svc+0x53/0x7c)
解决方案:更新 rkcif 驱动,添加中断异常处理。
6.5.4 如何确认不带 Sensor 镜头的出流效果
如果不带镜头的 Sensor 效果确认时,可以打开 pattern 测试模式,出彩条或灰阶图像,查看出大图效果。pattern 模式的代码添加在起流接口中,设置起流寄存器之前。以 SC200AI 为例:
#define SC200AI_REG_TEST_PATTERN 0x4501
#define SC200AI_TEST_PATTERN_BIT_MASK BIT(3)
...
static int sc200ai_enable_test_pattern(struct sc200ai *sc200ai, u32 pattern)
{
u32 val = 0;
int ret = 0;
ret = sc200ai_read_reg(sc200ai->client, SC200AI_REG_TEST_PATTERN,
SC200AI_REG_VALUE_08BIT, &val);
if (pattern)
val |= SC200AI_TEST_PATTERN_BIT_MASK;
else
val &= ~SC200AI_TEST_PATTERN_BIT_MASK;
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_TEST_PATTERN,
SC200AI_REG_VALUE_08BIT, val);
return ret;
}
static int sc200ai_set_ctrl(struct v4l2_ctrl *ctrl)
{
...
case V4L2_CID_TEST_PATTERN:
ret = sc200ai_enable_test_pattern(sc200ai, ctrl->val);
break;
...
}
6.5.5 kernel 启动后一直打印 MIPI SIZE ERROR 错误
需要确认是否是 rtt 阶段寄存器配置的实际出图宽、高大小有问题。仔细检查 Sensor 寄存器配置与手册说明。
6.5.6 Failed to stop decompress: decompress@ff520000
若遇到 “Failed to stop decompress: decompress@ff520000, ret=-119”, kernel 解压缩失败情况时,可尝试修改 sysdrv/source/uboot/u-boot/arch/arm/dts/rv1106-evb2.dts 中 spi_nor 节点的最大频率,降低到 100Mhz 或更低。
&spi nor {
spi-max-frequency = <125000000> 为 spi-max-frequency = <100000000>
6.5.7 Failed to stop decompress: decompress@ff520000, ret=-110
若遇到 “Failed to stop decompress: decompress@ff520000, ret=-110” , kernel 解压缩失败情况时,尝试修改 ramdisk 与 CMA 大小,二者不要设置太大。
6.5.8 如何设置快启应用不自启动
若 kernel 开机脚本中配置了开机自动快启应用,有时会引起开发调试不便,可使用下面 meta 命令设置快启应用不自启动。
make_meta --update --meta_path /dev/block/by-name/meta --cmdline NoAuto=1
使能 AE log 打印,同时设置不自启动快启应用。
make_meta --update --meta_path /dev/block/by-name/meta --cmdline "persist_camera_engine_log=0x1ff4 NoAuto=1"
注意:目前仅 RV1106/RV1103/RV1103B/RV1106B SDK 支持该命令。
6.5.9 如何设置快启阶段 kernel 的 log 打印
方法一:修改 相应 dts 中 的 bootargs 启动参数中的 loglevel=8,重新编译kernel,烧录boot分区固件。方法二:开机后,串口输入命令
make_meta --update --meta_path /dev/block/by-name/meta --cmdline "loglevel=8"
6.5.10 如何确认 rtt 切到大图配置以后,是否提前出流
rtt 切到大图序列的时候,要确认 sensor 是否有提前出流,可以在 spl while 1 住,不进内核去然后使用示波器测量 Sensor相关 MIPI data 波形。cd sysdrv/source/uboot/u-boot:
git diff common/spl/spl.c
diff --git a/common/spl/spl.c b/common/spl/spl.c
index 6836796b4e..070388c066 100644
--- a/common/spl/spl.c
+++ b/common/spl/spl.c
@@ -739,4 +739,5 @@ void spl_cleanup_before_jump(struct spl_image_info
*spl_image)
us = (get_ticks() - gd->sys_start_tick) / 24UL;
printf("Total: %ld.%ld ms\n\n", us / 1000, us % 1000);
+ while(1);
}
6.5.11 如何快速定位快启阶段离线帧的问题
vicap预留了一个debug开关,启动后,可以打印前15帧的FS/FE中断,以及 buffer 的轮转等 debug 信息。当出现拼帧/错帧等异常时,通过相关 log 来分析,可以较快定位出问题。开启方式:
diff --git a/drivers/media/platform/rockchip/cif/dev.c
b/drivers/media/platform/rockchip/cif/dev.c
index c3b59414eede3..bb616b1897b58 100644
--- a/drivers/media/platform/rockchip/cif/dev.c
+++ b/drivers/media/platform/rockchip/cif/dev.c
@@ -1957,7 +1957,7 @@ int rkcif_plat_init(struct rkcif_device *cif_dev, struct
device_node *node, int
cif_dev->sensor_linetime = 0;
cif_dev->early_line = 0;
cif_dev->is_thunderboot = false;
- cif_dev->rdbk_debug = 0;
+ cif_dev->rdbk_debug = 3;
cif_dev->resume_mode = 0;
memset(&cif_dev->channels[0].capture_info, 0, sizeof(cif_dev->channels[0].capture_info));
6.5.12 如何抓取 rtt 阶段的小图,查看效果
dts 中将所有rkisp 、rkcif、 mipi 注释掉: disabledcsi2_dphy_hw:disabledcsi2_dphy0:disabledmipi0_csi2:disabledrkcif:disabledrkcif_mipi_lvds:disabledrkcif_mipi_lvds_sditf:disabledrkisp:disabledrkisp_vir0:disabledio -rf /tmp/1.yuv -l 393216 0x866000 保存小图数据,只能保存最后5帧数据,可强制修改fastae_max_run_frame=5
[STREAM]: L 0, 1, 0, 0x866000, 783360, 0x45d, 0x8000, tick:59
[STREAM]: L 1, 2, 0, 0x926000, 783360, 0x45d, 0x8000, tick:76
[STREAM]: L 2, 3, 0, 0x9e6000, 783360, 0x45d, 0x8000, tick:87
[STREAM]: L 3, 4, 0, 0xaa6000, 783360, 0x45d, 0x1828, tick:98
[STREAM]: L 4, 5, 0, 0xb66000, 783360, 0x45d, 0x48d, tick:109
0x866000、0x926000 …等是rtt阶段保存的最后几帧数据。
6.5.13 首帧图像颜色不正常
首帧图像出现偏黑、偏绿时,一般是iq文件不正确,导致awb白平衡不正常导致异常。如下 rtt log 中 luma 亮度为很小的值,导致首帧偏黑:
[FASTTAE]: >>> als type: 1, als value: 0xa0000, night mode: 0
start_stream 278 tick 23
[isp_0]:rk_isr_hw_set_vicap_clk enable is 1
[FASTAE]: set_firstae 1733 tick 23
[STREAM]: L 0, 1, 0, 0x866000, 178r400, 0x2, 0x40, tick:43
[AELIB]: 1,luma=1.0000,setpoint=60.0000,curexp=0.0000,newexp=0.0000,T?
8.0000,G=1.0000,ispG=1.0000,regT=2,regG=64
[STREAM]: L 1, 2, 0, 0x9b80000 1382400, 0x2, 0x40, tick:62
[AELIB]:2,luma=1.0000,setpoint=60.0000,curexp=0.0000,newexp=0.0000,T=0.0000,G=1.0
000,ispG=1.0000 regT=2,regG=64
[STREAM]: L 2, 3, 0, 0xb0a000, 138r400, 0x2, 0x40, tick:80
[AELIB]: 3,luma=1.0000,setpoint=60.0000,curexp=0.0000 ==========fastae is
match===========
解决方案:检查 iq文件中 earlierAwbAct 参数是否使能,参数是否配置正确。如下
"earlierAwbAct": {
"enable": 1,
"mode": "CALIB_AWB_EARLACT_XYREG_FIXED",
"xyRegion": [{
"normal": [-161, 286, 81, -95],
"big": [-161, 286, 112, -125]
}, {
"normal": [-803, -161, 135, -199],
"big": [-803, -161, 185, -224]
}, {
"normal": [-1467, -796, 121, -78],
"big": [-1559, -796, 136, -93]
}, {
"normal": [-2062, -1535, 65, -130],
"big": [-2017, -1535, 130, -130]
}]
}
说明:针对快启应用,需要在json上打开 earlierAwbAct 功能,尽早进行awb的调整。 awb 正常是 2 帧才会算出一个结果,不开快速 awb,那就是用的初始值 awb,wb gain 都是1,就是绿的。针对快启,我们是要求 tuning 的时候,earlierawb 开启,选择 AUTO 模式,然后填上对应的参数。
7. AOV 相关
7.1 如何支持 Sensor 硬件 standby 模式
standby 模式是指 Sensor 为了节省功耗处在睡眠模式或者复位模式。睡眠模式Sensor 停止图像数据流,工作在低功耗状态,保持当前寄存器值。以 SC200AI 为例,有两种方式进入睡眠模式:
- 将 PWDN 拉低,此时不支持 I2C 的读写。
- 将出流控制寄存器写入0,此时支持 I2C 读写。复位模式Sensor 停止图像数据流,工作在低功耗状态,重置所有寄存器。以 SC200AI 为例,有两种方式进入复位模式:
- 将 XSHUTDN 拉低,此时不支持 I2C 读写。
- 将软使能复位寄存器写入1,此复位模式持续 150ns。硬件 standby 模式:硬件 standby 模式指通过拉低 PWDN 管脚,使 Sensor 进入睡眠模式,从而使其工作在低功耗状态,此时 I2C 不可读写。以 SC200AI 为例,dts 中 Sensor 节点中添加支持硬件 standby 模式属性 rockchip,camera-module-stb = <1> ,其属性值用来表示是否支持硬件 standby 方式,1 表示指支持,0 表示不支持。
sc200ai: sc200ai@30 {
compatible = "smartsens,sc200ai";
status = "okay";
reg = <0x30>;
...
rockchip,camera-module-stb = <1>;
port {
sc200ai_out: endpoint {
remote-endpoint = <&csi_dphy_input1>;
data-lanes = <1 2>;
};
};
};
Sensor 驱动 probe 接口中需添加解析该属性值,获取到硬件 standby 模式是否支持,并保存在sc200ai->standby_hw 成员变量中。若支持硬件standby 模式,ioctl 处理接口中, quitck_stream 命令会根据起流状态,进行上、下拉相关PWDN GPIO。休眠唤醒中会下发 v4l2 ioctl 命令来重新设置曝光、增益寄存器等。
case RKMODULE_SET_QUICK_STREAM:
stream = *((u32 *)arg);
if (sc200ai->standby_hw) { // hardware standby
if (stream) {
if (!IS_ERR(sc200ai->pwdn_gpio))
gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 1);
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT, SC200AI_MIPI_CTRL_ON);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_STREAMING);
dev_info(&sc200ai->client->dev, "quickstream, streaming on: exit standby mode\n");
sc200ai->is_standby = false;
} else {
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_SW_STANDBY);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT, SC200AI_MIPI_CTRL_OFF);
if (!IS_ERR(sc200ai->pwdn_gpio))
gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 0);
dev_info(&sc200ai->client->dev, "quickstream, streaming off: enter standby mode\n");
sc200ai->is_standby = true;
}
} else { // software standby
...
}
其他可参考 SC200AI 驱动。软件standby模式:软件 standby 模式是指,在休眠唤醒后,通过写入完整 Sensor 寄存器配置的方式,然后在Sensor ioctl 接口中,quick_steam 命令处理时,直接配置起流寄存器的休眠唤醒方式。
......
} else { // software standby
if (stream) {
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT, SC200AI_MIPI_CTRL_ON);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_STREAMING);
dev_info(&sc200ai->client->dev, "quickstream, streaming on: exit soft standby mode\n");
} else {
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT,
SC200AI_MODE_SW_STANDBY);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT,
SC200AI_MIPI_CTRL_OFF);
dev_info(&sc200ai->client->dev, "quickstream, streaming off: enter soft standby mode\n");
}
}
7.2 如何对补光灯进行控制
在 AOV 模式下,支持对补光灯进行控制。支持每个 Sensor 控制一个补光灯,支持 PWM 或 GPIO 类型的补光灯。控制的设置如下:
- 板级配置 DTS 中添加 light_ctl 节点,节点属性中根据硬件实际连接添加补光灯的 PWM 、 GPIO 对应的控制设备或管脚。如下示例:这里的补光灯有 pwm7 设备控制,index 为 0,gpio 未使用。
/ {
model = "Rockchip RV1106G EVB2 V10 Board";
compatible = "rockchip,rv1106g-evb2-v10", "rockchip,rv1106";
chosen {
bootargs = "loglevel=0 rootfstype=erofs rootflags=dax console=ttyFIQ0 root=/dev/rd0 snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 driver_async_probe=dwmmc_rockchip";
};
...
light_ctrl: light-ctl {
compatible = "rockchip,light-ctl";
pwms=<&pwm7 0 25000 0>;
light-gpios =<&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
rockchip,module-index = <0>;
status = "okay";
};
};
- kernel 配置中打开 CONFIG_LIGHT_CTL 配置。
- Sensor 驱动中添加相关控制接口。
#include <linux/clk.h>
@@ -40,8 +41,9 @@
#include "../platform/rockchip/isp/rkisp_tb_helper.h"
#include "cam-tb-setup.h"
#include "cam-sleep-wakeup.h"
+#include "light_ctl.h"
-#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x09)
+#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x10)
#ifndef V4L2_CID_DIGITAL_GAIN
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
@@ -192,6 +194,7 @@ struct sc200ai {
bool is_standby;
struct preisp_hdrae_exp_s init_hdrae_exp;
struct cam_sw_info *cam_sw_inf;
+ struct rk_light_param light_ctl_param;
};
#define to_sc200ai(sd) container_of(sd, struct sc200ai, subdev)
@@ -1191,6 +1194,8 @@ static long sc200ai_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
u32 i, h, w;
long ret = 0;
u32 stream = 0;
+ int rt = 0;
+ struct rk_light_param *light_param;
switch (cmd) {
case RKMODULE_GET_MODULE_INFO:
@@ -1238,6 +1243,22 @@ static long sc200ai_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
stream = *((u32 *)arg);
+ dev_err(&sc200ai->client->dev, "%s: quick_stream = %d\n",
+ __func__, stream);
+ // light control
+ if (stream) {
+ sc200ai->light_ctl_param.light_enable = true;
+ rt = light_ctl_write(sc200ai->module_index,
+ &sc200ai->light_ctl_param);
+ } else {
+ sc200ai->light_ctl_param.light_enable = false;
+ rt = light_ctl_write(sc200ai->module_index,
+ &sc200ai->light_ctl_param);
+ }
+
+ dev_err(&sc200ai->client->dev, "%s: light_ctl_write ret:%d\n",
+ __func__, rt);
+
if (sc200ai->standby_hw) { // hardware standby
if (stream) {
if (!IS_ERR(sc200ai->pwdn_gpio))
@@ -1284,6 +1305,18 @@ static long sc200ai_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
ch_info = (struct rkmodule_channel_info *)arg;
ret = sc200ai_get_channel_info(sc200ai, ch_info);
break;
+
+ case RKCIS_CMD_FLASH_LIGHT_CTRL:
+ light_param = (struct rk_light_param *)arg;
+ dev_err(&sc200ai->client->dev,
+ "%s: RKCIS_CMD_FLASH_LIGHT_CTRL type: %s enable: %s\n",
+ __func__,
+ light_param->light_type == LIGHT_PWM ? "pwm" : "gpio",
+ light_param->light_enable ? "enable" : "disable");
+
+ memcpy(&sc200ai->light_ctl_param, light_param, sizeof(*light_param));
+
+ break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1304,6 +1337,7 @@ static long sc200ai_compat_ioctl32(struct v4l2_subdev
*sd,
struct rkmodule_channel_info *ch_info;
long ret;
u32 stream = 0;
+ struct rk_light_param *light_param = NULL;
switch (cmd) {
case RKMODULE_GET_MODULE_INFO:
@@ -1400,6 +1434,19 @@ static long sc200ai_compat_ioctl32(struct v4l2_subdev
*sd,
}
kfree(ch_info);
break;
+ case RKCIS_CMD_FLASH_LIGHT_CTRL:
+ light_param = kzalloc(sizeof(*light_param), GFP_KERNEL);
+ if (!light_param) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ ret = copy_from_user(light_param, up, sizeof(*light_param));
+ if (!ret)
+ ret = sc200ai_ioctl(sd, cmd, light_param);
+ else
+ ret = -EFAULT;
+ kfree(light_param);
+ break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1451,6 +1498,10 @@ static int __sc200ai_stop_stream(struct sc200ai
*sc200ai)
sc200ai->is_first_streamoff = true;
pm_runtime_put(&sc200ai->client->dev);
}
+ sc200ai->light_ctl_param.duty_cycle = 0;
+ sc200ai->light_ctl_param.light_enable = false;
+ light_ctl_write(sc200ai->module_index,
+ &sc200ai->light_ctl_param);
return sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_SW_STANDBY);
}
@@ -1973,6 +2024,7 @@ static int sc200ai_initialize_controls(struct sc200ai
*sc200ai)
sc200ai->subdev.ctrl_handler = handler;
sc200ai->has_init_exp = false;
sc200ai->is_standby = false;
+ sc200ai->light_ctl_param.duty_cycle = 0;
return 0;
- 应用代码示例:
--- a/sample_aov_vi.c
+++ b/sample_aov_vi.c
@@ -572,6 +572,19 @@ int main(int argc, char *argv[]) {
if (!ctx->vi.bIfQuickStart) {
RK_MPI_VI_StartPipe(ctx->vi.u32PipeId);
}
+
+ // Enable VI light
+ VI_LIGHT_CTL_PARAM_S tLightCtlParam;
+ printf("%s - VI_LIGHT_CTL_PARAM_S size:%d\n", __func__,
sizeof(VI_LIGHT_CTL_PARAM_S));
+ memset(&tLightCtlParam, 0, sizeof(tLightCtlParam));
+ tLightCtlParam.light_enable = RK_TRUE;
+ tLightCtlParam.light_type = LIGHT_TYPE_PWM;
+ tLightCtlParam.duty_cycle = 25000;
+ tLightCtlParam.period = 25000;
+ tLightCtlParam.polarity = 0;
+
+ RK_MPI_VI_DevEnableLight(ctx->vi.s32DevId, ctx->vi.s32DevId,
&tLightCtlParam);
+
if (s32ViFrameMode == 0)
pthread_create(&vi_thread_id, 0, vi_get_stream, (void *)
(&ctx->vi));
7.3 AOV 开发参考文档
AOV 相关的详细开发文档,可参考 《Rockchip_RV1106_Developer_Guide_Linux_AOV_CN.md》。
原稿代码差异附录
正文按对应语言原稿,俄德按英文。以下另一语言原稿中不同的代码片段完整保留,含注释/排版差异。它们是独立示例,不可拼接执行;未统一覆盖源参数。
EN PDF p.7 · 29
# media-ctl -p -d /dev/media0 | tail -n 30
-> "rkcif_tools_id2":0 []
pad11: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 [ENABLED]
- entity 58: rockchip-csi2-dphy0 (2 pads, 2 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev1
pad0: Sink
[fmt:SBGGR10_1X10/2688x1520@10000/300000 field:none]
<- "m00_b_sc450ai 4-0030":0 [ENABLED]
pad1: Source
-> "rockchip-mipi-csi2":0 [ENABLED]
- entity 63: m00_b_sc450ai 4-0030 (1 pad, 1 link)
type V4L2 subdev subtype Sensor flags 0
device node name /dev/v4l-subdev2
pad0: Source
[fmt:SBGGR10_1X10/2688x1520@10000/300000 field:none]
-> "rockchip-csi2-dphy0":0 [ENABLED]
EN PDF p.8 · 33
+++ b/drivers/media/i2c/sc530ai.c
@@ -1593,8 +1593,8 @@ static int sc530ai_open(struct v4l2_subdev *sd, struct
v4l2_subdev_fh *fh)
}
#endif
-#define DST_WIDTH 2880
-#define DST_HEIGHT 1616
+#define DST_WIDTH 2560
+#define DST_HEIGHT 1440
EN PDF p.8 · 37
# i2ctransfer -f -y 4 w3@0x32 0x43 0x24 0x18
4: Represents the I2C bus ID (which can be 0, 1, 2, 3, ...)
w: Represents write
3: Represents writing 3 Bytes
0x32: Represents the I2C device address
The following three data represent the data to be written, where 0x4324 is
assumed to be a 16-bit register address, and 0x18 is the value to be written.
# i2ctransfer -f -y 4 w1@0x30 0x08 r3
4: Represents the I2C bus number (which can be 0, 1, 2, 3, ...)
w: Indicates write
1: Indicates writing 1 Byte
0x30: Represents the I2C device address
r: Represents read
3: Represents reading 3 Bytes
This command means to read 3 Bytes from the address offset by 0x08 from 0x30,
where the number following r indicates the number of bytes to be read.
EN PDF p.10 · 60
io -4 -l 0x100 0xfdd30000
EN PDF p.15 · 89
echo performance > /sys/class/devfreq/dmc/governor
EN PDF p.16 · 91
static const struct ov7251_mode supported_modes[] = {
{
.width = 640,
.height = 480,
EN PDF p.17 · 98
Common commands include:
# Capture raw image
rk_mpi_vi_test -w 1920 -h 1080 -d 0 -c 0 -m 0 -l 10 -n /dev/video0 -f 131076
# Capture yuv image from the mainpath channel and save it to a file. The file
directory is /data/test_0_0_0.bin, which can be opened with tools like 7yuv.
The file name is based on the dev id, pipe id, and chn id.
rk_mpi_vi_test -w 1920 -h 1080 -d 0 -c 0 -m 0 -l 10 -o 1
# Capture yuv image from the mainpath channel, encode it, and write it to a
file. The file is saved in /data/venc_0.bin, and the file name is related to
the chn id.
rk_mpi_vi_test -w 1920 -h 1080 -d 0 -c 0 -m 1 -l 10 -o 1
...
EN PDF p.17 · 100
# Use the simple_vi_bind_venc_rtsp demo for real-time preview via rtsp
(configure the board-side IP with tools like vlc or potplayer).
simple_vi_bind_venc_rtsp -I 0 -w 1920 -h 1080 (rtsp://ip/live/0)
EN PDF p.19 · 123
# media-ctl -p -d /dev/media0
...
- entity 2: rkisp1_mainpath (1 pad, 1 link) // Indicates that this
entity is MP (MainPath)
type Node subtype V4L flags 0
device node name /dev/video1 // The corresponding
device node is /dev/video1
pad0: Sink
<- "rkisp1-isp-subdev":2 [ENABLED]
- entity 3: rkisp1_selfpath (1 pad, 1 link) // Indicates that this
entity is SP (SelfPath)
type Node subtype V4L flags 0
device node name /dev/video2 // The corresponding
device node is /dev/video2
pad0: Sink
<- "rkisp1-isp-subdev":2 [ENABLED]
...
EN PDF p.20 · 125
# grep '' /sys/class/video4linux/video*/name
/sys/class/video4linux/video0/name:stream_cif
/sys/class/video4linux/video1/name:rkisp1_mainpath # MP node corresponds to
/dev/video1
/sys/class/video4linux/video2/name:rkisp1_selfpath # SP node corresponds to
/dev/video2
/sys/class/video4linux/video3/name:rkisp1_rawpath
/sys/class/video4linux/video4/name:rkisp1_dmapath
/sys/class/video4linux/video5/name:rkisp1-statistics
/sys/class/video4linux/video6/name:rkisp1-input-params
EN PDF p.23 · 142
# grep RK_AIQ_VERSION_REAL media/isp/camera_engine_rkaiq/rkaiq/RkAiqVersion.h
#define RK_AIQ_VERSION_REAL_V "v5.0x5.0"
EN PDF p.23 · 148
# dmesg | grep "version"
dmesg | grep "version"
[ 0.848252] udevd[65]: starting version 3.2.7
[ 3.889404] imx415 4-001a: driver version: 00.01.08
[ 3.967388] os04a10 4-0036: driver version: 00.01.05
[ 4.084418] sc4336 4-0030-3: driver version: 00.01.01
[ 4.114867] sc3336 4-0030-1: driver version: 00.01.01
[ 4.152066] sc530ai 4-0030: driver version: 00.01.01
[ 4.180572] sc200ai 4-0030-6: driver version: 00.01.09
[ 4.237776] rkcif rkcif-mipi-lvds: rkcif driver version: v00.02.00
[ 4.260419] rkisp rkisp-vir0: rkisp driver version: v02.05.00
EN PDF p.26 · 175
.
├── audio
│ ├── Makefile
│ ├── sample_ai_aenc.c
│ └── sample_ai.c
├── avs
│ ├── Makefile
│ ├── sample_avs.c
│ └── sample_multi_vi_avs.c
├── common
│ ├── fillimage.c
│ ├── isp2.x
EN PDF p.27 · 176
│ ├── isp3.x
│ ├── lib
│ ├── loadbmp.c
│ ├── loadbmp.h
│ ├── Makefile
│ ├── sample_comm_aenc.c
│ ├── sample_comm_ai.c
│ ├── sample_comm_ao.c
│ ├── sample_comm_avs.c
│ ├── sample_comm.c
│ ├── sample_comm.h
│ ├── sample_comm_iva.c
│ ├── sample_comm_ivs.c
│ ├── sample_comm.o
│ ├── sample_comm_rgn.c
│ ├── sample_comm_tde.c
│ ├── sample_comm_venc.c
│ ├── sample_comm_vi.c
│ ├── sample_comm_vo.c
│ ├── sample_comm_vpss.c
├── demo
│ ├── Makefile
│ ├── sample_demo_aiisp.c
│ ├── sample_demo_dual_aiisp.c
│ ├── sample_demo_dual_camera.c
│ ├── sample_demo_dual_camera_wrap.c
│ ├── sample_demo_multi_camera_eptz.c
│ ├── sample_demo_vi_avs_venc.c
│ ├── sample_demo_vi_venc.c
│ └── sample_rv1103_dual_memory_opt.c
├── Makefile
├── out
│ ├── bin
│ └── install_to_userdata
├── test
│ ├── Makefile
│ ├── sample_ai_aenc_adec_ao_stresstest.c
│ ├── sample_avs_stresstest.c
│ ├── sample_demo_aiisp_stresstest.c
│ ├── sample_demo_dual_aiisp_stresstest.c
│ ├── sample_demo_vi_avs_venc_stresstest.c
│ ├── sample_demo_vi_venc_stresstest.c
│ ├── sample_isp_stresstest.c
│ ├── sample_mulit_isp_stresstest.c
│ ├── sample_rgn_stresstest.c
│ ├── sample_venc_stresstest.c
│ ├── sample_vpss_stresstest.c
│ └── source
├── venc
│ ├── Makefile
│ ├── sample_multi_vi_avs_osd_venc.c
│ └── sample_vi_vpss_osd_venc.c
├── vi
│ ├── Makefile
│ ├── sample_multi_vi.c
│ ├── sample_vi.c
EN PDF p.28 · 177
│ └── sample_vi_eis.c
└── vo
├── Makefile
└── sample_vi_vo.c
EN PDF p.28 · 185
&rkisp_thunderboot {
/* reg's offset MUST match with RTOS */
/*
* vicap, capture raw10, ceil(w*10/8/256)*256*h *3(buf num) +
sizeof(rkisp_thunderboot_resmem_head)
* In RV1103B the size of rkisp_thunderboot_resmem_head is 0x9000, and it
is 0x6000 in RV1103/RV1106
* e.g. 2688x1520: 0xf96000(raw picture size) + 0x9000 = 0xf9f000
*/
EN PDF p.29 · 186
reg = <0x00860000 0xf9f000>;
};
&ramdisk_r {
reg = <0x17ff000 (10 * 0x00100000)>;
};
&ramdisk_c {
reg = <0x21ff000 (4 * 0x00100000)>;
};
EN PDF p.29 · 189
static int __sc401ai_start_stream(struct sc401ai *sc401ai)
{
int ret;
if (!sc401ai->is_thunderboot) {
ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list);
if (ret)
return ret;
/* In case these controls are set before streaming */
ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler);
if (ret)
return ret;
}
// Only write the start stream register.
return sc401ai_write_reg(sc401ai->client,
SC401AI_REG_CTRL_MODE,
SC401AI_REG_VALUE_08BIT,
SC401AI_MODE_STREAMING);
}
EN PDF p.29 · 191
static int sc401ai_set_ctrl(struct v4l2_ctrl *ctrl)
{
......
switch (ctrl->id) {
case V4L2_CID_EXPOSURE: // Exposure
if (sc401ai->cur_mode->hdr_mode == NO_HDR) {
EN PDF p.30 · 192
val = ctrl->val << 1;
/* 4 least significant bits of exposure are fractional part */
ret = sc401ai_write_reg(sc401ai->client,
SC401AI_REG_EXPOSURE_H,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_EXP_H(val));
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_REG_EXPOSURE_M,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_EXP_M(val));
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_REG_EXPOSURE_L,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_EXP_L(val));
}
break;
case V4L2_CID_ANALOGUE_GAIN: // Analog Gain
if (sc401ai->cur_mode->hdr_mode == NO_HDR)
ret = sc401ai_set_gain_reg(sc401ai, ctrl->val);
break;
case V4L2_CID_VBLANK: // VBLANK, affects frame rate
ret = sc401ai_write_reg(sc401ai->client,
SC401AI_REG_VTS_H,
SC401AI_REG_VALUE_08BIT,
(ctrl->val + sc401ai->cur_mode->height)
>> 8);
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_REG_VTS_L,
SC401AI_REG_VALUE_08BIT,
(ctrl->val + sc401ai->cur_mode->height)
& 0xff);
if (!ret)
sc401ai->cur_vts = ctrl->val + sc401ai->cur_mode->height;
sc401ai_modify_fps_info(sc401ai);
break;
case V4L2_CID_TEST_PATTERN: // pattern test mode
ret = sc401ai_enable_test_pattern(sc401ai, ctrl->val);
break;
case V4L2_CID_HFLIP: // Horizontal mirror
ret = sc401ai_read_reg(sc401ai->client, SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT, &val);
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_MIRROR(val, ctrl->val));
break;
case V4L2_CID_VFLIP: // Vertical flip
ret = sc401ai_read_reg(sc401ai->client, SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT, &val);
ret |= sc401ai_write_reg(sc401ai->client,
SC401AI_FLIP_MIRROR_REG,
SC401AI_REG_VALUE_08BIT,
SC401AI_FETCH_FLIP(val, ctrl->val));
break;
......
EN PDF p.31 · 193
}
pm_runtime_put(&client->dev);
return ret;
}
EN PDF p.31 · 197
static const uint8_t g_sc401ai_2560x1440_30fps_reg_table[] = {
......
0x3, 0x36, 0xf9, 0x14,
// 0x3, 0x01, 0x00, 0x01, // The 0x0100 register, which used to start
stream of the sensor, should not be configured
0x3a,
}
EN PDF p.31 · 201
./media/isp/release_camera_engine_rkaiq_rv1106_arm-rockchip830-linux-
uclibcgnueabihf/host/j2s4b input_json_file output_bin_file
EN PDF p.32 · 209
"earlierAwbAct": {
"enable": 1,
...
}
EN PDF p.32 · 211
"cac_v11": {
"SettingPara": {
"enable": 0,
}
}
EN PDF p.33 · 219
Meta: ok
## Verified-boot: 0
iq_bin_mode=0
scene(day) frmRate: mode=0, val=25, keep fps
scene(night) frmRate: mode=1, val=25, update fps=15
load iq bin file finished!
gc5603-sensor_0 cur resolution, width 1480, height 832, fps 60!
gc5603-sensor_0 find dst resolution, width 2880, height 1616, fps 15!
[FASTAE]: fps/hts/vts: RTT=[60, 3223, 875], MainAIQ=[15, 3200, 3500]
[AELIB]: fastae: 3.0.5
[FASTAE]: ADC run failed! can't find rk_adc0 device!
Illegal instruction
EN PDF p.33 · 221
typedef enum {
ALS_TYPE_NONE = 0, // Software Light Sensitivity
ALS_TYPE_ANALOG = 1, // Analog Light Sensitivity
ALS_TYPE_DIGITAL = 2, // Digital Light Sensitivity
} eALS_TYPE;
EN PDF p.34 · 231
#define SC200AI_REG_TEST_PATTERN 0x4501
#define SC200AI_TEST_PATTERN_BIT_MASK BIT(3)
...
static int sc200ai_enable_test_pattern(struct sc200ai *sc200ai, u32 pattern)
{
u32 val = 0;
int ret = 0;
ret = sc200ai_read_reg(sc200ai->client, SC200AI_REG_TEST_PATTERN,
SC200AI_REG_VALUE_08BIT, &val);
if (pattern)
val |= SC200AI_TEST_PATTERN_BIT_MASK;
else
val &= ~SC200AI_TEST_PATTERN_BIT_MASK;
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_TEST_PATTERN,
SC200AI_REG_VALUE_08BIT, val);
return ret;
}
static int sc200ai_set_ctrl(struct v4l2_ctrl *ctrl)
{
...
case V4L2_CID_TEST_PATTERN:
ret = sc200ai_enable_test_pattern(sc200ai, ctrl->val);
EN PDF p.35 · 232
break;
...
}
EN PDF p.35 · 237
&spi nor {
spi-max-frequency = <125000000> to spi-max-frequency = <100000000>
EN PDF p.40 · 278
case RKMODULE_SET_QUICK_STREAM:
stream = *((u32 *)arg);
if (sc200ai->standby_hw) { // hardware standby
if (stream) {
if (!IS_ERR(sc200ai->pwdn_gpio))
gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 1);
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT, SC200AI_MIPI_CTRL_ON);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_STREAMING);
dev_info(&sc200ai->client->dev, "quickstream, streaming on: exit standby mode\n");
sc200ai->is_standby = false;
} else {
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_SW_STANDBY);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT, SC200AI_MIPI_CTRL_OFF);
if (!IS_ERR(sc200ai->pwdn_gpio))
gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 0);
dev_info(&sc200ai->client->dev, "quickstream, streaming off: enter standby mode\n");
sc200ai->is_standby = true;
}
} else { // software standby
...
}
EN PDF p.40 · 280
......
} else { // software standby
if (stream) {
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT, SC200AI_MIPI_CTRL_ON);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_STREAMING);
EN PDF p.41 · 281
dev_info(&sc200ai->client->dev, "quickstream, streaming on: exit soft standby mode\n");
} else {
ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT,
SC200AI_MODE_SW_STANDBY);
ret |= sc200ai_write_reg(sc200ai->client, SC200AI_REG_MIPI_CTRL,
SC200AI_REG_VALUE_08BIT,
SC200AI_MIPI_CTRL_OFF);
dev_info(&sc200ai->client->dev, "quickstream, streaming off: enter soft standby mode\n");
}
}
EN PDF p.41 · 288
#include <linux/clk.h>
@@ -40,8 +41,9 @@
#include "../platform/rockchip/isp/rkisp_tb_helper.h"
EN PDF p.42 · 289
#include "cam-tb-setup.h"
#include "cam-sleep-wakeup.h"
+#include "light_ctl.h"
-#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x09)
+#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x10)
#ifndef V4L2_CID_DIGITAL_GAIN
#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
@@ -192,6 +194,7 @@ struct sc200ai {
bool is_standby;
struct preisp_hdrae_exp_s init_hdrae_exp;
struct cam_sw_info *cam_sw_inf;
+ struct rk_light_param light_ctl_param;
};
#define to_sc200ai(sd) container_of(sd, struct sc200ai, subdev)
@@ -1191,6 +1194,8 @@ static long sc200ai_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
u32 i, h, w;
long ret = 0;
u32 stream = 0;
+ int rt = 0;
+ struct rk_light_param *light_param;
switch (cmd) {
case RKMODULE_GET_MODULE_INFO:
@@ -1238,6 +1243,22 @@ static long sc200ai_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
stream = *((u32 *)arg);
+ dev_err(&sc200ai->client->dev, "%s: quick_stream = %d\n",
+ __func__, stream);
+ // light control
+ if (stream) {
+ sc200ai->light_ctl_param.light_enable = true;
+ rt = light_ctl_write(sc200ai->module_index,
+ &sc200ai->light_ctl_param);
+ } else {
+ sc200ai->light_ctl_param.light_enable = false;
+ rt = light_ctl_write(sc200ai->module_index,
+ &sc200ai->light_ctl_param);
+ }
+
+ dev_err(&sc200ai->client->dev, "%s: light_ctl_write ret:%d\n",
+ __func__, rt);
+
if (sc200ai->standby_hw) { // hardware standby
if (stream) {
if (!IS_ERR(sc200ai->pwdn_gpio))
@@ -1284,6 +1305,18 @@ static long sc200ai_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
ch_info = (struct rkmodule_channel_info *)arg;
ret = sc200ai_get_channel_info(sc200ai, ch_info);
break;
EN PDF p.43 · 290
+
+ case RKCIS_CMD_FLASH_LIGHT_CTRL:
+ light_param = (struct rk_light_param *)arg;
+ dev_err(&sc200ai->client->dev,
+ "%s: RKCIS_CMD_FLASH_LIGHT_CTRL type: %s enable: %s\n",
+ __func__,
+ light_param->light_type == LIGHT_PWM ? "pwm" : "gpio",
+ light_param->light_enable ? "enable" : "disable");
+
+ memcpy(&sc200ai->light_ctl_param, light_param, sizeof(*light_param));
+
+ break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1304,6 +1337,7 @@ static long sc200ai_compat_ioctl32(struct v4l2_subdev
*sd,
struct rkmodule_channel_info *ch_info;
long ret;
u32 stream = 0;
+ struct rk_light_param *light_param = NULL;
switch (cmd) {
case RKMODULE_GET_MODULE_INFO:
@@ -1400,6 +1434,19 @@ static long sc200ai_compat_ioctl32(struct v4l2_subdev
*sd,
}
kfree(ch_info);
break;
+ case RKCIS_CMD_FLASH_LIGHT_CTRL:
+ light_param = kzalloc(sizeof(*light_param), GFP_KERNEL);
+ if (!light_param) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ ret = copy_from_user(light_param, up, sizeof(*light_param));
+ if (!ret)
+ ret = sc200ai_ioctl(sd, cmd, light_param);
+ else
+ ret = -EFAULT;
+ kfree(light_param);
+ break;
default:
ret = -ENOIOCTLCMD;
break;
@@ -1451,6 +1498,10 @@ static int __sc200ai_stop_stream(struct sc200ai
*sc200ai)
sc200ai->is_first_streamoff = true;
pm_runtime_put(&sc200ai->client->dev);
}
+ sc200ai->light_ctl_param.duty_cycle = 0;
+ sc200ai->light_ctl_param.light_enable = false;
+ light_ctl_write(sc200ai->module_index,
+ &sc200ai->light_ctl_param);
return sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
SC200AI_REG_VALUE_08BIT, SC200AI_MODE_SW_STANDBY);
EN PDF p.44 · 291
}
@@ -1973,6 +2024,7 @@ static int sc200ai_initialize_controls(struct sc200ai
*sc200ai)
sc200ai->subdev.ctrl_handler = handler;
sc200ai->has_init_exp = false;
sc200ai->is_standby = false;
+ sc200ai->light_ctl_param.duty_cycle = 0;
return 0;
