三、调试(sensor终于拿到了)
将sensor接到板子上,开机后,ADB中运行调试程序,preview画面并没有出来,失败,有点小失望,本来觉得可以一气呵成的,但毕竟这是一个全新的sensor,任何一个地方没有想到位做到位都会导致失败。那就找原因吧。
1、首先从trace得知,I2C已经读到了sensor的ID:0x05CA,这可以说明I2C通讯是没有问题的
2、接着检查Sensor的电源配置,测量了供给sensor的3个电源,都是OK的。
3、测量MCLK,这个是提供给sensor使用的,正常(24MHZ)
4、测量PCLK,这个是sensor输出的,正常(58MHZ,高通上限为96MHZ),和寄存器中配置的一致。
5、测量H/V同步信号,这个是sensor输出的,正常。和FPS和分辨率一致。
6、测量数据信号,这个是sensor输出的,正常。(数据信号,示波器上可以看到)
这样看来,sensor已经在正常工作了,但为何preview画面没有出来呢?继续检查高通这边的设定。
从trace看,高通的VFE已经reset并且start了,但一直接没有输出preview数据,这就奇怪了,sensor明明已经输出了,为什么VFE接收后并没有把数据吐出来呢,难道这个sensor输出的数据VFE无法识别?为了验证这个问题,我在另一块板子上测量了OV sensor输出数据的波形,主要是M/P clk、H/V同步信号,然后再拿来对比,不过并没有发现异常,只是H/V同步信号有所不同,主要高低的占空比不太一致,会不会是这样信号的问题呢?为了进一步验证,我同时测量了H/V 信号和数据信号,这时发现OV sensor输出的数据信号是包在V帧同步信号的低电平中;而Samsung 5CA输出的数据信号是包在V帧同步信号的高电平中,会不会是因为V信号极性设置不对导致VFE没有读取到sensor输出的数据呢?重新检查了一下高通VFE的设定,果然有一个参数是用来设定V信号极性的,这个参数默认是Active Low的,我这边并没有去修改它。接着把这个参数修改为Active High,重新build、download后,开机运行,Ok了,preview画面可以正常显示了。到这里为止sensor的硬件调试可以算作完成了,后续的其他功能也可以慢慢完善了。
FSL调试之Camera
fsl的camera hal层没有实现上层到下层的设置参数的接口,所以需要自己实现。好在从应用到hal层的参数已经弄好,否则工作量就更大了。
参数设置在hal层调用的函数是status_t CameraHal::setParameters(const CameraParameters& params)。在这个函数里实现对每个参数的设置。参数设置主要通过 CameraParameters这个类实现的。通过观察这个类发现,里面有个get()函数,可以分别得到各个参数。如
const char *white_balance = params.get(CameraParameters::KEY_WHITE_BALANCE);这个可以得到目前白平衡的参数即返回值。然后根据返回值判断是哪种情况,如
if (strcmp(white_balance, CameraParameters::WHITE_BALANCE_AUTO) == 0) { //判断为自动白平衡
LOGV("white_balance to ioctl is auto !/n");
ctl.id = V4L2_CID_AUTO_WHITE_BALANCE; //自动白平衡命令,ctl为v4l2_control结构,该结构很有用
ctl.value = 1;
if (ioctl(camera_device, VIDIOC_S_CTRL, &ctl) < 0){ //通过 VIDIOC_S_CTRL把ctl结构体传下去
LOGE("set control failed/n");
//return -1;
}
}else if(strcmp(white_balance, CameraParameters::WHITE_BALANCE_INCANDESCENT) == 0){ //白炽灯模式
LOGV("white_balance to ioctl is incandescent !/n");
ctl.id = V4L2_CID_DO_WHITE_BALANCE; //其它白平衡情况都用该命令
ctl.value = 2; //根据用户自己定义的白平衡模式数目排列
if (ioctl(camera_device, VIDIOC_S_CTRL, &ctl) < 0){ //同样通过 VIDIOC_S_CTRL把ctl结构体传下去,然后在根据value值分情况讨论
LOGE("set control failed/n");
//return -1;
}
}
传到驱动的mxc_v4l2_capture.c文件的mxc_v4l_ioctl中,mxc_v4l_ioctl调用mxc_v4l_do_ioctl,mxc_v4l_do_ioctl对命令的解释如下
/*!
* V4l2 VIDIOC_S_CTRL ioctl
*/
case VIDIOC_S_CTRL: {
pr_debug(" case VIDIOC_S_CTRL/n");
retval = mxc_v4l2_s_ctrl(cam, arg);
break;
}
这样就到了mxc_v4l2_s_ctrl。在mxc_v4l2_s_ctrl通过对ctl.id分情况调用
switch (c->id) {
......
case V4L2_CID_AUTO_WHITE_BALANCE:
ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
ret = vidioc_int_s_ctrl(cam->sensor, c); //该函数是v4l2对应ov7670驱动中的s_ctl
ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
break;
case V4L2_CID_DO_WHITE_BALANCE:
ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, true, true);
ret = vidioc_int_s_ctrl(cam->sensor, c);
ipu_csi_enable_mclk_if(CSI_MCLK_I2C, cam->csi, false, false);
break;
......
其中vidioc_int_s_ctrl()是v4l2对应ov7670驱动中的 ioctl_s_ctrl,具体代码怎么对应由于篇幅原因就不贴出来。
根据ctl结构体的id分情况去实现即可。
switch (vc->id) {
.....
case V4L2_CID_AUTO_WHITE_BALANCE:
retval = ov7670_autowhitebalance(vc->value);
break;
case V4L2_CID_DO_WHITE_BALANCE:
retval = ov7670_dowhitebalance(vc->value);
break;
......
下面是whitebalance函数的实现
static int ov7670_autowhitebalance(int value)
{
unsigned char v = 0;
int ret;
printk("0v7670_autowhitebalance called/n");
ret = ov7670_read(ov7670_data.i2c_client, REG_COM8, &v);
if (value)
v |= COM8_AWB; //自动白平衡
msleep(10); /* FIXME */
ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x56);
ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x44);
ret += ov7670_write(ov7670_data.i2c_client, REG_COM8, v);
return ret;
}
static int ov7670_dowhitebalance(int value)
{
unsigned char v = 0;
int ret;
printk("0v7670_dowhitebalance called value:%d/n",value);
ret = ov7670_read(ov7670_data.i2c_client, REG_COM8, &v);
if (value)
v &= ~COM8_AWB; //关闭自动白平衡
msleep(10); /* FIXME */
ret += ov7670_write(ov7670_data.i2c_client, REG_COM8, v);
if(value == 2) //INCANDESCENCE //这个值就是ctl的value值
{
ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x8c);
ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x59);
}else if(value == 3) //FLUORESCENT
{
ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x7e);
ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x49);
}else if(value == 4) //DAYLIGHT
{
ret += ov7670_write(ov7670_data.i2c_client, 0x01, 0x52);
ret += ov7670_write(ov7670_data.i2c_client, 0x02, 0x66);
}
return ret;
}
其中函数中ox01、0x02分别是蓝红通道的增益的寄存器。
上面是白平衡从hal层最终到sensor的参数设置过程。其它如色彩效果、取景模式等都是同样的过程。
取景模式根据具体的情况如夜间模式等设定具体的寄存器即可
色彩效果主要通过设置uv的值实现的