/*
 * Interrupts example. For Vitis Unified
 *
 *
 */
 #include <stdio.h>
#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"
#include "xil_util.h"
#include "xgpio.h"
#include "xtmrctr.h"
#include "xinterrupt_wrap.h"


#define ASCENDING_MASK 0x1
#define DESCENDING_MASK 0x2
#define ENDING_MASK 0x8

#define TIMER_COUNTER_0	 0
#define GPIO_CHAN_1 1

volatile int Step = 0, Ascending = 0, IntFlag = 0, endProg = 0;

int SetupInterrupts(u32 BaseAddr, XGpio *GpioButtonsPtr, XTmrCtr *TimerPtr);
int SetupPeripherals(XGpio *GpioButtonsPtr, XGpio *GpioLedsPtr, XTmrCtr *TimerPtr);
static void GpioButtonsHandler(void *CallBackRef);
static void TimerHandler(void *CallBackReg);


int main(void)
{
  XGpio GpioLeds, GpioButtons;
  XTmrCtr Timer;
  int Status;

  printf("Interrupts Example for Vitis Unified\r\n");

  Status = SetupPeripherals(&GpioButtons, &GpioLeds, &Timer);  
    if (Status != XST_SUCCESS) {
      printf("ERROR: Set up of peripherals failed \n\r");
      return XST_FAILURE;
  }   

  Status = SetupInterrupts(XPAR_XSCUGIC_0_BASEADDR, &GpioButtons, &Timer);
  if (Status != XST_SUCCESS) {
      printf("ERROR: Set up of interrupts failed \n\r");
      return XST_FAILURE;
  }

  // Configure timer
  XTmrCtr_SetResetValue(&Timer, TIMER_COUNTER_0, 1000000);
  XTmrCtr_SetOptions(&Timer, TIMER_COUNTER_0, XTC_INT_MODE_OPTION | XTC_DOWN_COUNT_OPTION | XTC_AUTO_RELOAD_OPTION);

  // Configure GPIO interrupts
  XGpio_InterruptEnable(&GpioButtons, XGPIO_IR_CH1_MASK);
  XGpio_InterruptGlobalEnable(&GpioButtons);

  // Start timer
  XTmrCtr_Start(&Timer, TIMER_COUNTER_0);

  xil_printf("Button 0. counts up (.) - Button 1 counts down (*) - Button 3 ends prog\n\r");
  
  while(!endProg) {
      if (IntFlag) {
		  if (Ascending) {
			  XGpio_DiscreteWrite(&GpioLeds, 1, Step++);
			  if (Step>63) {Step=0; xil_printf(". ");}
		  } else {
			  XGpio_DiscreteWrite(&GpioLeds, 1, Step--);
			  if (Step<0) {Step=63; xil_printf("* ");}
		  }
		  IntFlag = 0;
      }
  }

  xil_printf("\n\rEnding Program Interrupts in Vitis Unified (Pressed button 3)\n\r");

  // Should never reach this
  return XST_SUCCESS;
}


int SetupPeripherals(XGpio *GpioButtonsPtr, XGpio *GpioLedsPtr, XTmrCtr *TimerPtr) {

  int Status;

  Status = XGpio_Initialize(GpioButtonsPtr, XPAR_PERIPHERALS_AXI_GPIO_BUTTONS_BASEADDR);
  if (Status != XST_SUCCESS) {
      printf("ERROR: Initialization of buttons GPIO failed \n\r");
      return XST_FAILURE;
  }

  Status = XGpio_Initialize(GpioLedsPtr, XPAR_PERIPHERALS_AXI_GPIO_LEDS_BASEADDR);
  if (Status != XST_SUCCESS) {
      printf("ERROR: Initialization of LEDs GPIO failed \n\r");
      return XST_FAILURE;
  }

  Status = XTmrCtr_Initialize(TimerPtr, XPAR_PERIPHERALS_AXI_TIMER_0_BASEADDR);
  if (Status != XST_SUCCESS) {
      printf("ERROR: Initialization of Timer failed \n\r");
      return XST_FAILURE;
  }

  return XST_SUCCESS;
}


int SetupInterrupts(u32 BaseAddr, XGpio *GpioButtonsPtr, XTmrCtr *TimerPtr)
{
  int Status;
  UINTPTR IntcBaseAddr;
  IntcBaseAddr = XGetEncodedIntcBaseAddr(XPAR_XSCUGIC_0_BASEADDR, XINTC_TYPE_IS_SCUGIC);

  Status =  XSetupInterruptSystem(GpioButtonsPtr, GpioButtonsHandler, XPAR_PERIPHERALS_AXI_GPIO_BUTTONS_INTERRUPTS, IntcBaseAddr, XINTERRUPT_DEFAULT_PRIORITY);
  if (Status != XST_SUCCESS) {
    printf("ERROR: Setting Buttons ISR failed \n\r");
    return XST_FAILURE;
  }

  Status =  XSetupInterruptSystem(TimerPtr, TimerHandler, XPAR_PERIPHERALS_AXI_TIMER_0_INTERRUPTS, IntcBaseAddr, XINTERRUPT_DEFAULT_PRIORITY);
  if (Status != XST_SUCCESS) {
    printf("ERROR: Setting Timer ISR failed \n\r");
    return XST_FAILURE;
  }

  return XST_SUCCESS;
}


static void GpioButtonsHandler(void *CallBackRef)
{
  XGpio *GpioButtonsPtr = (XGpio *) CallBackRef;

//   if (XGpio_DiscreteRead(GpioButtonsPtr, 1) & ASCENDING_MASK) Ascending = 1;
//   if (XGpio_DiscreteRead(GpioButtonsPtr, 1) & DESCENDING_MASK) Ascending = 0;
  u32 btns = XGpio_DiscreteRead(GpioButtonsPtr, GPIO_CHAN_1);
  if (btns & ENDING_MASK)     endProg = 1;
  if (btns & ASCENDING_MASK)  Ascending = 1;
  if (btns & DESCENDING_MASK) Ascending = 0;

  XGpio_InterruptClear(GpioButtonsPtr, XGPIO_IR_CH1_MASK);
}


static void TimerHandler(void *CallBackReg) {

  XTmrCtr *TimerPtr = (XTmrCtr *) CallBackReg;
  uint32_t csr;

  // Read the control/status register to check interrupt flag bit
  csr = XTmrCtr_ReadReg(TimerPtr->BaseAddress, TIMER_COUNTER_0, XTC_TCSR_OFFSET);

  if (csr & XTC_CSR_INT_OCCURED_MASK) {
    // Interrupt acknowledgement: Clear the timer interrupt
    csr |= XTC_CSR_INT_OCCURED_MASK;
    XTmrCtr_WriteReg(TimerPtr->BaseAddress, TIMER_COUNTER_0, XTC_TCSR_OFFSET, csr);
    // Signal interrupt on global flag
	IntFlag = 1;
  }
}