You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
3.1 KiB
172 lines
3.1 KiB
#include <avr/io.h>
|
|
#include <stdint.h>
|
|
#include <math.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
|
|
#include "main.h"
|
|
|
|
|
|
#include <spi.h>
|
|
|
|
/* SPI framework.
|
|
*
|
|
* currently alpha, uses interrupts, single master/single slave operation
|
|
*/
|
|
|
|
uint8_t readbuf[4];
|
|
uint8_t writebuf[4];
|
|
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
|
|
#endif
|
|
pos = 0;
|
|
}
|
|
|
|
|
|
|
|
void spi_mst_start_packet(){
|
|
#ifdef SPI_MASTER
|
|
SPI_SSOUT &= ~(_BV(SPI_SS_PIN));
|
|
#endif
|
|
}
|
|
|
|
void spi_mst_end_packet(){
|
|
#ifdef SPI_MASTER
|
|
SPI_SSOUT |= _BV(SPI_SS_PIN);
|
|
#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, do_write;
|
|
uint16_t data;
|
|
opcode = readbuf[0];
|
|
addr = readbuf[1];
|
|
do_write = spi_proto_needswrite(opcode);
|
|
if(do_write){
|
|
data = spi_proto_handlewrite(opcode, addr);
|
|
writebuf[0]=opcode;
|
|
writebuf[1]=addr;
|
|
writebuf[2]=(data>>8)&0xff;
|
|
writebuf[3]=(data)&0xff;
|
|
} else {
|
|
writebuf[0]=0;
|
|
writebuf[1]=0;
|
|
writebuf[2]=0;
|
|
writebuf[3]=0;
|
|
data = (readbuf[3]<<8)&0xff | readbuf[4];
|
|
spi_proto_handleread(opcode, addr, data);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ISR(SPI_STC_vect){
|
|
SPDR = *(writebuf+pos);
|
|
*(readbuf+pos) = SPDR;
|
|
pos++;
|
|
if(SPI_SSIN & _BV(SPI_SS_PIN)){
|
|
pos=0;
|
|
SPCR &= ~(_BV(SPIE));
|
|
sei();
|
|
spi_sla_handle_packet();
|
|
SPCR |= _BV(SPIE);
|
|
}
|
|
}
|
|
#endif //not SPI_MASTER
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|