基于avd7181c解决视频输入效果差的问题<二>---疑难调试手记
笔者在上一篇文章中讲述了基于AVD7181C来解决平台视频输入效果差的问题大概解决方案。在这个探索研究中,也遇到了一些比较麻烦的问题。下面就讲述一些遇到的几个让人欢喜让人忧的问题。
Avd7181c是通过IIC来控制的。因为iic也是一种非常成熟的通讯接口,我们就直接用全志平台里整理过的iic操作sensor_read, sensor_write接口,这两个接口在平台支持的camera有很多种型号,都是正常使用的,笔者利用者这些接口也移植到了其他几个iic接口的外设中。
/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/sundesheng125原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
刚开始,在驱动架构搭建好以后,加载AVD7181C驱动后,再加载csi的驱动,就会执行到AVD7181C probe函数,可以得到iic的client,得到adapter,也就是linux iic驱动中对象的总管。笔者调试时,希望在probe函数里大概操作一下AVD7181C的寄存器,也就把ypbpr设置的一组寄存器操作下去。结果显示,这一组寄存器全部都可以正常写进去,这样写寄存器就没有问题。但是,当笔者去读寄存器的值的时候,发现总是读不出来,iic总是返回错误,据错误码分析就iic发出读数据指令后,AVD7181C没有回应数据,但是是收到了接收iic地址的ack。因为用的AVD7181C小模块板也是新做的,我们又检查一下iic的配置以及其他reset、standby两个供电的正常性。
一步步排除,首先笔者移植了一个已经成熟使用的在其他平台是小模块,因为数据接口的一些不同,直接使用它调试不方便,但是为了验证iic驱动的问题,就飞了两个供电以及iic。问题还是一样,错误信息如下:
[ 44.891141] fuction=sensor_read, client->addr=0x21 [ 44.905885] incomplete xfer (0x48) [ 44.905894] [CSI_ERR][7181C]Error -70 on register read [ 44.905903] [CSI_ERR][7181C]err at sensor_read! i = 0, ret=-70,regs.value = -431473217 [ 44.934190] fuction=sensor_read, client->addr=0x21 [ 44.939380] incomplete xfer (0x48) [ 44.942838] [CSI_ERR][7181C]Error -70 on register read [ 44.948002] [CSI_ERR][7181C]err at sensor_read! i = 1, ret=-70,regs.value = -431473217
面对这种情况,非常的奇怪,wince的平台、AVD7181C的demo板子通过pc软件都可以正常操作AVD7181C的寄出去,读写正常。凭着经验判断,这肯定是iic通讯出了问题。可是移植的sensor_read接口也是很成熟,那么多camera都是使用那个接口,都可以正常的读,怎么会有问题呢?带着疑问,跟几个同事聊起这件奇怪的事情,刚好有个同事说以前在在小系统调试过AVD7181C的时候,碰到过类似的问题,就是iic在读的兼容性的问题。面对高手的指路,得一步步证实、验证。
笔者借用了逻辑分析仪来抓通讯的波形,抓到不能正常读的iic时序图,如下:
笔者另外又抓了一个用AVD7181C demo板能正常读它寄存器的iic时序图,如下:
面对这两幅有力的证据,下一步就是找解决办法了。很明显,全志写的sensor_read,很明显是一个读动作,是先发送iic地址后,送出寄存器的地址,然后就是一个stop的命令,接着再重启,发送iic地址,收到ack后,对方并没有发送应该送的数据。显示,就是没对上眼,对方没有理解主机发送的秋波。sensor_read的源代码如下:
static int sensor_read(struct v4l2_subdev *sd, unsigned char *reg, unsigned char *value) { struct i2c_client *client = v4l2_get_subdevdata(sd); u8 data[REG_STEP]; struct i2c_msg msg; int ret,i; for(i = 0; i < REG_ADDR_STEP; i++) data[i] = reg[i]; data[REG_ADDR_STEP] = 0xff; /* * Send out the register address... */ msg.addr = client->addr; msg.flags = 0; msg.len = REG_ADDR_STEP; msg.buf = data; ret = i2c_transfer(client->adapter, &msg, 1); if (ret < 0) { csi_dev_err("Error %d on register write\n", ret); return ret; } /* * ...then read back the result. */ msg.flags = I2C_M_RD; msg.len = REG_DATA_STEP; msg.buf = &data[REG_ADDR_STEP]; ret = i2c_transfer(client->adapter, &msg, 1); if (ret >= 0) { for(i = 0; i < REG_DATA_STEP; i++) value[i] = data[i+REG_ADDR_STEP]; ret = 0; } else { csi_dev_err("Error %d on register read\n", ret); } return ret; }
接着分析比较能够正常读的时序图,发现在是先发送iic地址后,送出寄存器的地址后,没有发送停止信号,而是直接重新启动,发送iic地址,收到AVD7181C的ack后,很快就收到了数据,然后才是整个通讯的停止信号。
为了解决这个问题,当然得按照能demo板的时序处理来改写sensor_read。改写后的程序大概如下:
static int sensor_read(struct v4l2_subdev *sd, unsigned char *reg, unsigned char *value) { struct i2c_client *client = v4l2_get_subdevdata(sd); u8 data[REG_STEP]; struct i2c_msg msg[2]; int ret,i; for(i = 0; i < REG_ADDR_STEP; i++) data[i] = reg[i]; data[REG_ADDR_STEP] = 0x0; /* * Send out the register address... */ msg[0].addr = gi2c_addr;//client->addr; msg[0].flags = 0x0; msg[0].len = REG_ADDR_STEP; msg[0].buf = data; msg[1].addr = gi2c_addr;//client->addr; msg[1].flags = I2C_M_RD; msg[1].len = REG_DATA_STEP; msg[1].buf = &data[REG_ADDR_STEP]; ret = i2c_transfer(client->adapter, msg, 2); if (ret < 0) { avd7181c_dev_err("Error %d on register write\n", ret); return ret; } for(i = 0; i < REG_DATA_STEP; i++) value[i] = data[i+REG_ADDR_STEP]; ret = 0; return ret; }
改写后,驱动读寄出去就正常了,说明AVD7181C iic读寄存器兼容性有问题,也确实不常见,现在多数的iic设备,两种sensor_read的处理方式都是可以支持的,AVD7181C就只正常比较老的那种也没什么别的办法。吃一堑,长一智,困难越来越多,疲劳越多,白头发也会越多,当然收获也是最多,也会更有价值。