ov5640_mipi.c分析

From:http://blog.csdn.net/yanbixing123?viewmode=contents

ov5640芯片手册中看到这样一句话:

The OV5640 supports both a digital video parallel port and a serial MIPI port.

所以ov5640既支持数字并口视频传输,同样支持mipi接口规范。

摄像头插入到开发板上面的时候,如果有匹配的驱动程序,就会调用到probe函数,先从probe函数来分析。

(一)probe函数

1.1 获取设备ID

probe函数中,首先通过几个of类函数来获取pwn-gpiosrst-gpios等的值。

然后就是设置sensor_data结构体ov5640_data。每个sensor_data结构体都代表一个具体的设备,来看看这个结构体:

[cpp] view plaincopy

  1. struct sensor_data {   
  2.     const struct ov5642_platform_data *platform_data;   
  3.     struct v4l2_int_device *v4l2_int_device;   
  4.     struct i2c_client *i2c_client;   
  5.     struct v4l2_pix_format pix;   
  6.     struct v4l2_captureparm streamcap;   
  7.     bool on;  //设备是否上电  
  8.   
  9.     /* control settings */   
  10.     int brightness;   
  11.     int hue;   
  12.     int contrast;   
  13.     int saturation;   
  14.     int red;   
  15.     int green;   
  16.     int blue;   
  17.     int ae_mode;   
  18.   
  19.     u32 mclk;  //mclk时钟  
  20.     u8 mclk_source;  //mclk时钟源  
  21.     struct clk *sensor_clk;   
  22.     int csi;   
  23.   
  24.     void (*io_init)(void);  //初始化函数  
  25. };  

然后就是填充这个结构体,重点是i2c_client的填充,需要根据填充的这个client来找到对应的设备。那么怎么确定找到的设备就是我们想要的呢?就是通过读设备的设备ID。可以看到在probe函数中通过:

retval= ov5640_read_reg(OV5640_CHIP_ID_HIGH_BYTE, &chip_id_high); 

retval= ov5640_read_reg(OV5640_CHIP_ID_LOW_BYTE, &chip_id_low);

来分别读取ov5640设备ID的高字节和低字节。我们可以看到,在ov5640_mipi.c中是这样定义的:

[cpp] view plaincopy

  1. #define OV5640_CHIP_ID_HIGH_BYTE    0x300A   
  2. #define OV5640_CHIP_ID_LOW_BYTE 0x300B  

我们将这两个地址去ov5640的芯片手册中搜索可以发现,


这两个地址就是ov5640设备的ID所在的地址,通过这个设备ID就能确定我们找到的设备。

1.2 ov5640_power_on函数

ov5640_power_on(dev);

[cpp] view plaincopy

  1. ov5640_power_on(dev);  
  2. static int ov5640_power_on(struct device *dev)   
  3. {   
  4.     int ret = 0;   
  5.   
  6.     io_regulator = devm_regulator_get(dev, "DOVDD");   
  7.     if (!IS_ERR(io_regulator)) {   
  8.         regulator_set_voltage(io_regulator,   
  9.                       OV5640_VOLTAGE_DIGITAL_IO,   
  10.                       OV5640_VOLTAGE_DIGITAL_IO);   
  11.         ret = regulator_enable(io_regulator);   
  12.         if (ret) {   
  13.             pr_err("%s:io set voltage errorn", __func__);   
  14.             return ret;   
  15.         } else {   
  16.             dev_dbg(dev,   
  17.                 "%s:io set voltage okn", __func__);   
  18.         }   
  19.     } else {   
  20.         pr_err("%s: cannot get io voltage errorn", __func__);   
  21.         io_regulator = NULL;   
  22.     }   
  23.   
  24.     core_regulator = devm_regulator_get(dev, "DVDD");   
  25.     if (!IS_ERR(core_regulator)) {   
  26.         regulator_set_voltage(core_regulator,   
  27.                       OV5640_VOLTAGE_DIGITAL_CORE,   
  28.                       OV5640_VOLTAGE_DIGITAL_CORE);   
  29.         ret = regulator_enable(core_regulator);   
  30.         if (ret) {   
  31.             pr_err("%s:core set voltage errorn", __func__);   
  32.             return ret;   
  33.         } else {   
  34.             dev_dbg(dev,   
  35.                 "%s:core set voltage okn", __func__);   
  36.         }   
  37.     } else {   
  38.         core_regulator = NULL;   
  39.         pr_err("%s: cannot get core voltage errorn", __func__);   
  40.     }   
  41.   
  42.     analog_regulator = devm_regulator_get(dev, "AVDD");   
  43.     if (!IS_ERR(analog_regulator)) {   
  44.         regulator_set_voltage(analog_regulator,   
  45.                       OV5640_VOLTAGE_ANALOG,   
  46.                       OV5640_VOLTAGE_ANALOG);   
  47.         ret = regulator_enable(analog_regulator);   
  48.         if (ret) {   
  49.             pr_err("%s:analog set voltage errorn",   
  50.                 __func__);   
  51.             return ret;   
  52.         } else {   
  53.             dev_dbg(dev,   
  54.                 "%s:analog set voltage okn", __func__);   
  55.         }   
  56.     } else {   
  57.         analog_regulator = NULL;   
  58.         pr_err("%s: cannot get analog voltage errorn", __func__);   
  59.     }   
  60.   
  61.     return ret;   
  62. }  

从上面的程序中可以看出来,它设置了三个regulator,中文翻译为”稳定器“,内核中有关于这个的模块的驱动框架,关于这个驱动框架的分析可以查看:

http://www.wowotech.net/pm_subsystem/regulator_framework_overview.html

我们这里只分析与我们相关的东西。

通过regulator_set_voltage函数来为这几个regulator设置电压,分别设置为OV5640_VOLTAGE_DIGITAL_IOOV5640_VOLTAGE_DIGITAL_COREOV5640_VOLTAGE_ANALOG

[cpp] view plaincopy

  1. #define OV5640_VOLTAGE_ANALOG               2800000   
  2. #define OV5640_VOLTAGE_DIGITAL_CORE         1500000   
  3. #define OV5640_VOLTAGE_DIGITAL_IO           1800000  

同时,在dts文件中,定义了它的电压为多少,

[cpp] view plaincopy

  1. ov564x_mipi: ov564x_mipi@3c { /* i2c2 driver */   
  2.     compatible = "ovti,ov564x_mipi";   
  3.     reg = <0x3c>;   
  4.     clocks = <&clks 201>;   
  5.     clock-names = "csi_mclk";   
  6.     <span style="color:#FF0000;">DOVDD-supply = <&vgen4_reg>; /* 1.8v */   
  7.     AVDD-supply = <&vgen3_reg>;  /* 2.8v, rev C board is VGEN3  
  8.                     rev B board is VGEN5 */   
  9.     DVDD-supply = <&vgen2_reg>;  /* 1.5v*/ </span>  
  10.     pwn-gpios = <&gpio1 19 1>;   /* active low: SD1_CLK */   
  11.     rst-gpios = <&gpio1 20 0>;   /* active high: SD1_DAT2 */   
  12.     csi_id = <1>;   
  13.     mclk = <24000000>;   
  14.     mclk_source = <0>;   
  15. };  

可以看出来,它们设置的一致。那么在dts文件中为啥这么设置呢?肯定是根据ov5640的芯片手册中设置的:


1.3 ov5640_reset函数

[cpp] view plaincopy

  1. ov5640_reset();  

这个函数用来重置摄像头,如下所示:

[cpp] view plaincopy

  1. static void ov5640_reset(void)   
  2. {   
  3.     /* camera reset */   
  4.     gpio_set_value(rst_gpio, 1);   
  5.   
  6.     /* camera power dowmn */   
  7.     gpio_set_value(pwn_gpio, 1);   
  8.     msleep(5);   
  9.   
  10.     gpio_set_value(pwn_gpio, 0);   
  11.     msleep(5);   
  12.   
  13.     gpio_set_value(rst_gpio, 0);   
  14.     msleep(1);   
  15.   
  16.     gpio_set_value(rst_gpio, 1);   
  17.     msleep(5);   
  18.   
  19.     gpio_set_value(pwn_gpio, 1);   
  20. }  

这里面的rst_gpiopwn_gpio是在probe函数中通过of类函数获取的,既然是通过of类函数获得的,那么在dts文件中肯定有对应的设置:

[cpp] view plaincopy

  1. ov564x_mipi: ov564x_mipi@3c { /* i2c2 driver */   
  2.     compatible = "ovti,ov564x_mipi";   
  3.     reg = <0x3c>;   
  4.     clocks = <&clks 201>;   
  5.     clock-names = "csi_mclk";   
  6.     DOVDD-supply = <&vgen4_reg>; /* 1.8v */   
  7.     AVDD-supply = <&vgen3_reg>;  /* 2.8v, rev C board is VGEN3  
  8.                     rev B board is VGEN5 */   
  9.     DVDD-supply = <&vgen2_reg>;  /* 1.5v*/   
  10.     <span style="color:#FF0000;">pwn-gpios = <&gpio1 19 1>;   /* active low: SD1_CLK */   
  11.     rst-gpios = <&gpio1 20 0>;   /* active high: SD1_DAT2 */ </span>  
  12.     csi_id = <1>;   
  13.     mclk = <24000000>;   
  14.     mclk_source = <0>;   
  15. };  

这个函数操作的是gpio11920位,关于这两位引脚的意义还需要继续深入。它一共包含3个参数,第一个参数表示gpio1,第二个参数是否表示gpio1的哪一位??第三个参数表示默认值。比如说pwn-gpios的默认值是1,说明置0的时候是上电,置1的时候是关电。

同时,重置摄像头的时候,ov5640_reset()数设置pwn-gpiosrst-gpios写入顺序是否是固定的?

1.3 ov5640_standby函数

[cpp] view plaincopy

  1. ov5640_standby(0);  
  2. static void ov5640_standby(s32 enable)   
  3. {   
  4.     if (enable)   
  5.         gpio_set_value(pwn_gpio, 1);   
  6.     else   
  7.         gpio_set_value(pwn_gpio, 0);  
  8.     msleep(2);   
  9. }  

这个函数就是根据函数的传入参数来向pwn_gpio寄存器写值,向pwn_gpio寄存器写1表示关电,0代表关电。至于向寄存器中写1代表上电还是关电,这个需要查看对应寄存器在dts文件中写进去的值。

1.4

先上电,通过ov5640_read_reg函数来获取摄像头的设备ID以后,再次使用ov5640_standby(1);来关电。

之后就是通过ov5640_int_device.priv= &ov5640_data;来将ov5640_int_device结构体的priv指向设置好的sensor_data结构体,然后通过

retval= v4l2_int_device_register(&ov5640_int_device);来将ov5640_int_device作为一个slave设备注册到v4l2框架中,在这个函数中,会将slave设备添加到int_list链表中,尝试使用v4l2_int_device_try_attach_all函数来匹配master设备。

(二)在probe函数执行完毕以后,就可以操作这个摄像头了,之后我们继续按照mxc_v4l2_capture.c这个应用程序的执行过程来完善ov5640_mipi的一些操作。首先是open函数。

2.1 vidioc_int_g_ifparm函数

open函数中,首先调用vidioc_int_g_ifparm(cam->sensor,&ifparm);函数来从slave设备中获取ifparm的信息,最终会调用到ov5640_mipi.cioctl_g_ifparm函数。先来看open函数中,它传入了两个参数:cam->sensor,ifparm;其实在ov5640_mipi.c中,它并没有从cam->sensor里面获取ifparm的信息来填充到ifparm中,而是在ioctl_g_ifparm函数中直接为ifparm中的各个成员变量赋值。主要是为ifparm.u.bt656成员赋值,包括clock_currmodeclock_minclock_max等等。

2.2 vidioc_int_g_fmt_cap函数

在这个函数中,就直接用f->fmt.pix= sensor->pix;来将sensor_data里面的pix结构体赋给了cam_fmt里面的fmt.pix结构体。

2.3 vidioc_int_s_power函数

[cpp] view plaincopy

  1. vidioc_int_s_power(cam->sensor, 1);  
  2.   
  3. static int ioctl_s_power(struct v4l2_int_device *s, int on)   
  4. {   
  5.     struct sensor_data *sensor = s->priv;   
  6.     if (on && !sensor->on) {   
  7.         if (io_regulator)   
  8.             if (regulator_enable(io_regulator) != 0)   
  9.                 return -EIO;   
  10.         if (core_regulator)   
  11.             if (regulator_enable(core_regulator) != 0)   
  12.                 return -EIO;   
  13.         if (gpo_regulator)   
  14.             if (regulator_enable(gpo_regulator) != 0)   
  15.                 return -EIO;   
  16.         if (analog_regulator)   
  17.             if (regulator_enable(analog_regulator) != 0)   
  18.                 return -EIO;   
  19.         /* Make sure power on */   
  20.         ov5640_standby(0);   
  21.     } else if (!on && sensor->on) {   
  22.         if (analog_regulator)   
  23.             regulator_disable(analog_regulator);   
  24.         if (core_regulator)   
  25.             regulator_disable(core_regulator);   
  26.         if (io_regulator)   
  27.             regulator_disable(io_regulator);   
  28.         if (gpo_regulator)   
  29.             regulator_disable(gpo_regulator);   
  30.         ov5640_standby(1);   
  31.     }   
  32.     sensor->on = on;   
  33.     return 0;   
  34. }  

在这个函数中,会根据vidioc_int_s_power函数传入的第二个参数的值on来决定是否将设备上电。1表示上电,0表示关电。这里面的io_regulatorcore_regulatoranalog_regulator是在ov5640_power_on函数中获取到的,gpo_regulator应该没有设置。然后需要注意的一点是:在ioctl_s_power函数中的第二个参数如果为1的话代表上电,为0的话代表关电。但是在ov5640_standby函数中,如果为0代表上电,为1代表关电。所以,在if(on &&!sensor->on)判断语句中,如果想要确定摄像头是上电的话,需要使用ov5640_standby(0);。这一点比较拗口。

2.4 vidioc_int_dev_init函数

[cpp] view plaincopy

  1. vidioc_int_dev_init(cam->sensor);  
  2.   
  3. static int ioctl_dev_init(struct v4l2_int_device *s)   
  4. {   
  5.     struct sensor_data *sensor = s->priv;   
  6.     u32 tgt_xclk;   /* target xclk */   
  7.     u32 tgt_fps;    /* target frames per secound */   
  8.     int ret;   
  9.     enum ov5640_frame_rate frame_rate;   
  10.     void *mipi_csi2_info;   
  11.   
  12.     ov5640_data.on = true;   
  13.   
  14.     /* mclk */   
  15.     tgt_xclk = ov5640_data.mclk;   
  16.     tgt_xclk = min(tgt_xclk, (u32)OV5640_XCLK_MAX);   
  17.     tgt_xclk = max(tgt_xclk, (u32)OV5640_XCLK_MIN);   
  18.     ov5640_data.mclk = tgt_xclk;   
  19.   
  20.     pr_debug("   Setting mclk to %d MHzn", tgt_xclk / 1000000);   
  21.   
  22.     /* Default camera frame rate is set in probe */   
  23.     tgt_fps = sensor->streamcap.timeperframe.denominator /   
  24.           sensor->streamcap.timeperframe.numerator;   
  25.   
  26.     pr_debug(" tft_fps is %d.n", tgt_fps);   
  27.   
  28.     if (tgt_fps == 15)   
  29.         frame_rate = ov5640_15_fps;   
  30.     else if (tgt_fps == 30)   
  31.         frame_rate = ov5640_30_fps;   
  32.     else   
  33.         return -EINVAL; /* Only support 15fps or 30fps now. */   
  34.   
  35.     mipi_csi2_info = mipi_csi2_get_info();   
  36.   
  37.     /* enable mipi csi2 */   
  38.     if (mipi_csi2_info)   
  39.         mipi_csi2_enable(mipi_csi2_info);   
  40.     else {   
  41.         printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!n",   
  42.                __func__, __FILE__);   
  43.         return -EPERM;   
  44.     }   
  45.   
  46.     ret = ov5640_init_mode(frame_rate, ov5640_mode_INIT, ov5640_mode_INIT);   
  47.   
  48.     return ret;   
  49. }  

2.4.1

在这个函数中,首先通过tgt_xclk= ov5640_data.mclk;来获取到mclk的值。这个ov5640_data.mclk是在probe函数中设置的。然后对tgt_xclk与摄像头允许的最大值最小值进行判断,

[cpp] view plaincopy

  1. #define OV5640_XCLK_MIN 6000000   
  2. #define OV5640_XCLK_MAX 24000000  

使得tgt_xclk位于这个区间内。

然后计算tgt_fps的值,它需要的两个值同样是在probe函数中设置的。

根据tgt_fps的值来设置frame_rate的值,这个frame_rate用在后面的ov5640_init_mode函数中。

然后通过mipi_csi2_get_info函数来获取到mxc_mipi_csi2.c文件中的gmipi_csi2结构体,这个结构体是一个全局变量。获取到这个结构体以后通过mipi_csi2_enable函数来使能。具体操作是设置mipi_csi2_info结构体里面的mipi_en位为true,然后通过clk_prepare_enable函数来使能mipi_csi2_info结构体里面的cfg_clkdphy_clk

之后就是调用ov5640_init_mode函数了。

2.4.2 ov5640_init_mode函数

这个函数在ioctl_s_parm函数和ioctl_dev_init函数中调用,这个函数用来设置ov5640的模式,有以下几种模式:

[cpp] view plaincopy

  1. enum ov5640_mode {   
  2.     ov5640_mode_MIN = 0,   
  3.     ov5640_mode_VGA_640_480 = 0,   
  4.     ov5640_mode_QVGA_320_240 = 1,   
  5.     ov5640_mode_NTSC_720_480 = 2,   
  6.     ov5640_mode_PAL_720_576 = 3,   
  7.     ov5640_mode_720P_1280_720 = 4,   
  8.     ov5640_mode_1080P_1920_1080 = 5,   
  9.     ov5640_mode_QSXGA_2592_1944 = 6,   
  10.     ov5640_mode_QCIF_176_144 = 7,   
  11.     ov5640_mode_XGA_1024_768 = 8,   
  12.     ov5640_mode_MAX = 8,   
  13.     ov5640_mode_INIT = 0xff, /*only for sensor init*/   
  14. };  

mxc_v4l2_capture.c这个应用程序中,可以通过-m选项来指定使用哪种模式,然后将-m后面指定的模式保存在g_capture_mode中,然后通过parm.parm.capture.capturemode= g_capture_mode;再调用ioctl(fd_v4l,VIDIOC_S_PARM, &parm)函数,最终就会调用这个ov5640_init_mode函数来修改ov5640的模式。

ioctl_dev_init函数中已经设置了frame_rate,然后设置ov5640_init_mode函数其他两个参数,第二个参数表示新设置的mode,第三个参数表示原来的mode。但是需要注意的是在mxc_v4l2_capture.c中的mxc_v4l_open函数,它调用ov5640_init_mode来初始化摄像头,这时候,摄像头的初始mode和新mode都没有指定,上面说了,它们是在VIDIOC_S_PARMioctl中指定的,这时候就需要指定ov5640_init_mode的第二个和第三个参数都为ov5640_mode_INIT

[cpp] view plaincopy

  1. static int ov5640_init_mode(enum ov5640_frame_rate frame_rate,   
  2.                 enum ov5640_mode mode, enum ov5640_mode orig_mode)   
  3. {   
  4.     struct reg_value *pModeSetting = NULL;   
  5.     s32 ArySize = 0;   
  6.     int retval = 0;   
  7.     void *mipi_csi2_info;   
  8.     u32 mipi_reg, msec_wait4stable = 0;   
  9.     enum ov5640_downsize_mode dn_mode, orig_dn_mode;   
  10.   
  11.     if ((mode > ov5640_mode_MAX || mode < ov5640_mode_MIN)   
  12.         && (mode != ov5640_mode_INIT)) {   
  13.         pr_err("Wrong ov5640 mode detected!n");   
  14.         return -1;   
  15.     }   

/*mode进行判断,根据上面列举的enumov5640_mode,它就是判断传入的mode是否合法。*/

[cpp] view plaincopy

  1. mipi_csi2_info = mipi_csi2_get_info(); //获取 mipi_csi2_info  
  2.   
  3. /* initial mipi dphy */   
  4. if (!mipi_csi2_info) {   
  5.     printk(KERN_ERR "%s() in %s: Fail to get mipi_csi2_info!n",   
  6.            __func__, __FILE__);   
  7.     return -1;   
  8. }   

/*判断mipi_csi2_info是否获取成功*/

[cpp] view plaincopy

  1. if (!mipi_csi2_get_status(mipi_csi2_info))   
  2.     mipi_csi2_enable(mipi_csi2_info);   
  3.   
  4. if (!mipi_csi2_get_status(mipi_csi2_info)) {   
  5.     pr_err("Can not enable mipi csi2 driver!n");   
  6.     return -1;   
  7. }   

/*首先判断mipi_csi2_infomipi_en是否置位,这一位表示是否使能了mipi摄像头。如果没有使能的话,就调用mipi_csi2_enable函数来使能mipi_csi2_info里面的cfg_clkdphy_clk,然后将mipi_en置位为true。设置好以后继续调用mipi_csi2_get_status函数来确定是否使能成功。*/

[cpp] view plaincopy

  1. mipi_csi2_set_lanes(mipi_csi2_info);   

/*在这个函数中将mipi_csi2_info里面的lanes的值写到MIPI_CSI2_N_LANES寄存器中,


而这个mipi_csi2_info里面的lanes的值是什么时候获取到的呢?在mxc_mipi_csi2.c文件中的mipi_csi2_probe函数中,通过of_property_read_u32函数来保存到mipi_csi2_info中的。

*/

[cpp] view plaincopy

  1. /*Only reset MIPI CSI2 HW at sensor initialize*/   
  2. if (mode == ov5640_mode_INIT)   
  3.     mipi_csi2_reset(mipi_csi2_info);   

/*如果是mode等于ov5640_mode_INIT,就表示是在ioctl_dev_init函数中调用的,就调用mipi_csi2_reset函数来初始化MIPI_CSI2相关的寄存器。这个函数在mxc_mipi_csi2.c文件中定义.

[cpp] view plaincopy

  1. int mipi_csi2_reset(struct mipi_csi2_info *info)   
  2. {   
  3.     _mipi_csi2_lock(info);   
  4.   
  5.     mipi_csi2_write(info, 0x0, MIPI_CSI2_PHY_SHUTDOWNZ);   
  6.     mipi_csi2_write(info, 0x0, MIPI_CSI2_DPHY_RSTZ);   
  7.     mipi_csi2_write(info, 0x0, MIPI_CSI2_CSI2_RESETN);   
  8.   
  9.     mipi_csi2_write(info, 0x00000001, MIPI_CSI2_PHY_TST_CTRL0);   
  10.     mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL1);   
  11.     mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);   
  12.     mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0);   
  13.     mipi_csi2_write(info, 0x00010044, MIPI_CSI2_PHY_TST_CTRL1);   
  14.     mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);   
  15.     mipi_csi2_write(info, 0x00000014, MIPI_CSI2_PHY_TST_CTRL1);   
  16.     mipi_csi2_write(info, 0x00000002, MIPI_CSI2_PHY_TST_CTRL0);   
  17.     mipi_csi2_write(info, 0x00000000, MIPI_CSI2_PHY_TST_CTRL0);   
  18.   
  19.     mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_PHY_SHUTDOWNZ);   
  20.     mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_DPHY_RSTZ);   
  21.     mipi_csi2_write(info, 0xffffffff, MIPI_CSI2_CSI2_RESETN);   
  22.   
  23.     _mipi_csi2_unlock(info);   
  24.   
  25.     return 0;   
  26. }   
  27. EXPORT_SYMBOL(mipi_csi2_reset);  

这个函数中反复向MIPI_CSI2_PHY_TST_CTRL0MIPI_CSI2_PHY_TST_CTRL1寄存器中写不同的值,到底有什么目的??明天来了把值写进去仔细看看。*/

[cpp] view plaincopy

  1. if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_UYVY)   
  2.     mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_YUV422);   
  3. else if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_RGB565)   
  4.     mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_RGB565);   
  5. else   
  6.     pr_err("currently this sensor format can not be supported!n");   

/*根据ov5640_data.pix.pixelformat的值来设置mipi_csi2_info里面的datatype的值。通过mipi_csi2_set_datatype函数来设置。这个ov5640_data.pix.pixelformat的值是在ov5640_probe函数中设置的。从这两个判断语句中可以推断出来,在这个驱动中,指定摄像头只支持V4L2_PIX_FMT_UYVYV4L2_PIX_FMT_RGB565两种格式。*/

/*看下面的代码前,需要对enumov5640_downsize_mode进行一个初步的了解:

[cpp] view plaincopy

  1. /* image size under 1280 * 960 are SUBSAMPLING  
  2.  * image size upper 1280 * 960 are SCALING  
  3.  */   
  4. enum ov5640_downsize_mode {   
  5.     SUBSAMPLING,   
  6.     SCALING,   
  7. };  

这个enum的注释写的很清楚了,当imagesize大于1280* 960时,downsize_modeSCALING,当imagesize小于1280* 960时,downsize_modeSUBSAMPLING。下面的代码主要是根据新modeov5640_downsize_mode类型和初始modeov5640_downsize_mode类型来决定使用哪个函数来切换mode模式。*/

[cpp] view plaincopy

  1. dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;   
  2. orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;   

/*在这里有一个二维数组ov5640_mode_info_data

staticstruct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX +1],再来看这个函数中,它会根据frame_ratemode两个下标来找到对应的元素。关于这个frame_rate,它是enumov5640_frame_rate类型的,

[cpp] view plaincopy

  1. enum ov5640_frame_rate {   
  2.     ov5640_15_fps,   
  3.     ov5640_30_fps   
  4. };  

这两个值默认为01.关于enum的默认值的分析可以看:《C语言enum枚举类型解析

http://blog.csdn.net/skyflying2012/article/details/22736633

*/

[cpp] view plaincopy

  1. if (mode == ov5640_mode_INIT) {   
  2.     pModeSetting = ov5640_init_setting_30fps_VGA;   
  3.     ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);   
  4.   
  5.     ov5640_data.pix.width = 640;   
  6.     ov5640_data.pix.height = 480;   
  7.     retval = ov5640_download_firmware(pModeSetting, ArySize);   
  8.     if (retval < 0)   
  9.         goto err;   
  10.   
  11.     pModeSetting = ov5640_setting_30fps_VGA_640_480;   
  12.     ArySize = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);   
  13.     retval = ov5640_download_firmware(pModeSetting, ArySize);   
  14. }   

/*modeov5640_mode_INIT时,代表第一次初始化摄像头设备,就直接设置pModeSettingArySize的值,然后调用ov5640_download_firmware函数来初始化摄像头。在这个文件中,可以看出来ov5640_init_setting_30fps_VGA数组是一堆寄存器的地址和值的组合,通过这个函数来设置摄像头内部寄存器的值,而不是设置开发板上面的控制寄存器。这些值可以对照ov5640摄像头的芯片手册来查看。但是不理解的是,在这里设置了两次,分别为ov5640_init_setting_30fps_VGAov5640_setting_30fps_VGA_640_480*/

[cpp] view plaincopy

  1. else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||   
  2.             (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {   
  3.         /* change between subsampling and scaling  
  4.          * go through exposure calucation */   
  5.         retval = ov5640_change_mode_exposure_calc(frame_rate, mode);   
  6.     }   

/*因为dn_modeorig_dn_mode都是enumov5640_downsize_mode类型的,它们都只有两种值,所以当两者不同时,就通过ov5640_change_mode_exposure_calc函数来改变摄像头的mode*/

[cpp] view plaincopy

  1. else {   
  2.         /* change inside subsampling or scaling  
  3.          * download firmware directly */   
  4.         retval = ov5640_change_mode_direct(frame_rate, mode);   
  5.     }   

/*dn_modeorig_dn_mode这两者相同时,就直接调用ov5640_change_mode_direct函数改变mode就行了。关于这三个函数的分析,在分析完这个函数后分析。*/

[cpp] view plaincopy

  1. if (retval < 0)   
  2.     goto err;   

/*总之,当调用ov5640_init_mode函数的时候,比如在ioctl_dev_init函数中调用,它就表示第一次使用摄像头,ov5640_init_mode函数的mode模式都为ov5640_mode_INIT,然后就会在ov5640_init_mode函数中调用ov5640_download_firmware函数来设置摄像头上面的寄存器。如果是在VIDIOC_S_PARMioctl调用的时候,这时候,就可能修改ov5640_init_mode函数里面的mode模式,这时候就需要根据mode模式里面的ov5640_downsize_mode来判断是否改变了,如果改变了的话,就会调用ov5640_change_mode_exposure_calc函数来设置摄像头上面的寄存器,如果没有改变的话,就直接调用ov5640_change_mode_direct设置摄像头上面的寄存器即可。*/

[cpp] view plaincopy

  1. OV5640_set_AE_target(AE_Target);   

/*关于这一块的讲解查看ov5640芯片手册的《4.5AEC/AGC algorithms》这一节,主要是设置自动曝光控制(AutoExposure ControlAEC)和自动增益控制(AutoGain ControlAGC)。


函数如下所示:

[cpp] view plaincopy

  1. static int OV5640_set_AE_target(int target)   
  2. {   
  3.     /* stable in high */   
  4.     int fast_high, fast_low;   
  5.     AE_low = target * 23 / 25;  /* 0.92 */   
  6.     AE_high = target * 27 / 25; /* 1.08 */   
  7.   
  8.     fast_high = AE_high<<1;   
  9.     if (fast_high > 255)   
  10.         fast_high = 255;   
  11.    
  12.     fast_low = AE_low >> 1;   
  13.   
  14.     ov5640_write_reg(0x3a0f, AE_high);   
  15.     ov5640_write_reg(0x3a10, AE_low);   
  16.     ov5640_write_reg(0x3a1b, AE_high);   
  17.     ov5640_write_reg(0x3a1e, AE_low);   
  18.     ov5640_write_reg(0x3a11, fast_high);   
  19.     ov5640_write_reg(0x3a1f, fast_low);   
  20.   
  21.     return 0;   
  22. }  

这个计算过程是摄像头相关的算法,在芯片手册中详细介绍了。

ov5640芯片上面的0x3a0f存储的是曝光时间的最大值,0x3a10存储的是曝光时间的最小值。通过这个函数可以看出来它的计算过程。

0x3a1b存储的是图像从稳定状态切换到不稳定状态时曝光时间的最大值,0x3a1e存储的是图像从稳定状态切换到不稳定状态时曝光时间的最小值。

这时候需要理解另外一个寄存器:0x56a1,这个寄存器中保存的是目标图像的亮度平均值,这个寄存器是一个只读寄存器。


0x56a1寄存器的值不在{0x3a1e,0x3a1b}这个区间之内时,AEC就调整它们,并且使他们位于{0x3a10,0x3a0f}这个区间。所以这个{0x3a1e,0x3a1b}这个区间称为稳定状态区间。

0x56a1寄存器的值位于{0x3a1e,0x3a1b}这个区间的时候,就是指图像处于稳定状态。反之,则称为不稳定状态。

上面的讲解是AEC处于auto状态时,AEC就会去自动调节这些参数,同时,这个AEC支持manual模式,关于这个模式的选择,是通过操作0x3503寄存器来完成的,我们在后面的OV5640_turn_on_AE_AG()函数分析中再具体分析。

AEC处于manual模式的时候,还分为normalfast选择。normal就是支持手工一点一点地调节曝光量,fast快速的调节曝光量。究竟有多快速呢。。。{0x3a1f,0x3a11}这个区间是fast情况下的曝光区间,当0x56a1寄存器里面的值小于0x3a1f时,AEC就直接将0x56a1寄存器里面的值乘2;当0x56a1寄存器里面的值大于0x3a11时,AEC就直接将0x56a1寄存器里面的值除以2

*/

[cpp] view plaincopy

  1. OV5640_get_light_freq();   

/*这个函数如下所示,这个函数的目的是获得ov5640的频闪,关于摄像头频闪的讲解,可以查看Camera图像处理原理分析抗噪变焦 频闪 等 

http://blog.csdn.net/colorant/article/details/1913334

[cpp] view plaincopy

  1. static int OV5640_get_light_freq(void)   
  2. {   
  3.     /* get banding filter value */   
  4.     int temp, temp1, light_freq = 0;   
  5.     u8 tmp;   
  6.   
  7.     temp = ov5640_read_reg(0x3c01, &tmp);   
  8.   
  9.     if (temp & 0x80) {   
  10.         /* manual */   
  11.         temp1 = ov5640_read_reg(0x3c00, &tmp);   
  12.         if (temp1 & 0x04) {   
  13.             /* 50Hz */   
  14.             light_freq = 50;   
  15.         } else {   
  16.             /* 60Hz */   
  17.             light_freq = 60;   
  18.         }   
  19.     } else {   
  20.         /* auto */   
  21.         temp1 = ov5640_read_reg(0x3c0c, &tmp);   
  22.         if (temp1 & 0x01) {   
  23.             /* 50Hz */   
  24.             light_freq = 50;   
  25.         } else {   
  26.             /* 60Hz */   
  27. /* 这里是不是一个bug,应该写上:light_freq = 60; */  
  28.         }   
  29.     }   
  30.     return light_freq;   
  31. }  



对比程序和芯片手册,可以看出来,首先会根据0x3c01bit[7]来判断是auto还是manual模式,

如果为auto模式的话,就会去读取0x3c0c寄存器的bit[0],为1的话就是50Hz的频闪,为0的话就是60Hz的频闪。

如果为manual模式的话,就会去读取0x3c00寄存器的bit[2],为1的话就是50Hz的频闪,为0的话就是60Hz的频闪。

*/

[cpp] view plaincopy

  1. OV5640_set_bandingfilter();   

/*这个函数是设置摄像头的工频干扰,CMOS是行曝光,也就是在每行曝光时间决定了画面的亮度,举例:一个50HZ的光源,电压曲线为正弦曲线,那能量曲线定性分析可以认为是取了绝对值的电压曲线。那就是能量做1/100秒的周期变化。那就要求曝光的时间必须是1/100秒的整数倍。如果没有把曝光时间调整到1/100秒的整数倍,就有可能会有每行的曝光值不一样,造成同一个image上有水波纹现象。CCD是整帧同时曝光,所以,工频干扰表现的就是图像有轻微的闪烁。产生的原理与CMOS sensor的原理相似。

[cpp] view plaincopy

  1. static void OV5640_set_bandingfilter(void)   
  2. {   
  3.     int prev_VTS;   
  4.     int band_step60, max_band60, band_step50, max_band50;   
  5.   
  6.     /* read preview PCLK */   
  7.     prev_sysclk = OV5640_get_sysclk();   
  8.     /* read preview HTS */   
  9.     prev_HTS = OV5640_get_HTS();   
  10.   
  11.     /* read preview VTS */   
  12.     prev_VTS = OV5640_get_VTS();   
  13.   
  14.     /* calculate banding filter */   
  15.     /* 60Hz */   
  16.     band_step60 = prev_sysclk * 100/prev_HTS * 100/120;   
  17.     ov5640_write_reg(0x3a0a, (band_step60 >> 8));   
  18.     ov5640_write_reg(0x3a0b, (band_step60 & 0xff));   
  19.   
  20.     max_band60 = (int)((prev_VTS-4)/band_step60);   
  21.     ov5640_write_reg(0x3a0d, max_band60);   
  22.   
  23.     /* 50Hz */   
  24.     band_step50 = prev_sysclk * 100/prev_HTS;   
  25.     ov5640_write_reg(0x3a08, (band_step50 >> 8));   
  26.     ov5640_write_reg(0x3a09, (band_step50 & 0xff));   
  27.   
  28.     max_band50 = (int)((prev_VTS-4)/band_step50);   
  29.     ov5640_write_reg(0x3a0e, max_band50);   
  30. }   

关于这个函数,它首先通过OV5640_get_sysclk函数来获取系统的时钟,然后OV5640_get_HTSOV5640_get_VTS函数分别获取ov5640Horizontaltotalsize verticaltotal size,对于50Hz60Hz,有不同的计算方式,但是这个计算方法看半天都没有理解。。。

*/

[cpp] view plaincopy

  1. ov5640_set_virtual_channel(ov5640_data.csi);   

/*这个函数设置虚拟通道,如下所示:

[cpp] view plaincopy

  1. static void ov5640_set_virtual_channel(int channel)   
  2. {   
  3.     u8 channel_id;   
  4.   
  5.     ov5640_read_reg(0x4814, &channel_id);   
  6.     channel_id &= ~(3 << 6);   
  7.     ov5640_write_reg(0x4814, channel_id | (channel << 6));   
  8. }  

但是在ov5640的芯片手册中,这几位显示的是DEBUGMODE,如下所示


*/

[cpp] view plaincopy

  1. /* add delay to wait for sensor stable */   
  2. if (mode == ov5640_mode_QSXGA_2592_1944) {   
  3.     /* dump the first two frames: 1/7.5*2  
  4.      * the frame rate of QSXGA is 7.5fps */   
  5.     msec_wait4stable = 267;   
  6. else if (frame_rate == ov5640_15_fps) {   
  7.     /* dump the first nine frames: 1/15*9 */   
  8.     msec_wait4stable = 600;   
  9. else if (frame_rate == ov5640_30_fps) {   
  10.     /* dump the first nine frames: 1/30*9 */   
  11.     msec_wait4stable = 300;   
  12. }   
  13. msleep(msec_wait4stable);   

/*根据不同的模式来选择等待sensor稳定的时间。*/

[cpp] view plaincopy

  1. if (mipi_csi2_info) {   
  2.     unsigned int i;   
  3.   
  4.     i = 0;   
  5.   
  6.     /* wait for mipi sensor ready */   
  7.     mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info);   
  8.     while ((mipi_reg == 0x200) && (i < 10)) {   
  9.         mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info);   
  10.         i++;   
  11.         msleep(10);   
  12.     }   
  13.   
  14.     if (i >= 10) {   
  15.         pr_err("mipi csi2 can not receive sensor clk!n");   
  16.         return -1;   
  17.     }   

/* mipi_csi2_dphy_status函数就是去读取MIPI_CSI2_PHY_STATE寄存器的值,然后保存在mipi_reg变量中。这个寄存器如下所示:




可以看出来,这个寄存器的bit[9]表示sensorclocklane处于什么样的状态。注意:这一位是activelow,所以置0时表示使能。再来看这一段代码,它会一直等待这一位置0,一直等待10×10ms,如果过了这一段时间,MIPI_CSI2_PHY_STATE寄存器的bit[9]还保持为1的状态时,就打印出“mipicsi2 can not receive sensor clk!”这句话报错。

*/

[cpp] view plaincopy

  1.     i = 0;   
  2.   
  3.     /* wait for mipi stable */   
  4.     mipi_reg = mipi_csi2_get_error1(mipi_csi2_info);   
  5.     while ((mipi_reg != 0x0) && (i < 10)) {   
  6.         mipi_reg = mipi_csi2_get_error1(mipi_csi2_info);   
  7.         i++;   
  8.         msleep(10);   
  9.     }   
  10.   
  11.     if (i >= 10) {   
  12.         pr_err("mipi csi2 can not reveive data correctly!n");   
  13.         return -1;   
  14.     }   
  15. }   
  16. rr:   
  17. return retval;   

/*这一段代码就是通过mipi_csi2_get_error1函数来读取MIPI_CSI2_ERR1寄存器的值。这个寄存器表示MIPI控制寄存器中是否有错误发生,如果都没有错误的话,这个寄存器里面的值都应该是0.关于这个寄存器中每一位的含义就不分析了。*/

至此,ov5640_init_mode函数就大致分析完毕了,也就代表ioctl_dev_init函数分析完毕。这个ioctl_dev_init函数是mxc_v4l2_open函数中的最后一个函数。

对于应用程序中调用的其他ioctl函数,大致过程都是相似的,不会涉及到芯片上面寄存器的设置,大部分都是与ov5640_probe函数中sensor_data结构体相关,就不再分析他们了。

下面分析ov5640_init_mode函数中没有分析的三个函数:

[cpp] view plaincopy

  1. ov5640_download_firmware  
  2. ov5640_change_mode_exposure_calc  
  3. ov5640_change_mode_direct  

这三个函数是关于芯片设置最重要的函数。在ov5640_init_mode函数中,会根据第二个参数和第三个参数的不同来选择执行哪一路过程。当mode== ov5640_mode_INIT时,会直接执行ov5640_download_firmware函数来设置ov5640摄像头。

先来看ov5640_download_firmware函数:

[cpp] view plaincopy

  1. /* download ov5640 settings to sensor through i2c */   
  2. static int ov5640_download_firmware(struct reg_value *pModeSetting, s32 ArySize)   
  3. {   
  4.     register u32 Delay_ms = 0;   
  5.     register u16 RegAddr = 0;   
  6.     register u8 Mask = 0;   
  7.     register u8 Val = 0;   
  8.     u8 RegVal = 0;   
  9.     int i, retval = 0;   
  10.   
  11.     for (i = 0; i < ArySize; ++i, ++pModeSetting) {   
  12.         Delay_ms = pModeSetting->u32Delay_ms;   
  13.         RegAddr = pModeSetting->u16RegAddr;   
  14.         Val = pModeSetting->u8Val;   
  15.         Mask = pModeSetting->u8Mask;   
  16.   
  17.         if (Mask) {   
  18.             retval = ov5640_read_reg(RegAddr, &RegVal);   
  19.             if (retval < 0)   
  20.                 goto err;   
  21.   
  22.             RegVal &= ~(u8)Mask;   
  23.             Val &= Mask;   
  24.             Val |= RegVal;   
  25.         }   
  26.   
  27.         retval = ov5640_write_reg(RegAddr, Val);   
  28.         if (retval < 0)   
  29.             goto err;   
  30.   
  31.         if (Delay_ms)   
  32.             msleep(Delay_ms);   
  33.     }   
  34. err:   
  35.     return retval;   
  36. }  

先看这个函数的注释:通过I2C总线下载设置到ov5640摄像头中。意思就是我们在驱动中写好ov5640摄像头上面寄存器的配置以后,通过这个函数设置进去。

再来看这个函数,这个函数设置的核心是reg_value结构体,这个结构体如下所示:

[cpp] view plaincopy

  1. struct reg_value {   
  2.     u16 u16RegAddr;   
  3.     u8 u8Val;   
  4.     u8 u8Mask;   
  5.     u32 u32Delay_ms;   
  6. };  

它就包含了所要设置一个寄存器所需要的所有元素:地址,值,掩码,延迟时间。以ov5640_init_setting_30fps_VGA[]为例:

[cpp] view plaincopy

  1. static struct reg_value ov5640_init_setting_30fps_VGA[] = {   
  2.     {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0},   
  3.     {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0},   
  4.     {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0},  
  5. 。。。。。。。。。。。。。。。。。。  
  6. };  

通过这个函数,就能够把这个结构体数组里面的值都设置到ov5640中去。而这个结构体数组中寄存器的值怎么来的就是关键了,理论上可以参考芯片手册一位一位地设置,但是厂家应该会提供这个初始化数组的。

下面来看第二个函数ov5640_change_mode_exposure_calc

[cpp] view plaincopy

  1. /* sensor changes between scaling and subsampling  
  2.  * go through exposure calcualtion  
  3.  */  
  4. static int ov5640_change_mode_exposure_calc(enum ov5640_frame_rate frame_rate,   
  5.                 enum ov5640_mode mode)   
  6. {   
  7.     struct reg_value *pModeSetting = NULL;   
  8.     s32 ArySize = 0;   
  9.     u8 average;   
  10.     int prev_shutter, prev_gain16;   
  11.     int cap_shutter, cap_gain16;   
  12.     int cap_sysclk, cap_HTS, cap_VTS;   
  13.     int light_freq, cap_bandfilt, cap_maxband;   
  14.     long cap_gain16_shutter;   
  15.     int retval = 0;   
  16.   
  17.     /* check if the input mode and frame rate is valid */   
  18.     pModeSetting =   
  19.         ov5640_mode_info_data[frame_rate][mode].init_data_ptr;   
  20.     ArySize =   
  21.         ov5640_mode_info_data[frame_rate][mode].init_data_size;   
  22.   
  23.     ov5640_data.pix.width =   
  24.         ov5640_mode_info_data[frame_rate][mode].width;   
  25.     ov5640_data.pix.height =   
  26.         ov5640_mode_info_data[frame_rate][mode].height;   
  27.   
  28.     if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||   
  29.         pModeSetting == NULL || ArySize == 0)   
  30.         return -EINVAL;   
  31.   
  32.     /* auto focus */   
  33.     /* OV5640_auto_focus();//if no af function, just skip it */   
  34.   
  35.     /* turn off AE/AG */   
  36.     OV5640_turn_on_AE_AG(0);   

/*这个函数的意思是根据传入的参数的值来决定打开还是关闭autoAE/AG。关于这个的寄存器地址是0x3503,可以看出来,想要关闭auto模式的话,只需要将bit[1:0]设置为0x03即可,打开auto的话,将bit[1:0]清空即可。OV5640_turn_on_AE_AG这个函数正是这么做的。


[cpp] view plaincopy

  1. static void OV5640_turn_on_AE_AG(int enable)   
  2. {   
  3.     u8 ae_ag_ctrl;   
  4.   
  5.     ov5640_read_reg(0x3503, &ae_ag_ctrl);   
  6.     if (enable) {   
  7.         /* turn on auto AE/AG */   
  8.         ae_ag_ctrl = ae_ag_ctrl & ~(0x03);   
  9.     } else {   
  10.         /* turn off AE/AG */   
  11.         ae_ag_ctrl = ae_ag_ctrl | 0x03;   
  12.     }   
  13.     ov5640_write_reg(0x3503, ae_ag_ctrl);   
  14. }  

*/


[cpp] view plaincopy

  1. /* read preview shutter */   
  2. prev_shutter = OV5640_get_shutter();   
  3. if ((binning_on()) && (mode != ov5640_mode_720P_1280_720)   
  4.         && (mode != ov5640_mode_1080P_1920_1080))   
  5.     prev_shutter *= 2;   

/*这个OV5640_get_shutter()函数就是去读取ov5640芯片上面的0x3500~0x3502地址里面的值,然后根据图示构造出Exposure的值,返回保存到prev_shutter中。在这里,shutter就是曝光时间的意思。


在这里有一个函数:binning_on()

[cpp] view plaincopy

  1. static bool binning_on(void)   
  2. {   
  3.     u8 temp;   
  4.     ov5640_read_reg(0x3821, &temp);   
  5.     temp &= 0xfe;   
  6.     if (temp)   
  7.         return true;   
  8.     else   
  9.         return false;   
  10. }  

关于binning的概念和理,可以查看《sensorskippingand binning 模式

http://blog.csdn.net/sloan6/article/details/8242713

ov5640的芯片手册中,可以看到,与这个概念有关的寄存器位于0x3821bit[0],但是个人感觉这个函数写的有问题,只涉及0x3821bit[0]位,如果只打算比较bit[0]而不改变其他位的话,应该:

if(temp & 0x01),根本不需要temp&= 0xfe操作,经过这一个操作的话,假如其他位有不为0的,那么这个temp的值就不为0.


*/

[cpp] view plaincopy

  1. /* read preview gain */   
  2. prev_gain16 = OV5640_get_gain16();   

/*看看OV5640_get_gain16()这个函数:

[cpp] view plaincopy

  1. static int OV5640_get_gain16(void)   
  2. {   
  3.      /* read gain, 16 = 1x */   
  4.     int gain16;   
  5.     u8 temp;   
  6.   
  7.     gain16 = ov5640_read_reg(0x350a, &temp) & 0x03;   
  8.     gain16 = (gain16<<8) + ov5640_read_reg(0x350b, &temp);   
  9.   
  10.     return gain16;   
  11. }  

从这个函数中很显然就能猜出来这个previewgain保存在0x350a0x350b这两个寄存器中,看看芯片手册:



最终这个函数读取这两个寄存器的值,然后保存在prev_gain16变量中。

*/

[cpp] view plaincopy

  1. /* get average */   
  2. ov5640_read_reg(0x56a1, &average);   

/*这里直接使用ov5640_read_reg函数来读取0x56a1的值保存在&average中:


*/

[cpp] view plaincopy

  1. /* turn off night mode for capture */   
  2. OV5640_set_night_mode();   

/*这个函数如下所示,这个函数与上面的binning_on()函数做比较的话,这个函数的设置是正确的,因为这个函数只想设置0x3a00bit[2]0,在不改变其他位的基础上面,通过mode&= 0xfb的形式是最好的。

[cpp] view plaincopy

  1. static void OV5640_set_night_mode(void)   
  2. {   
  3.      /* read HTS from register settings */   
  4.     u8 mode;   
  5.   
  6.     ov5640_read_reg(0x3a00, &mode);   
  7.     mode &= 0xfb;   
  8.     ov5640_write_reg(0x3a00, mode);   
  9. }  



*/

[cpp] view plaincopy

  1. /* turn off overlay */   
  2. /* ov5640_write_reg(0x3022, 0x06);//if no af function, just skip it */   
  3.   
  4. OV5640_stream_off();   
  5.   
  6. /* Write capture setting */   
  7. retval = ov5640_download_firmware(pModeSetting, ArySize);   
  8. if (retval < 0)   
  9.     goto err;   

/*最终这个函数里面也是调用ov5640_download_firmware函数来将从ov5640_mode_info_data数组中获取到的ov5640中寄存器的值写到对应的寄存器中。*/

[cpp] view plaincopy

  1. /* read capture VTS */   
  2. cap_VTS = OV5640_get_VTS();   
  3. cap_HTS = OV5640_get_HTS();   
  4. cap_sysclk = OV5640_get_sysclk();   

/*先来看前两个函数,读取寄存器的值来获取verticalzisehorizontalsize的值。寄存器如下所示:


这个OV5640_get_sysclk()函数有点复杂,以后再分析。

*/


[cpp] view plaincopy

  1. /* calculate capture banding filter */   
  2. light_freq = OV5640_get_light_freq();   
  3. if (light_freq == 60) {   
  4.     /* 60Hz */   
  5.     cap_bandfilt = cap_sysclk * 100 / cap_HTS * 100 / 120;   
  6. else {   
  7.     /* 50Hz */   
  8.     cap_bandfilt = cap_sysclk * 100 / cap_HTS;   
  9. }   
  10. cap_maxband = (int)((cap_VTS – 4)/cap_bandfilt);   
  11.   
  12. /* calculate capture shutter/gain16 */   
  13. if (average > AE_low && average < AE_high) {   
  14.     /* in stable range */   
  15.     cap_gain16_shutter =   
  16.       prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk   
  17.       * prev_HTS/cap_HTS * AE_Target / average;   
  18. else {   
  19.     cap_gain16_shutter =   
  20.       prev_gain16 * prev_shutter * cap_sysclk/prev_sysclk   
  21.       * prev_HTS/cap_HTS;   
  22. }   
  23.   
  24. /* gain to shutter */   
  25. if (cap_gain16_shutter < (cap_bandfilt * 16)) {   
  26.     /* shutter < 1/100 */   
  27.     cap_shutter = cap_gain16_shutter/16;   
  28.     if (cap_shutter < 1)   
  29.         cap_shutter = 1;   
  30.   
  31.     cap_gain16 = cap_gain16_shutter/cap_shutter;   
  32.     if (cap_gain16 < 16)   
  33.         cap_gain16 = 16;   
  34. else {   
  35.     if (cap_gain16_shutter >   
  36.             (cap_bandfilt * cap_maxband * 16)) {   
  37.         /* exposure reach max */   
  38.         cap_shutter = cap_bandfilt * cap_maxband;   
  39.         cap_gain16 = cap_gain16_shutter / cap_shutter;   
  40.     } else {   
  41.         /* 1/100 < (cap_shutter = n/100) =< max */   
  42.         cap_shutter =   
  43.           ((int) (cap_gain16_shutter/16 / cap_bandfilt))   
  44.           *cap_bandfilt;   
  45.         cap_gain16 = cap_gain16_shutter / cap_shutter;   
  46.     }   
  47. }   
  48.   
  49. /* write capture gain */   
  50. OV5640_set_gain16(cap_gain16);   
  51.   
  52. /* write capture shutter */   
  53. if (cap_shutter > (cap_VTS – 4)) {   
  54.     cap_VTS = cap_shutter + 4;   
  55.     OV5640_set_VTS(cap_VTS);   
  56. }   
  57. OV5640_set_shutter(cap_shutter);   
  58.   
  59. OV5640_stream_on();   
  60.   
  61. rr:   
  62. return retval;   

/*后面这些计算应该都是摄像头中定义的,有点看不懂,以后再分析吧*/

最后看看ov5640_change_mode_direct函数:

[cpp] view plaincopy

  1. static int ov5640_change_mode_direct(enum ov5640_frame_rate frame_rate,   
  2.                 enum ov5640_mode mode)   
  3. {   
  4.     struct reg_value *pModeSetting = NULL;   
  5.     s32 ArySize = 0;   
  6.     int retval = 0;   
  7.   
  8.     /* check if the input mode and frame rate is valid */   
  9.     pModeSetting =   
  10.         ov5640_mode_info_data[frame_rate][mode].init_data_ptr;   
  11.     ArySize =   
  12.         ov5640_mode_info_data[frame_rate][mode].init_data_size;   
  13.   
  14.     ov5640_data.pix.width =   
  15.         ov5640_mode_info_data[frame_rate][mode].width;   
  16.     ov5640_data.pix.height =   
  17.         ov5640_mode_info_data[frame_rate][mode].height;   
  18.   
  19.     if (ov5640_data.pix.width == 0 || ov5640_data.pix.height == 0 ||   
  20.         pModeSetting == NULL || ArySize == 0)   
  21.         return -EINVAL;   
  22.   
  23.     /* turn off AE/AG */   
  24.     OV5640_turn_on_AE_AG(0);   
  25.   
  26.     OV5640_stream_off();   
  27.   
  28.     /* Write capture setting */   
  29.     retval = ov5640_download_firmware(pModeSetting, ArySize);   
  30.     if (retval < 0)   
  31.         goto err;   
  32.   
  33.     OV5640_stream_on();   
  34.   
  35.     OV5640_turn_on_AE_AG(1);   
  36.   
  37. err:   
  38.     return retval;   
  39. }  

经过上一个函数的分析,这个函数看起来就相当简单了。

3.OV5640_get_sysclk 函数分析

[cpp] view plaincopy

  1. static int OV5640_get_sysclk(void)   
  2. {   
  3.      /* calculate sysclk */   
  4.     int xvclk = ov5640_data.mclk / 10000;   
  5.     int temp1, temp2;   
  6.     int Multiplier, PreDiv, VCO, SysDiv, Pll_rdiv;   
  7.     int Bit_div2x = 1, sclk_rdiv, sysclk;   
  8.     u8 temp;   

/*ov5640_data.mclk的值是在ov5640_probe函数中同dts文件中读取的,为24000000.*/

[cpp] view plaincopy

  1. int sclk_rdiv_map[] = {1, 2, 4, 8};   
  2.   
  3. temp1 = ov5640_read_reg(0x3034, &temp);   
  4. temp2 = temp1 & 0x0f;   
  5. if (temp2 == 8 || temp2 == 10)   
  6.     Bit_div2x = temp2 / 2;   

/*读取0x3034寄存器的值,取低4位除以2后保存在Bit_div2x中。从芯片手册中可以看出来,0x3034的低4位是MIPIbit mode,那么为什么还要除以2呢?在《ov5640_PLL_diagram.jpg》图片的左下角有这样一句话:note6MIPISCLK= (4 or 5) * PCLK if 2 lanes;= (8 or 10) * PCLK if 1lane。再看Bit_div2xPLLdiagram中对应的模块是BITdivider,而现在使用的是2lanes模式,所以需要除以2.


*/

[cpp] view plaincopy

  1. temp1 = ov5640_read_reg(0x3035, &temp);   
  2. SysDiv = temp1>>4;   
  3. if (SysDiv == 0)   
  4.     SysDiv = 16;   

/*读取0x3035寄存器的高4位保存在SysDiv中,看芯片手册中的解释是系统时钟分频器。在《ov5640_PLL_diagram.jpg》图片中对应SYSdivider0模块。


*/

[cpp] view plaincopy

  1. temp1 = ov5640_read_reg(0x3036, &temp);   
  2. Multiplier = temp1;   

/*读取0x3036寄存器的值保存在Multiplier中,看芯片手册中的解释是PLL倍频器。在《ov5640_PLL_diagram.jpg》图片中对应multiplier模块。


*/

[cpp] view plaincopy

  1. temp1 = ov5640_read_reg(0x3037, &temp);   
  2. PreDiv = temp1 & 0x0f;   
  3. Pll_rdiv = ((temp1 >> 4) & 0x01) + 1;   

/*读取0x3037寄存器的低4位保存在PreDiv中,读取0x3037寄存器的bit[4]保存在Pll_rdiv中。


PreDiv在《ov5640_PLL_diagram.jpg》图片中对应pre-divider模块,Pll_rdiv在《ov5640_PLL_diagram.jpg》图片中对应PLLR divider模块,因为上图中的解释是:如果bit[4]1的话,就分成2份,所以在代码中将从bit[4]中读取出来的值加1*/

[cpp] view plaincopy

  1. temp1 = ov5640_read_reg(0x3108, &temp);   
  2. temp2 = temp1 & 0x03;   
  3. sclk_rdiv = sclk_rdiv_map[temp2];   

/*读取0x3108寄存器的低2位保存在temp2中,然后将这个temp2作为下标来从这个数组里面取值:

intsclk_rdiv_map[] = {1, 2, 4, 8};

为什么要从这个数组里面取值呢?看芯片手册中,SCLKpll_clk1/2,1/4, 1/8。没法用两位来表示8,所以选择这种方式来选取值。


*/

[cpp] view plaincopy

  1. VCO = xvclk * Multiplier / PreDiv;   

/*这个VCO压控振荡VoltageControlledOscillatorPLL锁相环)的组成部分,在PLL中,一般是先分频,然后再经过VCO增频,这里先计算的是VCO的输出。在ov5640_PLL_diagram.jpg》中就是PLL1后的输出。*/

[html] view plaincopy

  1. sysclk = VCO / SysDiv / Pll_rdiv * 2 / Bit_div2x / sclk_rdiv;   

/*剩下的就是计算过程,根据图《ov5640_PLL_diagram.jpg》就可以写出这个计算过程。*/

[cpp] view plaincopy

  1. return sysclk;   

ov5640_PLL_diagram.jpg》是我们网上看到的一个有关ov5640的时钟控制图,我下载的芯片手册都没有这个图,所以一直对这个函数不理解,通过这个图,这个函数的设置过程就很清楚了,《ov5640_PLL_diagram.jpg》图片如下所示:


经过上面的步骤就可以得出OV5640的系统时钟参数。



参考:

CMOSSensor的调试经验分享

http://blog.csdn.net/yapingmcu/article/details/37817727


Published by

风君子

独自遨游何稽首 揭天掀地慰生平