/* * SPI slave abstraction layer. * * Copyright (C) 2008 Michael Buesch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "spi_slave.h" #include "util.h" #include enum spi_slave_xfer { SPI_SLAVE_DROP, SPI_SLAVE_XMIT, SPI_SLAVE_FETCH, }; struct spi_slave_xmit_state { const uint8_t *buf; uint16_t nr_bytes; uint16_t count; }; struct spi_slave_fetch_state { uint8_t *buf; uint16_t bufsize; uint16_t xfersize; uint16_t count; }; struct spi_slave_state { uint8_t current_xfer; union { struct spi_slave_xmit_state xmit; struct spi_slave_fetch_state fetch; }; }; static struct spi_slave_state state; static inline void spi_slave_xmit_next_byte(void) { if (state.xmit.count == 0) SPDR = hi8(state.xmit.nr_bytes); else if (state.xmit.count == 1) SPDR = lo8(state.xmit.nr_bytes); else SPDR = state.xmit.buf[state.xmit.count - 2]; state.xmit.count++; } ISR(SPI_STC_vect) { uint8_t data; uint16_t count; data = SPDR; switch (state.current_xfer) { case SPI_SLAVE_DROP: break; case SPI_SLAVE_XMIT: if (state.xmit.count == state.xmit.nr_bytes + 2) { /* Transmission finished. */ state.current_xfer = SPI_SLAVE_DROP; spi_slave_xfer_complete(state.xmit.count - 2); return; } spi_slave_xmit_next_byte(); break; case SPI_SLAVE_FETCH: count = state.fetch.count; if (count == 0) { state.fetch.xfersize = (((uint16_t)data) << 8); } else if (count == 1) { state.fetch.xfersize |= data; } else { if (count - 2 < state.fetch.bufsize) state.fetch.buf[count - 2] = data; } count = ++state.fetch.count; if ((count >= 2) && (count - 2 == state.fetch.xfersize)) { /* Fetch transfer finished. */ state.current_xfer = SPI_SLAVE_DROP; spi_slave_xfer_complete(state.fetch.xfersize); return; } break; } } int8_t spi_slave_xmit(const void *buf, uint16_t nr_bytes) { uint8_t sreg; sreg = irq_disable_save(); if (state.current_xfer != SPI_SLAVE_DROP) { irq_restore(sreg); return -1; } state.current_xfer = SPI_SLAVE_XMIT; state.xmit.buf = buf; state.xmit.nr_bytes = nr_bytes; state.xmit.count = 0; /* Put first byte into the buffer. */ spi_slave_xmit_next_byte(); /* Wait for the master to initiate the transfer. * The interrupt will remind us. */ irq_restore(sreg); return 0; } int8_t spi_slave_fetch(void *buf, uint16_t size) { uint8_t sreg; sreg = irq_disable_save(); if (state.current_xfer != SPI_SLAVE_DROP) { irq_restore(sreg); return -1; } state.current_xfer = SPI_SLAVE_FETCH; state.fetch.buf = buf; state.fetch.bufsize = size; state.fetch.xfersize = 0; state.fetch.count = 0; /* Wait for the master to initiate the transfer. * The interrupt will remind us. */ irq_restore(sreg); return 0; } void spi_slave_init(void) { state.current_xfer = SPI_SLAVE_DROP; /* SPI slave mode 0 with IRQ enabled. * Prescaler is 4. */ DDRB |= (1 << 4/*MISO*/); DDRB &= ~((1 << 5/*SCK*/) | (1 << 3/*MOSI*/) | (1 << 2/*SS*/)); SPCR = (1 << SPE) | (1 << SPIE) /*| (1 << SPR0) | (1 << SPR1)*/; (void)SPSR; /* clear state */ (void)SPDR; /* clear state */ }