Android Tegra 平台 Back Camera 驱动实现

Android Tegra平台back camera 驱动实现

一 序

        本Android Tegra平台back camera 驱动实现主要包含下述主要内容,在开序之前我先介绍下Tegra平台。Tegra是一种采用片上系统设计(system-on-a-chip)芯片,它集成了ARM架构处理器和NVIDIA的GeforceGPU,并内置了其它功能,产品主要面向小型设备。和Intel以PC为起点的x86架构相比,ARM架构的Tegra更像是以手机处理器为起点做出的发展。它不能运行x86 PC上的Windows XP等操作系统,但在手机上应用多年的ARM架构轻量级操作系统更能适应它高速低功耗的需求。

        camera 架构

        物理架构

        这个包括lens(镜头)、sensor(图像传感器) 、 ISP(图像信号处理)

        Android camera架构

        APP - Framework 、Framework - HAL、 Overlay 、HAL - driver 

         camera工作原理

        原理简介

        camera chip 电路图

        camera驱动的实现

        理解摄像头驱动需要三个前提

        camera驱动实现过程

        配置GPIO口和芯片上电时序(power on sequence)

        配置 I2C

        camera HAL的实现

        camera HAL实现流程

        camera具体实现files图表

        HAL实现的Interface

         camera HAL主要的库 

         android tegra平台中添加一个camera

        定义标识序列

        配置camera硬件连接参数

        关联camera设备入口地址和GUID

        在Android.mk文件中添加相应files

        添加HAL层枚举的camera类型

        camera设备的配置和功能的具体实现

        camera驱动过程中的debug

 

二 camera物理架构

        一般来说,camera主要是由lens和sensor IC两部分组成,其中有的sensor IC集成了DSP,有的没有集成,但也需要外部的DSP处理。细分的来讲,camera设备由下边几个部分构成:

lens

        lens即镜头,一般camera的镜头结构是由几片透镜组成,分有塑胶透镜(Plastic)和玻璃透镜(Glass),通常镜头有:1P,2P,1G1P,1G2P,2G2P,4G等

sensor

        sensor即图像传感器,Sensor是一种半导体芯片,有两种类型:CCD和CMOS。Sensor将从lens上传到过来的光线转换为电信号,再通过内部的AD转换为数字信号。由于Sensor的每个pixel只能感光R光或者B光或者G光,因此每个像素此时存贮的是单色的,我们称之为RAW DATA数据。要想将每个像素的RAW DATA数据还原成三基色,就需要ISP来处理。

ISP

        ISP即图像信号处理,主要完成数字图像的处理工作,把sensor采集到的原始数据转换为显示支持的格式。

        硬件方面,camera系统分为主控制器和摄像头设备,功能上主要有preview预览,takePicture拍照和recording录像。

camera物理架构

        IPU - Image Process Unit 图像处理单元,用于控制摄像机和显示屏。

        图像采集 - 由camera采集的图像数据信息通过IPU的CSI接口控制。

        DMA映射到内存 - IPU将采集到得数据通过DMA映射到一段内存。

        队列机制 - 为了更高效地传送数据,将内存中的数据取出加入一队列,并传送到另一队列。

        视频输出 - 将视频数据从队列中取出,通过IPU控制这段独立显存,最终将视频显示出来。

 

 

三 Android中的camera架构及工作原理

【感谢终结者投递本文】

        本篇文章主要介绍Android中的camera架构及其工作原理。

Android中的camera架构

        Android的camera系统架构自上而下分别为应用层、框架层、硬件抽象层及linux驱动层。下面将通过对其框架层、硬件抽象层即Linux驱动层做简单的介绍。

APP - Framework

        应用层与java框架层的间主要由Binder机制进行通信。

        系统初始化时会开启一个CameraService的守护进程,为上层应用提供camera对的功能接口。

Framework - HAL

        框架层与硬件抽象层间通过回调函数传递数据。

Overlay

        Overlay层由ServiceFlinger和OverlayHal组成,实现视频输出功能,只有camera框架层或者视频框架层能调用它,上层无法直接调用。

HAL - driver

        抽象层位于用户空间,通过系统调用如open(),read(),ioctl()等与内核空间进行数据传递。

camera工作原理

原理简介

        外部光线穿过lens后,经过color filter滤波后照射到sensor面上,sensor将从lens上传到过来的光线转换成电信号,再通过内部的AD转换为数字信号,如果sensor没有集成DSP,则通过DVP的方式传输到baseband,此时的数据格式是RAW DATA。如果集成了DSP,这RAW DATA数据经过AWB,color matrix,lens shading,gamma,sharpness,AE和de-noise处理后输出YUV或者RGB格式的数据。最后会由CPU送到framebuffer中进行显示,这样我们就看到camera拍摄到的景象了。

camera chip 电路图:

        5M back camera chip mt9d111的硬件电路图如下:

Android Tegra平台back camera 驱动实现三 Android中的camera架构及工作原理

        拿到原理图,我们需要关注的是19、21两个管脚分别连接到CAM_I2C_SDA和CAM_I2C_SCL,可以通过I2C来配置摄像头。另外调试摄像头的时候,可以根据这个原理图使用示波器来测量波形以验证代码是否正确。

        这里还需要注意的是开发驱动之前最好用万用表测量摄像头的各个管脚是否和芯片连接正确,否则即使代码没有问题也看不到图像。

        mt9d111是CMOS接口的图像传感器芯片,可以感知外部的视觉信号并将其转换为数字信号并输出。

        我们需要通过XVCLK1给摄像头提供时钟,RESET是复位线,PWDN在摄像头工作时应该始终为低。HREF是行参考信号,PCLK是像素时钟,VSYNC是场同步信号。一旦给摄像头提供了时钟,并且复位摄像头,摄像头就开始工作了,通过HREF,PCLK和VSYNC同步传输数字图像信号。mt9d111向外传输的图像格式是YUV的格式,YUV是一种压缩后的图像数据格式,它里面还包含很多具体的格式类型,我们的摄像头对应的是YCbCr(8 bits, 4:2:2, Interpolated color)。后面的驱动里面设置的格式一定要和这个格式一致。

 

Android Tegra平台back camera 驱动实现四 camera驱动的实现

欢迎光临弯柚博客,walfred盼和您共成长!

【感谢终结者投递本文】

        本篇文章主要介绍camera驱动的实现,这将会结合代码来叙述,这理解摄像头驱动需要三个前提,分别是:

摄像头基本的工作原理

platform_device和platform_driver工作原理

Linux内核I2C驱动架构

        驱动的编写主要有配置GPIO、I2C、MIPI、电压、时钟等, 编写驱动代码,调通camera sensor驱动,并实现前后置双camera的切换。根据芯片手册,实现基本功能 - 预览, 拍照, 录像, 效果(scene, effect, ev, iso, wb, contrast等等)。当然还要实现进阶扩展功能 - 防抖, 自动对焦, 闪光灯, 固件升级, 720P, wdr, panorama等。本篇只讲述些基本的功能实现。

配置GPIO口和芯片上电时序(power on sequence)

        查看datesheet芯片上电时序如下图所示:

Android Tegra平台back camera 驱动实现四 camera驱动的实现

上电时序一

Android Tegra平台back camera 驱动实现四 camera驱动的实现

上电时序二

        对应代码如下:

C/C++代码
  1. static struct camera_gpios yuv5_sensor_gpio_keys[] = {  
  2.     [0] = CAMERA_GPIO("cam_power_en", CAMERA_POWER_GPIO, 1, 0, GPIO_FREE),  
  3.     [1] = CAMERA_GPIO("yuv5_sensor_pwdn", YUV5_PWR_DN_GPIO, 0, 0, GPIO_FREE),  
  4.     [2] = CAMERA_GPIO("yuv5_sensor_rst_lo", YUV5_RST_L_GPIO, 1, 0, GPIO_FREE),  
  5. };  
  6. static int yuv5_sensor_power_on(void)  
  7. {  
  8.     int ret;  
  9.     int i;  
  10.     pr_err("%s: sandow pmu\n", __func__);  
  11.     for (i = 0; i < ARRAY_SIZE(yuv5_sensor_gpio_keys); i++) {  
  12.                 tegra_gpio_enable(yuv5_sensor_gpio_keys[i].gpio);  
  13.         ret = gpio_request(yuv5_sensor_gpio_keys[i].gpio,  
  14.             yuv5_sensor_gpio_keys[i].name);  
  15.         if (ret < 0) {  
  16.             pr_err("%s: gpio_request failed for gpio #%d\n",  
  17.                 __func__, i);  
  18.             goto fail;  
  19.         }  
  20.         gpio_direction_output(yuv5_sensor_gpio_keys[i].gpio,  
  21.             yuv5_sensor_gpio_keys[i].enabled);  
  22.                 gpio_export(yuv5_sensor_gpio_keys[i].gpio, false);  
  23.     }  
  24.   
  25.     return 0;  
  26. fail:  
  27.     while (i--)  
  28.         gpio_free(yuv5_sensor_gpio_keys[i].gpio);  
  29.     return ret;  
  30. }  
  31.   
  32. static int yuv5_sensor_power_off(void)  
  33. {  
  34.     int i;  
  35.     int gpio_pw_dn, gpio_camera_power;  
  36.     pr_err("%s: sandow pmu\n", __func__);  
  37.     //set 5M to power-down     
  38.     gpio_direction_output(YUV5_PWR_DN_GPIO,1);  
  39.     //cut off power          
  40.     gpio_direction_output(CAMERA_POWER_GPIO,0);  
  41.       
  42.     gpio_pw_dn = gpio_get_value(YUV5_PWR_DN_GPIO);  
  43.     gpio_camera_power= gpio_get_value(CAMERA_POWER_GPIO);     
  44.       
  45.     printk("%s: sandow pmu gpio_pw_dn: %d, camera_power: %d", __func__, gpio_pw_dn, gpio_camera_power);  
  46.        
  47.         i = ARRAY_SIZE(yuv5_sensor_gpio_keys);  
  48.     while (i--)  
  49.         gpio_free(yuv5_sensor_gpio_keys[i].gpio);  
  50.     return 0;  
  51. }  
  52.   
  53. struct yuv5_sensor_platform_data yuv5_sensor_data = {  
  54.     .power_on = yuv5_sensor_power_on,  
  55.     .power_off = yuv5_sensor_power_off,  
  56. };  
配置 I2C

        I2C通信本身要注意两点:

        1、SDA第9位ACK位为低时说明从设备有响应;

        2、Slave address,Spec给出的是8位地址,第8位是指Write-0或者Read-1,实际的I2C芯片地址是7位的。Linux源码里struct i2c_board_info的板基信息应填写7位I2C地址。

代码位于:board-ventana-sensors.c

C/C++代码
  1. {  
  2.          I2C_BOARD_INFO(SENSOR_5M_NAME, 0x3D),//I2C slave address  
  3.         .platform_data = &yuv5_sensor_data,  
  4.  },  
  5. ……  
  6. #define SENSOR_5M_NAME  "mt9d111"  
  7. ……  
  8. // platform driver是用来"触发"真正的i2c //driver的注册  
  9. static struct i2c_driver sensor_i2c_driver = {  .driver = {  
  10.         .name = SENSOR_5M_NAME,//与i2c_board_info 的type匹配  
  11.         .owner = THIS_MODULE,  
  12.     },  
  13.     .probe = sensor_probe,// 当有i2c_client和i2c_driver匹配时调用  
  14.     .remove = sensor_remove,  
  15.     .id_table = sensor_id,// //匹配规则  
  16. };  
  17. static struct miscdevice sensor_device = {  
  18.     .minor = MISC_DYNAMIC_MINOR,  
  19.     .name = SENSOR_5M_NAME,  
  20.     .fops = &sensor_fileops,  
  21. };  

        本例的camera驱动代码在/kernel/driver/media/video/tegra下:

        ltc3216.c(LED驱动器LTC3216)、yuv5_sensor.c、yuv_sensor.c、tegra_camera.c。这些源码里面最基础的是tegra_camera.c,这里面注册了一个platform_driver,在相应的平台代码里面有对应的platform_device的描述。这种SOC上的控制器一般都会挂接在platform_bus上以实现在系统初始化时的device和driver的匹配。在driver的probe函数里面,主要完成了资源获取以及misc设备的注册。

平台信息:

C/C++代码
  1. struct yuv5_sensor_platform_data yuv5_sensor_data = {  
  2.     .power_on = yuv5_sensor_power_on,  
  3.     .power_off = yuv5_sensor_power_off,  
  4. };  

        Probe具体流程如下图所示:

Android Tegra平台back camera 驱动实现四 camera驱动的实现

 

 

五 camera HAL的实现流程

         我们接着来介绍camera HAL的实现流程,为了实现一个具体功能的Camera,在HAL层需要一个硬件相关的Camera库(例如通过调用video for linux驱动程序和Jpeg编码程序实现或者直接用各个chip厂商实现的私有库来实现(本例使用后者),比如Qualcomm实现的libcamera.so和libqcamera.so),实现CameraHardwareInterface规定的接口,来调用相关的库,驱动相关的driver,实现对camera硬件的操作。这个库将被Camera的服务库libcameraservice.so调用。

camera具体实现files图表

        具体实现文档如下图表所示:

Android Tegra平台back camera 驱动实现五 camera HAL的实现流程

        HAL代码:

vendor/nvidia/tegra/hal/libnvomxcamera:

custcamerasettingsdefinition.cpp

nvomxcameracallbacks.h

nvomxcamera.cpp

nvomxcamera.h

nvomxcamerasettingsdefinition.h

nvomxcamerasettingsparser.cpp

nvomxcamerasettingsparser.h

HAL实现的Interface

        在tegra2平台中特有的:nvomxcamera.cpp,libcamera.so,实现CameraHardwareInterface接口,openCameraHardware()在该库中实现。

C/C++代码
  1. extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId)  
  2. {  
  3.     LOGVV("HAL_openCameraHardware ++\n");  
  4.   
  5.     return NvOmxCamera::createInstance(cameraId);  
  6. }  
camera HAL主要的库

        vendor/nvidia/tegra/core/drivers/openmax/il,libnvomx.so:OMX core 库,libnvodm_imager.so:odm image 的hal库,这部分默认情况下NV只提供了binary,在full build时会将此库copy到system目录下,继而集成到system.img中去。libnvodm_query.so:odm 的查询库,对GPIO,供电,i2c等其他相关硬件配置在本库完成。

        对于pad的前后camera的问题,个人看法是上层告诉底层使用哪一个camera,然后每次OMX会重新构建OMX Graph,并在最后enable port的时候使用不同的camera硬件。上层处理基本保持一致。

 

六 添加一个camera

        分析完了camera HAL的实现流程,我们就开始尝试着添加这么一个camera,具体的是在目录vendor/nvidia/tegra/odm/ventana/下,现在添加一个camera及其驱动,主要步骤如下:

定义标识序列

        vendor/nvidia/tegra/odm/ventana/odm_kit/query/include/nvodm_query_discovery_imager.h

        定义一个标识序列,例如:

C/C++代码
  1. #define SENSOR_YUV_GUID             NV_ODM_GUID('s', '_', 'S', 'M', 'P', 'Y', 'U', 'V')  
  2. #define SENSOR_YUV5_GUID            NV_ODM_GUID('s', '_', 'S', 'M', 'P', 'Y', 'U', '5')  
配置camera硬件连接参数

        odm_kit/query/subboards/ nvodm_query_discovery_pm275_addresses.h

        配置camera的硬件连接参数,如:

C/C++代码
  1. #define QQ1234_PINS (NVODM_CAMERA_DEVICE_IS_DEFAULT)  
  2. static const NvOdmIoAddress s_ffaImagerQQ1234Addresses[] =  
  3. {  
  4. I2C配置;  
  5. Reset GPIO 配置;  
  6. powerdown GPIO 配置;  
  7. Camera VDD 配置;  
  8. VCSI 配置;  
  9. Video input 配置;  
  10. external Clock (CSUS)配置;  
  11. };  
  12.   
  13. #define OV5650_PINS (NVODM_CAMERA_SERIAL_CSI_D1A | \  
  14.                      NVODM_CAMERA_DEVICE_IS_DEFAULT)  
  15. static const NvOdmIoAddress s_ffaImagerOV5650Addresses[] =  
  16. {  
  17.     { NvOdmIoModule_I2c,  0x0, 0x6C },  
  18.     { NvOdmIoModule_VideoInput, 0x00, OV5650_PINS },  
  19.     { NvOdmIoModule_ExternalClock, 2, 0 } // CSUS  
  20. };  

        本文中:

C/C++代码
  1. static const NvOdmIoAddress s_ffaImagerSensorYUVAddresses[] =  
  2. {  
  3.     { NvOdmIoModule_ExternalClock, 2, 0 } // CSUS  
  4. };  
  5. //END_END  
  6.   
  7. //COMPXX_START Sandow  
  8. static const NvOdmIoAddress s_ffaImagerSensorYUV5Addresses[] =  
  9. {  
  10.     { NvOdmIoModule_ExternalClock, 2, 0 } // CSUS  
  11. };  
关联camera设备入口地址和GUID

        odm_kit/query/subboards/ nvodm_query_discovery_pm275_peripherals.h

        camera设备入口地址同GUID关联:

C/C++代码
  1. //Sandow Imager Primary  
  2. #if CONFIG_I_LOVE_XX  
  3. {  
  4.     SENSOR_YUV5_GUID,  
  5.     s_ffaImagerSensorYUV5Addresses,  
  6.     NV_ARRAY_SIZE(s_ffaImagerSensorYUV5Addresses),  
  7.     NvOdmPeripheralClass_Imager  
  8. },  
  9. // Imager - Secondary  
  10. {  
  11.     SENSOR_YUV_GUID,  
  12.     s_ffaImagerSensorYUVAddresses,  
  13.     NV_ARRAY_SIZE(s_ffaImagerSensorYUVAddresses),  
  14.     NvOdmPeripheralClass_Imager  
  15. },  
在Android.mk文件中添加相应files

        vendor/nvidia/tegra/odm/template/odm_kit/adaptations/imager/Android.mk

C/C++代码
  1. LOCAL_SRC_FILES += sensor_yuv.c  
  2. LOCAL_SRC_FILES += sensor_yuv5.c  
添加HAL层枚举的camera类型

        vendor/nvidia/tegra/odm/template/odm_kit/adaptations/imager/imager_hal.c

        添加Hal层会枚举的camera类型如下:

C/C++代码
  1. #include "sensor_yuv.h"  
  2. #include "sensor_yuv5.h"  
  3. DeviceHalTable g_SensorHalTable[] ={  
  4. ....  
  5. {SENSOR_YUV_GUID,            SensorYuv_GetHal},  
  6. {SENSOR_YUV5_GUID,           SensorYuv5_GetHal},  
  7. ....  
  8. };  
camera设备的配置和功能的具体实现

        vendor/nvidia/tegra/odm/template/odm_kit/adaptations/imager/ sensor_yuv.c

        vendor/nvidia/tegra/odm/template/odm_kit/adaptations/imager/ sensor_yuv5.c

        NvBool SensorYuv_GetHal(NvOdmImagerHandle hImager);

        这是对camera设备的配置和功能的具体实现的文件。硬件校准之类的工作主要就是修改sensor_yuv.c(front camera)和sensor_yuv5.c(back camera)

C/C++代码
  1. NvBool SensorYuv_GetHal(NvOdmImagerHandle hImager)  
  2. {  
  3.     if (!hImager || !hImager->pSensor)  
  4.         return NV_FALSE;  
  5.   
  6.     hImager->pSensor->pfnOpen = SensorYuv_Open;  
  7.     hImager->pSensor->pfnClose = SensorYuv_Close;  
  8.     hImager->pSensor->pfnGetCapabilities = SensorYuv_GetCapabilities;  
  9.     hImager->pSensor->pfnListModes = SensorYuv_ListModes;  
  10.     hImager->pSensor->pfnSetMode = SensorYuv_SetMode;  
  11.     hImager->pSensor->pfnSetPowerLevel = SensorYuv_SetPowerLevel;  
  12.     hImager->pSensor->pfnGetPowerLevel = SensorYuv_GetPowerLevel;  
  13.     hImager->pSensor->pfnSetParameter = SensorYuv_SetParameter;  
  14.     hImager->pSensor->pfnGetParameter = SensorYuv_GetParameter;  
  15.     return NV_TRUE;  
  16. }  

 

七 camera驱动过程中的debug

        代码也分析了,camera也添加了,添加对了吗?代码有问题吗?所以还必须debug这个camera,最后分享下camera驱动的调试过程。

首先对照电路图,检查Camera的电路连接是否正确;

用万用表量Camera的电源管脚,查看Camera的供电是否正常,确定是否需要我们在程序中进行电源控制;

查看Camera的Spec文档,检查PWDN和RESET的管脚触发是否正常,是否需要在程序中进行控制;

在Camera的Datasheet中找出该设备的I2C地址,检查I2C地址配置是否正确;

查看I2C通信是否正常,是否能正常进行读写,用示波器量出I2C的SCL和SDA的波形是否正常,未通信时都为高电平,通信时SCL为I2C时钟信号,SDA为I2C数据通路信号;

用示波器量Camera的MCLK管脚,看是否正确,如果MCLK正常,通常情况下PCLK也应该有波形;

检查Camera的初始化寄存器列表的配置是否正确。

通过dynamic debug查看具体模块的log信息,分析driver是否work OK

操作驱动给出的相应接口来进行分析调试

        至此,在Android分析camera驱动及整个实现过程就全结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值