#include "spi.h"

void SPI::init() const volatile
{
	// Konfiguriere SPI DDRs
	DDRB |= _BV(SLSL) | _BV(MOSI) | _BV(SCLK);
	DDRB &= ~_BV(MISO);

	// Konfiguriere DMUX DDRs
	DDRD |= _BV(DMUX1) | _BV(DMUX2) | _BV(DMUX3);

	// aktiviere SPI, Interruptbetrieb, Master Modus, SPI Modus 0
	// F_SPI = F_CPU / 2   (prescaler 2)
	SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPIE);
	SPSR = _BV(SPI2X);

	// waehle keinen SPI Slave aus
	setAdr(SPIADR::NONE);
}

void SPI::handleTransfer() volatile
{
	if(!active)
	{
		setAdr(SPIADR::NONE);
		return;
	}
	
	uint8_t next = buffer[index];
	buffer[index++] = SPDR;
	
	if(index >= length)
	{
		index = 0;
		active = false;
	}
	
	SPDR = next;
}

void SPI::addByte(uint8_t b) volatile
{
	wait();
	buffer[index++] = b;
}

void SPI::transfer(SPIADR adr) volatile
{
	if(!index)
		return;
	
	wait();
	active = true;
	
	setAdr(adr);
	
	this->length = index;
	this->index = 0;
	
	handleTransfer();
}

void SPI::wait() const volatile
{
	while(active);
}

void SPI::setAdr(SPIADR adr) const volatile
{
	PORTD &= ~(_BV(DMUX1) | _BV(DMUX2) | _BV(DMUX3));
	PORTD |= (adr & 0x01) ? _BV(DMUX1) : 0;
	PORTD |= (adr & 0x02) ? _BV(DMUX2) : 0;
	PORTD |= (adr & 0x04) ? _BV(DMUX3) : 0;
}