
// Its a component of the library
#define BUILDING_LIBRARY

// Include hardware info for this device	
#include <avr/io.h>

		
#if defined (__AVR_ATmega328P__)
// Each timer has A,B channels etc
// Map the UART vectors
#  define USART0_RX_vect USART_RX_vect
#  define USART0_TX_vect USART_TX_vect
#else
#  error "Processor mismatch in utilities.xsl"
#endif
				
#include <libdefs.h>	
#include <uartsw.h>
#include <timer.h>


// Define the transmit pin	
#define TRANSMIT_PORT  PORTB
#define TRANSMIT_DDR   DDRB
#define TRANSMIT_PIN   PB0

// Mask the prescaler value in the register
#define PRESCALE_MASK 7

#define THE_UART &_SerialServoDriver_uart
extern SW_UART _SerialServoDriver_uart;


		
		
// Forward definitions
static void	startXmitMode(UART* _uart);
static void	endXmitMode(UART* _uart);

	
// Timer channel A is used for timing the transmit bits
		
#define CHANNELA_THRESHOLD OCR0A
#define CHANNELA_INT_PENDING bit_is_set(TIFR0,OCF0A)		
#define CHANNELA_INT_CLEAR sbi(TIFR0,OCF0A)		
		
#define CHANNELA_INT_ENABLE sbi(TIMSK0,OCIE0A)		
#define CHANNELA_INT_DISABLE cbi(TIMSK0,OCIE0A)		
		
		
#define NOW TCNT0
#define TIMER_IS_RTC FALSE	
				
#define TIMER_PRESCALER TCCR0B
#define TIMER_TOP 0xFFU			
				
// Use Timer channel A compare interrupt to schedule sending next bit
ISR(TIMER0_COMPA_vect){
	SW_UART* uart = THE_UART;
	if(uart->txBitNum){
		// there are bits still waiting to be transmitted
		if(uart->txBitNum > 1){
			// transmit data bits (inverted, LSB first)
			if( (uart->txData & 0x01) ){
				sbi(TRANSMIT_PORT,TRANSMIT_PIN);
			}else{
				cbi(TRANSMIT_PORT,TRANSMIT_PIN);
			}
			// shift bits down
			uart->txData >>= 1;
		}else{
			// transmit stop bit
			if(uart->inverted){
				cbi(TRANSMIT_PORT,TRANSMIT_PIN);
			}else{
				sbi(TRANSMIT_PORT,TRANSMIT_PIN);
			}
		}

		// schedule the next bit
		CHANNELA_THRESHOLD += uart->dataBitLength;

		// count down
		uart->txBitNum--;
	}else{
		// We have finished sending the current byte - look for the next byte to send
		__uartTransmitService(&uart->_uart_);
	}
}
	
	

// Disable the uart
static void __uartswOff(UART* _uart){
	// detach the service routines
	
	CHANNELA_INT_DISABLE;
	
}


	
static void setTxIdle(SW_UART* uart){
	// set the output idle state of the transmitter
	if(uart->inverted){
		cbi(TRANSMIT_PORT,TRANSMIT_PIN);
	}else{
		sbi(TRANSMIT_PORT,TRANSMIT_PIN);
	}	
	sbi(TRANSMIT_DDR,TRANSMIT_PIN);	// Make into output
}


// enable and initialize the software uart - called with interrupts turned off
static void __uartswInit(UART* _uart,BAUD_RATE baud){
	SW_UART* uart = (SW_UART*)_uart;
	if(uart->_uart_.rx_pin || uart->_uart_.tx_pin){
		// initialize baud rate
		uartSetBaudRate(uart,baud);

	
		// setup the transmitter if there is one
		if(uart->_uart_.tx_pin){
			uart->txBitNum=0;

			
			// set the output idle state of the transmitter
			setTxIdle(uart);
			
		}
		
	}
	
}


static void __uartswSetBaudRate(UART* _uart,BAUD_RATE baudrate){
	SW_UART* uart = (SW_UART*)_uart;
	uint16_t top = TIMER_TOP;

	// Get the current prescaler
	uint16_t prescale = 1;

	// calculate division factor for requested baud rate, and set it
calcBaud:
	uart->dataBitLength = (uint16_t)((F_CPU+(baudrate/2L)) / (prescale * baudrate * 1L)  );

	if(uart->dataBitLength > top){
		uart->dataBitLength = top;
		// Try increasing the prescaler
		uint16_t nextpre = timerNextPrescaler(prescale, TIMER_IS_RTC);
		if(nextpre != 0){
			prescale = nextpre;
			goto calcBaud;
		}
	}
	// Set the timer prescaler 
	TIMER_PRESCALER &= ~ PRESCALE_MASK;
	TIMER_PRESCALER |= timerGetPrescalerIndex(prescale,TIMER_IS_RTC);

	// Fudge factor compensates for interrupt processing overhead
	// The larger this number then the shorter the start bit
	//
	uint16_t pre = prescale;
	uint16_t fudge = 104U;
	while(pre > 1){
		fudge>>=1;
		pre>>=1;
	}
	uart->startBitLength = uart->dataBitLength - fudge;
}

// Start transmitting the given byte
static void __uartswStartXmit(UART* _uart,uint8_t data){
	
	SW_UART* uart = (SW_UART*)_uart;

	// save data
	uart->txData = (uart->inverted) ? ~data : data;

	// set number of bits (+1 for stop bit)
	uart->txBitNum = 9;

	if(uart->_uart_.tx_pin){
	
		// schedule the next bit
		CHANNELA_THRESHOLD = NOW + uart->startBitLength;
		CHANNELA_INT_CLEAR;					// Clear interrupt pending
		// set the start bit
		if(uart->inverted){
			sbi(TRANSMIT_PORT,TRANSMIT_PIN);
		}else{
			cbi(TRANSMIT_PORT,TRANSMIT_PIN);
		}
		CHANNELA_INT_ENABLE;				// Turn on transmit interrupts
	}else{
		// There is no iopin - so we have finished sending the current byte
		__uartTransmitService(&uart->_uart_);
	}

}

// We are about to start sending stuff
static void	startXmitMode(UART* _uart){

}


// We have finished sending stuff
static void	endXmitMode(UART* _uart){

	// Turn timer interrupt off
	CHANNELA_INT_DISABLE;
	
}


/*
static MAKE_READER(reader){
	return uartGetByte(THE_UART);
}
static MAKE_WRITER(writer){
	return uartSendByte(THE_UART,byte);
}
*/

// Create a class with the method overrides for this type of UART
const UART_CLASS PROGMEM c_sw_uart0 = MAKE_UART_CLASS( \
		&__uartswSetBaudRate, \
		&__uartswStartXmit, \
		&__uartswInit, \
		&__uartswOff, \
		&startXmitMode, \
		&endXmitMode \
		);

