rgbyteclock-code/spi.c

214 lines
4.8 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<<PC3);
else PORTC &= ~(1<<PC3);
}
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 (PINB & (1<<PB4)) {
// 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<<PCINT12);
PCICR |= (1<<PCIE1);
/* Set MISO output, all others input */
DDRB |= (1<<PB6);
DDRB &= ~((1<<PB7)|(1<<PB5)|(1<<PB4));
/* Enable SPI */
SPCR = (1<<SPE) | (1<<SPIE); // enable SPI
DDRC &= ~(1<<PC4); // ACK_MOSI
PORTC &= ~(1<<PC4);
DDRC |= (1<<PC3); // 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;
}
}
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);
}
}
else {
return -127;
}
}