#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/twi.h>
#include <stdlib.h>
#include <stdint.h>

inline uint8_t getbit(volatile uint8_t reg, uint8_t bit)
{
  return (reg & (1 << bit)) >> bit;
}

//inicializace TWI:
void MCPinicializace()
{
  PORTC = (1 << 4) | (1 << 5); //zapnuti pull-upu
  TWBR0 = 72;  //100 kHz
  TWSR0 = 0;
}

//bazova adresa MCP23008:
const uint8_t MCP_ADDR = 0b01001110;
//cisla prikazu MCP23008:
const uint8_t MCP_IODIR = 0; //nastaveni smeru
const uint8_t MCP_GPIO  = 9; //cteni/zapis v/v
const uint8_t MCP_GPPU  = 6; //nastaveni pull-upu
const uint8_t MCP_IPOL  = 1; //negace vstupu

uint8_t MCPoperace(uint8_t bazovaadresa,
                   uint8_t prikaz,
                   uint8_t vstupnidata,
                   bool cteni = false,
                   uint8_t* vystupnidata = NULL)
{
  //START:
  TWCR0 = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
  while(!getbit(TWCR0, TWINT));
  if((TWSR0 & TW_STATUS_MASK) != TW_START)
    return 1; //chybny START

  //SLAVE ADRESA_W:
  TWDR0 = bazovaadresa;
  TWCR0 = (1 << TWINT) | (1 << TWEN);
  while(!getbit(TWCR0, TWINT));
  if((TWSR0 & TW_STATUS_MASK) != TW_MT_SLA_ACK)
    return 2; //chybne poslani adresy

  //posle cislo registru (prikazu):
  TWDR0 = prikaz;
  TWCR0 = (1 << TWINT) | (1 << TWEN);
  while(!getbit(TWCR0, TWINT));
  if((TWSR0 & TW_STATUS_MASK) != TW_MT_DATA_ACK)
    return 3; //chybne poslani dat

  if(!cteni)
  { //zapis
    //odeslani dat:
    TWDR0 = vstupnidata;
    TWCR0 = (1 << TWINT) | (1 << TWEN);   
    while(!getbit(TWCR0, TWINT));
    if((TWSR0 & TW_STATUS_MASK) != TW_MT_DATA_ACK)
      return 4; //chybne poslani dat
  }
  else  //cteni
  {
    //RESTART:
    TWCR0 = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
    while(!getbit(TWCR0, TWINT));
    if((TWSR0 & TW_STATUS_MASK) != TW_REP_START)
    return 5; //chybny RESTART

    //SLAVE ADRESA_R:
    TWDR0 = bazovaadresa + 1;
    TWCR0 = (1 << TWINT) | (1 << TWEN);
    while(!getbit(TWCR0, TWINT));
    if((TWSR0 & TW_STATUS_MASK) != TW_MR_SLA_ACK)
      return 6; //chybne poslani adresy
      
    //cteni dat:  
    TWCR0 = (1 << TWINT) | (1 << TWEN);  
    while(!getbit(TWCR0, TWINT));
    if((TWSR0 & TW_STATUS_MASK) != TW_MR_DATA_NACK)
      return 7; //chybny prijem dat  
    
    *vystupnidata = TWDR0; //ulozeni vysledku
  }
 
  //STOP:
  TWCR0 = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
  while(getbit(TWCR0, TWSTO));

  return 0;
}

int main(void)
{
  uint8_t data;
  
  MCPinicializace(); //inicializace TWI

  //7..4 vystupy, 3..0 vstupy:
  MCPoperace(MCP_ADDR, MCP_IODIR, 0b00001111);  
  //pull-upy pro vstupy:  
  MCPoperace(MCP_ADDR, MCP_GPPU, 0b00001111);
  //inverze pro vstupy: 
  MCPoperace(MCP_ADDR, MCP_IPOL, 0b00001111);   
    
  while(1)
  {
    //cteni stavu vstupu (spinacu):
    MCPoperace(MCP_ADDR, MCP_GPIO, 0, true, &data);
    //zapis na vystupy (LED):  
    MCPoperace(MCP_ADDR, MCP_GPIO, data << 4);
  }
}


