#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "IMX253_SPI_2.h"
#include "IMX253_Timer.h"
#include "delayer.h"
#include "IMX253_register_map.h"
#include "IMX253_userroi.h"

//Interrupt>
#include <stdlib.h>
#include "xil_io.h"
#include "xil_exception.h"
#include "xparameters.h"
#include "xil_cache.h"
#include "xil_printf.h"
#include "xil_types.h"
#include "xscugic.h"

//Interrupt{
XScuGic InterruptController; 	     /* Instance of the Interrupt Controller */
static XScuGic_Config *GicConfig;
extern int volatile lock_spi = 0;
extern int prenos_ok = 1;

#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
#define INTC_DEVICE_INT_ID XPS_FPGA0_INT_ID

void printBits(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char*) ptr;
    unsigned char byte;
    int i, j;

    for (i=size-1;i>=0;i--)
    {
        for (j=7;j>=0;j--)
        {
            byte = (b[i] >> j) & 1;
            printf("%u", byte);
        }
    }
    puts("");
}

//Interrupt{
void DeviceDriverHandler(void *CallbackRef){
	lock_spi = 0;
	return;
}

int spi_interrupt_ini(u16 DeviceId)
{
	int Status;
	u32 IntID;
	//Initialize the interrupt controller driver so that it is ready to use
	GicConfig = XScuGic_LookupConfig(DeviceId);
	if (NULL == GicConfig) {
		return XST_FAILURE;
	}
	//Initialize the SCU and GIC to enable the desired interrupt configuration
	Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,	GicConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	XScuGic_SetPriorityTriggerType(&InterruptController, INTC_DEVICE_INT_ID,0xA0,0x3);
	//Connect the interrupt handler that will be called when an interrupt occurs for the device
	Status = XScuGic_Connect(&InterruptController, INTC_DEVICE_INT_ID, (Xil_ExceptionHandler)DeviceDriverHandler, (void *)&InterruptController);

	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	//Enable the interrupt for the SPI controller device
	XScuGic_Enable(&InterruptController, INTC_DEVICE_INT_ID);
	//Initialize the exception table and register the interrupt controller handler with the exception table
	Xil_ExceptionInit();
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler) XScuGic_InterruptHandler, &InterruptController);
	//Enable non-critical exceptions
	Xil_ExceptionEnable();
	return XST_SUCCESS;
}

int main()
{
	int write_result;
	int i = 0;
	int spi_buffer = 0;
	int status;
    init_platform();
    imx253_registermap_init();
    prenos_ok = 1;
    spi_set_interrupt_delay(15000000);
    timer_set_mode(0);
    timer_xtrig_width(99);
    userroi_set_start_row(4);
    userroi_set_end_row(3800);
    userroi_set_start_column(4);
    userroi_set_end_column(3000);

    status = spi_interrupt_ini(INTC_DEVICE_ID);

    //Initialization:
    //pin XCLR <= '1';
    //pin INCK <= '101010...';
    spi_set_register(stblvds,0);
    /*
     * 0: 16 channel (default)
     * 1: 8 channel
     * 2: 4 channel
     * STANDBY MODE (str. 66)
     * This sensor stops its operation and goes into standby mode
     * which reduces the power consumption by writing “1” to the 
     * standby control register STANDBY. Standby mode is also 
     * established after power-on or other system reset operation.
     */
    spi_set_register(xmsta,1);
    /*
     * master mode disable
     * This register is probably irrelevant, because XMASTER pin 
     * is HIGH - sensor is operating in slave mode. (page 67)          
     * XMSTA is probably something like "enable" for timing 
     * signals (XVS, XHS) in master mode.    
     * Set the XMSTA register to “0” in order to start
     * the operation after setting to master mode.
     */
    spi_set_register(trigen,1);
    /*
     * 0: normal mode (default)
     * 1: trigger mode
     * Both modes are implemented
     * Exposure time in normal mode is defined by register setting
     * Exposure time in trigger mode is set by function
     * "timer_xtring_width(u32 width);"     
     * The integration time can be controlled by varying the 
     * pulse width that is input to XTRIG pin. The pulse width
     * designated in XHS unit [H]. For the transition from normal
     * mode to trigger mode, set 1 to the register TRIGEN.
     */
    spi_set_register(adbit,1);
    /*
     * 0: 10bit (default)
     * 1: 12bit
     * 2: 8 bit
     * adbit should be always set the same as odbit
     * The output pin width can be selected from 8-bit, 10-bit
     * or 12-bit output using register ADBIT, ODBIT.
     */
    spi_set_register(winmode,0);
    /*
     * //monochromatic: vertical
     * 0: all pixel (default)
     * 1: 1/2 subsampling
     * 2: FD binning
     *
     * //bayer: vertical
     * 0: all pixel (default)
     * 1: 1/2 subsampling
     */
    spi_set_register(hmode,0);
    /*
     * //horizontal
     * 0: all pixel (default)
     * 1: 1/2 subsampling
     */
    spi_set_register(hadd_on,0);
    /*
     * //monochromatic: horizontal binning
     * 0: off (default)
     * 1: on
     *
     * //bayer: horizontal binning
     * 0: off (default)
     */
    spi_set_register(vreverse,0);
    /*
     * 0: normal (default)
     * 1: inverted
     * The sensor readout direction (normal / inverted) in
     * vertical direction can be switched by the VREVERSE register
     * setting and sensor readout direction (normal / inverted)
     * in horizontal direction can be switched by the HREVERSE 
     * register setting.
     */
    spi_set_register(hreverse,0);
    /*
     * 0: normal (default)
     * 1: inverted
     * The sensor readout direction (normal / inverted) in
     * vertical direction can be switched by the VREVERSE register
     * setting and sensor readout direction (normal / inverted)
     * in horizontal direction can be switched by the HREVERSE
     * register setting.
     */
    spi_set_register(odbit,1);
    /*
     * 0: 10bit (default)
     * 1: 12bit
     * 2: 8bit
     * adbit should be always set the same as odbit
     * The output pin width can be selected from 8-bit, 10-bit
     * or 12-bit output using register ADBIT, ODBIT.
     */
    spi_set_register(freq,0);
    /*
     * 0: normal (default)
     * 1: 1/2 data rate
     */
    spi_set_register(oportsel,9);
    /*
     * 1: 8ch
     * 3: 4ch
     * 9: 16ch (default)
     * Output channel selection.
     */
    spi_set_register(tout1sel,1);
    /*
     * 0: low fixed (default)
     * 1: pulse output
     * This sensor has a pulse output function that indicates
     * each state of shutter operation. The pulse output from
     * TOUT1 pin and TOUT2 pin. The rise timing and fall timing
     * of pulse are set by Register. For the reference point
     * (The timing when register value set to 0) to be set, see
     * the table “List of Reference point". The pulse is output
     * asynchronously with other signals on the basis of the
     * sensor internal timing shown in the
     * “List of Reference point" page 76
     */
    spi_set_register(tout2sel,0);
    /*
     * 0: low fixed (default)
     * 1: pulse output
     * This sensor has a pulse output function that indicates
     * each state of shutter operation. The pulse output from
     * TOUT1 pin and TOUT2 pin. The rise timing and fall
     * timing of pulse are set by Register. For the reference
     * point (The timing when register value set to 0) to be
     * set, see the table “List of Reference point". The pulse
     * is output asynchronously with other signals on the basis
     * of the sensor internal timing shown in the
     * “List of Reference point" page 76
     */
    spi_set_register(syncsel,0);
    /*
     * 0: normal output (default)
	 * 3: high-Z
	 * XHS, XVS pin setting
     */
    spi_set_register(pulse1_en_nor,0);
    /*
     * 0: disable (default)
     * 1: enable
     * Pulse1 output in normal mode.
     * page 76
     */
    spi_set_register(pulse1_en_trig,0);
    /*
     * 0: disable (default)
     * 1: enable
     * Pulse1 output in trigger mode
     * page 76
     */
    spi_set_register(pulse1_pol,0);
    /*
     * 0: high active (default)
     * 1: low active
     * Pulse1 polarity selection
     * page 76
     */
    spi_set_register(pulse1_up,0);
    /*
     * 0x00000 (default) - 0xFFFFF
     * Pulse1 active period start timing setting designated in
     * line units from reference point.
     * page 76
     */
    spi_set_register(pulse1_dn,0);
    /*
     * 0x00000 (default) - 0xFFFFF
     * Pulse1 active period end timing setting designated in
     * line units from readout start.
     * page 76
     */
    spi_set_register(pulse2_en_nor,1);
    /*
     * 0: disable (default)
     * 1: enable
     * Pulse2 output in normal mode.
     * page 76
     */
    spi_set_register(pulse2_en_trig,1);
    /*
     * 0: disable (default)
     * 1: enable
     * Pulse2 output in trigger mode
     * page 76
     */
    spi_set_register(pulse2_pol,0);
    /*
     * 0: high active (default)
     * 1: low active
     * Pulse1 polarity selection
     * page 76
     */
    spi_set_register(pulse2_up,0);
    /*
     * 0x00000 (default) - 0xFFFFF
     * Pulse2 active period start timing setting designated in
     * line units from reference point.
     * page 76
     */
    spi_set_register(pulse2_dn,0);
    /*
     * 0x00000 (default) - 0xFFFFF
     * Pulse2 active period end timing setting designated in
     * line units from readout start.
     * page 76
     */
    spi_set_register(incksel0,0x10);
    /*
     * INCK = 37.125 MHz: 	0x10 (default)
	 * INCK = 54 	 MHz: 	0x16
	 * INCK = 74.25  MHz: 	0x10 (default)
	 * Set according to INCK frequency and drive mode.
	 * page 51
     */
    spi_set_register(incksel1,0x00);
    /*
     * INCK = 37.125 MHz: 	0x02
     * INCK = 54 	 MHz: 	0x00 (default)
     * INCK = 74.25  MHz: 	0x00 (default)
     * Set according to INCK frequency and drive mode.
     * page 51
     */
    spi_set_register(incksel2,0x10);
    /*
     * INCK = 37.125 MHz: 	0x10 (default)
     * INCK = 54 	 MHz: 	0x16
     * INCK = 74.25  MHz: 	0x10 (default)
     * Set according to INCK frequency and drive mode.
     * page 51
     */
    spi_set_register(incksel3,0x00);
    /*
     * INCK = 37.125 MHz: 	0x02
     * INCK = 54 	 MHz: 	0x00 (default)
     * INCK = 74.25  MHz: 	0x00 (default)
     * Set according to INCK frequency and drive mode.
     * page 51
     */
    spi_set_register(shs,0x00014);
    /*
     * 0x0x00000 - 0x00014 (default) - 0xFFFFF
     * Important:
     * The integration time can be controlled by varying the
     * electronic shutter timing. In the electronic shutter
     * settings, the integration time is controlled by the
     * SHS [19:0] register. page 69, 70
     */
    spi_set_register(vint_en,1);
    /*
     * 0: disable
     * 1: enable (default)
     * Setting of Interrupt mode in Trigger Mode
     */
    spi_set_register(lowlagtrg,0);
    /*
     * 0: sequential trigger mode (default)
     * 1: fast trigger mode (not implemented/not verified)
     * Selection of trigger mode
     */
    spi_set_register(overlap_roi_en,0);
    /*
     * 0: ROI mode (default)
     * 1: overlap ROI mode
     * ROI mode setting
     */
    spi_set_register(gain,0x000);
    /*
     * 0x000: 0db (default) - 0x1E0: 48db
     * Gain setting. 0.1dB step. Setting value: Gain[dB] * 10
     */
    spi_set_register(gaindly,0x08);
    /*
     * 0x08 : gain reflect at the frame
     * 0x09 : gain reflect at the next frame (same timing as
     *    SHS reflecting output)
     * others: setting prohibited (default (0x00) - probably err.)
     * Setting of Gain Reflection Timing at Normal Mode.
     */
    spi_set_register(blklevel,0x0F0);
    /*
     * 0x000 - 0xFFF
     * The black level offset (offset variable range: 000h to
     * 1FFh) can be added relative to the data in which the
     * digital gain modulation was performed by the 
     * BLKLEVEL [11:0] register. When the BLKLEVEL [11:0] setting
     * is increased by 1 LSB, the black level is increased by 1 LSB.
	 * Recommended values:
     * 0x00F: 8bit
     * 0x03C: 10bit (default)
     * 0x0F0: 12bit
     */
    spi_set_register(vopb_vblk_hwidth,0x1000);
    /*
     * 0x0000 - 0x1000 (default) - 0xFFFF
     * VOPB effective area and V Blank width setting.
     * Něco s ROI, zdá se.
     */
    spi_set_register(finfo_hwidth,0x0800);
    /*
     * 0x0000 - 0x0800 (default) - 0xFFFF
     * FINFO width setting.
     * Něco s ROI, zdá se.
     */



    spi_set_register(standby,1);
    /*
     * 0: operating
     * 1: standby (default)
     * This sensor stops its operation and goes into standby mode
     * which reduces the power consumption by writing “1” to 
     * the standby control register STANDBY. Standby mode is also
     * established after power-on or other system reset operation.
     */

    timer_enable();
    while(1){

    }
    cleanup_platform();
    return 0;
}
