/******************************************************************************

Copyright (c) 2005 Analog Devices.  All Rights Reserved.

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.  

******************************************************************************/



/******************************************************************************

$File:		main.c $
$Revision: 1.3 $
$Date: 2007/11/19 04:23:40 $

Project:	SensorStream
Title:		Sensor Stream to Video/LCD Output application
Author(s):	BJ

Description:
	This application stream video images from either an Omnivision OV7X48 VGA
	sensor or Micron Black and White VGA sensor and output to either the 
	ADV7179 video encoder in NTSC or PAL mode, or to a NEC VGA LCD panel.

Configuration:
	See readme file

*******************************************************************************

Modification History:
====================
Revision 1.0 - BJ 2006/07/02
	Created application

Revision 1.1 - BJ 2006/07/17
	Fixed major screen flicker when running on BF561 under Debug mode

******************************************************************************/



/******************************************************************************

Include files

******************************************************************************/

#include <services/services.h>		// system service includes
#include <SDK-ezkitutilities.h>		// EZ-Kit utilities
#include <adi_ssl_Init.h>		// adi_ssl_Init utilities


#include <SensorCommon/common.h>
#include <SensorCommon/device.h>




/******************************************************************************

Configuration

******************************************************************************/

// select default video output mode
//#define VIDEO_MODE_VGA
#define VIDEO_MODE_PAL
//#define VIDEO_MODE_NTSC


// uncommen next line to use deferred callbacks
//#define USE_DEFERRED_CALLBACKS




/******************************************************************************

Global definitions

******************************************************************************/

// a macro to halt execution and return control to the ICE
#define WAIL() asm("EMUEXCPT;"); asm("NOP;")


#define VIDEO_REFRESH_VGA	60
#define VIDEO_REFRESH_NTSC	25
#define VIDEO_REFRESH_PAL	30

#if defined(__ADSP_TETON__)
#define INPUT_PPI 0
#define OUTPUT_PPI 1
#else
#error This application is only tested to work on Blackfin Teton
#endif



/******************************************************************************

Static data

******************************************************************************/

#if defined (__ADSP_TETON__)
section("sdram_bank1") static u32 InputData[720*480/2];
section("sdram_bank3") static u32 OutputData[GFX_PAL_FRAME_SIZE/2];
section("sdram_bank0") static u16 OverlayData[GFX_PAL_FRAME_SIZE];
#endif




/******************************************************************************

Global variables

******************************************************************************/

static ADI_DCB_HANDLE			DCBManagerHandle=NULL;	// handle to Callback service


static ADI_DEV_BUFFER *InputBuffer=NULL;
volatile u32 InputFrame=0;

static ADI_DEV_BUFFER *OutputBuffer=NULL;
volatile u32 OutputFrame=0;


#if !defined(VIDEO_MODE_NTSC) & !defined(VIDEO_MODE_PAL) & defined(VIDEO_MODE_VGA)
GFX_MODE ModeDefault=GFX_MODE_VGA;
DEVICE_TYPE TypeDefault=DEVICE_NL6448BC33;
#elif !defined(VIDEO_MODE_NTSC) & defined(VIDEO_MODE_PAL) & !defined(VIDEO_MODE_VGA)
GFX_MODE ModeDefault=GFX_MODE_PALI;
DEVICE_TYPE TypeDefault=DEVICE_ADV7179;
#elif defined(VIDEO_MODE_NTSC) & !defined(VIDEO_MODE_PAL) & !defined(VIDEO_MODE_VGA)
GFX_MODE ModeDefault=GFX_MODE_NTSCI;
DEVICE_TYPE TypeDefault=DEVICE_ADV7179;
#elif defined(VIDEO_MODE_NTSC) | defined(VIDEO_MODE_PAL) | defined(VIDEO_MODE_VGA)
#error Error: One or more default video modes is selected, please define either VIDEO_MODE_NTSC, VIDEO_MODE_PAL or VIDEO_MODE_VGA
#else
#error Error: No default video mode is selected, please define either VIDEO_MODE_NTSC, VIDEO_MODE_PAL or VIDEO_MODE_VGA
#endif




/******************************************************************************

Function definitions

******************************************************************************/

// exception handlers
static ADI_INT_HANDLER(ExceptionHandler);
static ADI_INT_HANDLER(HWErrorHandler);


// callback functions
static void CallbackOutput(void *,u32,void *);
static void CallbackInput(void *,u32,void *);




/******************************************************************************

	Function: main

******************************************************************************/

int main(void)
{
	u32 i,Code,Count,Flag,Update,Transparent,FrameCount,Frame,Mask;
	bool Blank;
	short Retry;
	ADI_DEV_DEVICE_HANDLE Input,Output;
	GFX_CANVAS Overlay,Header,Info;
	DEVICE_TYPE Type,TypeOld,InputType;
	GFX_MODE Mode,ModeOld;
	
	
	enum
	{
		STATE_INPUT,
		STATE_INPUT_OPEN,
		STATE_INPUT_FRAME,
		STATE_INPUT_CLOSE,
		
		STATE_OUTPUT,
		STATE_OUTPUT_OPEN,
		STATE_OUTPUT_FRAME,
		STATE_OUTPUT_CLOSE,
	} State=STATE_OUTPUT;
	
	
	
	
/* Initialize blackfin device ************************************************/
	
	// Initialise processor
	ezInit(1);
	
	adi_ssl_Init();
	
	ezErrorCheck(adi_int_CECHook(3,ExceptionHandler,NULL,FALSE));
	ezErrorCheck(adi_int_CECHook(5,HWErrorHandler,NULL,FALSE));
	
	/* speed up clock frequency */
#if defined(__ADSP_EDINBURGH__)			
	/* only configure 533 as 561 cannot operate a max speed */
	adi_pwr_SetFreq( 0, 0, ADI_PWR_DF_NONE );
#endif 

	
/* Initialize ez-kit *********************************************************/
	
	for (i=EZ_FIRST_LED;i<EZ_NUM_LEDS;i++) ezInitLED(i);
	ezTurnOffAllLEDs();
	
	
	ezDisableVideoEncoder();
	ezDisableVideoDecoder();
	
	
#if defined(__ADSP_TETON__)
	
	// setup the DMA channel priorities so that the output PPI has higher
	// priority than the input PPI
	ezErrorCheck(adi_dma_SetMapping(ADI_DMA_PMAP_PPI1,ADI_DMA_DMA1_1));
	ezErrorCheck(adi_dma_SetMapping(ADI_DMA_PMAP_PPI2,ADI_DMA_DMA1_0));
	
#else
#error This application is only tested to work on Blackfin Teton
#endif
	
	
/* Initialize application variables ******************************************/
	
	Code=ADI_DEV_RESULT_SUCCESS;
	
	Transparent=0xFFFFFFFF;
	
	ModeOld=GFX_MODE_UNKNOWN;
	Mode=ModeDefault;
	TypeOld=DEVICE_UNKNOWN;
	Type=TypeDefault;
	
	InputType=DEVICE_UNKNOWN;
	
	Flag=1;
	Update=0;
	Blank=true;
	Input=Output=NULL;
	Frame=0;
	
	device_init(adi_dev_ManagerHandle,adi_dma_ManagerHandle,DCBManagerHandle);
	
	
/* Main application loop *****************************************************/
	
	while (Code==ADI_DEV_RESULT_SUCCESS)
	{
		switch (State)
		{
			case STATE_INPUT:
				Retry=0;
				State=STATE_INPUT_OPEN;
			
			
			case STATE_INPUT_OPEN:
			{
				ADI_DEV_CMD_VALUE_PAIR Micron[]={
					{MICRON_CMD_SET_MODE,(void *)Mode},
					{ADI_DEV_CMD_END,NULL}
				}, Omnivision[]={
					{OMNIVISION_CMD_SET_MODE,(void *)Mode},
					{ADI_DEV_CMD_END,NULL}
				};
				
				FrameCount=InputFrame+1;
				if (Input==NULL)
				{
					if ((Input=device_open(DEVICE_OV7648,INPUT_PPI,(u16 *)InputData,sizeof(InputData[0]),CallbackInput,Omnivision))) InputType=DEVICE_OV7648;
					else if ((Input=device_open(DEVICE_MT9V022M,INPUT_PPI,(u16 *)InputData,sizeof(InputData[0]),CallbackInput,Micron))) InputType=DEVICE_MT9V022M;
				}
				if (Input) State=STATE_INPUT_FRAME;
				else if (Retry--<=0) State=STATE_OUTPUT;
				break;
			}
			
			
			case STATE_INPUT_FRAME:
				if ((InputFrame-FrameCount)&0x80000000) break;
				State=STATE_OUTPUT;
				break;
			
			
			case STATE_INPUT_CLOSE:
				State=STATE_INPUT;
				device_close(Input);
				Input=NULL;
				break;
			
			
			case STATE_OUTPUT:
			{
				if (Type!=TypeOld||Mode!=ModeOld)
				{
					GFX_CANVAS Canvas;
					u32 OverlayColour;
					
					device_close(Input);
					Input=NULL;
					InputType==DEVICE_UNKNOWN;
					
					gfx_init(&Canvas,Mode,(u16 *)OutputData,0,0,0,0);
					gfx_frame(&Canvas,gfx_getcolour(&Canvas,128,128,255));
					
					InputBuffer=OutputBuffer=NULL;
					InputType=DEVICE_UNKNOWN;
					
					gfx_init(&Overlay,Mode,OverlayData,0,0,0,0);
					Transparent=gfx_getcolour(&Overlay,16,16,16);
					gfx_frame(&Overlay,Transparent);
					OverlayColour=gfx_getcolour(&Overlay,0,64,255);
					switch (Overlay.Mode)
					{
						case GFX_MODE_VGA:
							gfx_init(&Header,Overlay.Mode,OverlayData,10,10,Overlay.Width/2,40);
							break;
						
						case GFX_MODE_PALI:
						case GFX_MODE_NTSCI:
							gfx_init(&Header,Overlay.Mode,OverlayData,30,20,-60,40);
					}
					
					gfx_init(&Info,Overlay.Mode,OverlayData,Header.Left,Header.Top+Header.Height+10,Header.Width,75);
					gfx_boxf(&Info,0,0,Info.Width-1,Info.Height-1,OverlayColour,Transparent);
					gfx_line(&Info,1,15,Info.Width-2,15,OverlayColour);
					
					gfx_init(&Info,Overlay.Mode,OverlayData,Info.Left+5,Info.Top+5,Info.Width-10,10);
					gfx_setfont(&Info,3);
					gfx_setcolour(&Info,OverlayColour);
					gfx_printf(&Info,"Hardware Status");
					
					gfx_init(&Info,Overlay.Mode,OverlayData,Info.Left,Info.Top+15,Info.Width,50);
					
					gfx_boxf(&Header,0,0,Header.Width-1,Header.Height-1,OverlayColour,Transparent);
					gfx_line(&Header,1,15,Header.Width-2,15,OverlayColour);
					
					gfx_init(&Header,Overlay.Mode,OverlayData,Header.Left+5,Header.Top+5,Header.Width-10,10);
					gfx_setfont(&Header,3);
					gfx_setcolour(&Header,OverlayColour);
					gfx_printf(&Header,"%s",VERSION);
					
					gfx_init(&Header,Overlay.Mode,OverlayData,Header.Left,Header.Top+15,Header.Width,20);
					gfx_setfont(&Header,2);
					gfx_setcolour(&Header,OverlayColour);
					gfx_printf(&Header,"This is a sensor input to %s output test with overlay.",
						(char *)(Overlay.Mode==GFX_MODE_VGA?"VGA LCD":
						Overlay.Mode==GFX_MODE_PAL||Overlay.Mode==GFX_MODE_PALI?"PAL video":
						"NTSC video"));
					
					gfx_init(&Header,Overlay.Mode,OverlayData,0,Overlay.Height-40,Overlay.Width,40);
					gfx_setfont(&Header,4);
					gfx_setcolour(&Header,OverlayColour);
					gfx_setjustify(&Header,GFX_JUSTIFY_CENTER);
					gfx_goto(&Header,Header.Width/2,0);
					gfx_printf(&Header,"www.analog.com/blackfin");
				}
				Retry=10;
				State=STATE_OUTPUT_OPEN;
			}
			
			
			case STATE_OUTPUT_OPEN:
			{
				ADI_DEV_CMD_VALUE_PAIR *Config=NULL;
				u32 Refresh=1;
				
				switch (Mode)
				{
					case GFX_MODE_VGA:
					{
						static ADI_DEV_CMD_VALUE_PAIR Args[]={
							{ADI_DEV_CMD_END,NULL}
						};
						Type=DEVICE_NL6448BC33;
						Refresh=VIDEO_REFRESH_VGA;
						Config=Args;
						Mask=1;
						break;
					}
				
					case GFX_MODE_PALI:
					{
						static ADI_DEV_CMD_VALUE_PAIR Args[]={
							{ENCODER_CMD_SET_MODE,(void *)GFX_MODE_PALI},
							{ADI_DEV_CMD_END,NULL}
						};
						Type=DEVICE_ADV7179;
						Refresh=VIDEO_REFRESH_PAL;
						Config=Args;
						Mask=3;
						break;
					}
					
					default: //default mode is NTSC
					{
						static ADI_DEV_CMD_VALUE_PAIR Args[]={
							{ENCODER_CMD_SET_MODE,(void *)GFX_MODE_NTSCI},
							{ADI_DEV_CMD_END,NULL}
						};
						Mode=GFX_MODE_NTSCI;
						Type=DEVICE_ADV7179;
						Refresh=VIDEO_REFRESH_NTSC;
						Config=Args;
						Mask=3;
						break;
					}
				}
				ModeOld=Mode;
				TypeOld=Type;
				
				FrameCount=OutputFrame+(InputType==DEVICE_UNKNOWN?1:4*Refresh);
				if (Output==NULL) Output=device_open(Type,OUTPUT_PPI,(u16 *)OutputData,sizeof(OutputData[0]),CallbackOutput,Config);
				
				if (Output) State=STATE_OUTPUT_FRAME;
				else if (Retry--<=0) State=STATE_OUTPUT; // if cant open output then try again
				break;
			}
			
			
			case STATE_OUTPUT_FRAME:
			{
				if (Frame!=OutputFrame)
				{
					u32 NotFound,Found,Running;
					
					// copy the overlay/input image to the output buffer
					ImageCopy(Mask==0?3:Mask,InputType,Mode,InputBuffer,OutputBuffer,&Overlay,Transparent);
					
					NotFound=gfx_getcolour(&Info,128,128,128);
					Found=gfx_getcolour(&Info,0,128,0);
					Running=gfx_getcolour(&Info,0,160,0);
					
					// update the hardware status window only
					gfx_fill(&Info,Transparent);
					gfx_setfont(&Info,2);
					gfx_setcolour(&Info,Running);
					gfx_goto(&Info,0,0);
					gfx_printf(&Info,"Input frames %lu",InputFrame);
					gfx_goto(&Info,0,10);
					gfx_printf(&Info,"Output frames %lu",OutputFrame);
					
					
					gfx_goto(&Info,0,20);
					switch (device_detect(DEVICE_ADV7179))
					{
						case ADI_DEV_RESULT_SUCCESS:
							gfx_setcolour(&Info,Found);
							gfx_printf(&Info,"Analog device ADV7179 detected");
							break;
						
						case ADI_DEV_RESULT_DEVICE_IN_USE:
							gfx_setcolour(&Info,Running);
							gfx_printf(&Info,"Analog device ADV7179 in use");
							break;
						
						default:
							gfx_setcolour(&Info,NotFound);
							gfx_printf(&Info,"Analog device ADV7179 not found");
					}
					
					
					gfx_goto(&Info,0,30);
					switch (device_detect(DEVICE_OV7648))
					{
						case ADI_DEV_RESULT_SUCCESS:
							gfx_setcolour(&Info,Found);
							gfx_printf(&Info,"Omnivision OV7X48 detected");
							break;
						
						case ADI_DEV_RESULT_DEVICE_IN_USE:
							gfx_setcolour(&Info,Running);
							gfx_printf(&Info,"Omnivision OV7X48 in use");
							break;
						
						default:
							gfx_setcolour(&Info,NotFound);
							gfx_printf(&Info,"Omnivision OV7X48 not found");
					}
					
					
					gfx_goto(&Info,0,40);
					switch (device_detect(DEVICE_MT9V022M))
					{
						case ADI_DEV_RESULT_SUCCESS:
							gfx_setcolour(&Info,Found);
							gfx_printf(&Info,"Micron MT9V022 detected");
							break;
						
						case ADI_DEV_RESULT_DEVICE_IN_USE:
							gfx_setcolour(&Info,Running);
							gfx_printf(&Info,"Micron MT9V022 in use");
							break;
						
						default:
							gfx_setcolour(&Info,NotFound);
							gfx_printf(&Info,"Micron MT9V022 not found");
					}
					
					
					Frame=OutputFrame;
					Mask^=3;
				}
				if ((OutputFrame-FrameCount)&0x80000000) break;
				State=INPUT_PPI==OUTPUT_PPI?STATE_OUTPUT_CLOSE:STATE_INPUT;
				break;
			}
			
			case STATE_OUTPUT_CLOSE:
				State=STATE_INPUT;
				device_close(Output);
				Output=NULL;
				break;
			
			
			default:
				State=STATE_OUTPUT;
		}
	}
	
	
	// all done, so input and output devices
	device_close(Input);
	device_close(Output);
	
	
	// close the Device 
	adi_ssl_Terminate();
	
	WAIL();
	return Code;
}




/******************************************************************************

Exception interrupt handlers

Note: should never get an exception or hardware error...

******************************************************************************/

static ADI_INT_HANDLER(ExceptionHandler)
{
	WAIL();
	return ADI_INT_RESULT_PROCESSED;
}


static ADI_INT_HANDLER(HWErrorHandler)
{
	WAIL();
	return ADI_INT_RESULT_PROCESSED;
}




/******************************************************************************

	Function: CallbackInput
	
	Description: The default callback for input devices

******************************************************************************/

void CallbackInput(void *Handle,u32 Event,void *pArg)
{
	switch (Event)
	{
		case ADI_DEV_EVENT_BUFFER_PROCESSED:
		{
			if (Handle&&pArg&&((ADI_DEV_BUFFER *)pArg)->TwoD.pAdditionalInfo!=ADI_DEV_BUFFER_UNDEFINED)
				adi_dev_Read(
					*((ADI_DEV_DEVICE_HANDLE *)Handle),
					(ADI_DEV_BUFFER_TYPE)((ADI_DEV_BUFFER *)pArg)->TwoD.pAdditionalInfo,
					((ADI_DEV_BUFFER *)pArg));
			
			
			InputBuffer=(ADI_DEV_BUFFER *)pArg;
			InputFrame++;
			break;
		}
	}
}




/******************************************************************************

	Function: CallbackOutput
	
	Description: The output device callback routine

******************************************************************************/

void CallbackOutput(void *Handle,u32 Event,void *pArg)
{
	switch (Event)
	{
		case ADI_DEV_EVENT_BUFFER_PROCESSED:
			if (Handle&&pArg&&((ADI_DEV_BUFFER *)pArg)->TwoD.pAdditionalInfo!=ADI_DEV_BUFFER_UNDEFINED)
				adi_dev_Write(
					*((ADI_DEV_DEVICE_HANDLE *)Handle),
					(ADI_DEV_BUFFER_TYPE)((ADI_DEV_BUFFER *)pArg)->TwoD.pAdditionalInfo,
					((ADI_DEV_BUFFER *)pArg));
			
			
			OutputBuffer=(ADI_DEV_BUFFER *)pArg;
			OutputFrame++;
			break;
	}
}

/*****************************************************************************/
