/*
 * Project: A Lithium Battery Charger with Load Sharing
 * Author: Zak Kemble, contact@zakkemble.co.uk
 * Copyright: (C) 2013 by Zak Kemble
 * License: WTFPL - Do What the Fuck You Want to Public License
 * Web: http://blog.zakkemble.co.uk/a-lithium-battery-charger-with-load-sharing/
 */

#define F_CPU 8000000UL
#define BAUD 76800

#include <stdbool.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <util/setbaud.h>

typedef enum{
	CHARGE_OFF = 0,
	CHARGE_SLOW, // 100mA (10K)
	CHARGE_MID, // 370mA (2K7)
	CHARGE_FAST // 470mA (2K7 || 10K = 2.126K)
}charge_t;

static uint16_t batteryVoltage(void);
static void setChargeRate(charge_t);
static int uart_putchar(char, FILE*);

FILE uart_output = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

int main(void)
{
	// Charge control pins as output
	DDRD |= _BV(DDD4);
	DDRD |= _BV(DDD5);

	// Turn off charging
	setChargeRate(CHARGE_OFF);

	// Battery voltage divider control pin as output
	DDRD |= _BV(DDD7);

	// Charge state pin as input with pull-up
	PORTD |= _BV(PORTD3);

	// Don't need to do anything with the USB power sense pin since
	// default is input with pull-up disabled (R3 is a pull-down).

	// UART
	PORTD |= _BV(PD0);
	UBRR0 = UBRR_VALUE;
#if USE_2X
	UCSR0A = _BV(U2X0);
#endif
	UCSR0B = _BV(TXEN0);

	stdout = &uart_output;

	// ADC
	ADMUX = _BV(REFS1)|_BV(REFS0)|_BV(MUX1)|_BV(MUX0); // Internal 1.1V reference, use ADC3
	ADCSRA = _BV(ADEN)|_BV(ADPS2)|_BV(ADPS1); // Enable ADC, prescaler set to 64 (125KHz @ 8MHz)
	DIDR0 = _BV(ADC3D); // Disable digital input buffer on ADC3 pin

	while(1)
	{
		// Get battery voltage
		uint16_t voltage = batteryVoltage();

		// Convert to mV
		voltage = ((uint32_t)voltage * 4200) / 1023;

		// Get charge state
		bool isCharging = bit_is_clear(PIND, PIND4);

		// Get USB state
		bool usbPower = bit_is_set(PIND, PIND6);

		// Set charge
		// We're using transistors in the schematic which draw current through their bases when turned on,
		// this wastes battery power when USB isn't plugged in so turn them off when charging isn't needed.
		// If you swap them with MOSFETs then you don't need to worry about turning them on and off since the
		// gates don't draw any current.
		if(usbPower)
			setChargeRate(CHARGE_SLOW); // USB connected, enable charging
		else
			setChargeRate(CHARGE_OFF); // USB not connected, disable charging

		// Print to UART
		printf_P(PSTR("USB Power: %hhu\nCharging: %hhu\nVoltage: %umV\n---\n"), usbPower, isCharging, voltage);

		_delay_ms(1000);
	}
}

static uint16_t batteryVoltage()
{
	// Turn on battery voltage divider
	PORTD |= _BV(PD7);

	// Wait a little bit for things to turn on
	// (200us is probably more than enough)
	_delay_us(200);

	// Start ADC
	ADCSRA |= _BV(ADSC);

	// Wait for ADC to complete
	loop_until_bit_is_clear(ADCSRA, ADSC);

	// Turn off battery voltage divider
	PORTD &= ~_BV(PD7);

	// Get and return ADC value
	return ADC;
}

static void setChargeRate(charge_t charge)
{
	switch(charge)
	{
		case CHARGE_OFF: // Both transistors off
			PORTD &= ~_BV(PD4);
			PORTD &= ~_BV(PD5);
			break;
		case CHARGE_SLOW: // Turn on transistor with 10K
			PORTD &= ~_BV(PD4);
			PORTD |= _BV(PD5);
			break;
		case CHARGE_MID: // Turn on transistor with 2K7
			PORTD |= _BV(PD4);
			PORTD &= ~_BV(PD5);
			break;
		case CHARGE_FAST: // Both transistors on
			PORTD |= _BV(PD4);
			PORTD |= _BV(PD5);
			break;
		default:
			break;
	}
}

static int uart_putchar(char c, FILE *stream)
{
	if (c == '\n')
		uart_putchar('\r', stream);
	loop_until_bit_is_set(UCSR0A, UDRE0);
	UDR0 = c;
	return 0;
}
