#include "i2c_master.h"
#include <avr/io.h>

uint8_t i2c_clock(uint8_t type);

void i2c_init(void)
{
	// Define SCL and SDA as Output
	DDRA |= (1 << 4) | (1 << 6);

	// Preload dataregister with "released level" data
	USIDR = 0xFF;

	// Use SCL and SDA pins
	// Select clock sources
	USICR = (1 << USIWM1) | (1 << USICS1) | (1 << USICLK);

	// Clear flags and reset counter
	USISR = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC);
}

uint8_t i2c_start(uint8_t address)
{
	// FOR REPEATED START
	// Release SCL
	PORTA |= (1 << 4);
	// Verify that SCL becomes high
	while (!( PINA & (1 << 4) ));

	// GENERATE START CONDITION
	// Force SDA LOW
	PORTA &= ~(1 << 6);

	// Pull SCL LOW
	PORTA &= ~(1 << 4);
	// Release SDA
	PORTA |= (1 << 6);

	return i2c_write(address);
}

uint8_t i2c_write(uint8_t data)
{
	// Pull SCL LOW
	PORTA &= ~(1 << 4);
	// Setup data
	USIDR = data;

	// Send 8 bits on bus
	i2c_clock(8);

	// Enable SDA as input
	DDRA &= ~(1 << 6);

	// Receive 1 bit on bus & Check for NACK
	if (i2c_clock(1) & (1 << 0))
	{
		return 1;
	}
	return 0;
}

uint8_t i2c_read(uint8_t nack)
{
	// Enable SDA as input.
    DDRA &= ~(1 << 6);
    // Read 8 bits
    uint8_t result = i2c_clock(8);

    if (nack)
    {
		// Load NACK
		USIDR = 0xFF;
    }
    else
    {
		// Load ACK
		USIDR = 0x00;
    }

    i2c_clock(1);
    return result;
}

void i2c_stop(void)
{
	// Pull SDA low
	PORTA &= ~(1 << 6);
	// Release SCL
	PORTA |= (1 << 4);
	// Wait for SCL to go high
	while (!( PINA & (1 << 4) ));

	// Release SDA
	PORTA |= (1 << 6);
}

uint8_t i2c_clock(uint8_t count)
{
	uint8_t reg_temp = (1 << USISIF) | (1 << USIOIF) | (1 << USIPF) | (1 << USIDC);
	// Counter counts number of edges
	// Overflow signals end of transmission
	reg_temp |= 16 - (count * 2);

	USISR = reg_temp;

	// Set clock source & toggle clock prepare
	reg_temp = (1 << USIWM1) | (1 << USICS1) | (1 << USICLK) | (1 << USITC);
	do
	{
		// Generate positve SCL edge.
		USICR = reg_temp;

		// Wait for SCL to go high
		while (!( PINA & (1 << 4)));

		// Generate negative SCL edge
		USICR = reg_temp;
	}
	// Wait for counter overflow (all edges are completed)
	while (!( USISR & (1 << USIOIF)));

	// Read data
	reg_temp = USIDR;
	// Load dataregister with "released level" data
	USIDR = 0xFF;

	// Enable SDA as output
	DDRA |= (1 << 6);
	return reg_temp;
}
