#include <avr/io.h>
#include <stdint.h>

inline void setbit(volatile uint8_t &reg, uint8_t bit)
{
  reg = reg | (1 << bit);
}

inline void clearbit(volatile uint8_t &reg, uint8_t bit)
{
  reg = reg & ~(1 << bit);
}

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

//inicializace SPI1 pro pouziti MCP23S08:
void MCPinicializace()
{
  DDRE = (1 << 2) | (1 << 3); //SS1, MOSI1
  DDRC = (1 << 1); //SCK1
  PORTE = PORTE | (1 << 2); //SS1 = 1
  SPCR1 = (1 << SPE) | (1 << MSTR);  //master
  SPSR1 = (1 << SPI2X);  //8 Mbit/s
}

//adresy MCP23S08 pro zapis a cteni:
const uint8_t MCP_ADDR_W = 0b01000110;  
const uint8_t MCP_ADDR_R = 0b01000111;
//cisla prikazu MCP23S08:
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

//zapis na MCP:
uint8_t MCPoperace(uint8_t adresa,
                   uint8_t prikaz,
                   uint8_t data)
{
  clearbit(PORTE, 2); //SS1 = 0
  
  SPDR1 = adresa;  //posle adresu
  while(!getbit(SPSR1, SPIF));
  SPDR1 = prikaz;  //posle prikaz
  while(!getbit(SPSR1, SPIF));
  SPDR1 = data;   //posle data
  while(!getbit(SPSR1, SPIF));
  
  setbit(PORTE, 2); //SS1 = 1
  
  return SPDR1;  //vrati prijata data
}

int main(void)
{
  uint8_t data;

  MCPinicializace();
  //7..4 vystupy, 3..0 vstupy:
  MCPoperace(MCP_ADDR_W, MCP_IODIR, 0b00001111);
  //pull-upy pro vstupy:  
  MCPoperace(MCP_ADDR_W, MCP_GPPU, 0b00001111);
  //inverze pro vstupy: 
  MCPoperace(MCP_ADDR_W, MCP_IPOL, 0b00001111); 

  while(1)
  {
    //cteni stavu vstupu (spinacu):
    data = MCPoperace(MCP_ADDR_R, MCP_GPIO, 0);
    //zapis na vystupy (LED):
    MCPoperace(MCP_ADDR_W, MCP_GPIO, data << 4);
  }
}


