//*****************************************************************************
//
//  I2Cdriver.c - Knihovna pro komunikaci po IIC sbernici
//
// Copyright (c) 2005-2008 Luminary Micro, Inc.  All rights reserved.
// 
// Software License Agreement
// 
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
// 
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws.  All rights are reserved.  You may not combine
// this software with "viral" open-source software in order to form a larger
// program.  Any use in violation of the foregoing restrictions may subject
// the user to criminal sanctions under applicable laws, as well as to civil
// liability for the breach of the terms and conditions of this license.
// 
// THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 3416 of the Stellaris Peripheral Driver Library.
//
//*****************************************************************************

#include <hw_memmap.h>
#include <hw_types.h>
#include <hw_ints.h>
#include <debug.h>
#include <gpio.h>
#include <i2c.h>
#include <ssi.h>
#include <sysctl.h>
#include <interrupt.h>
#include <I2Cdriver.h>


static volatile IICData IICComm;

/******************************************************************************/

//I2C_IntHandler / Stavovy automat pro komunikaci I2C--------------------------
void I2C_IntHandler(void)
{
    I2CMasterIntClear(I2C0_MASTER_BASE);  // Clear the I2C interrupt.

    switch(IICComm.State) // Determine what to do based on the current state.
    {
        case I2C_STATE_IDLE:  // The idle state.
        {
            // There is nothing to be done.
            break;
        }
        case I2C_STATE_WRITE_NEXT:  // The state for the middle of a burst write.
        {
            // If there is one byte left, set the next state to the final write
            // state.
            if(--IICComm.Count == 1) IICComm.State = I2C_STATE_WRITE_FINAL;

            // Write the next byte to the data register.
            I2CMasterDataPut(I2C0_MASTER_BASE, *IICComm.Data++);
            // Continue the burst write.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
            break;
        }
        case I2C_STATE_WRITE_FINAL: // The state for the final write of a burst sequence.
        {
            // Write the final byte to the data register.
            I2CMasterDataPut(I2C0_MASTER_BASE, *IICComm.Data++);
            IICComm.Count--;
            // The next state is to wait for the burst write to complete.
            //g_ulState = I2C_STATE_SEND_ACK;
            IICComm.State = I2C_STATE_IDLE;
            // Finish the burst write.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
            break;
        }
        /*
        case I2C_STATE_WAIT_ACK:  // Wait for an ACK on the read after a write.
        {
            // See if there was an error on the previously issued read.
            if(I2CMasterErr(I2C0_MASTER_BASE) == I2C_MASTER_ERR_NONE)
            {
                // Read the byte received.
                I2CMasterDataGet(I2C0_MASTER_BASE);
                // There was no error, so the state machine is now idle.
                g_ulState = I2C_STATE_IDLE;
                break;
            }
        }
        
        // Send a read request, looking for the ACK to indicate that the write
        // is done.
        case I2C_STATE_SEND_ACK:
        {
            // The next state is the wait for the ack.
            g_ulState = I2C_STATE_WAIT_ACK;

            // Put the I2C master into receive mode.
            I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, g_ulSlaveAddress, true);
            // Perform a single byte read.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
            break;
        }
        */
        case I2C_STATE_READ_ONE:  // The state for a single byte read.
        {
            // The next state is the wait for final read state.
            IICComm.State = I2C_STATE_READ_WAIT;

            // Put the I2C master into receive mode.
            I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, IICComm.SlaveAddress, true);
            // Perform a single byte read.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
            break;
        }
        case I2C_STATE_READ_FIRST:  // The state for the start of a burst read.
        {
            if(--IICComm.Count <= 1) IICComm.State = I2C_STATE_READ_FINAL;
            else IICComm.State = I2C_STATE_READ_NEXT;
            
            // Put the I2C master into receive mode.
            I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, IICComm.SlaveAddress, true);
            // Start the burst receive.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
            // The next state is the middle of the burst read.

            break;
        }
        case I2C_STATE_READ_NEXT:   // The state for the middle of a burst read.
        {
            // If there are two characters left to be read, make the next
            // state be the end of burst read state.
            if(--IICComm.Count <= 2) IICComm.State = I2C_STATE_READ_FINAL;

            // Read the received character.
            *IICComm.Data++ = I2CMasterDataGet(I2C0_MASTER_BASE);
            // Continue the burst read.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
            break;
        }
        case I2C_STATE_READ_FINAL:  // The state for the end of a burst read.
        {
            IICComm.Count--;
            // The next state is the wait for final read state.
            IICComm.State = I2C_STATE_READ_WAIT;
            // Read the received character.
            *IICComm.Data++ = I2CMasterDataGet(I2C0_MASTER_BASE);
            // Finish the burst read.
            I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
            break;
        }
        case I2C_STATE_READ_WAIT: // This state is for the final read of a single or burst read.
        {
            // Read the received character.
            *IICComm.Data++  = I2CMasterDataGet(I2C0_MASTER_BASE);
            IICComm.Count--;  
            // The state machine is now idle.
            IICComm.State = I2C_STATE_IDLE;
            break;
        }
    }
}

//I2C_Write / Zapis do I2C zarizeni----------------------------------------------
void I2C_Write(unsigned char address, unsigned char *data, unsigned char count)
{
    // Save the data buffer to be written.
    IICComm.Data = data;
    IICComm.Count = count;
    
    if(count==1)
    {
      // Set the next state of the interrupt state machine based on the number of
      // bytes to write.
      IICComm.State = I2C_STATE_IDLE;
      // Set the slave address and setup for a transmit operation.
      I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, address, false);
      // Write a data byte
      I2CMasterDataPut(I2C0_MASTER_BASE, *IICComm.Data++);
      IICComm.Count--;
      // Start the burst cycle, writing the address as the first byte.
      I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_SEND);
    }
    else
    {
      // Set the next state of the interrupt state machine based on the number of
      // bytes to write.
      IICComm.State = (count == 2 ? I2C_STATE_WRITE_FINAL : I2C_STATE_WRITE_NEXT);
      // Set the slave address and setup for a transmit operation.
      I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, address, false);
      // Place the address to be written in the data register.
      I2CMasterDataPut(I2C0_MASTER_BASE, *IICComm.Data++);
      // Start the burst cycle, writing the address as the first byte.
      I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_START);      
    }
    // Wait until the I2C interrupt state machine is idle.
    while(IICComm.State != I2C_STATE_IDLE);
}

//I2C_Read / Cteni z I2C zarizeni----------------------------------------------
void I2C_Read( unsigned char address, unsigned char *data, unsigned char info)
{
    // Save the data buffer to be read.
    IICComm.Data = data;
    IICComm.Count = info & 0x0f;
    IICComm.SlaveAddress = info & 0xf0;
    IICComm.DataAddress=address;
    
    //Nastaveni spravne HW adresy chipu, se kterym budu komunikovat
    switch (IICComm.SlaveAddress){
    case EEPROM:                                    //Bude se komunikovat s EEPROM
      {
        IICComm.SlaveAddress=ADDRESS_EEPROM;
      break; 
      }
    case TEMP_SENS:                                 //Bude se komunikovat s teplotnim snimacem
      {
        IICComm.SlaveAddress=ADDRESS_TEMP_SENS;
      break;
      }
    case EP_IO:
      {
        IICComm.SlaveAddress=ADDRESS_EP_DIGIO;
      break;
      }
    case EP_LED:
      {
        IICComm.SlaveAddress=ADDRESS_EP_LED;
      break;
      }
    case EP_ADDR:
      {
        IICComm.SlaveAddress=ADDRESS_EP_ADDR;
      break;
      }      
    default:                                        //Pokud jina adresa, neprovede se nic
      {
        IICComm.SlaveAddress=ADDR_ERROR;
      }  
    }

    if(IICComm.SlaveAddress!=ADDR_ERROR)
    {
      // Set the next state of the interrupt state machine based on the number of bytes to read.
      IICComm.State = (IICComm.Count == 1 ? I2C_STATE_READ_ONE : I2C_STATE_READ_FIRST); 
       
      //Slave adresa
          I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, IICComm.SlaveAddress, false);
      // Place the address to be written in the data register.
          if((IICComm.SlaveAddress==ADDRESS_EEPROM)||(IICComm.SlaveAddress==ADDRESS_TEMP_SENS))   //pokud EEPROM nebo snim.tepl. odesilam adresu odkud chci cist
            I2CMasterDataPut(I2C0_MASTER_BASE, IICComm.DataAddress);
      // Perform a single send, writing the address as the only byte.  
          I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_SEND);   
      // Wait until the I2C interrupt state machine is idle.
        while(IICComm.State != I2C_STATE_IDLE);
    }
}

//I2C_Init / Inicializace I2C sbernice-----------------------------------------
void I2C_Init(void)
{
	
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);                                 // Povoleni IIC periferie
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);                   // Nastaveni pinu 2 a 3 na portu B pro IIC sbernici
    I2CMasterInitExpClk(I2C0_MASTER_BASE, SysCtlClockGet(), true);              // Nastaveni rychlosti IIC sbernice a povoleni MASTER modulu

    IICComm.State=I2C_STATE_IDLE;                                               //Vychozi stav automatu pro obsluhu IIC sb.
    IntEnable(INT_I2C0);                                                        // Povoleni I2C preruseni.
    IntMasterEnable();                                                          // Globalni povoleni prerusenich
    I2CMasterIntEnable(I2C0_MASTER_BASE);                                       // Povoleni IIC MASTER preruseni
    I2CIntRegister(I2C0_MASTER_BASE, I2C_IntHandler);
}
