基于ARM9的CMOS图像采集系统的设计
// Camera clock = UPLL/[(CAMCLK_DIV+1)X2]
SetCAMClockDivider(CAMCLK24000000); //Set Camera Clock 24MHz s5x532, OV9650
先设置UPLL的时钟为48MHz,然后设置摄像头的时钟: 其中SetCAMClockDivider()函数的原型为:
void SetCAMClockDivider(int divn)
{ rCAMDIVN = (rCAMDIVN & ~(0xf))|(1<<4)|(divn); // CAMCLK is divided..} 即设置CAMDIVN的值为1,是摄像头的时钟为24M。
设置寄存器CLKCON的第19位为1,使能摄像头的时钟输入。并且初始化摄像头的时钟输入,OV9650的时钟输入为24M。
3)设置摄像头的端口。其具体的操作如下:
void CamPortSet(void) { }
save_GPJCON = rGPJCON; save_GPJDAT = rGPJDAT; save_GPJUP = rGPJUP; rGPJCON = 0x2aaaaaa; rGPJDAT = 0; rGPJUP = 0;
由于J端口为摄像头的复用端口所以设置相应的J端口适用于输入还是输出来控制硬件的连接。
4)测试OV9650。具体函数如下所示: int Test_OV9650(void) {
int ret;
init_yuvtable(); // 初始化yuv格式列表 s3c2440_cam_gpio_init(); //初始化摄像头I/O端口J
s3c2440_camif_init(); //摄像头控制器初始化,主要是时钟初始化 OV9650_init(); //初始化SCCB通信端口GPE14、GPE15 printk(\
–21–
基于ARM9的CMOS图像采集系统的设计
}
ret = check_OV9650() ; //获取ov9650的ID号
if (ret) {
printk(\ return ret; }
printk(\OV9650_config(); //配置OV9650
return 0; //配置成功返回0,否则配置失败返回1
? 其中OV9650_init()函数的原型如下: void __inline OV9650_init(void) {
CFG_WRITE(SIO_C); CFG_WRITE(SIO_D); mdelay(10);
}用于初始化摄像头的时钟和数据端口GPE14、GPE15。
? 其中check_OV9650()的函数的主要功能就是获取摄像头的ID号,OV9650有两个只
读寄存器——0x1C和0x1D,用于存放厂家ID,数据分别为0x7F和0xA2,我们可以通过读取它们来判断s3c2440 是否连接了OV9650。当确认连接了OV9650后,我们就可以把上面的那个数组写入 OV9650内,其函数的原型如下: int __inline check_OV9650(void) {
int ret = 0;
int OV9650_mid = 0;
int try_count =0; / /2 times
try_again:
OV9650_mid = (OV9650_sccb_receivebyte(0x1c) << 8); OV9650_mid |= OV9650_sccb_receivebyte(0x1d); if (OV9650_mid != OV9650_PRODUCT_ID) { if (!try_count++) goto try_again;
//两次检测摄像头的ID号, 其中OV9650_PRODUCT_ID为:0x7F和0xA2
–22–
基于ARM9的CMOS图像采集系统的设计
}
printk(\ OV9650_mid, OV9650_PRODUCT_ID); ret = -ENODEV; } else {
//printk(\}
return ret;
? 其中OV9650_config()的函数原型入下:
void __inline OV9650_config(void) { int i;
for (i = 0; i < OV9650_REGS; i++) {
if (ov9650_reg[i].subaddr == CHIP_DELAY) Delay(ov9650_reg[i].value); //mdelay(ov9650_reg[i].value); else
OV9650_sccb_sendbyte(ov9650_reg[i].subaddr & 0xff , ov9650_reg[i].value & 0xff); } }
其中static struct OV9650_t {
int subaddr; int value;
这里我们利用结构体来定义配置ov9650的寄存器,OV9650的寄存器较多,定义了一个VGA(640×480)模式下YUV 彩色空间的配置例子,括号内第一个元素表示寄存器地址,第二个元素表示要写入的数据[12]。
设置0x01、0x02寄存器的值为0x80、Ox80,表示图像色彩调节为白平衡。 设置0x12寄存器的值为0x40,表示输出图像数据格式为YCbCr,窗口大小为640×480(VGA);
设置0x15寄存器的值为0x10,表示在PCLK时钟的下降沿更新数据;
} ov9650_reg[] = {、、、、、、、}
–23–
基于ARM9的CMOS图像采集系统的设计
设置0x17、Oxl8、0x32寄存器的值分别为0x60、0x10、0xbf,表示图像采样窗口的水平大小为640;
设置0xla、0x19、0x03寄存器的值分别为0x01、0x3d、0x00,表示图像采样窗口的垂直大小为480;
设置0x3a寄存器的值为Ox00,表示图像数据输出的顺序为Y Cb Y Cr。
最后在函数Test_OV9650()中定义一个循环体,将结构体数组ov9650_reg[]中的配置数据写入OV9650内部寄存器。 程序流程图如图5.1所示:
开始条件:sccb_start()发送OV9650的设备写入地址:sccb_write_char(0x60)发送OV9650寄存地址:sccb_write_char(subaddr)发送寄存器配置数据:sccb_write_char(data)结束条件:sccb_stop() 图5.1 寄存器配置信息流程图
5.1.2 图像采集程序设计
设置s3c2440摄像接口一个很重要的步骤就是设置视频尺寸大小。我们把由OV9650采集到的视频尺寸称为源,即源水平尺寸和源垂直尺寸,其中源水平尺寸必须是 8的整数倍。这个尺寸是通过配置OV9650的相关寄存器实现的。我们把这两个值分别放入输入源格式寄存器CISRCFMT的第16位至第28位,和第0位至第12位内,例如通过
–24–
基于ARM9的CMOS图像采集系统的设计
OV9650,采集的到的视频尺寸为640×480,则把640和480分别放入寄存器CISRCFMT中的相应位置即可。
我们把实际显示的视频尺寸称为目标,即目标水平尺寸和目标垂直尺寸,这里这个尺寸就是LCD 的尺寸。我们把这两个值分别放入预览DMA目标图像格式寄存器CIPRTRGFMT的第16位至第28位,和第 0 位至第 12 位内,例如 LCD 的尺寸为320×240,则把320 和240分别放入寄存器CIPRTRGFMT中的相应位置即可。另外还需要把这两个值的乘积放入预览缩放目标面积寄存器CIPRTAREA内[13]。
源尺寸和目标尺寸往往是不一样大小的,那么可能还需要设置偏移量,即水平偏移量和垂直偏移量,应该把这两个值分别放入窗口偏移寄存器CIWDOFST的第16位至第26位,和第0位至第10位内,其中这个寄存器的第31位用于控制是否需要设置偏移量,当偏移量为0或不需要设置偏移量时,这一位应为0,否则为1。
具体实现函数如下所示:
void Test_CamPreview(void) {
Uart_Printf(\
//camera global variables 摄像头全局变量设置 camTestMode=CAM_TEST_MODE_PVIEW; camCodecCaptureCount=0; camPviewCaptureCount=0;
camPviewStatus=CAM_STOPPED; camCodecStatus=CAM_STOPPED; flagCaptured_P=0;
Lcd_Init(); //初始化lcd液晶屏 Lcd_PowerEnable(0, 1); //液晶屏上电 Lcd_EnvidOnOff(1); //turn on vidio // Initialize Camera interface
// if(LCD_Type == LCDW43) 所用的是4.3寸屏幕
100,
100,
CAM_FRAMEBUFFER_C,
CamInit(480, 272, 480, 272, CAM_FRAMEBUFFER_P); // Start Capture
rSUBSRCPND |= BIT_SUB_CAM_C|BIT_SUB_CAM_P; //开摄像头中断 ClearPending(BIT_CAM); //开摄像头中断
CamPreviewIntUnmask(); //开摄像头中断
–25–