216 lines
4.9 KiB
C
216 lines
4.9 KiB
C
/*
|
|
* ----------------------------------------------------------------------------
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* <struppi@struppi.name> wrote this file. As long as you retain this notice you
|
|
* can do whatever you want with this stuff. If we meet some day, and you think
|
|
* this stuff is worth it, you can buy me a beer in return.
|
|
* (c) 2013,2014 Stefan Rupp
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
#include "spi.h"
|
|
#include "ringbuffer.h"
|
|
#include "crc.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
|
|
#define COMM_OBJECT_SIZE (16)
|
|
#define NUM_COMM_OBJECTS (4)
|
|
#define RB_SIZE (NUM_COMM_OBJECTS)
|
|
#define SPI_COMM_PADDING (0x00)
|
|
#define LEN_SIZE (1) // LEN is 1 byte long
|
|
|
|
static uint8_t rbuff_used_rx[RB_BUFSIZE(RB_SIZE)];
|
|
static uint8_t rbuff_used_tx[RB_BUFSIZE(RB_SIZE)];
|
|
static uint8_t rbuff_unused[RB_BUFSIZE(RB_SIZE)];
|
|
|
|
static uint8_t comm_objects[NUM_COMM_OBJECTS][COMM_OBJECT_SIZE];
|
|
|
|
|
|
struct spi_comm_t {
|
|
uint8_t txout_buf[COMM_OBJECT_SIZE];
|
|
uint8_t rxin_buf[COMM_OBJECT_SIZE];
|
|
uint16_t crc;
|
|
uint8_t count;
|
|
};
|
|
static struct spi_comm_t spi_comm;
|
|
|
|
|
|
static inline void set_ack_miso(uint8_t ack)
|
|
{
|
|
if (ack) PORTC |= (1<<PC7);
|
|
else PORTC &= ~(1<<PC7);
|
|
}
|
|
|
|
|
|
|
|
static void set_crc(void* const buf)
|
|
{
|
|
uint8_t* const data = buf;
|
|
uint8_t len = data[0];
|
|
uint16_t crc = CRC_INIT_VALUE;
|
|
uint16_t *crc_ptr;
|
|
|
|
for (int i=1; i<=len; ++i) {
|
|
crc = crc_step(crc, data[i]);
|
|
}
|
|
|
|
crc_ptr = buf + len + 1;
|
|
*crc_ptr = crc;
|
|
len += 2;
|
|
data[0] = len;
|
|
}
|
|
|
|
|
|
static inline uint8_t get_ack_mosi(void)
|
|
|
|
{
|
|
|
|
#if 0
|
|
return (PINB & (1<<PB5))?1:0;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
|
|
ISR(SPI_STC_vect)
|
|
{
|
|
SPDR = spi_comm.txout_buf[spi_comm.count+1];
|
|
spi_comm.rxin_buf[spi_comm.count] = SPDR;
|
|
spi_comm.crc = crc_step(spi_comm.crc, spi_comm.rxin_buf[spi_comm.count]);
|
|
spi_comm.count++;
|
|
|
|
|
|
}
|
|
|
|
|
|
// This is the interrupt handler for the CS line.
|
|
// in case of a falling edge, we prepare for the
|
|
// SPI transfer that is about to come in in 100uS.
|
|
// in case of an rising edge, we do some cleanup.
|
|
ISR(PCINT1_vect)
|
|
{
|
|
uint8_t tx_obj, rx_obj;
|
|
|
|
if (PINC & (1<<PC1)) {
|
|
// rising edge
|
|
if ( spi_comm.rxin_buf[0] != SPI_COMM_PADDING ) {
|
|
uint8_t len = spi_comm.rxin_buf[0];
|
|
|
|
uint8_t data_valid = (spi_comm.crc == 0x0000);
|
|
data_valid = 1; // FIXME: CRC not implemented on python side, so ignore for now
|
|
set_ack_miso(data_valid);
|
|
|
|
if (data_valid && ringbuf_get_nb(rbuff_unused, &rx_obj)) {
|
|
spi_comm.rxin_buf[0] = len-2; // strip CRC from data
|
|
if (len > COMM_OBJECT_SIZE) {len = COMM_OBJECT_SIZE;}
|
|
memcpy(comm_objects[rx_obj], spi_comm.rxin_buf, len);
|
|
ringbuf_put_nb(rbuff_used_rx, rx_obj);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// falling edge
|
|
spi_comm.crc = CRC_INIT_VALUE;
|
|
spi_comm.count = 0;
|
|
uint8_t ack_mosi = get_ack_mosi();
|
|
if (ack_mosi) {
|
|
if ( ringbuf_get_nb(rbuff_used_tx, &tx_obj) ) {
|
|
uint8_t len = comm_objects[tx_obj][0];
|
|
if (len > COMM_OBJECT_SIZE) {len = COMM_OBJECT_SIZE;}
|
|
memcpy(spi_comm.txout_buf, comm_objects[tx_obj], len);
|
|
SPDR = comm_objects[tx_obj][0];
|
|
ringbuf_put_nb(rbuff_unused, tx_obj); // there ought always be enough space in this ringbuffer
|
|
}
|
|
else {
|
|
memset(spi_comm.txout_buf, SPI_COMM_PADDING, COMM_OBJECT_SIZE-1);
|
|
SPDR = SPI_COMM_PADDING;
|
|
}
|
|
}
|
|
else { // no ACK from master, so retransmit same frame once more. no need to copy anything, as txbuf wasn't touched.
|
|
SPDR = spi_comm.txout_buf[0];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int8_t spi_slave_init()
|
|
{
|
|
|
|
ringbuf_init(rbuff_used_rx, RB_SIZE, NULL);
|
|
ringbuf_init(rbuff_used_tx, RB_SIZE, NULL);
|
|
ringbuf_init(rbuff_unused, RB_SIZE, NULL);
|
|
|
|
for (uint8_t i=0; i<NUM_COMM_OBJECTS; i++) {
|
|
ringbuf_put_nb(rbuff_unused, i);
|
|
}
|
|
|
|
PCMSK1 |= (1<<PCINT9);
|
|
PCICR |= (1<<PCIE1);
|
|
|
|
/* Set MISO output, all others input */
|
|
DDRD |= (1<<PD2);
|
|
DDRD &= ~((1<<PD3)|(1<<PD4));
|
|
/* Enable SPI */
|
|
|
|
SPCR = (1<<SPE) | (1<<SPIE); // enable SPI
|
|
|
|
DDRB &= ~(1<<PB5); // ACK_MOSI
|
|
PORTB &= ~(1<<PB5);
|
|
|
|
DDRC |= (1<<PC7); // ACK_MISO
|
|
set_ack_miso(0);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int8_t spi_prepare_to_send(void *data)
|
|
{
|
|
uint8_t tx_object;
|
|
uint8_t len = ((uint8_t *)data)[0];
|
|
|
|
if ((len + LEN_SIZE + CRC_LEN) > COMM_OBJECT_SIZE) {
|
|
return -1;
|
|
}
|
|
if ( ringbuf_get_nb(rbuff_unused, &tx_object) ) {
|
|
set_crc(data);
|
|
memcpy(comm_objects[tx_object], data, len);
|
|
if ( ringbuf_put_nb(rbuff_used_tx, tx_object) ) {
|
|
return tx_object;
|
|
}
|
|
else {
|
|
return -tx_object;
|
|
//PORTB ^= (1<<PB3);
|
|
}
|
|
}
|
|
else {
|
|
return (-127);
|
|
}
|
|
}
|
|
|
|
|
|
int8_t spi_receive(void *data)
|
|
{
|
|
uint8_t rx_object;
|
|
|
|
if ( ringbuf_get_nb(rbuff_used_rx, &rx_object) ) {
|
|
memcpy(data, comm_objects[rx_object], COMM_OBJECT_SIZE);
|
|
if ( ringbuf_put_nb(rbuff_unused, rx_object) ) {
|
|
return rx_object;
|
|
}
|
|
else {
|
|
return (-rx_object);
|
|
//PORTB ^= (1<<PB3);
|
|
}
|
|
}
|
|
else {
|
|
return -127;
|
|
}
|
|
}
|