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

File:		pwm8.c

Purpose:	100 step 8 channel 25 KHz PWM

Version:	$Revision: 1.2 $

*/

#pragma config PLLDIV = 5
#pragma config CPUDIV = OSC1_PLL2
#pragma config FOSC = HS
#pragma config WDT = OFF
#pragma config USBDIV = 2

// ------------------- includes -----------------------------------------------
#include	<p18f4550.h>				// Register definitions
#include "system\usb\usb.h"
#include "io_cfg.h"
#include "pwm8_io.h"

// ------------------- defines ------------------------------------------------

// State Machine states
typedef enum _SM_CMD {
	CMD_IDLE,		// Waiting for the start of a command
	CMD_PARAM1,		// Received single letter command, waiting for param1
	CMD_PARAM2,		// Received param1, waiting for param2
	CMD_ERROR,		// Command error, just wait for <CR>
	CMD_COMPL,		// Complete command received, wait for main routine to process it
	CMD_SEND,
	CMD_SEND_ERR,	// Send an error
	CMD_PREP		// Prepare a status line
} SM_CMD;

#define MAX_PWM_BUF		50
#define MAX_PWM_VALUE	MAX_PWM_BUF * 2
#define MAX_CHANNELS	8

// ------------------- variable declarations ----------------------------------
unsigned int tick16;
char inpChar[1];
int inpInx = 0;
char cmd;
int param1;
int param2;
char outBuf[64];
int outBufInx = 0;
byte cmdState = CMD_IDLE;
unsigned char pwmBuf[8];
int pwmBufInx = 0;
unsigned char portBuf[MAX_PWM_BUF + 1];
int portBufInx = 0;

// ------------------- function prototypes ------------------------------------
static void InitializeSystem(void);
void high_isr(void);
void low_isr(void);
void UserInit(void);

void TaskUSBInternal(void);
void TaskUsbIO(void);
void RefreshPortBuf(void);

void setOK(void);
void setError(void);

/////////////////////////////////////////////////
// Vector Remapping required for bootloader. Bootloader is from 0 - 0x7ff
extern void _startup (void);		// See c018i.c in your C18 compiler dir

// ------------------- interrupt vectors --------------------------------------

#pragma code _RESET_INTERRUPT_VECTOR = 0x000800

void _reset (void)
{
	_asm goto _startup _endasm
}

#pragma code

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
//#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008

void _high_ISR (void)
{
	_asm goto high_isr _endasm
}

#pragma code

#pragma code _LOW_INTERRUPT_VECTOR = 0x000818
//#pragma code _LOW_INTERRUPT_VECTOR = 0x000018

void _low_ISR (void)
{
	_asm goto low_isr _endasm
}

#pragma code

/////////////////////////////////////////////////
//Low Interrupt ISR
#pragma interrupt low_isr
void low_isr(void) {
}

#pragma code

/////////////////////////////////////////////////
//High Interrupt ISR
#pragma interrupt high_isr
void high_isr(void) {
	PIR1bits.TMR2IF = 0;
	LATDbits.LATD2 = 1;

	_asm
		MOVLB	0
		MOVF portBuf, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 1, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 2, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 3, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 4, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 5, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 6, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 7, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 8, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 9, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 10, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 11, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 12, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 13, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 14, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 15, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 16, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 17, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 18, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 19, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 20, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 21, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 22, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 23, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 24, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 25, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 26, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 27, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 28, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 29, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 30, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 31, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 32, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 33, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 34, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 35, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 36, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 37, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 38, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 39, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 40, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 41, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 42, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 43, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 44, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 45, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 46, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 47, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 48, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 49, 0, BANKED
		MOVWF PORTB, ACCESS
		MOVF portBuf + 50, 0, BANKED
		MOVWF PORTB, ACCESS
	_endasm

	LATDbits.LATD2 = 0;
}

// ------------------- code starts here ---------------------------------------
#pragma code

/**
 * Main function
 * Main program entry point.
 *
 */
void main(void) {
	InitializeSystem();

	while(1) {
		TaskUSBInternal();		// Internal USB Tasks
		TaskUsbIO();			// Service USB port
	}
}

/**
 * InitializeSystem is a centralize initialization routine.
 * All required USB initialization routines are called from here.
 *
 * User application initialization routine should also be
 * called from here.
 *
 */
static void InitializeSystem(void) {
	ADCON1 = MASK_ADCON1;

	T2CON = 0x0d;			//xxxx xx01 - Prescaler = 4
							//xxxx x1xx - Tmr2 on
							//x000 1xxx - Postscaler = 2 (0=1, 1=2 ... 11=12)
	// Crystal is 20MHz, prescaler set to 4, postscaler set to 2
	// Clock = 4 / 20MHz = 200ns
	// Timer period = 200ns * 4 * 2 = 1.6us
	// Interrupt period = 40us i.e. 40/1.6 = 25
	PR2 = 24;
	PIE1bits.TMR2IE = 1;	//Enable Timer2 interrupt


	/////////////////////////////////////////////////
	//USB stack defines
	#if defined(USE_USB_BUS_SENSE_IO)
	tris_usb_bus_sense = INPUT_PIN; // See io_cfg.h
	#endif
	
	#if defined(USE_SELF_POWER_SENSE_IO)
	tris_self_power = INPUT_PIN;
	#endif
	
	mInitializeUSBDriver();		// See usbdrv.h
	
	UserInit();					// See user.c & .h

	/////////////////////////////////////////////////
	//Global interrupt enable
	INTCONbits.PEIE = 1;			//Enable Peripheral interrups (TMR2, ....)
	INTCONbits.GIE = 1;			//Global interrupt enable
}

/**
 * Service loop for USB tasks.
 *
 * @PreCondition	InitializeSystem has been called.
 */
void TaskUSBInternal(void) {
	/*
	* Servicing Hardware
	*/
	USBCheckBusStatus();					// Must use polling method
	if(UCFGbits.UTEYE!=1)
		USBDriverService();				// Interrupt or polling method
	
	#if defined(USB_USE_CDC)
	CDCTxService();
	#endif
}

void UserInit(void) {
	TRISB = MASK_TRISB;
	TRISD = MASK_TRISD;

	pwmBuf[0] = 0;
	pwmBuf[1] = 12;
	pwmBuf[2] = 13;
	pwmBuf[3] = 49;
	pwmBuf[4] = 50;
	pwmBuf[5] = 51;
	pwmBuf[6] = 99;
	pwmBuf[7] = 100;

	RefreshPortBuf();
}

void setOK(void) {
	outBuf[0] = '\n';
	outBuf[1] = 'O';
	outBuf[2] = 'K';
	outBuf[3] = '\r';
	outBuf[4] = '\n';
	outBuf[5] = '\0';
}

void setError(void) {
	outBuf[0] = '\n';
	outBuf[1] = 'E';
	outBuf[2] = 'R';
	outBuf[3] = 'R';
	outBuf[4] = '\r';
	outBuf[5] = '\n';
	outBuf[6] = '\0';
}

void TaskUsbIO(void) {
	static byte bytesRead;

	// User Application USB tasks
	if((usb_device_state < CONFIGURED_STATE)||(UCONbits.SUSPND==1)) {
		return;
	}

	switch (cmdState) {
		case CMD_IDLE:
			//Check if any data was received via the virtual serial port
			if(bytesRead = getsUSBUSART(inpChar, 1)) {
				if (inpChar[0] == 'D') {
					cmd = inpChar[0];
					cmdState = CMD_PARAM1;
					inpInx = 0;
					param1 = 0;
				}
				else {
					cmdState = CMD_ERROR;
				}
			}
			else {
			}
			break;
		case CMD_PARAM1:
			if(bytesRead = getsUSBUSART(inpChar, 1)) {
				if (inpChar[0] == ' ') {
					cmdState = CMD_PARAM2;
					param2 = 0;
					inpInx = 0;
				}
				else if (inpInx > 1) {
					if (inpChar[0] == 0xd) {
						cmdState = CMD_SEND_ERR;
					}
					else {
						cmdState = CMD_ERROR;
					}
				}
				else if (inpChar[0] >= '0' && inpChar[0] <= '7') {
					param1 = inpChar[0] - '0';
					inpInx++;
				}
				else {
					cmdState = CMD_ERROR;
				}
			}
			break;
		case CMD_PARAM2:
			if(bytesRead = getsUSBUSART(inpChar, 1)) {
				if (inpInx > 3) {
					if (inpChar[0] == 0xd) {
						cmdState = CMD_SEND_ERR;
					}
					else {
						cmdState = CMD_ERROR;
					}
				}
				else if (inpChar[0] >= '0' && inpChar[0] <= '9') {
					param2 *= 10;
					param2 += inpChar[0] - '0';
					inpInx++;
				}
				else if (inpChar[0] == 0xd) {
					cmdState = CMD_COMPL;
				}
				else {
					cmdState = CMD_ERROR;
				}
			}
			break;
		case CMD_COMPL:
			if (cmd == 'D') {
				if (param2 <= MAX_PWM_VALUE) {
					pwmBuf[param1] = param2;
					RefreshPortBuf();
					setOK();
					cmdState = CMD_SEND;
				}
				else {
					setError();
					cmdState = CMD_SEND_ERR;
				}
			}
			else {
				setError();
				cmdState = CMD_SEND_ERR;
			}
			break;
		case CMD_ERROR:
			if(bytesRead = getsUSBUSART(inpChar, 1)) {
				if (inpChar[0] == 0xd) {
					cmdState = CMD_SEND_ERR;
				}
			}
			break;
		case CMD_SEND_ERR:
			setError();

			//Send contents of output buffer
			cmdState = CMD_SEND;

			break;
		case CMD_SEND:
			if(mUSBUSARTIsTxTrfReady()) {
				putsUSBUSART(outBuf);

				//Back to wait for input state
				cmdState = CMD_IDLE;
			}
			break;
	}
}

void RefreshPortBuf(void) {
	static unsigned char dc;
	static unsigned char mask;

	pwmBufInx = 0;
	while (pwmBufInx < MAX_CHANNELS) {
		mask = 1 << pwmBufInx;

		dc = pwmBuf[pwmBufInx];

		portBufInx = 0;
		while (portBufInx <=	MAX_PWM_BUF) {
			if (dc < MAX_PWM_BUF) {
				if (portBufInx < dc) {
					portBuf[portBufInx] |= mask;
				}
				else {
					portBuf[portBufInx] &= ~mask;
				}
			}
			else {
				if (portBufInx < MAX_PWM_BUF * 2 - dc) {
					portBuf[portBufInx] &= ~mask;
				}
				else {
					portBuf[portBufInx] |= mask;
				}
			}

			portBufInx++;
		}
		pwmBufInx++;
	}
}
