应用开发参考
1. 总体框架¶
本文档仅描述各个模块的基本使用流程,详细的接口及参数描述请查阅各个模块的API手册。
软件的总体框架如下图,开发人员通过mi_xxx.lib
库提供的接口来调用实际的硬件模块。
表1-1 常用的基本模块
简称 | 全称 | 功能职责 |
---|---|---|
SNR | Sensor | 获取摄像头接口信息、调整分辨率和帧率等功能 |
VIF | Video Input interface | MIPI/BT656信号采集模块,VIF支持预缩放功能以在特定的场景下减少系统频宽占用,一路Sensor视频输入,VIF可以同时输出一路源尺寸和一路缩放尺寸(¼)到DRAM上 |
VPE | Video Process Engine | 对视频数据进行缩放、裁剪、降噪、图像增强等 |
DIVP | Deinterlace & Video Post Process Engine | DIVP Engine主要有如下两个功能: 1、对Interlace的数据进行De处理; 2、对视频数据做Scale、Rotate等。 |
VENC | Video Encoder | H264/H265/MotionJpeg编码器,对输入的YUV数据进行编码处理,输出ES流 |
RGN | Region Overlay&Cover | RGN模块主要提供两个功能: 1、Overlay支持在VPE、DIVP上叠加,如OSD字幕; 2、Cover支持在VPE/DIVP上叠加遮挡区域。 |
IVE | Intelligent Video Engine | 提供图像智能识别算法中的基本算子支持 |
AI | Audio Input Interface | 音频数据采集模块,负责从Micro In采集音频数据 |
AO | Audio Output Interface | 音频数据输出模块,支持PCM格式音频输出到Lineout/IIS Out |
IPU | Intelligent Process Unit | 图像智能处理单元,对接Artificial Intelligence网络模型 |
各个模块之间通过sys模块的bind接口串联起来,模块之间的buffer传递如下图:
由上图可以看到Bind有frame mode跟realtime mode两种模式
模式 | 特点 | 使用场景 |
---|---|---|
Realtime mode | 硬件直连,不占用DRAM内存 | 只有单sensor时可以使用 |
Frame mode | 占用多张yuv内存。(由depth设置决定) | 单/多sensor都可以使用 |
Frame mode的depth概念
可以通过如下接口设置模块输出口depth参数(如前文所述,模块Input port buffer是上一级输出的Output port buffer)
1. /* 2. * BufQueueDepth:port能同时申请的最多buffer块数,默认为4; 3. * UserFrameDepth:客户能通过MI_SYS_ChnOutputPortGetBuf()同时取到的最多buffer块数; 4. * BufQueueDepth >= UserFrameDepth; 5. */ 6. MI_S32 MI_SYS_SetChnOutputPortDepth(MI_SYS_ChnPort_t *pstChnPort , MI_U32 u32UserFrameDepth , MI_U32 u32BufQueueDepth);
2. 基本模块介绍¶
以下所有的模块均依赖sys模块,在整个app的生命周期里必须且只能初始化一次MI_SYS_Init()。
2.1. Sensor模块¶
初始化流程:
1. // 设置plane mode 2. MI_SNR_SetPlaneMode(eSnrPadId, true) 3. // 查询sensor支持的分辨率 4. MI_SNR_QueryResCount(eSnrPadId, &u32ResCount) 5. // 查询index对应的分辨率参数 6. for(u8ResIndex = 0; u8ResIndex < u32ResCount; u8ResIndex++) 7. MI_SNR_GetRes(eSnrPadId, u8ResIndex, &stRes) 8. // 设置出图分辨率 9. MI_SNR_SetRes(eSnrPadId, u8ChocieRes) 10. // 设置sensor出图帧率 11. MI_SNR_SetFps(eSnrPadId, SENSOR_FPS) 12. // 使能sensor 13. MI_SNR_Enable(eSnrPadId) 14. // 查询当前出图分辨率 15. MI_SNR_GetCurRes(eSnrPadId, &u8CurResIdx, &stCurRes)
2.2. VIF模块¶
VIF模块只有输出,没有输入,其基本使用流程为:
1. // SSA330 Sensor pad2与pad0是同一组mipi分出来的,pad2使用vifDev 1 2. vifDev = (s32vifDev == 2) ? 1 : s32vifDev; 3. vifChn = vifDev * 4; 4. // 设置vif device属性 5. MI_VIF_SetDevAttr(vifDev, &stDevAttr) 6. // 使能vif device 7. MI_VIF_EnableDev(vifDev) 8. // 设置output port属性 9. MI_VIF_SetChnPortAttr(VifChn, VifPort, &stChnPortAttr) 10. // 使能output port 11. MI_VIF_EnableChnPort(VifChn, VifPort)
2.3. VPE模块¶
VPE模块对一幅输入的图像首先进行图像质量调整,包括降噪,锐化,亮度调整等,然后再分别缩放到一定的分辨率通过各个output port 口输出。该模块另外包含HDR,旋转,裁剪等功能。
VPE单端输入多端输出,且不同芯片的处理流程不完全一致,从MI VPE API中可以找到SSD222芯片的VPE模块的处理逻辑如下图:
可以看到,且各个端口可以分别配置不同的功能,在使用时需根据内部处理的流程配置合适的参数,基本的使用流程如下:
1. VpeChannel = s32vifDev 2. MI_VPE_SetChannelParam(VpeChannel, &stVpeChnParam) 3. MI_VPE_CreateChannel(VpeChannel, &stChannelVpeAttr) 4. MI_VPE_StartChannel(VpeChannel) 5. MI_VPE_GetPortMode(VpeChannel, VpePort, &stVpeMode) 6. // Change some parameter 7. MI_VPE_SetPortMode(VpeChannel, VpePort, &stVpeMode) 8. if(need crop) 9. MI_VPE_SetPortCrop(VpeChannel, VpePort, &stOutCropInfo) 10. MI_SYS_SetChnOutputPortDepth(&stChnPort, 0, 5) 11. MI_VPE_EnablePort(VpeChannel, VpePort)
2.4. DIVP模块¶
DIVP模块支持图像裁剪、图像像素格式转换、图像的旋转、图像的镜像操作以及图像的拉伸和缩放,单输入输出。不同芯片支持的功能不完全一致,从MI DIVP API中可以找到SSD222芯片的DIVP模块处理流程为:
在同时开启DIVP的多个功能(crop、rotate、mirror等)时需注意以上处理逻辑的先后顺序,以免无法得到预期的结果。DIVP模块的基本使用流程为:
1. MI_DIVP_CreateChn(u32DivpChn, &stDivpAttr) 2. MI_DIVP_StartChn(u32DivpChn) 3. MI_DIVP_SetOutputPortAttr(u32DivpChn, &stDivpOutputAttr)
2.5. DISP模块¶
DISP是一个视频显示单元,主要功能是对前端输出的图像做硬件拼图,并对硬件拼图后的图像进行颜色空间转换,最终通过HDMI/VGA/MIPI/TTL等接口输出到显示器或LCD。DISP模块流程框图为:
基本使用流程为:
1. DispDev = 0 2. DispLayer = 0 3. // Set disp pub attribute 4. MI_DISP_SetPubAttr(DispDev, &stPubAttr) 5. MI_DISP_Enable(DispDev) 6. // Set layer 7. MI_DISP_BindVideoLayer(DispLayer,DispDev) 8. MI_DISP_SetVideoLayerAttr(DispLayer, &stLayerAttr) 9. MI_DISP_EnableVideoLayer(DispLayer) 10. // Set input port attribute 11. MI_DISP_SetInputPortAttr(DispLayer, u8DispInport, &stInputPortAttr) 12. MI_DISP_EnableInputPort(DispLayer, u8DispInport) 13. MI_DISP_SetInputPortSyncMode(DispLayer, u8DispInport, E_MI_DISP_SYNC_MODE_FREE_RUN)
2.6. PANEL模块¶
1. MI_PANEL_IntfType_e eLinkType = E_MI_PNL_INTF_TTL 2. /* 如果是SPI屏需先给屏下发SPI参数! */ 3. MI_PANEL_Init(eLinkType) 4. // 获取panel分辨率参数,给divp/disp使用 5. MI_PANEL_GetPanelParam(eLinkType, &stParamCfg)
2.7. RGN模块¶
RGN模块可以用于绘制OSD(时间、人脸框等)或区域遮盖,它可以贴在vpe或divp的output port上
1. // init 2. MI_RGN_Init() // 传入调色板 3. MI_RGN_Create() 4. MI_RGN_AttachToChn() 5. // get canvas 6. MI_RGN_GetCanvasInfo() 7. // Draw on canvas 8. // Update canvas 9. MI_RGN_UpdateCanvas()
2.8. IQ Server¶
IQSERVER(Image Quality tuning Server)图像质量调节服务,用来完成调节工具(IQ Tool)和开发板之间的数据通信,包括ISP参数设置、获取图像、上传/下载相关文件等功能。在创建完vpe通道后调用OPEN接口即可。
1. /* 2. * width: Sensor输出分辨率宽度 3. * height: Sensor输出分辨率高度 4. * vpeChn: vpe通道号 5. */ 6. MI_IQSERVER_Open(width, height, vpeChn)
IQ Tool工具连接主板调节完参数后会生成一个IQ参数文件给用户app使用。
2.9. ISP模块¶
ISP模块是对Video source输入的数据进行分析、处理,设定相关视频参数以及进行Camera的调整,以实现黑电平校正、镜头校正、3A、2D/3D降噪、CCM、Gamma…等功能。一般情况下普通用户app只会用这个模块提供的接口来加载调好的IQ参数文件,加载流程如下:
1. do 2. { 3. MI_ISP_IQ_GetParaInitStatus(Vpechn,&bstatus) 4. if(bstatus.stParaAPI.bFlag != 1) 5. { 6. // 检查退出条件以免死循环 7. if(timeout) 8. { 9. break; 10. } 11. continue; 12. } 13. // pConfigPath为已调好的IQ参数文件路径 14. MI_ISP_API_CmdLoadBinFile(Vpechn, (char*)pConfigPath, 1234) 15. } 16. while();
3. 双Sensor应用示例¶
3.1. 基本数据流¶
常见的用户场景为:两路sensor(RGB+IR)分别出图送到算法处理,同时通过RTSP出流。RGB Sensor送到LCD上显示。
添加一个switch用于支持切换lcd的显示内容,可切换显示sensor-0、sensor-1或黑屏(通过Inject Task注入黑色数据,为了在不出图的情况下正常显示GUI)
使用中如果divp不够用可以根据实际场景将crop/scale/rotate放到vpe0中处理,这边拆分到显示部分的divp中处理是为了去除接口耦合,以便用户能更灵活的配置“竖屏横用”或“横屏竖用”等场景。
3.2. DIVP抓图流程¶
通过sys模块接口获取到divp output port描述符,使用此接口读取divp的输出数据,基本代码流程如下:
1. MI_SYS_GetFd(&stChnPort, &s32Fd) 2. MI_SYS_SetChnOutputPortDepth(&stDivpChnOutput, 1, 5) 3. while(1) 4. { 5. FD_ZERO(&read_fds); 6. FD_SET(s32Fd, &read_fds); 7. TimeoutVal.tv_sec = 1; 8. TimeoutVal.tv_usec = 0; 9. 10. /*Waiting for frame*/ 11. s32Ret = select(s32Fd + 1, &read_fds, NULL, NULL, &TimeoutVal); 12. if(s32Ret > 0 && FD_ISSET(s32Fd, &read_fds)) 13. { 14. /* Get a frame from divp */ 15. if(MI_SUCCESS == MI_SYS_ChnOutputPortGetBuf(&stDivpChnOutput, &stBufInfo, &hHandle)) 16. { 17. #if 0 18. if(stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_420) 19. { 20. //memcpy(dstbuf, (char *)stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 3 >> 1); 21. } 22. else if((stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_YUV422_YUYV)) 23. { 24. //memcpy(dstbuf, stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 2); 25. } 26. 27. else if((stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_RGB565)) 28. { 29. //memcpy(dstbuf, (char *)stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 2); 30. } 31. else if(stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_ARGB8888) 32. { 33. //memcpy(dstbuf, (char *)stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 4); 34. } 35. #endif 36. /*Free frame*/ 37. s32Ret = MI_SYS_ChnOutputPortPutBuf(hHandle); 38. if(s32Ret != MI_SUCCESS) 39. { 40. ST_ERR("MI_SYS_ChnOutputPortPutBuf failed.s32Ret:0x%x !\n", s32Ret); 41. } 42. 43. } 44. else 45. { 46. ST_ERR("MI_SYS_ChnOutputPortGetBuf failed!\n"); 47. } 48. } 49. } 50. MI_SYS_CloseFd(s32Fd)
3.3. RGN及FB绑定位置选择¶
RGN及FB支持绑定到VPE或DIVP的输出口,从绑定位置起后续所有的数据流均带有绑定上去的数据,这意味着后续所有的对数据流的处理均会影响到绑定上去的内容。选择绑定位置的原则是根据输出口的属性(主要是分辨率参数)及数据流的影响范围确定一个最合适的位置。比如:
-
通过RGN画人脸框,算法识别的是原始尺寸的图片,给的人脸框坐标必然也是基于原图计算出来的,要将人脸框显示到正确的位置的话那么就将RGN attach到vpe0的port2;
-
在FB上画GUI,LCD竖屏横用的情况,FB应该绑定到divp2(for crop and scale)的output,因为这个输出口的图像就是实际显示到LCD上的图像,后续的rotate处理GUI也将同时旋转,以便获取预期的显示效果。
4. 如何新加一个应用¶
这里以创建spi_app应用为例,介绍往sdk中添加一个新的应用的步骤方法
4.1. 创建目录¶
APP推荐创建在rtk/proj/sc/application
下,以spi_app
为例,目录结构如下:
inc : 存放head file的位置
src : 存放 src file的位置
spi_app.mak : 类似makefile的功能,里面的几个字段功能如下:
-
PROCESS : 一般都是lib,也就是这个app最终也是编成一个lib库用于最终链接成rtos bin文件
-
PATH_C : 定义源文件的路径
-
PATH_H :定义头文件路径
-
SRC_C_LIST :添加需要编译的源文件
4.2. 把应用加入编译¶
此步骤介绍如何把4.1步骤添加的app加入编译,主要修改如下文件:
-
rtk/proj/mak/common/paths.mak
在
paths.mak
中指定编译路径 ,如下: -
rtk/proj/mak/common/libs_common.mak
libs_common.mak
中添加对lib的引用,如下: -
mak/product/options_xxx.mak
options_xxx.mak
中添加可选的编译项CONFIG_APPLICATION_SPI = TRUE
上述三步是完整的做法,可以通过修改mak来决定是否将app加入编译。
注:这是新版本的目录结构,同之前SDK的目录结构有差异。不过这个添加方法是一个通用做法,完全可以根据这个来添加自己的应用
5. RTOS编译方法¶
cd proj; // 进入proj目录下 ./tng/configure.pl // 选择对应的product config make clean;make // 编译
6. RTOS Demo使用介绍¶
6.1. 目录结构¶
rtk/proj/sc/application/
common/
disp_app/
dualos_camera/
iqserver/
lvgl_app/
spi_app/
usb_gadget_app/
目录介绍:
common: 主要是放置一些公用函数,比如vpe vif venc spi的封装函数
disp_app: sensor出流转panel播放video的demo
dualos_camera: 主要做一些初始化的操作,比如MI模块初始化等。usb设备的初始化入口也是在这里面(void composite_app_init(void))
iqserver: IQ相关demo
lvgl_app: 使用lvgl点TTL/SPI屏的demo
spi_app: 使用SPI点panel的demo
usb_gadget_app: 整个rtos作为一个复合设备,总入口在composite_app.c中,根据mak的配置选择初始化那些usb class,比如uvc/uac/hid/cdc等
6.2. product config以及相关宏定义介绍¶
usb相关的config如下:
index | config | DDR SIZE(M) | FLASH TYPE | FUNCTION |
---|---|---|---|---|
0 | pioneer3_ssc020a_128_freertos_smp_isw_display | 128 | SPINOR | Dual Sensor + Display |
1 | pioneer3_ssc020a_64_freertos_smp_isw_display | 64 | SPINOR | TTL Panel Display |
2 | pioneer3_ssc020a_64_freertos_smp_isw_usbdev | 64 | SPINOR | Dual Sensor UVC |
3 | pioneer3_ssc021a_16_freertos_smp_isw_display | 16 | SPINOR | SPI Panel Display |
4 | pioneer3_ssc021a_16_freertos_smp_isw_usbdev | 16 | SPINOR | Single Sensor UVC |
5 | pioneer3_ssc021a_64_freertos_smp_isw_display | 64 | SPINOR | SPI Panel Display |
6 | pioneer3_ssc021a_64_freertos_smp_isw_usbdev | 64 | SPINOR | Dual Sensor UVC |
如上,我们提供了常用的product config,根据需要选择对应的config即可。这些config之间也支持自由组合。但是注意IKAYAKI系列芯片只有4个usb ep(ep0用于控制端点,可用只有3个ep),所以组合时需要注意这个限制。例如,使用CDC需要3个usb ep,那无法使用其他的UVC Function进行组合。
SDK默认使用NOR的配置,如果需要使用NAND,请修改选项CONFIG_STORAGE_NAND_ONEBIN = TRUE
和CONFIG_STORAGE_NOR_ONEBIN = FALSE
即可。
6.3. UVC/UAC需要的其它设置¶
6.3.1 IIC padmux配置¶
IIC padmux的配置文件可在sysdesc中指定,如下图:
如上图表示的就是i2c 0 配置为mode 0(不配置),i2c 1配置为mode 4,具体padmux是什么需要根据实际板子上用的引脚,去查表看对应的mode。
我们目前提供的demo板有SSC9211(qfn68),SSD222(qfn128)。
针对我们提供的qfn68的demo板,使用的是i2c 1 mode 2。
针对我们提供的qfn128的demo板,使用的是i2c 1 mode 6。
6.3.2 mipi_ctrl_mode配置¶
sysdesc类似Linux上的dts文件,针对不同型号有不同的.sys
文件,具体用到哪个文件,可以从proj/mak/product/xxx.mak
中的SYSDESC变量查看。
针对我们提供的qfn68的demo板,需要修改配置文件中的vif_sr0_mipi_ctrl_mode_u8为2。
针对我们提供的qfn128的demo板,需要修改配置文件中的vif_sr0_mipi_ctrl_mode_u8为1。
6.3.3 dmic padmux 设置¶
sysdesc类似Linux上的dts文件,针对不同型号有不同的.sys
文件,具体用到哪个文件,可以从proj/mak/product/xxx.mak
中的SYSDESC变量查看。
在里面的sound节点下的digmic_padmux_u8。
我们提供的qfn68 demo板对应的是mode 2,所以需要设置digmic_padmux_u8为2。
6.3.4 pspi sensor padmux 配置¶
sysdesc文件就是类似Linux上的dts文件,针对不同型号有不同的.sys
文件,具体用到哪个文件,可以从proj/mak/product/xxx.mak
中的SYSDESC变量查看。
查看pspi0节点的pspi_padmux_u8、sr00_mclk_padmux_u8、mclk_freq_u32、rst_u32。
6.3.5 sensor类型指定¶
用哪颗sensor是在proj/mak/product/options_pioneer3_xxxx.mak
中的SENSOR0 SENSOR1配置。
7. CUS3A使用参考¶
sdk中提供了关于3A的使用demo,路径在proj/sc/application/dualos_camera/rtos_preload.c
使用时,只需要extern void DrvAlgoEntry(void); DrvAlgoEntry();
即可。
Sigmastar 有内置的3a算法,如果什么都不动,调用DrvAlgoEntry(),跑的就是默认的Sigmastar 3A。isp_algo_entry.c中有提供cus3a的简单示例,如果不用默认的3A算法,请修改为如下: