Wince6.0 下Camera(OV3640) 驱动开发笔记

来源:互联网 发布:江西网络行政学院 编辑:程序博客网 时间:2024/04/18 23:49

Wince6.0下Camera(OV3640) 驱动开发笔记1——基于S5PC100

    由于项目需要开始搞S5PC100的摄像头驱动,camera类型是OV3640.以前还没有做摄像头驱动,缺乏相关的经验,另外基于S5PC100的摄像头驱动代码结构又比较复杂,谁让A8的功能强大呢,功能多了强了开发起来难度自然就增大了。总之,目前的感觉是——好复杂!

   既然项目需要,那么容易要上,有困难也要上,慢慢啃吧。

   先说一下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

[cpp:collapse]+ expand sourceview plaincopyprint?

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

[cpp:collapse]+ expand sourceview plaincopyprint?

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硬件调试

     我采用的是camera300万像素的OV3640,处理器为S5PC100,接口电路如下图所示:

 

       第一步要干什么呢,当然是要先确保硬件没有了问题了。从上图可以看出OV3640IIC接口的,也就是它的初始化和各种功能控制都是通过IIC接口写入对应的命令来实现的,因此首要任务是确保IIC驱动能正确读写。我刚开始就卡在这里,三星给的BSP里的IIC1驱动有BUG,我的camera就是连到IIC1了。IIC1驱动的读和写都有问题,而且板子上没有类似EEPRAM等具有IIC接口可读写的芯片,所以验证IIC1成了一个问题。因此在调试IIC1驱动阶段耗费了不少时间。

       怎么确定你的IIC已经能正常工作了?通过读OV3640IDID能正确读出了就说明你CAMAERA的电源接口没有问题,IIC没有问题。在此要提醒一点,OV3640PWDN在正常工作模式要拉低,否则不能正常工作,从上面的原理图可以看出我搞错了,后来只能跳线解决。

接下来需要验证的是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主要分为两大部分CAMcamdriver,camdevice)与PINpindriver,pindevice,其中camdriverpindriver是流接口;camdevicepindevicePDD层函数的封装。具体的函数调用顺序为:

 

   .实现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测试应该可以看到图像了。今天先写到这里,应用部分改天再写。

 

 

原创粉丝点击