/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2009, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

/**
 *  \page imagesensor_capture CMOS Image Sensor Capture Example
 *
 *  \section Purpose
 *
 *  This example is aimed to demonstrate how to configure and use the PIO's
 *  capture mode with PDC support to capture image from CMOS Image Sensor.
 *  The output size of the sensor is QVGA. The capture is triggered by
 *  pushbutton. The captured image is displayed on the LCD screen if provided.
 *
 *  \section Requirements
 *
 *  This package can only be used with sam4s-wpir-rd.
 *
 *  \section Description
 *
 *  The PIO Controller integrates an interface able to read data from a CMOS
 *  digital image sensor, a high-speed parallel ADC, a DSP synchronous port in
 *  synchronous mode, etc....
 *
 *  The data lines of the image sensor is directly connected to PIO thanks to
 *  the parallel capture feature. The controlling interface of the sensor which
 *  is called SCCB in OVT product, is connected to a TWI interface on SAM4S chip.
 *  The application employs SRAM for capture buffer the start address of which
 *  is defined by SRAM_BASE (0x60000000 by default)
 *
 *  The capture is trigged by pressing BP1 on the board. Then a QVGA size image
 *  will be transfered to SRAM. After the transfer is done, the image would be
 *  displayed on the LCD if provided. Becuase the default output of the sensor
 *  is YUV format, a coversion to RGB is necessary before flushed to LCD. It's
 *  done on the fly during pixel filling.
 *
 *  \section Usage
 *
 *  -# Build the program and download it inside the evaluation board. Please
 *     refer to the
 *     <a href="http://www.atmel.com/dyn/resources/prod_documents/doc6224.pdf">
 *     SAM-BA User Guide</a>,
 *     application note or to the
 *     <a href="ftp://ftp.iar.se/WWWfiles/arm/Guides/EWARM_UserGuide.ENU.pdf">
 *     IAR EWARM User Guide</a>,
 *     depending on your chosen solution.
 *  -# After dowloading trough sam-ba or IAR flash loader, run the application.
 *  -# If the LCD is mounted, the screen of which will be colored with navy
 *     after startup. And some words may appeared for prompt. The capture could
 *     be done whenever BP1 is pressed.
 *
 */

/** \file
 *
 *  This file contains all the specific code for the imagesensor_capture.
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "board.h"


#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>

/*----------------------------------------------------------------------------
 *        Local definitions
 *----------------------------------------------------------------------------*/

#define IMAGE_WIDTH     (320)
#define IMAGE_HEIGHT    (240)
/** the captured data length per line in different color mode,double the size
in YUV full output*/
#define IMAGE_LINE_COLORED    (IMAGE_WIDTH * 2)
#define IMAGE_LINE_MONO  (IMAGE_WIDTH)

#define SRAM_BASE (0x60000000)
#define CAP_DEST  (SRAM_BASE)
//#define DISP_DEST (SRAM_BASE + 0x4000)

/** TWI clock frequency in Hz (400KHz) */
#define TWCK            400000

#define ON  true
#define OFF false

/** The output formats supported by the sensor*/
typedef enum {
    OUT_RGB,
    OUT_YUV
}OUT_FMT;

#define DEFAULT_SENSOR_YUV422
/** uncomment for monochrome output*/
#define DEFAULT_MODE_COLORED

/*----------------------------------------------------------------------------
 *        Local variables
 *----------------------------------------------------------------------------*/
/** the output format of image sensor*/
OUT_FMT out_format = OUT_YUV;
PIO_OUT_MODE out_mode = PIO_OUT_MONO;
/** the output color mode of image sensor*/

/** is pushbutton triggered*/
volatile bool isTriggered = false;

/** is displaying needed*/
bool isDisplayed = true;

/** capturing destination buffer*/
uint8_t *cap_dest_buf;

/** displaying destination buffer*/
uint8_t *disp_dest_buf;

/** cpaturing rows*/
uint16_t cap_rows = IMAGE_HEIGHT;

/** cap line length*/
uint16_t cap_line = IMAGE_LINE_COLORED;

/** VSYNC flag*/
volatile bool vsync_flag = false;

/** HSYNC flag*/
bool hsync_flag = false;

/** pointer to PIO PDC*/

/** TWI driver*/
static Twid twid;

const Pin pinBP1 = PIN_PUSHBUTTON_1;

const Pin pinHSYNC = PIN_OV_HSYNC;

const Pin pinVSYNC = PIN_OV_VSYNC;

/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/

/** entry for capturing*/
static void _DoCapture(void)
{
    uint8_t *buf;

    /* set capturing destination address*/
    cap_dest_buf = (uint8_t*)CAP_DEST;

    buf = cap_dest_buf;

    cap_rows = IMAGE_HEIGHT;

    /* enable vsync interrupt*/
    PIO_EnableIt(&pinVSYNC);

    /* sync with flag*/
    while(!vsync_flag);

    /* disable first*/
    PIO_DisableIt(&pinVSYNC);

    PIO_Capture_Switch(PIOA,ON);

    /* only using Vsync*/
    PIO_CaptureToBuffer(PIOA,buf, (cap_line * cap_rows)>>2);
    while(!PIO_Capture_BUFF(PIOA));
    PIO_Capture_Switch(PIOA,OFF);
    vsync_flag = false;
}

/** intialize LCD for debugging output*/
static void _Init_LCD(void)
{
    /* Initialize LCD */
    TimeTick_Configure( (BOARD_MCK) );
    LCDD_Initialize();
    LCD_On();
    LCDD_Fill(COLOR_TURQUOISE);

}

/** draw lcd in b&w */
static void _DrawFrame_YUV_BW8( void )
{
    volatile uint32_t dwCursor ;
    uint8_t *pucData ;

    pucData=(uint8_t*)cap_dest_buf ;

    LCD_SetDisplayLandscape(0);

    LCD_SetWindow(0,0,IMAGE_HEIGHT,IMAGE_WIDTH);
    LCD_SetCursor(0,0) ;
    LCD_WriteRAM_Prepare() ;

    for ( dwCursor=IMAGE_WIDTH*IMAGE_HEIGHT ; dwCursor != 0 ; dwCursor-- ,pucData++)
    {
        // Black and White using Y
        LCD_WriteRAMByte( *pucData ) ;
        LCD_WriteRAMByte( *pucData ) ;
        LCD_WriteRAMByte( *pucData ) ;

    }
}

static inline uint8_t _clip( int32_t i )
{
    if ( i > 255 )
    {
        return 255 ;
    }

    if ( i < 0 )
    {
        return 0 ;
    }

    return (uint8_t)i ;
}

/** draw lcd in color with integral algorithm*/
static void _DrawFrame_YUV_ColorInt( void )
{
    uint32_t dwCursor ;
    int32_t C ;
    int32_t D ;
    int32_t E ;
    int32_t dw1_1 ;
    int32_t dw1_2 ;
    int32_t dw1_3 ;
    uint8_t* pucData ;

    pucData=(uint8_t*)cap_dest_buf ;

    LCD_SetDisplayLandscape(0);
    LCD_SetWindow( 0, 0,IMAGE_HEIGHT ,IMAGE_WIDTH  ) ;

    LCD_SetCursor( 0,0 ) ;
    LCD_WriteRAM_Prepare() ;

    for ( dwCursor=IMAGE_WIDTH*IMAGE_HEIGHT ; dwCursor != 0 ; dwCursor-=2, pucData+=4 )
    {
        C=pucData[0] ; // Y1
        C-=16 ;
        D=pucData[3] ; // U
        D-=128 ;
        E=pucData[1] ; // V
        E-=128 ;

        dw1_1=516*D+128 ;
        dw1_2=-100*D-208*E+128 ;
        dw1_3=409*E+128 ;

        // BLUE
         LCD_WriteRAMByte( _clip(( 298 * C + dw1_1 ) >> 8) ) ;
        // GREEN
        LCD_WriteRAMByte( _clip(( 298 * C + dw1_2 ) >> 8) ) ;
        // RED
        LCD_WriteRAMByte( _clip(( 298 * C + dw1_3 ) >> 8) ) ;

        C=pucData[2] ; // Y2
        C-=16 ;
        LCD_WriteRAMByte( _clip(( 298 * C + dw1_1 ) >> 8) ) ;
        LCD_WriteRAMByte( _clip(( 298 * C + dw1_2 ) >> 8) ) ;
        LCD_WriteRAMByte( _clip(( 298 * C + dw1_3 ) >> 8) ) ;

    }
}

static void _Display(void)
{
    if(out_mode == PIO_OUT_COLOR)
    {
        _DrawFrame_YUV_ColorInt();
    }
    else
    {
        _DrawFrame_YUV_BW8();
    }
}

/** turn on or off image sensor power*/
static void _Turn_ImageSensor(bool on)
{
    Pin pinsPower[]={ PINS_OV };
    PIO_Configure(pinsPower,PIO_LISTSIZE(pinsPower));

    if(on)
    {
        PIO_Clear(&pinsPower[0]);
    }
    else
    {
        PIO_Set(&pinsPower[0]);
    }
}


/** Initialize Image Sensor*/
static void _Init_Image_Sensor(const EOV7740_Format eFormat)
{
    /* intitializ Master Clock to xvclk1*/
    Pin pinPCK = PIN_PCK0;
    const Pin pinsTWI[] = {PINS_TWI0};
    Pmc *pmc = (Pmc*) PMC;
    /* Power on*/
    _Turn_ImageSensor(true);

    PIO_Configure(&pinPCK,1);

    /* PLLA is 96MHz so that PCK0 is 96MHz/4 = 24MHz*/
    pmc->PMC_PCK[0] = (PMC_PCK_PRES_DIV4 | PMC_PCK_CSS_PLLACLOCK);

    pmc->PMC_SCER = PMC_SCER_PCK0;

    while(!( pmc->PMC_SCSR & PMC_SCSR_PCK0));

    /* twi*/
    PIO_Configure(pinsTWI,PIO_LISTSIZE(pinsTWI));

    PMC_EnablePeripheral(ID_TWI0);

    TWI_ConfigureMaster(TWI0, TWCK, BOARD_MCK);

    TWID_Initialize(&twid, TWI0);	

    /* Configure TWI interrupts */
    NVIC_DisableIRQ(TWI0_IRQn);
    NVIC_ClearPendingIRQ(TWI0_IRQn);
    NVIC_SetPriority(TWI0_IRQn, 0);
    NVIC_EnableIRQ(TWI0_IRQn);

    while( ov_init(&twid) == 0 )
    {
        printf("-I- Retry init\n\r");
    }
    printf("-I- Init passed\n\r");

    /* OV7740 configuration */
    ov_configure(&twid, eFormat);
}

/** Handler for Button 1 rising edge interrupt.*/
static void _Button1_Handler( const Pin* pin )
{
    isTriggered = true;
}

/** interrupt handler for vsync*/
static void _Vsync_Handler(const Pin *pin)
{
    vsync_flag = true;
}


/** intialize fram marker signal response*/
static void _Init_HVSync_Interrupts(void)
{
    PIO_Configure(&pinVSYNC,1);

    PIO_ConfigureIt(&pinVSYNC, _Vsync_Handler);

    /* Enable PIO controller IRQs. */
    NVIC_EnableIRQ(PIOA_IRQn);
}

/** initialize pushbutton for state transition*/
static void _Init_Pushbutton_Trigger(void)
{
    /* Configure pios as inputs. */
    PIO_Configure( &pinBP1, 1 ) ;

    /* Adjust pio debounce filter patameters, uses 10 Hz filter. */
    PIO_SetDebounceFilter( &pinBP1, 10 ) ;

    /* Initialize pios interrupt handlers, see PIO definition in board.h. */
    PIO_ConfigureIt( &pinBP1, _Button1_Handler ) ; /* Interrupt on rising edge  */

    /* Enable PIO controller IRQs. */
    NVIC_EnableIRQ( (IRQn_Type)pinBP1.id ) ;

    /* Enable PIO line interrupts. */
    PIO_EnableIt( &pinBP1 ) ;

}
/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/
void SysTick_Handler( void )
{
    TimeTick_Increment() ;
}
/**
 *  \brief imagesensor_capture Application entry point.
 *
 *  \return Unused (ANSI-C compatibility).
 *  \callgraph
 */
extern int main( void )
{
    /* Disable watchdkog */
    WDT_Disable( WDT ) ;

    /* OUTPUT color mode*/
#ifdef DEFAULT_MODE_COLORED
    out_mode = PIO_OUT_COLOR;
#else
    out_mode = PIO_OUT_MONO;
#endif
    PIO_InitializeInterrupts(0);

    /* configure SRAM*/
    BOARD_ConfigureSRAM(SMC);

    /* initizale push botton for capturing trigger*/
    _Init_Pushbutton_Trigger();

    /* intialize Frame signal*/
    _Init_HVSync_Interrupts();

    /* initialize PIODC*/
    PIODC_Capture_Init(PIOA, ID_PIOA, out_mode);

    if(isDisplayed)
    {
        TimeTick_Configure(BOARD_MCK);
        Wait(1000); // wait for 1s to be sure LCD is ready for initialization
        _Init_LCD();
        LCDD_DrawString(0,10,"Imagesensor_capture\nexample",COLOR_BLACK);

        LCDD_DrawString(0,70,"Press BP1 Button to\ntake a picture",COLOR_BLACK);

    }

    /** intialize capturing line*/
    if(out_mode == PIO_OUT_COLOR)
    {
        cap_line = IMAGE_LINE_COLORED;
    }
    else if(out_mode == PIO_OUT_MONO)
    {
        cap_line = IMAGE_LINE_MONO;
    }
    else
    {
        cap_line = IMAGE_LINE_COLORED;
    }

#ifdef DEFAULT_SENSOR_YUV422
    _Init_Image_Sensor(QVGA_YUV422);
#endif

    while(1)
    {
        while(!isTriggered);

        /* reset triggere flag */
        isTriggered = false;
        _DoCapture();

        if(isDisplayed)
        {
            _Display();
        }
    }

}

