#define mainISR_TASK_PRIORITY			( tskIDLE_PRIORITY + 3 )
#define mainISRTASK_LED					( 2 )
#define mainT5PRESCALAR					( 6 )
#define mainT5_SEMAPHORE_RATE			( 31250 )

static void prvISRBlockTask( void* pvParameters )
{
    /* local variables marked as volatile so the compiler does not optimize them away */
    volatile uint64_t resAcc;
    volatile uint32_t arg1, arg2;

        /* Create the semaphore used to signal this task */
        vSemaphoreCreateBinary( xBlockSemaphore );

        /* Set up timer 5 to generate an interrupt every 50 ms */
	T5CON = 0;
	TMR5 = 0;

	/* Timer 5 is going to interrupt at 20Hz Hz. (40,000,000 / (64 * 20) */
        T5CONbits.TCKPS = mainT5PRESCALAR;
	PR5 = mainT5_SEMAPHORE_RATE;

	/* Setup timer 5 interrupt priority to be the maximum allowed */
	IPC6bits.T5IP = ( configMAX_SYSCALL_INTERRUPT_PRIORITY );

	/* Clear the interrupt as a starting condition. */
	IFS0bits.T5IF = 0;

	/* Enable the interrupt. */
	IEC0bits.T5IE = 1;

	/* Start the timer. */
	T5CONbits.TON = 1;

        arg1 = 10;
        arg2 = 2;

        for( ;; )
        {
            /* block on the binary semaphore given by an ISR */
            xSemaphoreTake( xBlockSemaphore, portMAX_DELAY );

            vParTestToggleLED( mainISRTASK_LED );
            /* perform some maths operations to exercise the accumulators */
            resAcc = resAcc * arg2 + arg1;
        }
}
/*-----------------------------------------------------------*/

void vT5InterruptHandler( void )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

        /* This function is the handler for the peripheral timer interrupt.
         The interrupt is initially signalled in a separate assembly file
         which switches to the system stack and then calls this function.
         It gives a semaphore which signals the prvISRBlockTask */
        xSemaphoreGiveFromISR( xBlockSemaphore, &xHigherPriorityTaskWoken );

        /* Clear the interrupt */
        IFS0CLR = _IFS0_T5IF_MASK;

        portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

/*-----------------------------------------------------------*/

#define DMA_BUFF_SIZE   400
uint32_t dmaBuff[2][DMA_BUFF_SIZE];
static void dmaTask(void* pvParameters)
{
    uint32_t i;
    /* this tasks hammers the dma copying data from one buffer to another */
    DMACONbits.SUSPEND = 1; //Suspend ALL DMA transfers

    /* currently the data will be placed in the cache and nothing will be copied
     * by the dma as it only accesses physical memory, this test is designed to stress the system
     * and confirm correct operation in a heavy interrupt environment */
    for(i = 0; i < DMA_BUFF_SIZE; i++) {
        dmaBuff[0][i] = i;
    }

    /* set the transfer event control: what event is to start the DMA transfer */
    DCH1ECONbits.CHSIRQ = _TIMER_6_VECTOR;
    DCH1ECONbits.SIRQEN = 1;

    /* set up transfer */
    DCH1SSA = KVA_TO_PA((void*) &dmaBuff[0][0]);
    DCH1DSA = KVA_TO_PA((void*) &dmaBuff[1][0]);
    DCH1SSIZ = DMA_BUFF_SIZE;
    DCH1DSIZ = DMA_BUFF_SIZE;
    DCH1CSIZ = 4;

    /* setup interrupt response */
    IPC33bits.DMA1IP = 3;
    DCH1INTbits.CHBCIE = 1;
    IEC4bits.DMA1IE = 1;
    DCH1CONbits.CHPRI = 0b10;

    /* once we configured the DMA channel we can enable it */
    DCH1CONbits.CHEN = 1;
    DMACONbits.ON = 1;
    DMACONbits.SUSPEND = 0;

    /* setup T6 to trigger the transfers */
    T6CON = 0x0000;
    IEC0CLR = _IEC0_T6IE_MASK;
    IFS0CLR = _IFS0_T6IF_MASK;
    TMR6 = 0;
    PR6 = 1;
    T6CONSET = _T6CON_ON_MASK;

    /* once the dma is setup we delete this task */
    vTaskDelete(NULL);
}

void __attribute__((vector(_DMA1_VECTOR), interrupt(ipl3))) DMAInterruptHandler(void)
{
    portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
    uint32_t i;

    /* clear the destination buffer */
    for(i = 0; i < DMA_BUFF_SIZE; i++) {
        dmaBuff[1][i] = 0;
    }

    xSemaphoreGiveFromISR( xBlockSemaphore, &xHigherPriorityTaskWoken );

    /* we have just finished copying from buffer0 to buffer 1 so restart the copy operation */
    DCH1INTCLR = _DCH1INT_CHBCIF_MASK;
    IFS4CLR = _IFS4_DMA1IF_MASK;
    DCH1CONSET = _DCH1CON_CHEN_MASK;
    portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}

/*-----------------------------------------------------------*/

 * The Blinky ISR Test:
 * This demonstrates triggering an ISR from a peripheral timer. A task is created
 * which blocks on a semaphore. Separately a peripheral timer is set to cause an
 * interrupt every 50ms. The ISR handler (in a separate assembly file) then
 * releases the semaphore which causes the task to unblock and toggle an LED. This
 * sequence tests operation of the ISR and system stack handling.
 *
static void prvISRBlockTask( void *pvParameters );
static void dmaTask(void *pvParameters);

/* The timer 5 interrupt handler.  As this interrupt uses the FreeRTOS assembly
entry point the IPL setting in the following function prototype has no effect. */
void __attribute__( (interrupt(ipl3), vector(_TIMER_5_VECTOR))) vT5InterruptWrapper( void );

/*-----------------------------------------------------------*/

/* The semaphore used to signal the ISRBlockTask */
static xSemaphoreHandle xBlockSemaphore;


//                xTaskCreate( prvISRBlockTask, ( signed char * ) "ISR", configMINIMAL_STACK_SIZE, ( void * ) NULL, mainISR_TASK_PRIORITY, NULL );
//                xTaskCreate( dmaTask, (signed char *) "DMA", configMINIMAL_STACK_SIZE, (void*) NULL, 2, NULL);
