以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
一、sensor_write_register函数的解析
在第4季4:图像sensor的驱动源码解析中写到,sensor_register_callback函数的调用关系如下:
sensor_register_callback //位于ar0130_cmos.c文件文件
cmos_init_sensor_exp_function //位于ar0130_cmos.c文件文件
sensor_init //位于ar0130_sensor_ctl.c文件
sensor_init_720p_30fps //位于ar0130_sensor_ctl.c文件
sensor_write_register //位于ar0130_sensor_ctl.c文件
sensor_i2c_init //位于ar0130_sensor_ctl.c文件,只执行一次
ioctl //I2C驱动提供给应用层的接口函数
我们重点关注其中的sensor_write_register函数,该函数主要完成对某个寄存器的赋值操作,比如在函数sensor_init_720p_30fps中,调用sensor_write_register函数的情形如下。
void sensor_init_720p_30fps()
{
//[720p30]
sensor_write_register( 0x301A, 0x0001 ); // RESET_REGISTER
delay_ms(200); //ms 因为操作可能需要耗费一定的时间
sensor_write_register( 0x301A, 0x10D8 ); // RESET_REGISTER
delay_ms(200); //ms
//Linear Mode Setup
//AR0130 Rev1 Linear sequencer load 8-2-2011
sensor_write_register( 0x3088, 0x8000 );// SEQ_CTRL_PORT
sensor_write_register( 0x3086, 0x0225 );// SEQ_DATA_PORT
//省略部分代码
}
我们来详细分析sensor_write_register函数的代码细节。
int sensor_write_register(int addr, int data)
{
#ifdef HI_GPIO_I2C //可略过,因为这部分是使用GPIO模拟I2C的
i2c_data.dev_addr = sensor_i2c_addr;
i2c_data.reg_addr = addr;
i2c_data.addr_byte_num = sensor_addr_byte;
i2c_data.data = data;
i2c_data.data_byte_num = sensor_data_byte;
ret = ioctl(g_fd, GPIO_I2C_WRITE, &i2c_data);
if (ret)
{
printf("GPIO-I2C write faild!\n");
return ret;
}
#else //我们重点关注这部分
if(flag_init == 0)//如果还没有执行过I2C的初始化
{
sensor_i2c_init();//执行初始化
flag_init = 1;//之后不会再初始化
}
int idx = 0;
int ret;
char buf[8];//定义了8个字节长度的字符数组
buf[idx++] = addr & 0xFF;// buf[0]= 地址的低8位
if (sensor_addr_byte == 2)//如果地址类型是16位的。
//对于AR0130,其寄存器地址就是16bit的,这个成立。
{
ret = ioctl(g_fd, I2C_16BIT_REG, 1);
buf[idx++] = addr >> 8;// buf[1]= 16位地址的高8位
}
else
{
ret = ioctl(g_fd, I2C_16BIT_REG, 0);
}
if (ret < 0)
{
printf("CMD_SET_REG_WIDTH error!\n");
return -1;
}
buf[idx++] = data;//buf[2]=数据的低8位?这里为何不“& 0xFF”呢?
if (sensor_data_byte == 2)//如果数据长度是16位的。
//对于AR0130,寄存器的长度就是16位的,这个成立。
{
ret = ioctl(g_fd, I2C_16BIT_DATA, 1);
buf[idx++] = data >> 8;//buf[3]=数据的高8位
}
else
{
ret = ioctl(g_fd, I2C_16BIT_DATA, 0);
}
if (ret)
{
printf("hi_i2c write faild!\n");
return -1;
}
//至此,寄存器的地址、寄存器的数据都存储在buf数组中了。
//接下来要调用I2C驱动提供给应用层的接口(即write函数)来进行真正的数据写入。
ret = write(g_fd, buf, idx);
if(ret < 0)
{
printf("I2C_WRITE error!\n");
return -1;
}
#endif
return 0;
}
其中的sensor_i2c_init函数内容如下:
int sensor_i2c_init(void)
{
if(g_fd >= 0)
{
return 0;
}
//省略部分代码
int ret;
g_fd = open("/dev/i2c-0", O_RDWR);//打开I2C驱动对应的设备文件
if(g_fd < 0)
{
printf("Open /dev/i2c-0 error!\n");
return -1;
}
//利用ioctl函数来将I2C强制转换为slave模式?i2c地址要查数据手册
ret = ioctl(g_fd, I2C_SLAVE_FORCE, sensor_i2c_addr);
if (ret < 0)
{
printf("CMD_SET_DEV error!\n");
return ret;
}
return 0;
}
二、sensor寄存器的解析
对于sensor_write_register函数,它的参数1是sensor寄存器的地址,参数2是要写入sensor寄存器中的数值。如何查看sensor寄存器的地址以及某个寄存器的位含义?可以查看sensor的数据手册。这里以AR0130的数据手册《AR0130_RR_C》为例。
上面的sensor_init_720p_30fps函数的第一行代码如下,其中0x301A表示地址为0x301A的寄存器,0x0001表示要往该寄存器中写入的数值。根据注释,这行代码完成寄存器的复位工作(即复位AR0130)。
sensor_write_register( 0x301A, 0x0001 ); // RESET_REGISTER
我们通过查找数据手册来验证是否如此。在该数据手册的第7页前后有如下内容,从中可知寄存器的地址为16位的,寄存器的数据长度也是16位的。圈出的部分是地址为0x301A的寄存器的内容,可知它的名字是“reset_register”,猜想和寄存器复位(即复位AR0130)有关。
我们进一步找到这个寄存器的位含义表格(在第27页前后)。根据描述,bit[0]如果写1则会产生复位信号(复位的时候sensor如果正在采集数据,则会丢弃这帧数据)。上面的那行代码中的参数2为0x0001,这意味着给bit[0]写1,所以那行代码就是用来复位sensor的,就如注释写的一样。
三、sensor寄存器的操作实验
在数据手册的29页前后,有一个地址为0x3040的寄存器的描述,该寄存器用来设置图像的翻转。
(1)我们可以在mpp/component/isp/sensor/ar0130/ar0130_sensor_ctl.c文件中的sensor_init_720p_30fps函数的合适位置(大概330行处),修改代码如下。
//Enable Parallel Mode
sensor_write_register( 0x301A, 0x10D8); // RESET_REGISTER
sensor_write_register( 0x31D0, 0x0001); // HDR_COMP
//新添的内容,翻转是flip,镜像是mirror
//sensor_write_register( 0x3040, 0x0000);//第一次:未flip,未mirror
//sensor_write_register( 0x3040, 0x8000);//第二次:flip,未mirror,即仅水平翻转
//sensor_write_register( 0x3040, 0x4000);//第三次:未flip,但mirror,即仅垂直翻转
//sensor_write_register( 0x3040, 0xC000);//第四次:flip、mirror,即同时水平、垂直翻转
然后我们在mpp/component/isp下执行make(为了生成更改后的库形式的“应用层驱动”。这里不用执行make clean,因为Makefile感知到源码修改了,因此会更新(作为目标的)库文件)。
接着我们在mpp/sample_ortp_ar0130/venc目录下执行make clean再执行make(这里就需要先执行make clean,因为之前的操作,没有修改源码sample_venc.c,而只是修改了库文件,该目录下的Makefile不会感知到库文件的更新,从而不会更新sampl_venc。如果不make clean,执行make的时候将不进行任何操作),生成samp_venc这个可执行程序。
接着将sample_venc文件拷贝到虚拟机/home/xjh/iot/hisi_development/hisi_rootfs/目录中,然后将该目录挂载到板载系统的/mnt目录,在/mnt目录下执行“./sample_venc 0”,输入c。
接着在VLC播放器中,选择媒体,打开文件,选择sdp文件(提取码ms4a)(注意这文件里的设置要与实际对应)。此时可以看到实时画面,我们截图进行对比。
第一次:原来图像。
第二次:水平翻转图像(flip)。
第三次:垂直翻转(mirror)
第四次:水平翻转与垂直翻转。