#include #include #include #include #include "main.h" #include /* SPI framework. * * currently alpha, uses interrupts, single master/single slave operation */ void spi_init(){ uint8_t spcr, spsr, d; /* calculate clock divisor, * gcc with optimize will do that calculation compile-time. */ uint8_t spi_clock_divisor(){ double d; d = F_CPU / SPI_BAUDRATE; d = ceil((log(d)/log(2))*0.95); // clock needs dividing by 2^d // the 0.95 to avoid ceil issues return d-1; // the -1 because minimum divisor is /2 } /* do pseudo-calculation in preprocessor, to get warnings * we right-shift 8 times; if result >0, d will be >7 * so output warning in that case */ #define D_VAL (F_CPU / SPI_BAUDRATE) #if ((D_VAL >> 8) > 0 ) #warning "spi baudrate too slow, cannot be set" #endif d = spi_clock_divisor(); if(d>7){ // warning was (hopefully) output previously by cpp d=7; } //DDRs setzen #ifdef SPI_MASTER DDRB |= _BV(5) | _BV(3) | _BV(2); //MOSI, SCK, unused SS DDRB &= ~( _BV(4)); //MISO SPI_SSDDR |= _BV(SPI_SS_PIN); // actually used SS #else //SPI_MASTER DDRB |= _BV(4); //MISO DDRB &= ~( _BV(5) | _BV(3) | _BV(2)); //MOSI, SCK, SS #endif //SPI_MASTER spsr = 0; spsr |= (d & 1) ? 0 : _BV(SPI2X); spcr = 0 | _BV(SPE); spcr |= (d & 2) ? _BV(SPR0) : 0; spcr |= (d & 4) ? _BV(SPR1) : 0; #ifdef SPI_MASTER spcr |= _BV(MSTR); #endif SPSR = spsr; SPCR = spcr; #ifndef SPI_MASTER SPCR |= _BV(SPIE); //enable interrupts #endif } #ifdef SPI_MASTER void spi_mst_start_packet(){ SPI_SSOUT &= ~(_BV(SPI_SS_PIN)); } void spi_mst_end_packet(){ SPI_SSOUT |= _BV(SPI_SS_PIN); } #endif //SPI_MASTER #define SPI_WAIT while(SPSR & _BV(SPIF)){;} void spi_mst_write(uint8_t len, uint8_t *data){ while(len>0){ SPDR = *data++; len--; SPI_WAIT; } } void spi_mst_read(uint8_t len, uint8_t *data){ while(len>0){ SPDR = 0; len--; SPI_WAIT; *data++ = SPDR; } } #ifndef SPI_MASTER void __attribute__((always_inline)) spi_sla_handle_packet(){ // TODO: make slave not hangup in case of partial read uint8_t opcode, addr, do_write; uint16_t data; SPI_WAIT; SPDR = 0x53; // random ack SPI_WAIT; opcode = SPDR; do_write = spi_proto_needswrite(opcode); SPI_WAIT; addr = SPDR; SPDR = 0x00; // buys some pause if(do_write){ data = spi_proto_handlewrite(opcode, addr); SPI_WAIT; SPDR = data >> 8; SPI_WAIT; SPDR = data & 0xff; } else { SPI_WAIT; data = SPDR << 8; SPI_WAIT; data |= SPDR; spi_proto_handleread(opcode, addr, data); } } ISR(SPI_vect){ SPCR &= ~(_BV(SPIE)); //disable spi interrupts spi_sla_handle_packet(); SPCR |= _BV(SPIE); } #endif //not SPI_MASTER