/*
 * Bat_Detector_v2___ATTiny48.c
 *
 * Created: 12/12/2014 23:22:45
 *  Author: Mike
 */ 

#define F_CPU 8000000

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdio.h>
#include "lcd.h"
#include "Decode Encoder - 2 Detents Per Pulse.h"

/* volatile variables that pass between main() and ISR*/
static volatile unsigned long int freq_Hz = 0;
static volatile uint8_t count_flag = 0;
static volatile uint8_t count_rep = 0;
/* variables used to calculate averages */
static unsigned long int freq_Hz_0 = 0;
static unsigned long int freq_Hz_1 = 0;
static unsigned long int freq_Hz_2 = 0;
static unsigned long int freq_Hz_avg = 0;
/* integer array stores the digipot values to be used to make frequency control linear across the range; 75 elements */
const static unsigned int digipot_linear_values[] = { 31, 70, 98, 122, 148, 170, 190, 207, 223, 237, 250, 266, 278, 290, 300, 310, 320, 330, 338, 346, 353, 360, 366, 373, 379, 385, 390, 395, 400, 405, 409, 413, 417, 420, 424, 428, 432, 435, 439, 442, 445, 447, 450, 453, 456, 458, 461, 463, 466, 468, 470, 472, 474, 476, 478, 480, 482, 484, 486, 488, 489, 491, 492, 494, 495, 497, 498, 500, 501, 503, 504, 505, 507, 508, 510, 511};
/* variable to count rotary encoder position changes; bounded to the number of values in above array - 0-75 inclusive*/
static uint8_t enc_count = 35;

/* example function taken from ATTiny48 datasheet*/
static void SPI_MasterInit(void)
{
	/* Set MOSI and SCK output */
	DDRB |= (1<<DDB3) | (1<<DDB5);
	/* Enable SPI, Master, set clock rate fck/16 */
	SPCR = (1 << SPE)| (1 << MSTR) | (1 << SPR0);
}

/* example function taken from ATTiny48 datasheet*/
static void SPI_MasterTransmit(uint8_t SPI_byte)
{
	/* Start transmission */
	SPDR = SPI_byte;
	/* Wait for transmission to complete */
	while(!(SPSR & (1<<SPIF)))
	;
}

/* function sets timer1 to count external pulses whilst timer0 counts internal clock cycles to provide a known time period.
 * timer0 is 8-bit so with a clock of 8MHz and prescaler of /1024 it takes (2^8)*(1/(8MHz/1024)) = 32.768ms to overflow */
static void init_freq_measurement(void)
{
	TIMSK0 = (1 << TOIE0);												// Enable overflow interrupt on timer0
	TCNT0 = 0;															// Reset Timer0 register to zero
	TCNT1 = 0;															// Reset Timer1 register to zero
	TCCR1A = 0;															// Set normal mode of operation
	TCCR1C = 0;															// Set normal mode of operation
	TCCR1B = (1 << CS12) | (1 << CS11) | (1 << CS10);					// Set timer1 to count on external clock source on rising edge
	TCCR0A = (1 << CS02) | (0 << CS01) | (1 << CS00);					// Set timer0 to internal clock source with /1024 prescaler
	
	count_flag = 1;														// Flag set to indicate that a measurement is in progress
}

/* interrupt routine on timer0 overflow reads timer1 register and calculates the frequency of the external signal */
ISR(TIMER0_OVF_vect)
{
	TCCR1B = (0 << CS12) | (0 << CS11) | (0 << CS10);					// Stop counting on timer1
	TCCR0A = (0 << CS02) | (0 << CS01) | (0 << CS00);					// Stop counting on timer0
	
	unsigned long int freq_counts;
	
	if ((TIFR1 & (1 << TOV1)) != 0 )									// Check timer1 overflow flag
	{
		TIFR1 = (1 << TOV1);											// Clear the overflow flag
		freq_counts = 0xFFFF;											// Assign maximum value to indicate overload
	}
	
	else	{freq_counts = TCNT1;}										// Else if the overflow flag is not set assign the value of the timer register
	
	unsigned int count_cal = 3126;										// Calibration variable
	freq_Hz = ((freq_counts * count_cal) / 100 );						// Calculate Hz
	
	count_rep += 1;														// Increment rep counter
	count_flag = 0;														// Clear flag to indicate measurement no longer in progress
}

/* function interfaces with digipot0 over SPI to set new value */
static void update_digipot_0(unsigned int new_value)
{	uint8_t set0 = 0b00000000;											// Command byte to set side A of digipot to the value of the next byte sent
	uint8_t set1 = 0b00010000;											// Command byte to set side B of digipot to the value of the next byte sent
	PORTB &= ~(1<<PINB0);												// Clear slave select line to select chip
	_delay_us(1);
	SPI_MasterTransmit(set0);											// Side A command byte
	SPI_MasterTransmit(new_value);										// New value byte
	SPI_MasterTransmit(set1);											// Side B command byte
	SPI_MasterTransmit(new_value);										// New value byte
	PORTB |= (1<<PINB0);												// Set slave select line to de-select chip
	_delay_us(1);
}

/* function interfaces with digipot1 over SPI to set new value */
static void update_digipot_1(unsigned int new_value)
{	uint8_t set0 = 0b00000000;											// Command byte to set side A of digipot to the value of the next byte sent
	uint8_t set1 = 0b00010000;											// Command byte to set side B of digipot to the value of the next byte sent
	PORTB &= ~(1<<PINB1);												// Clear slave select line to select chip
	_delay_us(1);
	SPI_MasterTransmit(set0);											// Side A command byte
	SPI_MasterTransmit(new_value);										// New value byte
	SPI_MasterTransmit(set1);											// Side B command byte
	SPI_MasterTransmit(new_value);										// New value byte
	PORTB |= (1<<PINB1);												// Set slave select line to de-select chip
	_delay_us(1);
}

/* function updates the digipot (2 digipots in series) with any value from 0 to 511, values outside this range are ignored  */
static void update_digipot(unsigned int new_value)
{	if (new_value < 0)
	{	return;	}														// Illegal value
			
	if (new_value > 511)
	{	return;	}														// Illegal value		
					
	if (new_value <= 255)		
	{	update_digipot_1(0);											// Set digipot1 to zero
		update_digipot_0(new_value);									// Set digipot0 to new value	
		return;
	}
		
	if (new_value > 255)
	{	update_digipot_0(255);											// Set digipot0 to 255
		update_digipot_1(new_value-256);								// Set digipot1 to new value minus 256		
		return;
	}
}


static void itokhz(unsigned int freq, char* char_freq)
{	char buffer[8];
	buffer[7] = '\0';
	freq /= 100;														// get to the hundreds
	buffer[6]=(freq % 10) + '0';										// convert the hundreds to ASCII char
	freq /= 10;															// get to the thousands
	buffer[5]='.';														// insert decimal point
	buffer[4]=(freq % 10) + '0';										// convert the thousands to ASCII char
	freq /= 10;															// get to the ten thousands
	if (freq)															// if the ten thousands is non-zero
	{	buffer[3]=(freq % 10) + '0';									// convert the ten thousands
		freq /= 10;														// get to the hundred thousands
	}
	if (freq)															// if the hundred thousands is non-zero
	{	buffer[2]=(freq % 10) + '0';									// convert the hundred thousands to ASCII char
		freq /= 10;
	}
	if (freq)															// if the millions is non-zero
	{	buffer[1]=(freq % 10) + '0';									// convert the millions to ASCII char
		freq /= 10;
	}
	if (freq)															// if the tens of millions is non-zero
	{	buffer[0]=(freq % 10) + '0';									// convert the tens of millions to ASCII char
		freq /= 10;
	}
	char buffer2[8] = "0001.2";
	
	strcpy(char_freq, buffer2);
	strcat(char_freq, " kHz");
}



int main(void)
{
	sei();																// Set global interrupts
	
	lcd_init(LCD_DISP_ON);												// Initialize LCD to 4-bit mode
	lcd_clrscr();														// Clear display
//	lcd_gotoxy(0,0);													// Go to first line
//	lcd_puts("Frequency:");												// Put character string to display
//	lcd_gotoxy(11,1);													// Go to second line
//	lcd_puts("Hz");														// Put character string to display
	
	EncoderInit_2DetentsPerPulse();										// Initialize encoder
	
	DDRB |= (1<<DDB0) | (1<<DDB1) | (1<<DDB2);							// Set slave select lines to output
	PORTB |= (1<<PINB0) | (1<<PINB1) | (1<<PINB2);						// Set slave select lines high
	SPI_MasterInit();													// Initialise SPI as Master
	
	while (1)
	{
		/* if there is no frequency measurement in progress, then initiate one and capture the last measurement*/
		if (count_flag == 0)											// If there is no frequency measurement in progress...
		{	switch (count_rep)											
			{	case '0':												// Record the last measurement
				{	freq_Hz_0 = freq_Hz;
					break;
				}
				case '1':												// Record the last measurement
				{	freq_Hz_1 = freq_Hz;
					break;
				}
				case '2':												// Record the last measurement
				{	freq_Hz_2 = freq_Hz;
					break;
				}
				case '3':												// Calculate average of the last three measurements
				{	freq_Hz_avg = (freq_Hz_0 + freq_Hz_1 + freq_Hz_2) / 3;
					count_rep = 0;
					char char_freq_Hz_avg[16];							// Declare character string to be used as output of sprintf()
//					sprintf(char_freq_Hz_avg, "%lu.%lu kHz   ", freq_Hz_avg/1000, (freq_Hz_avg % 1000) / 100);
					
					itokhz(freq_Hz_avg, char_freq_Hz_avg);
					
					lcd_gotoxy(0,1);									// Go to second line
					lcd_puts(char_freq_Hz_avg);							// Put character string to display
					break;
				}
//				default :												// If somehow count_rep is not any of the expected values, set it back to zero
//				{	count_rep = 0;										// Why is this not working
//					break;
//				}					
			}
			init_freq_measurement();									// Initiate the next measurement
		}

		int result = ReadEncoder_2DetentsPerPulse();					// Poll encoder outputs
		if ((enc_count + result >= 0) && (enc_count + result <=75))		// If still in bounds
		{	enc_count += result;	}									// Update encoder counter
		
//		unsigned int digipot_value = digipot_linear_values[enc_count];	// Copy element from array into variable digipot_value
					
//		char char_digipot_value[16];
//		sprintf(char_digipot_value,"%u",digipot_value);
//		lcd_gotoxy(0,0);
//		lcd_puts(char_digipot_value);

		update_digipot(enc_count);										// Update digipot with new value

//		char char_enc_count[8];											// Declare character string to be used as output of sprintf()
//		sprintf(char_enc_count,"%u",enc_count);							// Format string as decimal from unsigned integer enc_count
//		lcd_gotoxy(0,0);												// Go to first line
//		lcd_puts(char_enc_count);										// Put character string to display

	}
}