/******************************************************************************
*
* Copyright (C) 2010 - 2015 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/***************************** Include Files *********************************/

#include "xparameters.h"		/* XPAR parameters */
#include "xusbps.h"			/* USB controller driver */
#include "xscugic.h"
#include "xusbps_cdc_ch9.h"		/* Generic Chapter 9 handling code */
#include "xusbps_cdc_ch9_cdc.h"	/* Storage class handling code */
#include "xil_exception.h"
#include "xpseudo_asm.h"
#include "xreg_cortexa9.h"
#include "xil_cache.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xusbps_cdc.h"
#include "xusbps_cdc_irq.h"		/* USB interrupt processing code */
#include "xusbps_cdc_buffer.h"
#include "xgpiops.h"
#include "xspips.h"

volatile uint8_t Ep2_Tx_Done = 1;

XSpiPs SPI0;
XSpiPs_Config *SpiConfig;

XUsbPs usb;

uint8_t Set_Channel[2];	//洢͵ͨ8*2=16bit
unsigned int restart;		//ź
unsigned int DataNum;		//
unsigned char ChannelSel;

uint32_t send_data = 0;//ֽ
uint8_t ADC_Raw_Data[2] = {0x00,0x00};				//洢ȡADCԭʼ8*2=16bit
uint32_t uiIdx = 0;

void usb_get_cmd(void);
void adc_data_usb_send(void);

static int setup_interrupts(XScuGic *intc) {
	int status;
	XScuGic_Config *intc_config;

	intc_config = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
	if (NULL == intc_config) {
		return XST_FAILURE;
	}

	status = XScuGic_CfgInitialize(intc, intc_config, intc_config->CpuBaseAddress);
	if (status != XST_SUCCESS) {
		return status;
	}

	Xil_ExceptionInit();

	/* Connect the GIC interrupt handler to the exception vector in the processor */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
								 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
								 intc);

	return XST_SUCCESS;
}

//static void reset_usb(void) {
//	// Ensure that the PHY is out of reset
//	volatile u32 *gpio_base;
//	volatile u32 *gpio_oen;
//	volatile u32 *gpio_dir;
//
//	/* Ensure that the USB PHY is not in reset */
//	gpio_base = (u32 *)0xE000A000;
//	gpio_oen = (u32 *)0xE000A208;
//	gpio_dir = (u32 *)0xE000A204;
//
//	*(gpio_oen) |= 0x00000080;
//	*(gpio_dir) |= 0x00000080;
//	*gpio_base = 0xff7f0080;
//}

static void reset_usb(void) {
    XGpioPs Gpio;
    XGpioPs_Config *ConfigPtr;

    ConfigPtr = XGpioPs_LookupConfig(XPAR_PS7_GPIO_0_DEVICE_ID);
    XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);

    XGpioPs_SetDirectionPin(&Gpio, 46, 1);
    XGpioPs_SetOutputEnablePin(&Gpio, 46, 1);
    XGpioPs_WritePin(&Gpio, 46, 0x1);
}


int main(void) {
	int status;
	XUsbPs usb;
	XScuGic intc;
	u8 text_buffer[512];
	u32 bytes;

	xil_printf("\n\n--- USB CDC ACM Test Application ---\n\n");

	reset_usb();

	//SPIʼ
	//豸IDñвãȻʼ
	SpiConfig = XSpiPs_LookupConfig(XPAR_XSPIPS_0_DEVICE_ID);
	XSpiPs_CfgInitialize(&SPI0, SpiConfig, SpiConfig->BaseAddress);

	//SPIķƵ
	XSpiPs_SetClkPrescaler(&SPI0, XSPIPS_CLK_PRESCALE_16);

	//SPIģʽʹ
	XSpiPs_SetOptions(&SPI0, XSPIPS_MASTER_OPTION | XSPIPS_CLK_ACTIVE_LOW_OPTION | XSPIPS_CLK_PHASE_1_OPTION);

	// Set up and configure the interrupt system
	status = setup_interrupts(&intc);
	if (status != XST_SUCCESS) {
		xil_printf("ERROR: Unable to initialize interrupt system: %d\n", status);
		exit(1);
	}

	status = xusbps_cdc_register_interrupt(&intc, &usb, XPAR_PS7_USB_0_INTR);
	if (status != XST_SUCCESS) {
		xil_printf("ERROR: Unable to register USB interrupts: %d\n", status);
		exit(1);
	}

	// Enable interrupts in the processor
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

	/* Initialize the USB controller */
	status = xusb_cdc_init(&usb, XPAR_PS7_USB_0_DEVICE_ID, XPAR_PS7_USB_0_INTR, 64 * 1024);
	if (status != XST_SUCCESS) {
		xil_printf("ERROR: Unable to set up USB controller: %d\n", status);
		exit(1);
	}



//		while(1) {
//
//				bytes = xusb_cdc_rx_bytes_available() > 5 ? 5 : xusb_cdc_rx_bytes_available();
//				if (bytes != 0) {
//					bytes = xusb_cdc_receive_data(text_buffer, bytes);
//					text_buffer[bytes] = 0;
//						xusb_cdc_send_data(&usb, (u8 *)hello, 9);
//						xusb_cdc_send_data(&usb, (u8 *)text_buffer, 1);
//						xusb_cdc_send_data(&usb, (u8 *)hai, 2);
//
//						}
//		}
//	return 0;

	while(1) {

			bytes = xusb_cdc_rx_bytes_available();

			if(bytes != 0)
			{
				//xil_printf("bytes is %d\n", bytes);
				if(bytes > 512)
					bytes = 512;
				else
					bytes = xusb_cdc_rx_bytes_available();
			}

			if (bytes != 0) {
				bytes = xusb_cdc_receive_data(text_buffer, bytes);
				xusb_cdc_send_data(&usb, (u8 *)text_buffer, bytes);
			}


	}
	return 0;


}

//
void usb_get_cmd(void)
{
	u8 text_buffer[16];
	u32 bytes;
	unsigned char cmd[8] = {0};
	unsigned char cnt = 0;
	bytes = xusb_cdc_rx_bytes_available();
	if(bytes != 0)
	{
		bytes = xusb_cdc_receive_data(text_buffer, bytes);
		//ָ
		unsigned char cmd_num = (bytes)/8;//ָ
		for(int i = 0; i < cmd_num; i++){
			memcpy(cmd,text_buffer + 8*i, 8);
			if((cmd[0] == 0x55) && (cmd[1] == 0xA5) && (cmd[7] == 0xF0))  //֡ͷ֡β
			{
				switch(cmd[2])
				{
					case  1:ChannelSel =(cmd[5] << 8) | cmd[6];
							while(ChannelSel >=1)
							{
								ChannelSel>>=1;
								cnt++;
							}
							Set_Channel[0] = (cnt << 3) - 1;break;
					case  2:DataNum = (cmd[3] << 24) | (cmd[4] << 16) | (cmd[5] << 8) | cmd[6];
							restart = 1;break;
					default:break;
				}
			}
		}
	}
}

//ݷ
void adc_data_usb_send(void)
{
	if(send_data ==0){
		for(int i=0;i<DataNum;i++){
			// ͨSPIȡADC
			XSpiPs_PolledTransfer(&SPI0, Set_Channel,ADC_Raw_Data, 2);
			// дֽ
			*(volatile u8 *)(0x1800000 + 2*i) = ADC_Raw_Data[1] ;
			// д2ֽ (עƫ 2*i + 1)
			*(volatile u8 *)(0x1800000 + 2*i + 1) = ADC_Raw_Data[0]& 0x0F;
		}
		send_data = DataNum*2;
	}
	if(Ep2_Tx_Done){
		Ep2_Tx_Done = 0;
		if(send_data <= 512){
			xusb_cdc_send_data(&usb,(u8*)(0x1800000 + uiIdx),send_data);
			xusb_cdc_send_data(&usb, NULL, 0); //  ZLP ǿƽ
			send_data = 0;
			uiIdx = 0;
			restart = 0;
		}
		else{
			xusb_cdc_send_data(&usb,(u8*)(0x1800000 + uiIdx),512);
			send_data = send_data - 512;
			uiIdx += 512 ;
		}
	}
}
