/*
 * File: ping-pong DMA0 and DMA2
 * bit outputs on RA0 and RB0
 * Bruce Land
 * Target PIC:  PIC32MX250F128B
 */
// all peripheral library includes
#include <plib.h>

#pragma config FNOSC = FRCPLL, POSCMOD = OFF
//#pragma config FNOSC = PRIPLL, POSCMOD = EC, OSCIOFNC = OFF
#pragma config FPLLIDIV = DIV_2, FPLLMUL = MUL_20, FPLLODIV = DIV_2  //40 MHz
#pragma config FPBDIV = DIV_1 // PB 40 MHz
// turn off alternative functions for port pins B.4 and B.5
#pragma config JTAGEN = OFF, DEBUG = OFF
#pragma config FSOSCEN = OFF

// DMA tables
unsigned char LED_pattern0[]=
{
	0x01,	0x00,	0x01,	0x00,	0x01,	0x00,	0x01,	0x00,
	0x01,	0x00,	0x01,	0x00,	0x01,	0x00,	0x01,	0x00
};
// DMA tables
unsigned char	LED_pattern1[]=
{
	0x01,	0x01,	0x00,	0x00,	0x01,	0x01,	0x00,	0x00,
	0x01,	0x01,	0x00,	0x00,	0x01,	0x01,	0x00,	0x00
};

// to clear bits in DMA 0 flag register
// using DMA 1 transfer
unsigned short clear_flag = 0x0000 ;

// === Main  ======================================================
void main(void) {
 
    SYSTEMConfig(40000000, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);
  

    // === DMA setup  =====================
    // Open the all the  DMA channels.
	// Enable the AUTO option for all
	DmaChnOpen(0, 0, DMA_OPEN_AUTO);
    DmaChnOpen(1, 0, DMA_OPEN_AUTO);
    DmaChnOpen(2, 0, DMA_OPEN_AUTO);
    DmaChnOpen(3, 0, DMA_OPEN_AUTO);
    
	// set the transfer parameters: source & destination address,
    //   source & destination size, number of bytes per event
    // DMA0 writes to the A port
    // DMA1 writes to the DMA0 control block to clear a flag
    // DMA2 writes to the B port
    // DMA3 writes to the DMA2 control block to clear a flag  
    DmaChnSetTxfer(0, LED_pattern0, (void*)&LATA, 16, 1, 1);
    DmaChnSetTxfer(1, &clear_flag, (void*)&DCH0INT, 2, 2, 2);
    DmaChnSetTxfer(2, LED_pattern1, (void*)&LATB, 16, 1, 1);
    DmaChnSetTxfer(3, &clear_flag, (void*)&DCH2INT, 2, 2, 2);

	// set the transfer event control: what event is to start the DMA transfers
        // In this case, DMA0 triggers DMA2 and vice-versa
        // and cell tranfer done on DMA0 triggers channel 1 to clear the DMA0 flag
        // and cell tranfer done on DMA2 triggers channel 3 to clear the DMA2 flag
    // net result is to bounce between DMA0 and DMA2 as fast as possible
    // BUT you need to force a transfer to start the system
	DmaChnSetEventControl(0, DMA_EV_START_IRQ(_DMA2_IRQ)); //_TIMER_2_IRQ
    DmaChnSetEventControl(1, DMA_EV_START_IRQ(_DMA0_IRQ)); 
    DmaChnSetEventControl(2, DMA_EV_START_IRQ(_DMA0_IRQ)); 
    DmaChnSetEventControl(3, DMA_EV_START_IRQ(_DMA2_IRQ));
	
    // Set up the cell transfer done flags
    // needed to set up cell done enable bit
    DmaChnSetEvEnableFlags(0, DMA_EV_CELL_DONE);
    DmaChnSetEvEnableFlags(2, DMA_EV_CELL_DONE);
    
    // start the channels
    DmaChnEnable(0);
    DmaChnEnable(1);
    DmaChnEnable(2);
    DmaChnEnable(3);
    
    // force the first transfer
    // After this, the system just runs
    DmaChnForceTxfer(0);
    
    // set up i/o port pins
    ANSELA =0; // turn off analog on A
    ANSELB =0; // turn off analog on B
    mPORTAClearBits(BIT_0 );		//Clear A bits to ensure light is off.
    mPORTASetPinsDigitalOut(BIT_0 );    //Set A port as output
    mPORTBClearBits(BIT_0 );		//Clear B bits to ensure light is off.
    mPORTBSetPinsDigitalOut(BIT_0 );    //Set B port as output
    
	while(1){
        // keep moving -- nothing here to see
    };   
  } // main

// === end  ======================================================

