// taken and adapted from
// Programming Sharp’s Memory LCDs
// by Ken Green

// LCD commands - Note: the bits are reversed per the memory LCD data
// sheets because of the order the bits are shifted out in the SPI
// port.
#define MLCD_WR 0x80		//MLCD write line command
#define MLCD_CM 0x20		//MLCD clear memory command
#define MLCD_NO 0x00		//MLCD NOP command (used to switch VCOM)

//LCD resolution
#define MLCD_XRES 96		//pixels per horizontal line
#define MLCD_YRES 96		//pixels per vertical line
#define MLCD_BYTES_LINE MLCD_XRES / 8 //number of bytes in a line
#define MLCD_BUF_SIZE MLCD_YRES * MLCD_BYTES_LINE

//defines the VCOM bit in the command word that goes to the LCD
#define VCOM_HI 0x40
#define VCOM_LO 0x00

static char *frmbufter; //current address of buffer to be displayed
static char locbuf[MLCD_BYTES_LINE + 3];	// local line buffer
static char linenum;				// current line number being transmitted

//there are 3 stages in transmitting a buffer:
//stage 0: first line (has command in it)
//stage 1: 2nd through last line (no command)
//stage 2: null byte trailer
static int stage = 0;

//current state of vcom. This should alternate
//between VCOM _ HI and VCOM _ LO on a 1-30 second
//period.
extern char vcom;

////////////////////////////////////////////////////////////////
//
// This routine transmits the contents of the pixel memory in
// a frame buffer to the memory LCD. It uses DMA to transfer
// each individual line. The address of the frame buffer to
// transfer is in the global variable show _ frm.
//
//
// NOTE: this routine also acts as the interrupt handler for SPI interrupts.
//
//
// INPUTS:
// The SPI and DMA units must have been previously initialized
// show _ frm:
// pointer to the buffer to be displayed
//
////////////////////////////////////////////////////////////////
void show _ frame(char *show _ frm) {
int i;
unsigned long sts;

	switch(stage) {
		case 0:
			// stage 0: sending the first line. The command is
			// included in this line
			set_scs(HIGH);	//set SCS high
			frmbufptr = show_frm;	//init pointer to frame buffer
			linebuf = locbuf; //init pointer to line buffer
			linenum = 1; //init line address

			//first line of data is preceeded by a write command
			*linebuf++ = MLCD _ WR | vcom; //command (note: no need to swap)
			*linebuf++ = swap(linenum++); //line number (address)

			for (i=0; i<MLCD_BYTES_LINE; i++)
				*linebuf++ = swap(*frmbufptr++); //swap the order of the bits
			*linebuf++ = 0; //trailer

			//Setup the SPI DMA controller (starting addr, size)
			TransferSetup(locbuf, linebuf - locbuf);
			//Start the xfer
			TransferStart;
			stage = 1; //set to next stage
			break;

		case 1: //Sending a line (other than the first line). At this
			//point the DMA transfer for the previous line is done
			//and the channel disabled.
			linebuf = locbuf; //init pointer to line buffer
			*linebuf++ = swap(linenum++); //line number
			for (i=0; i<MLCD_BYTES_LINE; i++)
				*linebuf++ = swap(*frmbufptr++); //swap the order of the bits
			*linebuf++ = 0; //trailer
			//set up DMA control
			TransferSetup(locbuf, linebuf - locbuf);
			//Start the xfer
			TransferStart;
			if (linenum > MLCD_YRES)
				stage = 2; //all lines sent, go to next stage
			break;
		case 2:
			// All lines sent, send a fi nal null byte trailer. The DMA
			//transfer of the last line is fi nished and the channel
			//disabled. All that is left is to write a trailing null
			//byte. It’s not worth using DMA to transfer 1 byte, so
			//it’s done by directing writing to the SPI port.
			//Write the last (null) byte
			Write_to_SPI(0);
			//Since there is no interrupt on transmit buffer empty, loop
			//here until the byte is sent, then drop SCS.
			i = 0;
			while (SPI_BSY); //wait until done
			set_scs(LOW);
			stage = 0; //go back to stage 0 - sending out first line
	}
}