#include <atmel_start.h>
#include <stdio.h>
#include <util/delay.h>
#include "adc_basic.h"
#include "pwm_basic.h"

/*
	PWM = VPP
	PB0 = VDD
	PB1 = MISO (data output of MCU)
	PB2 = MOSI (data input of MCU)
	PB3 = SCLK
	
	PA6 = ADC6 = voltage monitor input (5k1/10k divider)
*/

#define P_VDD  PB0
#define P_MISO PB1
#define P_MOSI PB2
#define P_SCLK PB3

static uint8_t cur_dutycycle=0;

/*
	Initialize ports and turn MCU off.
*/
void SP_init(void) {

	PWM_0_enable_output_ch0();
	PWM_0_load_duty_cycle_ch0(0x00);
	PWM_0_load_counter(0);
	cur_dutycycle=0;

	PORTB_set_pin_level(P_VDD,false);
	PORTB_set_pin_dir(P_VDD,PORT_DIR_OUT);
	PORTB_set_pin_dir(P_MISO,PORT_DIR_IN);
	PORTB_set_pin_dir(P_MOSI,PORT_DIR_OUT);
	PORTB_set_pin_level(P_MOSI,false);
	PORTB_set_pin_dir(P_SCLK,PORT_DIR_OUT);
	PORTB_set_pin_level(P_SCLK,false);
	_delay_ms(1);	
}

/*
	Ramp VPP to target voltage. Needs to be replaced with better controller 
	
	adctarget=Vpp*70.06   (Aref=5V, Rdivier 5k1/10k)
*/
void SP_RampVpp(uint16_t adctarget,uint8_t pwmstart) {

	uint16_t adcout;

    adcout = ADC_0_get_conversion(6);
	if (adcout>=adctarget) return;
	
	if (pwmstart) {
		cur_dutycycle = pwmstart;
		PWM_0_load_duty_cycle_ch0(cur_dutycycle);
		_delay_ms(10);		
	}

    adcout = ADC_0_get_conversion(6);
    if (adcout>=adctarget) return;

	do {
		cur_dutycycle +=1;
		PWM_0_load_duty_cycle_ch0(cur_dutycycle);
		_delay_ms(5);
	    adcout = ADC_0_get_conversion(6);		
	} while(adcout<adctarget);

	return;
}

/*
	Send out up to 16 bits on MOSI.
	MSB first
	"length" has number of bits to send
*/

void SP_SendWord(uint16_t word, uint8_t length) {
	uint8_t i;
	word=word<<(16-length);	
	for (i=0; i<length; i++) {
		PORTB_set_pin_level(P_SCLK,false);
		PORTB_set_pin_level(P_MOSI,word&0x8000);		
		word<<=1;
		_delay_us(3);
		PORTB_set_pin_level(P_SCLK,true);		
		_delay_us(3);
	}
	PORTB_set_pin_level(P_SCLK,false);	
}

/*
	Receive up to 16 bits on MISO.
	MSB first
	"length" has number of bits to send
*/

uint16_t SP_ReceiveWord(uint8_t length) {
	uint8_t i;
	uint16_t word=0;
	
	for (i=0; i<length; i++) {
		PORTB_set_pin_level(P_SCLK,false);
		_delay_us(3);
		PORTB_set_pin_level(P_SCLK,true);
		_delay_us(2);
		word<<=1;
		if (PORTB_get_pin_level(P_MISO)) word|=0x1;   // Ready after delay to fix for first bit issue.
		_delay_us(1);		
	}
	PORTB_set_pin_level(P_SCLK,false);	
	
	return word;
}

/*
	Enter programming mode. 
	Command=A6 for reading, A7 for writing
*/

void SP_EnterPGMmode(uint8_t command) {
	
	SP_init();
	
	SP_RampVpp((uint16_t)(70.7*7.5),1);  // set Vpp to 7.5V
	
	PORTB_set_pin_level(P_VDD,true);	  // turn on VDD
	_delay_us(500);
	SP_SendWord(0xA5A5,16);
	SP_SendWord(0xA5A5,8);
	SP_SendWord(command,8);
}

/*
	Read memory word
	att: Read mode needs to be active
*/

uint16_t SP_ReadWord(uint16_t address) {	
	SP_SendWord(address,12);
	return SP_ReceiveWord(13);	
}


/*
	Write memory word. Memory is written in pairs of two words.
	att: Write mode needs to be active
*/

void SP_WriteWords(uint16_t word1,uint16_t word2, uint16_t address) {	
	SP_SendWord(word1,13);
	SP_SendWord(word2,13);
	SP_SendWord(address,12);
	SP_SendWord(0,1);  // padding bit
	
	// Write execution sequence (stretched SCLK cycle and slow clock on MOSI)
	PORTB_set_pin_level(P_SCLK,true);
	_delay_us(1);
	for (uint8_t i=0; i<8; i++) {
		PORTB_set_pin_level(P_MOSI,true);
		_delay_us(31);
		PORTB_set_pin_level(P_MOSI,false);
		_delay_us(31);		
	}
	PORTB_set_pin_level(P_SCLK,false);
	_delay_us(1);		
	SP_SendWord(0,1);  // padding bit
}

/*
	Read Device ID Sequence
*/

uint16_t SP_ReadDeviceIDSequence(void) {
	
	uint16_t DeviceID;
	
	SP_EnterPGMmode(0xA7);   // Activate write mode

	SP_SendWord(0x0000,13);  // Send Dummydata 2x13 bits
	SP_SendWord(0x0000,13);
	_delay_us(3);
	DeviceID=SP_ReceiveWord(12);  // Receive 12 bit Device ID during address phase
	_delay_us(20);

	SP_init();  // Turn MCU off again.

	_delay_ms(10);
	
	return DeviceID;
}



int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	printf("Initializing...\n");
	uint16_t adcout,DeviceID;

	DeviceID=SP_ReadDeviceIDSequence();
	adcout = ADC_0_get_conversion(6);
	
	printf("DeviceID: %X \tVpp Standby: %i\n",DeviceID,adcout);
	_delay_ms(1);
	SP_init();
	_delay_ms(20);

	printf("Dumping memory...\n");
	SP_EnterPGMmode(0xA6);  // Enter read mode
	
	adcout = ADC_0_get_conversion(6);
	printf("Vpp read mode: %i\n",adcout);

	uint16_t address=0x3f0;
	uint8_t i;
	
	for (address=0x000; address<0x3ff; address+=16) {		
		printf("%.4X: ",address);
		for (i=0; i<16; i++) {
			printf("%.4X ",SP_ReadWord(address+i));
		}
	printf ("\n");	
}
	SP_init();
	_delay_ms(20);
		
	printf("Writing to memory...\n");
	SP_EnterPGMmode(0xA7);  // Enter write mode
	adcout = ADC_0_get_conversion(6);
	printf("Vpp initial: %i\tPWM: %i\n",adcout,cur_dutycycle);
	SP_RampVpp((uint16_t)(70.7*10.8),10);  // set Vpp to 10.8V
	_delay_ms(10);  // stabilize
	adcout = ADC_0_get_conversion(6);
	printf("Vpp write mode: %i\tPWM: %i\n",adcout,cur_dutycycle);

	SP_WriteWords(0x1A5A,0x15A5,0x38);
	SP_WriteWords(0x1A5A,0x15A5,0x3a);
	SP_WriteWords(0x1A5A,0x15A5,0x3c);
	SP_WriteWords(0x1A5A,0x15A5,0x3e);
	adcout = ADC_0_get_conversion(6);
	printf("Vpp after writing: %i\n",adcout);

	SP_init();
	_delay_ms(20);
		
	//SP_EnterPGMmode(0xA7);
	adcout = ADC_0_get_conversion(6);	
	printf("Vpp off: %i\n",adcout);
	
	
	while(1);
}
