/*
    File:       main.c
    Version:    1.0.0
    Date:       Feb. 25, 2006
    
    AVR 3-Wire Interface to a PC-1202-A LCD Display (4-wires if you want the AVR
    to control the backlight as well).
    
    ****************************************************************************
    Copyright (C) 2006 Micah Carrick   <email@micahcarrick.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    ****************************************************************************
*/

/*
 *****************************************************************************
 08-28-2008 
 modified by Robert Ryan for voltage and current monitoring
 in a power supply to be designed and built for CIE 14 at Sierra College
 under the instruction of Mike Robinson.

 08-31-2008
 I struggled to make this code fit on the 1024 bytes of a tiny13 and almost
 decided to step up to the tiny25. after sleeping on it I realized I could 
 make a slight modification to my circuit and drop a variable to free up
 some much needed space. this also allowed me to use a 10 bit ADC reading for
 my current measurement. I believe this code is complete with the exception of
 slight adjustments depending on size of shunt resistor and range of power 
 supply. code size is currently 1012 bytes.

 in 20.00v mode this circuit will need a voltage divider, looking through
 my assortment of resistors I had a hard time finding any pair with a 1:3 ratio
 this got me thinking, why can't I use 4 resistors (three in parallel) to do
 the same thing. maybe not the best solution, but it works well. I intend to 
 put a 5.1v zener diode from ground to the ADC inputs to protect them from
 any voltage spikes or increases over 20.00v

 09-05-2008
 I decided to further optimize the code using methods suggested by friends. I 
 have eliminated my need for a decimal in the current readout, allowing me to
 display in mA rather than A which is good cause the power supply is probably
 going to top out around .80 A.. 803 mA looks a lot better even if the 
 resolution is slightly worse than 3mA @ 20v. I had a lot of trouble getting 
 the code to fit and I had to remove the lcd_puts_P() function (it is still in 
 lcd.c but commented out).  I believe this will be the final revision of this 
 code unless bugs are found. currently I am using 1000 bytes. 

 ACCURACY
 
 Scale | Voltage | Current
 ------|---------|--------
  5v   | 4.9mV   | 0.84mA
  20v  | 19.5mV  | 3.37mA

 05.00 / 1024 = 0.00488  = 4.88mV
 20.00 / 1024 = 0.01953  = 19.53mV
 .00488 / 5.8 = 0.000842 = 0.84mA
 .01953 / 5.8 = 0.003367 = 3.37mA

*/

#define F_CPU 9600000UL 

#include <avr/io.h>    
#include <avr/pgmspace.h>    
#include <util/delay.h>
#include "lcd.h"

#define ON 1
#define OFF 0

int32_t voltread;

char dig[6];
char num[5] = "";

void adcinit(void)
{
 ADCSRA |= (1 << ADEN)|                 // ADC enable
           (1 << ADPS1)|                // set prescaler to FCPU/64
	   (1 << ADPS2);
}

int adcread(void)
{
 ADCSRA |= (1 << ADEN);                 // ADC enable
 ADCSRA |= (1 << ADSC);                 // Discard first conversion
 while (ADCSRA & (1 << ADSC));          // wait until conversion is done
 ADCSRA |= (1 << ADSC);                 // start single conversion
 while (ADCSRA & (1 << ADSC));          // wait until conversion is done
 
 return ADC;     			// return value (10 bit)
}

char *itoa(char str[6], int num, char dec)
{
	char digit;
	char i;
	char *p = str;
	
	int org = num;
	
	for(i = 4; i > -1; i--)
	{
		if(i == 2 && dec == 1)
		{
			str[i] = '.';
			continue;
		}
		else if(i == 2 && dec == 0)
		{
			str[i] = '\0';
		}
		digit = num % 10;
		num /= 10;
		str[i] = '0' + digit;
	}
	str[5] = '\0';
	while(*p == '0' && *(p+1) != '.')
		*p++ = ' ';
	if (org == 0)
	{
		str[4] = '0';
	}
	return str;
}

int main (void)
{
 DDRB |= (1 << PB0)|			// set outputs
 	 (1 << PB1)|
	 (1 << PB2);			
 adcinit ();
 lcd_initialize(LCD_FUNCTION_8x2, LCD_CMD_ENTRY_INC, LCD_CMD_ON);

 for (;;)
 {
  lcd_move_cursor(LCD_LINE_1, 4);

  ADMUX |= (1 << MUX1);     		// use ADC2 (PB4) -- VOLTAGE
  ADMUX &= ~(1 << MUX0);		// make sure MUX0 is cleared
  
  voltread = adcread();
  voltread = (voltread*500)/1023;	// 05.00v scale
//  voltread = (voltread*2000)/1023;	// 20.00v scale
  
//  lcd_puts_P( PSTR("Volts: ") );

  lcd_puts(itoa(dig, voltread, ON));
  lcd_putc(' ');
  lcd_putc('V');
  
  lcd_move_cursor(LCD_LINE_2, 4);

  ADMUX |= (1 << MUX0)|
           (1 << MUX1); 		// use ADC3 (PB3) -- CURRENT

  voltread = adcread();			// read 10bit ADC value
  voltread = (voltread*500)/1023;	// 05.00v scale
//  voltread = (voltread*2000)/1023;	// 20.00v scale
  voltread = (voltread*100)/56;		// I = E / R :) *10 because it's 5.6ohm

//  lcd_puts_P( PSTR("Amps:  ") );
  lcd_puts(itoa(dig, voltread, OFF));
  lcd_putc(' ');
  lcd_putc('m');
  lcd_putc('A');

  _delay_ms(10);
  lcd_move_cursor(LCD_LINE_1, 4);
}        
 return (0);
}
