/*
 *  ======== My lab02.c ========
 *  Mark A. Yoder
 *  Rose-Hulman Institute of Technology
 *  812-877-8291
 *  Mark.A.Yoder@Rose-Hulman.edu
 */
 
/*
	The goal of this lab is to program the EDMA to get samples from
	the PCM3003 Daughter card via McBSP1.  The daughter card
	has jumper JP12 set to 3-4 and JP11 to 3-4 so it is sampling
	16-bit values at 48K.  All the other jumps are in the original
	position.
	
	The left and right channels arrive in one 32-bit word.  Left is
	the upper 16 bits and right is the lower 16 bits.
	These 32-bit words are stored in gBufferRcvPing (or Pong).
	
	Audio is output by placing it in gBufferXmtPing (or Pong).
	
	Presently it doesn't always start, however seleting Debug:Restart a
	time or two fixes the problem.
	Once running it works fine.
*/

/*
 *  ======== Include files ========
 */
#include <c6x.h>           // C6000 compiler definitions
#include <csl.h>
#include <csl_edma.h>
#include <csl_irq.h>
#include <bsl.h>
#include <bsl_dip.h>
#include <bsl_led.h>
#include "lab02cfg.h"
#include "lowpass2k.h"

/*
 *  ======== Prototypes ========
 */
void initEmif(void);
void initEdma(void);
void initHwi(void);
extern void blockSine(short *buf, int len);
void edmaHwi(void);
void FIRfilter(int *inbuf, int *outbuf, float *coeff, int buffSize, int Norder);
void processBuffer(void); 		// 6-16

/*
 *  ======== Declarations ========
 */
#define PING 0				// 5-10
#define PONG 1				// 5-10
#define BUFFSIZE	256

/*
 *  ======== Global Variables ========
 */

int gBufferXmtPing[BUFFSIZE];		// 5-10
int gBufferXmtPong[BUFFSIZE];
int gBufferRcvPing[BUFFSIZE];
int gBufferRcvPong[BUFFSIZE];

#pragma DATA_SECTION(gDelay, ".far");
#define	gDELAYSIZE	12000
int gDelay[gDELAYSIZE];
int gIndex=0;

EDMA_Handle hEdmaXmt;
EDMA_Handle	hEdmaReloadXmtPing;		// 3-23
EDMA_Handle	hEdmaReloadXmtPong;		// 5-10
EDMA_Handle hEdmaRcv;
EDMA_Handle	hEdmaReloadRcvPing;		// 3-23
EDMA_Handle	hEdmaReloadRcvPong;		// 5-10

short gXmtChan;
short gRcvChan;			// 4-16

/**************************************************************\
*  The "EDMA Config" type data structure holds the
*    parameters to be programmed into a EDMA channel.
*  Register Make (RMK) macros build a 32-bit unsigned int;
*    below it is used to build the Options (OPT) register.
*  The OF macros provide the proper typecasting needed for
*    the EDMA Config data structure.
*
* To locate the structure below, use:
*
* Help-->User Manuals-->
* SPRU401 - TMS320C6000 Chip Support Library API Reference Guide
*
* 1. Open the SPRU401 .pdf file.
* 2. Search for "EDMA_OPT_field_symval" and go to the link
* 
* Notice that Table B-23 specifies how to build the 
* OPT RMK structure. If you want to know the options for 
* each field, just look at the table.
* 
* You can locate the other Config fields by searching for:
*
* 	"EDMA_SRC_field_symval"
\**************************************************************/
EDMA_Config gEdmaConfigXmt = {  		// 3-12
	EDMA_OPT_RMK(
		EDMA_OPT_PRI_LOW,    // Priority?
//		EDMA_OPT_ESIZE_16BIT,  // Element size?
		EDMA_OPT_ESIZE_32BIT,  // Element size?
		EDMA_OPT_2DS_NO,	 // 2 dimensional source?
		EDMA_OPT_SUM_INC,	 // Src update mode?
		EDMA_OPT_2DD_NO,	 // 2 dimensional dest?
		EDMA_OPT_DUM_NONE,	 // Dest update mode?	4-16
		EDMA_OPT_TCINT_YES,  // Cause EDMA interrupt?
		EDMA_OPT_TCC_OF(0),// Transfer complete code?
		EDMA_OPT_LINK_YES,   // Enable link parameters?
		EDMA_OPT_FS_NO	     // Use frame sync?		4-16
	),
	EDMA_SRC_OF(gBufferXmtPing),  // src address?  5-10
	EDMA_CNT_OF(BUFFSIZE),        // Count = buffer size 
	EDMA_DST_OF(0),        // dest address?    4-16  
	EDMA_IDX_OF(0),		 // frame/element index value?
	EDMA_RLD_OF(0)		 // reload
};

EDMA_Config gEdmaConfigRcv = {  		// 4-16
	EDMA_OPT_RMK(
		EDMA_OPT_PRI_LOW,    // Priority?
//		EDMA_OPT_ESIZE_16BIT,  // Element size?
		EDMA_OPT_ESIZE_32BIT,  // Element size?
		EDMA_OPT_2DS_NO,	 // 2 dimensional source?
		EDMA_OPT_SUM_NONE,	 // Src update mode?	4-16
		EDMA_OPT_2DD_NO,	 // 2 dimensional dest?
		EDMA_OPT_DUM_INC,	 // Dest update mode?	4-16
		EDMA_OPT_TCINT_YES,  // Cause EDMA interrupt?
		EDMA_OPT_TCC_OF(0),// Transfer complete code?
		EDMA_OPT_LINK_YES,   // Enable link parameters?
		EDMA_OPT_FS_NO	     // Use frame sync?		4-16
	),
	EDMA_SRC_OF(0),        // src address?
	EDMA_CNT_OF(BUFFSIZE),        // Count = buffer size 
	EDMA_DST_OF(gBufferRcvPing),  // dest address?    4-16, 5-10
	EDMA_IDX_OF(0),		 // frame/element index value?
	EDMA_RLD_OF(0)		 // reload
};

/*
 *  ======== main ========
 */
void main()
{
	int i;
    initEmif();						// Dummy function - see note below
    for(i=0; i<BUFFSIZE;i++) {		// 4-16
    	gBufferXmtPing[i]=0;		// 5-10
    	gBufferXmtPong[i]=0;
    	}
    for(i=0; i<gDELAYSIZE; i++)
    	gDelay[i] = 0;

    MCBSP_enableXmt(hMcbsp1);		// 4-14
    MCBSP_enableRcv(hMcbsp1);		// 4-14
    while( !(MCBSP_rrdy(hMcbsp1)));	// Wait for McBSP to be ready.

    initEdma();
    initHwi();
}

/*
 *	======== initEmif ========
 *	Configure External Memory Interface (EMIF)
 *
 *  We have a dummy function here to setup the EMIF. In a "real"
 *  system you would be required to setup the EMIF but in our
 *  development system CCS sets up the memory for us (refer to 
 *  the DSK6211_6711.gel file for the CCS script that 
 *  accomplishes this task.
 */
 
void initEmif(void){
}

void initEdma(void){
// Setup transmit side
//	hEdmaXmt = EDMA_open(EDMA_CHA_XEVT0, EDMA_OPEN_RESET);		// 4-17
	hEdmaXmt = EDMA_open(EDMA_CHA_XEVT1, EDMA_OPEN_RESET);		// 4-17
	hEdmaReloadXmtPong = EDMA_allocTable(-1);		// 3-23
	hEdmaReloadXmtPing = EDMA_allocTable(-1);		// 5-11

	gEdmaConfigXmt.dst = MCBSP_getXmtAddr(hMcbsp1);		// 4-17

	gXmtChan = EDMA_intAlloc(-1);
	gEdmaConfigXmt.opt |= EDMA_FMK(OPT, TCC, gXmtChan);

	EDMA_config(hEdmaXmt, &gEdmaConfigXmt);
	EDMA_config(hEdmaReloadXmtPing, &gEdmaConfigXmt);	// 3-23, 5-11

	gEdmaConfigXmt.src = EDMA_SRC_OF(gBufferXmtPong);	// 5-11
	EDMA_config(hEdmaReloadXmtPong, &gEdmaConfigXmt);	// 5-11
	
	EDMA_link(hEdmaXmt, hEdmaReloadXmtPong);			// 3-23
	EDMA_link(hEdmaReloadXmtPong, hEdmaReloadXmtPing);	// 3-23, 5-11
	EDMA_link(hEdmaReloadXmtPing, hEdmaReloadXmtPong);	// 3-23, 5-11

// Setup receive side
//	hEdmaRcv = EDMA_open(EDMA_CHA_REVT0, EDMA_OPEN_RESET);		// 4-17
	hEdmaRcv = EDMA_open(EDMA_CHA_REVT1, EDMA_OPEN_RESET);		// 4-17
	hEdmaReloadRcvPing = EDMA_allocTable(-1);		// 3-23, 5-11
	hEdmaReloadRcvPong = EDMA_allocTable(-1);		// 3-23

	gEdmaConfigRcv.src = MCBSP_getRcvAddr(hMcbsp1);		// 4-17

	gRcvChan = EDMA_intAlloc(-1);
	gEdmaConfigRcv.opt |= EDMA_FMK(OPT, TCC, gRcvChan);
	
	EDMA_config(hEdmaRcv, &gEdmaConfigRcv);
	EDMA_config(hEdmaReloadRcvPing, &gEdmaConfigRcv);	// 3-23, 5-11

	gEdmaConfigRcv.dst = EDMA_DST_OF(gBufferRcvPong);	// 5-11
	EDMA_config(hEdmaReloadRcvPong, &gEdmaConfigRcv);	// 5-11
	
	EDMA_link(hEdmaRcv, hEdmaReloadRcvPong);			// 3-23, 5-11
	EDMA_link(hEdmaReloadRcvPong, hEdmaReloadRcvPing);	// 3-23, 5-11
	EDMA_link(hEdmaReloadRcvPing, hEdmaReloadRcvPong);	// 3-23, 5-11
	
	// Clear pending flags in the EDMA's CIPR register.	  4-18
	EDMA_intClear(gXmtChan);		// Clear spurious interrupts 3-22
	EDMA_intClear(gRcvChan);		// Clear spurious interrupts 3-22
	
	// Enable the interupts from the EDMA channels (CIER register) to the CPU 4-18
	EDMA_intEnable(gXmtChan);
	EDMA_intEnable(gRcvChan);
	
	// Enable the EDMA channels themselves  4-18
	EDMA_enableChannel(hEdmaXmt);
	EDMA_enableChannel(hEdmaRcv);
	
}

void edmaHwi(void) {		// 3-21
	static Uint32 pingOrPong = PING;		// 5-12
/*  4-19
	EDMA_intClear(0);
	
    blockSine(gBufferXmt, BUFFSIZE);	// Fill the buffer with sine data
    
    EDMA_setChannel(hEdmaXmt);
*/
    
	if(EDMA_intTest(gRcvChan) & EDMA_intTest(gXmtChan)) {	// 4-19
//		load(gLoadValue);			// 5-8

		// Clear both CIPR bits.
		EDMA_intClear(gRcvChan);
		EDMA_intClear(gXmtChan);
		
		if(pingOrPong == PING) {		// 6-17
			SWI_or(&processBufferSwi, PING);
			pingOrPong = PONG;
		} else {
			SWI_or(&processBufferSwi, PONG);
			pingOrPong = PING;
		}
	}
}

void initHwi(void) {
	IRQ_enable(IRQ_EVT_EDMAINT);	// HWI_INT8  3-11
	IRQ_globalEnable();		// turn on interrupts globally  3-11
}

void processBuffer(void) {		// 6-16
	int *gBufferRcv, *gBufferXmt;		// Points to the ping or pong buffer.
	Uint32 pingPong = SWI_getmbox();	// 6-16

	if(pingPong == PING) {	// Make sure we're pointing to the right buffer.
		gBufferRcv = gBufferRcvPing;
		gBufferXmt = gBufferXmtPing;
	} else {
		gBufferRcv = gBufferRcvPong;
		gBufferXmt = gBufferXmtPong;	
	}
		
	if(DIP_get(DIP_1) == 0)
		addSine(gBufferRcv, BUFFSIZE);	
	if(DIP_get(DIP_3) == 0)
		FIRfilter(gBufferRcv, gBufferXmt, coeff, BUFFSIZE, NUMCOEFFS-1);
	else
		copyData(gBufferRcv, gBufferXmt, BUFFSIZE);
}

/*
	The following code is run by the period timer.  It checks the
	MSB (not the sign bit).  If it is 1, it turns the LED on.
	This is a way to see if there is clipping.	
*/
void blinkLeds(void) {
	int i;
	short lmax=0, ltmp;
	short rmax=0, rtmp;
	
	for(i=0; i<BUFFSIZE; i++) {
		ltmp = (short) _abs(gBufferRcvPing[i]>>16);
		rtmp = (short) _abs(gBufferRcvPing[i]);
		lmax = lmax<ltmp ? ltmp: lmax;
		rmax = rmax<rtmp ? rtmp: rmax;
	}
	LOG_printf(&logTrace, "blinkLeds:  max = %d, %d", lmax, rmax);
	// Turn LED on if MSB (not sign bit) is on.
	if(lmax & 0x4000) LED_on(LED_1); else LED_off(LED_1);
	if(rmax & 0x4000) LED_on(LED_3); else LED_off(LED_3);
}

/*
	This routine convolves the present and the Norder previous input 
	samples with the Norder+1 FIR coefficients  h(0) through h(Norder):

            y(n) = h(0)*x(n) + h(1)*x(n-1) + ... + h(Norder)*x(n-Norder)
*/
    
void FIRfilter(int *inbuf, int *outbuf, float *coeff, int buffSize,
				int Norder) {
    int intsamp,i,j;
    float floatsamp,sum;
    static float x[NUMCOEFFS];

for(j=0; j<buffSize; j++) {
    
    intsamp=inbuf[j];				/* read the input */  
    intsamp = intsamp & 0xffff;		/* Mask right channel data (bottom 16 bits) */
    if(intsamp & 0x8000) 
        intsamp = intsamp | 0xffff0000;	/* Sign extend right channel data */
    floatsamp = (float) intsamp;		/* Convert right channel to floating point */
    
/*
	Update past input sample array, where x[0] holds present sample, x[1] holds 
	sample from 1 sample period ago, x[N] holds sample from N sampling periods ago.
 
	This time-consuming loop could be eliminated if circular buffering were
	to be employed.       
*/
    for(i=Norder; i >= 0;i--)
		x[i]=x[i-1];
    x[0] = floatsamp;
    sum = 0.0;
    for(i=0; i<=Norder; i++)			/* Perform FIR filtering (convolution) */
		sum=sum+x[i]*coeff[i]; 
   
    intsamp = ((int) sum) & 0xffff;		/* Convert result back to integer form.	*/
    outbuf[j] = intsamp << 16 | intsamp;	/* Send to buffer (both channels )	*/
	}
}
