查看: 8949|回复: 4
收起左侧

OV3640软件调试过程

[复制链接]
发表于 2013-11-27 14:14:11 | 显示全部楼层 |阅读模式
Wince6.0 下Camera(OV3640) 驱动开发笔记
    由于项目需要开始搞S5PC100的摄像头驱动,camera类型是OV3640.以前还没有做摄像头驱动,缺乏相关的经验,另外基于S5PC100的摄像头驱动代码结构又比较复杂,谁让A8的功能强大呢,功能多了强了开发起来难度自然就增大了。总之,目前的感觉是——好复杂!
   OV的FAE不好请,既然项目需要,有困难也要上,慢慢啃吧,下面以三星S5K4EAGX芯片为例说明。
   先说一下S5PC100支持输入的camera数据类型,有三种:ITU-R BT 60/656 类型,DMA(AXI 64bit interface)类型,MIPI(CSI)类型。相应的接口有两种方式:并口方式和MIPI方式。在三星官方开发板上有这两种类型的camera接口。
   看了一天官方给的camera驱动代码,发现其结构相比2440的比较复杂,总体接口分为三大部分,即S5PC100 camera控制器设置部分,摄像头操作部分(读、写、控制等),摄像头滤波器部分(主要是特殊效果处理,对这一部分还不是很清楚)。通过阅读代码发现,每一种类型的摄像头抽象出来一个类,包含了控制摄像头的各个函数,例如:
class S5K4EAGX : public CamModule
{
public:
    S5K4EAGX();   
    S5K4EAGX(ICamComm *CommIF);
    ~S5K4EAGX();   
    MODULE_STATUS Init();
    MODULE_STATUS Deinit();
    MODULE_STATUS InitSensor();
    MODULE_STATUS Power(BOOL bOnOff);
    MODULE_STATUS Standby(BOOL bActive);
    MODULE_STATUS Reset(BOOL bActive);
    MODULE_STATUS GetSupportFormat(MODULE_DESCRIPTOR *outModuleDesc);
    MODULE_STATUS SetFormatSize(CAMIF_IMG_SIZE Size);
};
  该类是对摄像头S5K4EAGX的抽象类,包括了初始化,电源管理,相关设置等。可以发现该类是CamModule的继承类,那么CamModule是一个什么类呢?从继承与被继承的知识可以知道CamModule类应该是对所有类型摄像头的抽象,其结构如下:
class CamModule : public ICamModule, public ICamModuleExtension
{
public:
    CamModule();   
    CamModule(ICamComm *CommIF);
    virtual ~CamModule();
    virtual MODULE_STATUS SetOperationMode(CAMIF_OPERATION_MODE mode);
    virtual MODULE_STATUS StartCapture();
    virtual MODULE_STATUS StopCapture();
    // Extension
    virtual MODULE_STATUS Flash();
    virtual MODULE_STATUS SetBrightness();
    virtual MODULE_STATUS Effect();
    virtual MODULE_STATUS Whitebalance();
    virtual MODULE_STATUS Framerate();   
   
protected:
    ICamComm *m_oCommIF;
    MODULE_PWR_STATUS PowerStatus;
    UINT32 SensorID;
    BOOL IsInitialize;
    CAMIF_OPERATION_MODE OperationMode;
    CAMIF_IMG_SIZE PreviewSize;
    CAMIF_IMG_SIZE StillSize;
    MODULE_DESCRIPTOR   ModuleDescriptor;
    volatile S5PC100_GPIO_REG    *m_regIOP;   
//  Properties[];   
}
  从其成员函数可以看出该类是更高层次的操作,包括设置操作方式,开始(停止)采集,设置图像效果等。该类还有两个父类ICamModule, ICamModuleExtension。通过阅读代码可知这两个类是两个虚拟类,相应代码如下:
ICamModule
1.   class ICamModule  
2.   {  
3.   public:  
4.       virtual MODULE_STATUS Init() = 0;  
5.       virtual MODULE_STATUS Deinit() = 0;  
6.       virtual MODULE_STATUS InitSensor() = 0;  
7.       virtual MODULE_STATUS Power(BOOL bOnOff) = 0;  
8.       virtual MODULE_STATUS Standby(BOOL bActive) = 0;  
9.       virtual MODULE_STATUS Reset(BOOL bActive) = 0;  
10.   
11.      virtual MODULE_STATUS GetSupportFormat(MODULE_DESCRIPTOR *outModuleDesc) = 0;  
12.      virtual MODULE_STATUS SetOperationMode(CAMIF_OPERATION_MODE mode) = 0;      
13.      virtual MODULE_STATUS SetFormatSize(CAMIF_IMG_SIZE imageSize) = 0;  
14.        
15.      virtual MODULE_STATUS StartCapture() = 0;  
16.      virtual MODULE_STATUS StopCapture() = 0;  
17.  }  
ICamModuleExtension
1.   class ICamModuleExtension  
2.   {  
3.   public:  
4.       // Feature Extension   
5.       virtual MODULE_STATUS Flash() = 0;  
6.       virtual MODULE_STATUS SetBrightness() = 0;  
7.       virtual MODULE_STATUS Effect() = 0;  
8.       virtual MODULE_STATUS Whitebalance() = 0;  
9.       virtual MODULE_STATUS Framerate() = 0;  
10.  }  
由此可见开发摄像头驱动的起点是从 CamModule一个继承类,类似于class S5K4EAGX : public CamModule{}。这一点有点类似于display驱动。
好了,知道从那里下手了,接下来的就是编写OV3640的抽象类,其父类是 CamModule。
今天就先到这里,明天编写OV3640的抽象类以及成员变量和成员函数的实现。

     从开始搞camera驱动到正式搞好经历了差不多一个月的时间,现在回头来看发现走了很多弯路,耗费了不必要的时间和精力,也许这是菜鸟必经的过程吧。周末没事把调camera驱动的整个过程总结一下,以免以后忘了对不起那一个月的时间和精力,同时也给正在调camera驱动的朋友一点参考。
一.camera硬件调试
     我采用的是camera是300万像素的OV3640,处理器为S5PC100,接口电路如下图所示:
  
       第一步要干什么呢,当然是要先确保硬件没有了问题了。从上图可以看出OV3640是IIC接口的,也就是它的初始化和各种功能控制都是通过IIC接口写入对应的命令来实现的,因此首要任务是确保IIC驱动能正确读写。我刚开始就卡在这里,三星给的BSP里的IIC1驱动有BUG,我的camera就是连到IIC1了。IIC1驱动的读和写都有问题,而且板子上没有类似EEPRAM等具有IIC接口可读写的芯片,所以验证IIC1成了一个问题。因此在调试IIC1驱动阶段耗费了不少时间。
       怎么确定你的IIC已经能正常工作了?通过读OV3640的ID,ID能正确读出了就说明你CAMAERA的电源接口没有问题,IIC没有问题。在此要提醒一点,OV3640的PWDN在正常工作模式要拉低,否则不能正常工作,从上面的原理图可以看出我搞错了,后来只能跳线解决。
接下来需要验证的是XCLK,也就是cpu输出的时钟信号。从3640的文档上可以知道,需要的标准时钟是24M,通过示波器来验证你的cpu输出的XCLK是否是24M。
        当上述工作都得到了确定,下面就要开始初始化3640了。初始化文件可以向FEA要,自己写就比较纠结了。也许在这里会有些疑问,在哪里对camera进行初始化操作呢?由于目前只是来验证硬件接口是否正常,没有必要一定要在camera驱动程序里进行初始化,可以在任何一个驱动中调用IIC驱动对camera进行初始化。假设你的IIC读写驱动已经正确了,那么此时camera就会数据输出,帧同步,行同步和像素时钟也都会有规律的波形输出。如果没有,那么就要回头检测IIC驱动了。
二.camera驱动分析
     wince6.0 下的camera驱动结构是基于MDD/PDD的分层结构,整体结构如下图所示:
   
   应用程序是基于DirectShow框架的,这一部分这里暂时不谈,到后面再说。这里要分析的重点是camera驱动的MDD/PDD结构,其详细结构图为:
   
        由上图可知,MDD主要分为两大部分CAM(camdriver,camdevice)与PIN(pindriver,pindevice),其中camdriver与pindriver是流接口;camdevice与pindevice是PDD层函数的封装。具体的函数调用顺序为:
   三.实现camera驱动
   实现camera驱动有两种方法,一种是利用微软提供的camera驱动框架实现,框架代码位于:/WINCE600/PUBLIC/COMMON/OAK/DRIVERS/CAPTURE/CAMERA。这部分代码这是一个框架,没有具体的硬件相关。这种方法难度很难大,一般只有那些OEM厂家才会采用此方法。另一种相对比较简单的方法就是在原有正确的camera驱动上改,我用的三星BSP中有这样的camera驱动。看过camera驱动的人都知道每一种不同的camera被抽象为一个类,因此首先要抽象为你特定类型camera的类。当然这部分也不用从头写,也可以在其他camera模板上改。需要改动主要有以下几处:
1.camera参数宏定义
//========================================================
//  S5K4EAGX default mode
#define S5K4EAGX_MODULE_ITUXXX         CAM_ITU601
#define S5K4EAGX_MODULE_MIPI           (TRUE)
#define S5K4EAGX_MODULE_LANE           (DATA_LANE_1)
#define S5K4EAGX_MODULE_JPEG           (1)
#define S5K4EAGX_MODULE_YUVORDER       CAM_ORDER_CRYCBY
#define S5K4EAGX_MODULE_PREVIEWHSIZE   640
#define S5K4EAGX_MODULE_PREVIEWVSIZE   480
#define S5K4EAGX_MODULE_HSIZE          640
#define S5K4EAGX_MODULE_VSIZE          480
#define S5K4EAGX_MODULE_HOFFSET        0
#define S5K4EAGX_MODULE_VOFFSET        0
#define S5K4EAGX_MODULE_UVOFFSET       CAM_UVOFFSET_0
#define S5K4EAGX_MODULE_CLOCK          24000000
#define S5K4EAGX_MODULE_CODEC          CAM_FORMAT_YCBCR422_1PLANE
#define S5K4EAGX_MODULE_HIGHRST        0        // This is affected by Electic circuit for reset or power control PIN, BB37->0, SMDK->1
#define S5K4EAGX_MODULE_INVPCLK        0
#define S5K4EAGX_MODULE_INVVSYNC       0
#define S5K4EAGX_MODULE_INVHREF        0
//========================================================
这里就要根据你实际采用的摄像头来设置了,提醒一点的是同步时钟的极性要与cpu需要的相匹配。
2.camera初始化函数:InitSensor()
   摄像头的初始化就是在这个函数里完成的,根据你的camera进行改动。
3.camera电源管理函数Power(BOOL bOnOff)
   在这个函数里需要完成打开camera电源或关闭电源的操作,根据具体硬件改写。
4.CCameraPdd::GetVideoFormatList( DWORD dwSensorID )
   该函数比较重要,应用程序就是通过此函数来得到camera支持何种数据数据的信息的,如下所示:

    case SYSLSI_S5K4EAGX:
        // Video Format initialization
        i = 0;
     
        m_pModeVideoFormat[CAPTURE].pCsDataRangeVideo[i++]  = &DCAM_StreamMode_YV12_640x480_30;
        m_pModeVideoFormat[CAPTURE].pCsDataRangeVideo[i++]  = &DCAM_StreamMode_YV12_1280x720_30;
        m_pModeVideoFormat[CAPTURE].ulAvailFormats          = i;
        // Still Format initialization
        i = 0;
        // With S5K4EAGX Sensor module, IJPG is used for Still shot, and there is no test kit.
        // The user should dump the camera still output pin buffer to check frame data
        if(bForCETK)
        {
            // CETK can test only RGB format.
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_RGB565_640x480_30;
        }
        else
        {
            // Use Default Format
            // YV format also can be used. actually it shows faster performance than RGB format.
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_IJPG_640x480_30;
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_IJPG_800x480_30;
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_IJPG_1280x960_30;
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_IJPG_1600x1200_30;
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_IJPG_2048x1536_30;
            m_pModeVideoFormat[STILL].pCsDataRangeVideo[i++]    = &DCAM_StreamMode_IJPG_2592x1944_30;        
        }
        m_pModeVideoFormat[STILL].ulAvailFormats            = i;
        // if preview pins supports
        if( MAX_SUPPORTED_PINS == m_ulCTypes )
        {
            i = 0;
            if(bForCETK)
            {
                // CETK can test only RGB format.
                m_pModeVideoFormat[PREVIEW].pCsDataRangeVideo[i++]  = &DCAM_StreamMode_RGB565_640x480_30;
                m_pModeVideoFormat[PREVIEW].pCsDataRangeVideo[i++]  = &DCAM_StreamMode_RGB565_800x480_30;            
            }
            else
            {
                // YV format also can be used. actually it shows faster performance than RGB format.
                m_pModeVideoFormat[PREVIEW].pCsDataRangeVideo[i++]  = &DCAM_StreamMode_YV12_640x480_30;
                m_pModeVideoFormat[PREVIEW].pCsDataRangeVideo[i++]  = &DCAM_StreamMode_YV12_1280x720_30;            
            }
            m_pModeVideoFormat[PREVIEW].ulAvailFormats          = i;
        }
        break;
     这个就有根据你camera实际输出数据的格式改写了,对应的宏定义(如DCAM_StreamMode_YV12_640x480_30)在sensorformats.h(位于WINCE600/PLATFORM/SMDKC100/SRC/DRIVERS/CAMERAFILTER/CAMERA_PDD),如果其中没有与你camera输出格式匹配的格式,要自己按照它的格式添加上。
      到此,驱动部分基本完成。调用CameraDshowApp.exe测试应该可以看到图像了。今天先写到这里,应用部分改天再写。

发表于 2014-1-13 13:38:58 | 显示全部楼层
学习了
发表于 2014-2-17 08:10:02 | 显示全部楼层
好论坛,学习的好地方啊!
发表于 2016-12-1 14:01:55 | 显示全部楼层
楼主好厉害!学习了
发表于 2017-4-25 20:41:41 | 显示全部楼层
学习了。。。。。。。。。。。。。。。。。。。。。。。。
高级模式
B Color Image Link Quote Code Smilies @朋友

本版积分规则

在线客服

客服电话

欢迎来电咨询

188-9985 8350

微信关注

手机APP程序:
扫码下载访问

微信公众平台:
摄像头之家公众号

微信小程序:
摄像头小程序

返回顶部

QQ|站点统计|小黑屋|手机版|Archiver|摄像头模组论坛网-摄像头方案网CCM99 ( 粤ICP备18155214号 )

Powered by Discuz! X3.4 Licensed© 2001-2013 Comsenz Inc.