
#include "Graphics\ov9650.h"

// Local Function Prototype
void camCmdWr(BYTE reg, BYTE cmd);		//write to a certain register with command cmd
BYTE camRegRd(BYTE reg);				//Read from a regiter reg

/*
*************************************************************************************
* Function: 
*
* PreCondition: 
*
* Arguments:	
*
* Return:		none
*
* Note:			
*************************************************************************************
*/
void camInit(void)
{
	//Low level init on the DV clock for SSD1928
	SetRegB(REG_VCLK_CONFIG_0, 0x02); //VCLKO_SRC=PLL_CLK/(VCLKO_SRC Divide + 1) = 72MHz/(2+1) = 24MHz
	SetRegB(REG_VCLK_CONFIG_1, 0x97); //VCLK Output Enable, etc.

	//Set digital video interface configuration registers for HValid, VValid etc
	SetRegB(REG_DV_TV_0,0x12);
	SetRegB(REG_DV_TV_1,0x01);
	SetRegB(REG_DV_TV_2,0x04);

	//Set JPEG horizontal & vertical decimation ratio to zero
	SetRegB(REG_DV_JHDEC_RATIO,0x00);
	SetRegB(REG_DV_JVDEC_RATIO,0x00);

	//Set Frame sampling rate
	SetRegB(REG_DV_FRAME_SAMPLING, 0x00);

	//Set New Fram position after DV_VValid signal
	SetRegW(REG_DV_NFRAME_POS_0, 0x0002);
	//???
	SetRegW(REG_DV_HORI_PERIOD_0, CAM_HOR_SIZE);
	//???
	SetRegB(REG_DV_FRAME_PULSE_WIDTH, 0x08);
	//These parameters (DV_Y, CB, CR) adjust the hue of display
	SetRegB(REG_DV_CSC_MODE, 0x00);
	SetRegB(REG_DV_Y, 0x00);
	SetRegB(REG_DV_CB, 0x80);
	SetRegB(REG_DV_CR, 0x80);

	// hardware camera initialization here
	camHwInit();
}

/*
*************************************************************************************
* Function: 	Enable/Disable camera preview function. The width and height
*				of preview has been fixed to CAM_HOR_SIZE and CAM_VER_SIZE defined
*				in ov9650.h.
*
* PreCondition: Call camInit() prior to calling this function
*
* Arguments:	'DWORD' addr is the starting address for preview image in the display buffer
*				'BOOL' on is the ON/OFF switch for preview. TRUE->ON, FALSE->OFF
*				
* Return:		None
*
* Note:			The DV Mode Register at REG[160h] is an important register for this
*				function. It has been found that, JPEG Still Picture Enable and Video Still
*				Enable feature cannot be implemented without a bit-set on Video Start Enable bit.
*
*				One must watch out that, when we use FALSE as the argument for preview,
*				the background of the LCD display will NOT be refreshed. One needs to 
*				do a manual refresh on the content of the backgound. For example, if the whole
*				320*240 matrix has been used as the video preview area, a paused image
*				of the video preview would be seen if camVideoPreview(addr, OFF) is called.
*				It is except that, when the address of preview is pointing to a floating window,
*				one may be able to disable the floating window to retain the background image
*				of the main window.
*************************************************************************************
*/
void camVideoPreview(DWORD addr, BOOL on)
{
	//Set preview horizontal & vertical crop start to zero
	SetRegW(REG_DV_HCROP_STR_0,0x00);
	SetRegW(REG_DV_VCROP_STR_0,0x00);
	//Set preview horizontal & vertical decimation ratio to zero
	SetRegB(REG_DV_VHDEC_RATIO,0x00);
	SetRegB(REG_DV_VVDEC_RATIO,0x00);

	//Set preview horizontal & vertical crop size to full QVGA height and width
	SetRegW(REG_DV_HCROP_SIZE_0,CAM_HOR_SIZE);
	SetRegW(REG_DV_VCROP_SIZE_0,CAM_VER_SIZE);

	//Set Preview video memory start1 and start2 address to the same address base
	//In video preview mode, preview video data are fit starting from
	//Preview Video Memory Address 1 (REG_DV_VMEM_STR_ADDR1_0) or 
	//Preview Video Memory Address 2 (REG_DV_VMEM_STR_ADDR2_0) alternatively at a rate set
	//by Frame Sampling Register (REG_DV_FRAME_SAMPLING). When single preview window is used,
	//both value of Address 1 and Address 2 should be the same.
	SetRegL(REG_DV_VMEM_STR_ADDR1_0,addr>>2);
	SetRegL(REG_DV_VMEM_STR_ADDR2_0,addr>>2);

	//Set digital video window display start address0 and address1 to the same address base as Preview
	SetRegL(REG_DV_DV0_START_ADDR_0,addr>>2);
	SetRegL(REG_DV_DV1_START_ADDR_0,addr>>2);

	if(on)
	{
		SetRegB(REG_DV_OP_MODE, GetReg(REG_DV_OP_MODE)&~(1<<3));
		SetRegB(REG_DV_OP_MODE, GetReg(REG_DV_OP_MODE)|0x12);
	} 
		else
	{
		SetRegB(REG_DV_OP_MODE, GetReg(REG_DV_OP_MODE)&~0x12);
	}
}

/*
*************************************************************************************
* Function: 	Enable video still function.
*
* PreCondition: Call camInit() and camVideoPreview() prior to calling this function
*
* Arguments:	'DWORD' addr is the starting address for preview image in the display buffer
*				'BOOL' 	on is the ON/OFF switch for video still.
*						TRUE->ON, that means the video will be freezed
*						FALSE->OFF, that means nothing will change for Preview
*				
* Return:		None
*
* Note:			The DV Mode Register at REG[160h] is an important register for this
*				function. It has been found that, JPEG Still Picture Enable and Video Still
*				Enable feature cannot be implemented without a bit-set on Video Start Enable bit.
*
*				One must watch out that, when we use FALSE as the argument for still,
*				only preview is maintained. Therefore if we want to pause the video, and 
*				then make the image "disappear" after a certain amount of time, we 
*				need to code a simple delay after the video still function and then
*				redraw the background by your application.
*************************************************************************************
*/
void camVideoStill(DWORD addr, BOOL on)
{
	if(on) 
		SetRegB(REG_DV_OP_MODE,GetReg(REG_DV_OP_MODE)|0x01);
	else
		SetRegB(REG_DV_OP_MODE,GetReg(REG_DV_OP_MODE)&~0x01);

}

/*
*************************************************************************************
* Function: Low level configuration for Registers of OV9650
*
* PreCondition: Call I2C_Init() prior to his function
*
* Arguments:	BYTE reg	is the register to write
*				BYTE cmd	is the command to issue
*
* Return:		none
*
* Note:			One may refer to an application note released by OmniVision on
*				Functional Specification of OmniVision Serial Camera Control Bus(SCCB)
*				Document Version 2.1, last mofidied 26 February 2003
*				Communication is done in max. 3 phases.
*				The first phase denotes the device ID, in our case it is constant CAM_DEVICE_ID
*				defined under ov9650.h
*				The second phase denotes the sub-address to identify the register location
*				of the specified slave
*				The third phase denotes the data to be written
*************************************************************************************
*/
void camCmdWr(BYTE reg, BYTE cmd)
{
	ssd1928_I2C_Ctl(SSD1928_I2C_START|SSD1928_I2C_WRITE);		//low level I2C bits for I2C start condition and I2C write command
	ssd1928_I2C_Write(CAM_DEVICE_ID);							//device ID for OV9650
	ssd1928_I2C_Ctl(SSD1928_I2C_WRITE);
	ssd1928_I2C_Write(reg);

	ssd1928_I2C_Ctl(SSD1928_I2C_WRITE);
	ssd1928_I2C_Write(cmd);

	ssd1928_I2C_Ctl(SSD1928_I2C_STOP);							//I2C stop
	ssd1928_I2C_Write(0xFF);									//dummy data for I2C stop
}

/*
*************************************************************************************
* Function: Read registers from OV9650
*
* PreCondition: Call I2C_Init() prior to his function
*
* Arguments:	BYTE reg	is the register to read
*
* Return:		This function returns the byte read from a register 'reg' as the argument
*
* Note:			Optional timeout function may be implemented after if(ssd1928_I2C_Wait()!=0)
*				
*************************************************************************************
*/
BYTE camRegRd(BYTE reg)
{
	BYTE data;

	ssd1928_I2C_Ctl(SSD1928_I2C_START|SSD1928_I2C_WRITE);		//low level I2C bits for I2C start condition and I2C write command
	ssd1928_I2C_Write(CAM_DEVICE_ID);							//device ID for OV9650
	if(ssd1928_I2C_Wait()!=0) 
		;//debug camRegRd() line 1
	ssd1928_I2C_Ctl(SSD1928_I2C_WRITE);
	ssd1928_I2C_Write(reg);
	if(ssd1928_I2C_Wait()!=0) 
		;//debug camRegRd() line 2
	ssd1928_I2C_Ctl(SSD1928_I2C_STOP);
	ssd1928_I2C_Write(reg);
	if(ssd1928_I2C_Wait()!=0) 
		;//debug camRegRd() line 3	

	ssd1928_I2C_Ctl(SSD1928_I2C_START|SSD1928_I2C_WRITE);		//low level I2C bits for I2C start condition and I2C write command
	ssd1928_I2C_Write(CAM_DEVICE_ID|0x01);
	if(ssd1928_I2C_Wait()!=0) 
		;//debug camRegRd() line 4
	ssd1928_I2C_Ctl(SSD1928_I2C_READ_WO_ACK|0x10);
	data = ssd1928_I2C_Read();
	
	return data;
}

/*
*************************************************************************************
* Function:		Low level hardware initialization for ov9650 CMOS sensor
*
* PreCondition:	None
*
* Arguments:	None
*				
* Return:		None
*
* Note:			OV9650 accepts Serial Camera Control Bus (SCCB)
*				Reference @
*				1. DS(1.3) for OV9650	
*************************************************************************************
*/
void camHwInit(void)
{
	//There are two ways for a sensor reset
	//1. Hardware reset - pulling the RESET pin high and keeping it high for at least 1ms.
	//		After a reset has been initiated, the sensor will be stablized after 4096 external clock
	//		If it is a 24MHz MCLK operation, the stablization period = 171us, i.e. less than 1 ms
	//2. Software reset - writing 0x80 to register 0x12 for a software reset. If a software reset
	//		is used, a reset operation done twice is recommended to make sure the sensor is stable.
	//
	//This is a low level camera module hardware reset by a bit set action on 
	//RESET pin 	0:normal operating mode 	1:Reset mode
	//PWDN pin 		0:normal mode 				1:power down mode
	//We need to use REG_DV_ENB register REG[1AFh] to control both PWDN and RESET pins
	SetRegB(REG_DV_ENB, 0x01);	//hold in reset
	DelayMs(2);					//keep hardware reset pin high for 2ms
	SetRegB(REG_DV_ENB, 0x00);	//normal operation to bring RESET pin low and PWDN pin low as well
	DelayMs(1);

	ssd1928_I2C_Init();
	
    camCmdWr(0x12, 0x10);    // COM7       set output to QVGA mode
    camCmdWr(0x11, 0x81);    // CLKRC      set the system clock division
    camCmdWr(0x6b, 0x0a);    // DBLV       band gap reference adjustment
    camCmdWr(0x6a, 0x41);    // 		   LSB of Banding Filter (effective only when COM11[0] is high)
    camCmdWr(0x3b, 0x09);    // COM11      use half frame and manual banding filter mode
	camCmdWr(0x13, 0xe0);    // COM8       enable fast AGC/AEC, enable AEC, and enable Banding filter
    camCmdWr(0x01, 0x80);	 // BLUE	   Blue channel gain setting, default 0x80
	camCmdWr(0x02, 0x80);	 // RED		   Red channel gain setting, default 0x80
	camCmdWr(0x00, 0x00);	 // GAIN	   AGC, range 0x00 to 0xFF
    camCmdWr(0x10, 0x00);    // AECH       exposure value set default 0x40
    camCmdWr(0x13, 0xe5);    // COM8       now enable AGC/AEC
    camCmdWr(0x39, 0x43);    // OPON       ???
    camCmdWr(0x38, 0x12);    // ACOM       ???
	camCmdWr(0x37, 0x00);	 // ADC		   ???
    camCmdWr(0x35, 0x91);    // RSVD	   ???
    camCmdWr(0x0e, 0x20);    // COM5       ???
    camCmdWr(0x1e, 0x30);    // MVFP       Mirror/VFlip enable, mirror and vflip enable for self-portrait (hardware OV9650_BO)
    camCmdWr(0x04, 0x00);    // COM1       no line skip option
    camCmdWr(0x0c, 0x00);    // COM3       set to default
	camCmdWr(0x0d, 0x00);	 // COM4	   default to 0x00
	
	// window start and stop for output size control
	// HSTOP[REG18h] is the high 8 bit of H STOP (low 3 bits are at HREF[5:3])
	// Because HREF[5:3] = 0b100, HSTOP = 0x5A*2^3 + 4 = 0x2D0
	// HSTART[REG17h] is the high 8-bit of H START (low 3 bits are at HREF[2:0])
	// Because HREF[2:0] = 0b100, HSTART = 0x32*2^3 + 4 = 0x190
	// Therefore HSTOP-HSTART = (0x2D0+4)-(0x190+4) = 0x140 (320 dec.)
    camCmdWr(REG_HREF, 0xA4);// HREF       window control
    camCmdWr(REG_HSTOP, 0x5A);
    camCmdWr(REG_HSTART, 0x32);

	// VSTRT      window control
	// VREF[REG03h] contains the start and end low 3 bits for VSTOP and VSTRT
	// VSTRT[REG19h] = 0x31*2^3 = 0x188
	// VSTOP[REG1Ah] = 0x4F*2^3 = 0x278
	// Therefore VSTOP - VSTRT = (0x278+2)-(0x188+2) = 0xF0 (240 dec.)
    camCmdWr(REG_VREF, 0x12);
    camCmdWr(REG_VSTART, 0x31);    
    camCmdWr(REG_VSTOP, 0x4F);

    camCmdWr(0x14, 0x1a);    // COM9       automatic gain control ceilings
    camCmdWr(0x15, 0x10);    // COM10      set up pclk, vsync and href signals
	camCmdWr(0x3f, 0xa6);	 // EDGE	   what edge enhancement ???

	camCmdWr(0x41, 0x02);	 // COM16	   Color matrix coefficient double option enable ???
	camCmdWr(0x42, 0xc1);	 // COM17	   edge enhancement option, tri-state output etc
    camCmdWr(0x1b, 0x00);    // PSHFT      set pclk delay from href

	camCmdWr(0x16, 0x06);	 // RSVD	   another reserved register!
    camCmdWr(0x33, 0xe2);    // CHLF       array current control
	camCmdWr(0x34, 0xbf);    // ARBLM      array reference control

	camCmdWr(0x96, 0x04);	 // RSVD	   again, reserved register
 	camCmdWr(0x3a, 0x0c); 	 // TSLB	   line buffer test option for UV output and sequence setting
 
	camCmdWr(0x8e, 0x00);	 // COM24	   Reserved register
    camCmdWr(0x3c, 0x77);    // COM12      href options

	camCmdWr(0x8b,0x06);
	camCmdWr(0x94,0x88);
	camCmdWr(0x95,0x88);
	camCmdWr(0x40,0xc1);
	camCmdWr(0x29,0x3f);
	camCmdWr(0x0f,0x42);
	camCmdWr(0x3d,0x92);
	camCmdWr(0x69,0x40);	 // Manual Banding Filter MSB
	camCmdWr(0x5c,0xb9);	 // REG[0x59-0x61] all RSVD
	camCmdWr(0x5d,0x96);	 
	camCmdWr(0x5e,0x10);
	camCmdWr(0x59,0xc0);
	camCmdWr(0x5a,0xaf);
	camCmdWr(0x5f,0xe0);
	camCmdWr(0x60,0x8c);
	camCmdWr(0x61,0x20);

	camCmdWr(0x5b,0x55);	 // REG[0x59-0x61] all RSVD
	camCmdWr(0x43,0xf0);	 // REG[0x43-0x4E] all RSVD
	camCmdWr(0x44,0x10);
	camCmdWr(0x45,0x68);
	camCmdWr(0x46,0x96);
	camCmdWr(0x47,0x60);
	camCmdWr(0x48,0x80);

	camCmdWr(0xa5,0xd9);	 // RSVD
	camCmdWr(0xa4,0x74);	 // RSVD

	camCmdWr(0x8d,0x02);	 // color bar test mode disable, digital AWB enable
	
	camCmdWr(0x13,0xe7);	 // AGC, AWB, AEC all enabled here
	
	camCmdWr(0x4f,0x3a);	 // REG[4F-58] are Matrix coefficients
	camCmdWr(0x50,0x3d);
	camCmdWr(0x51,0x03);
	camCmdWr(0x52,0x12);
	camCmdWr(0x53,0x26);
	camCmdWr(0x54,0x38);
	camCmdWr(0x55,0x40);
	camCmdWr(0x56,0x40);
	camCmdWr(0x57,0x40);
	camCmdWr(0x58,0x0d);

	camCmdWr(0x8c,0x23);	 // De-noise enable, white-pixel erase enable
	camCmdWr(0x3e,0x02);
	
	camCmdWr(0xa9,0xb8);	 // REG[A8-AA] all RSVD
	camCmdWr(0xaa,0x92);
	camCmdWr(0xab,0x0a);

	camCmdWr(0x8f,0xdf);	 // Digital BLC offset sign???

	camCmdWr(0x90,0x00);	 // Digital BLC B channel offset value set default
	camCmdWr(0x91,0x00);	 // Digital BLC R channel offset value set default
	camCmdWr(0x9f,0x00);	 // Digital BLC Gb channel offset value set default
	camCmdWr(0xa0,0x00);	 // Digital BLC Gr channel offset value set default

	camCmdWr(0x24,0x98);	 // AEW stable operating region upper limit
	camCmdWr(0x25,0x88);	 // AEW stable operating region lower limit
	camCmdWr(0x26,0xd3);	 // AGC/AEC fast mode operating region
	camCmdWr(0x2a,0x00);	 // Horizontal dummy pixel insert MSB
	camCmdWr(0x2b,0x00);	 // Horizontal dummy insert LSB
	camCmdWr(0x2d,0x00);	 // Vertical LSB of dummy lines 

	camCmdWr(0x6c,0x40);	 // REG[6C-7B] are Gamma Curve setup GSP
	camCmdWr(0x6d,0x30);	 
	camCmdWr(0x6e,0x4b);
	camCmdWr(0x6f,0x60);
	camCmdWr(0x70,0x70);
	camCmdWr(0x71,0x70);
	camCmdWr(0x72,0x70);
	camCmdWr(0x73,0x70);
	camCmdWr(0x74,0x60);
	camCmdWr(0x75,0x60);
	camCmdWr(0x76,0x50);
	camCmdWr(0x77,0x48);
	camCmdWr(0x78,0x3a);
	camCmdWr(0x79,0x2e);
	camCmdWr(0x7a,0x28);
	camCmdWr(0x7b,0x22);

	camCmdWr(0x7c,0x04);	 // REG[7C-8A] are Gamma Curve setup GST
	camCmdWr(0x7d,0x07);
	camCmdWr(0x7e,0x10);
	camCmdWr(0x7f,0x28);
	camCmdWr(0x80,0x36);
	camCmdWr(0x81,0x44);
	camCmdWr(0x82,0x52);
	camCmdWr(0x83,0x60);
	camCmdWr(0x84,0x6c);
	camCmdWr(0x85,0x78);
	camCmdWr(0x86,0x8c);
	camCmdWr(0x87,0x9e);
	camCmdWr(0x88,0xbb);
	camCmdWr(0x89,0xd2);
	camCmdWr(0x8a,0xe6);

}
