#include #include #include #include #include #include "main.h" #include "spi.h" /* SPI framework. * * currently alpha, uses interrupts, single master/single slave operation */ uint8_t readbuf[5]; uint8_t writebuf[5]; uint8_t pos; 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 (clamped to minimum)" #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 PCMSK0 |= _BV(2); PCICR |= _BV(0); #endif pos = 0; } void spi_mst_start_packet(){ #ifdef SPI_MASTER SPI_SSOUT &= ~(_BV(SPI_SS_PIN)); _delay_us(1e6/SPI_BAUDRATE); #endif } void spi_mst_end_packet(){ #ifdef SPI_MASTER _delay_us(1e6/SPI_BAUDRATE); SPI_SSOUT |= _BV(SPI_SS_PIN); _delay_us(1e6/SPI_BAUDRATE); #endif } #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; } } void spi_mst_write_read(uint8_t len, uint8_t *data){ while(len>0){ SPDR = *data; len--; SPI_WAIT; *data++ = SPDR; } } #ifndef SPI_MASTER void spi_sla_handle_packet(){ // TODO: make slave not hangup in case of partial read uint8_t opcode, addr; uint16_t data, newdata; writebuf[0] = 0; // generate invalid replies, so no inconsistent packets // are sent. opcode = readbuf[0]; addr = readbuf[1]; data = (readbuf[2]<<8) | readbuf[3]; newdata = spi_proto_slaveaction(opcode, addr, data); writebuf[1]=addr; writebuf[2]=(newdata>>8)&0xff; writebuf[3]=(newdata)&0xff; writebuf[0]=opcode; // set opcode last to validate packet (it's a kind of locking) } ISR(SPI_STC_vect){ SPDR = writebuf[pos]; readbuf[pos] = SPDR; pos++; if(pos>=5){ pos=4; } } ISR(PCINT0_vect){ // SS-line changed if(SPI_SSIN & _BV(SPI_SS_PIN)){ // SS-line was actually released pos=0; SPCR &= ~(_BV(SPIE)); sei(); spi_sla_handle_packet(); SPCR |= _BV(SPIE); } } #endif //not SPI_MASTER