//=================================================================================
//=================================================================================

/*****************************************************************************/
/* Most of this code came from the design example generted automatically by XPS. 
   In the corresponding project, I add pushbuttons with interrupt, LEDs and the
   uart light. A series of directories are added as the board support package,
   which contains, among others, an xgpio and intc directories. This code assumes
   that there is an interrupt controller in the hardware system and the GPIO device 
   is connected to the interrupt controller.
 
   The buttons and LEDs are on 2 seperate channels of the GPIO so that interrupts
   are not caused when the LEDs are turned on and off.
******************************************************************************/

/***************************** Include Files *********************************/

#include "xparameters.h"
#include "xgpio.h"
#include "xil_exception.h"
#include "xintc.h"
#include "stdio.h"

/************************** Constant Definitions *****************************/
/* The following constants map to the names of the hardware instances that
   were created in the EDK XPS system.  They are only defined here such that
   a user can easily change all the needed device IDs in one place.  */
#define GPIO_DEVICE_ID		XPAR_PUSH_BUTTONS_3BIT_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_INTC_0_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID	XPAR_INTC_0_GPIO_1_VEC_ID
#define GPIO_CHANNEL1 1

/* The following constants define the positions of the buttons and LEDs each
   channel of the GPIO */
#define GPIO_ALL_LEDS		0xFFFF
#define GPIO_ALL_BUTTONS	0xFFFF

/* The following constants define the GPIO channel that is used for the buttons
   and the LEDs. They allow the channels to be reversed easily.  */
#define BUTTON_CHANNEL	 1	/* Channel 1 of the GPIO Device */
#define LED_CHANNEL	 2	/* Channel 2 of the GPIO Device */
#define BUTTON_INTERRUPT XGPIO_IR_CH1_MASK  /* Channel 1 Interrupt Mask */

/* The following constant determines which buttons must be pressed at the same
   time to cause interrupt processing to stop and start */
#define INTERRUPT_CONTROL_VALUE 0x7

/* The following constant is used to wait after an LED is turned on to make
   sure that it is visible to the human eye.  This constant might need to be
   tuned for faster or slower processor speeds. */
#define LED_DELAY	 1000000

#define INTR_DELAY	0x00FFFFFF


/************************** Function Prototypes ******************************/
void GpioDriverHandler(void *CallBackRef);

int GpioIntrExample(XIntc* IntcInstancePtr, XGpio* InstancePtr,
			u16 DeviceId, u16 IntrId,
			u16 IntrMask, u32 *DataRead);

int GpioSetupIntrSystem(XIntc* IntcInstancePtr, XGpio* InstancePtr,
			u16 DeviceId, u16 IntrId, u16 IntrMask);

void GpioDisableIntr(XIntc* IntcInstancePtr, XGpio* InstancePtr,
			 u16 IntrId, u16 IntrMask);

/************************** Variable Definitions *****************************/

/* The following are declared globally so they are zeroed and so they are
   easily accessible from a debugger */
XGpio Gpio; /* The Instance of the GPIO Driver */
XIntc Intc; /* The Instance of the Interrupt Controller Driver */

static u16 GlobalIntrMask; /* GPIO channel mask that is needed by
			    * the Interrupt Handler */

static volatile u32 IntrFlag; /* Interrupt Handler Flag */

/****************************************************************************/
/* Main initializes the GPIO device, sets up the interrupts and provides 
   a foreground loop such that interrupt can occur in the background.  
   IntcInstancePtr is a reference to the Interrupt Controller driver Instance,
   GpioInstancePtr is a reference to the GPIO driver Instance, GPIO_DEVICE_ID is 
   the XPAR_<GPIO_instance>_DEVICE_ID value from xparameters.h INTC_GPIO_INTERRUPT_ID 
   is XPAR_<INTC_instance>_<GPIO_instance>_IP2INTC_IRPT_INTR value from xparameters.h
   GPIO_CHANNEL1 is the GPIO channel mask, DataRead is the pointer where the data read 
   from GPIO Input is returned.
*****************************************************************************/
int main(void)
   {
   int Status;
   u32 DataRead;
   XIntc *IntcInstancePtr = &Intc;
   XGpio *GpioInstancePtr = &Gpio;
   u32 delay;

   IntrFlag = 0;

   print("Press button to Generate Interrupt\r\n");

/* Initialize the GPIO driver. Basically copies the BaseAddress, InterruptPresent and 
   and IsDual fields from the XGpio_ConfigTable into the instance pointer, and sets the
   IsReady field to 'ready'. Note the instance typedef has no DeviceId field -- it is
   replaced with the 'IsReady' field. This function defined in 
   microblaze_0/libsrc/gpio_v3_00_a/src/xgpio_sinit.c */
   Status = XGpio_Initialize(GpioInstancePtr, GPIO_DEVICE_ID);
   if (Status != XST_SUCCESS) 
      { 
      print("Failed to initialize GPIO driver!\n");
      return; 
      }

/* The following statements set up the GPIO for Interrupts. First set GlobalIntrMask to the 
   GPIO channel mask so the ISR can access it globally. */
   GlobalIntrMask = GPIO_CHANNEL1;

/* Initialize the interrupt controller driver so that it's ready to use.
   Specify the Interrupt controller device ID that was generated in xparameters.h.
   This function defined in microblaze_0/libsrc/intc_v2_02_a/src/xintc.c */
   Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
   if (Status != XST_SUCCESS) 
      { 
      print("Failed to initialize Interrupt Controller driver!\n");
      return; 
      }

/* Hook up the interrupt service routine. Pass the GpioInstancePtr as data to the ISR
   when called. Never returns anything but XST_SUCCESS! This function defined in 
   microblaze_0/libsrc/intc_v2_02_a/src/xintc.c */
   Status = XIntc_Connect(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID, 
      (XInterruptHandler)GpioDriverHandler, GpioInstancePtr);

/* Enable the GPIO channel interrupts so that push button can be detected and enable 
   interrupts for the GPIO device. Defined in 
   microblaze_0/libsrc/gpio_v3_00_a/src/xgpio_intr.c */
   XGpio_InterruptEnable(GpioInstancePtr, GPIO_CHANNEL1);

/* Enable the global interrupt signal for the GPIO channel. Also defined in 
   microblaze_0/libsrc/gpio_v3_00_a/src/xgpio_intr.c */ 
   XGpio_InterruptGlobalEnable(GpioInstancePtr);

/* Enable this specific interrupt vector on the interrupt controller. Also defined in
   microblaze_0/libsrc/intc_v2_02_a/src/xintc.c */
   XIntc_Enable(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID);

/* Initialize the exception table and register the interrupt controller handler with the 
   exception table. Defined in the xil functions (somewhere). */
   Xil_ExceptionInit();

/* The XIntc_InterruptHandler function is defined in 
   microblaze_0/libsrc/intc_v2_02_a/src/xintc_intr.c but simply calls
   XIntc_DeviceInterruptHandler which is defined in microblaze_0/libsrc/intc_v2_02_a/src/xintc_l.c 
   This function services all interrupts, calling the handlers for those interrupts that are
   active and then acknowledging them. */
   Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XIntc_InterruptHandler,
      IntcInstancePtr);

/* Enable non-critical exceptions */
   Xil_ExceptionEnable();

/* Start the interrupt controller such that interrupts are recognized and handled by the 
   processor. Writes MER is '11' -- preventing software testing. Enables the connection between
   the interrupt controller and the processor. Defined in  microblaze_0/libsrc/intc_v2_02_a/src/xintc.c */
   Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);

   if (Status != XST_SUCCESS) 
      { 
      print("Failed to setup Interrupt Controller!\n");
      return; 
      }

   delay = 0;
   while (!IntrFlag && (delay < INTR_DELAY)) 
      { delay++; }

/* Disable the interrupt in the GPIO */
   XGpio_InterruptDisable(GpioInstancePtr, GPIO_CHANNEL1);

/* Disable in the GPIO interrupt in the controller (xintc.c) */
   XIntc_Disable(IntcInstancePtr, INTC_GPIO_INTERRUPT_ID);

   DataRead = IntrFlag;

   if ( Status == 0 )
      {
      if (DataRead == 0)
         print("No button pressed. \r\n");
      else
         print("Gpio Interrupt Test PASSED. \r\n");
      } 
   else 
      { print("Gpio Interrupt Test FAILED.\r\n"); }
   }


/******************************************************************************/
/* This is the interrupt handler routine for the GPIO for this example.
  @param	CallbackRef is the Callback reference for the handler.
  @return	None.
******************************************************************************/
void GpioDriverHandler(void *CallbackRef)
   {
   XGpio *GpioPtr = (XGpio *)CallbackRef;

   IntrFlag = 1;

/* Clear the Interrupt */
   XGpio_InterruptClear(GpioPtr, GlobalIntrMask);
   }

