/* * SPI master 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_master.h" #include "util.h" #include enum spi_master_xfer { SPI_MSTR_IDLE, SPI_MSTR_XMIT, SPI_MSTR_FETCH, }; struct spi_master_xmit_state { const uint8_t *buf; uint16_t nr_bytes; uint16_t count; }; struct spi_master_fetch_state { uint8_t *buf; uint16_t bufsize; uint16_t xfersize; uint16_t count; }; struct spi_master_state { uint8_t current_xfer; union { struct spi_master_xmit_state xmit; struct spi_master_fetch_state fetch; }; }; static struct spi_master_state state; static inline void spi_master_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; /* Give the slave enough time to handle the transfer. */ udelay(200); switch (state.current_xfer) { case SPI_MSTR_XMIT: if (state.xmit.count == state.xmit.nr_bytes + 2) { /* Transmission finished. */ state.current_xfer = SPI_MSTR_IDLE; spi_master_xfer_complete(state.xmit.count - 2); return; } spi_master_xmit_next_byte(); break; case SPI_MSTR_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_MSTR_IDLE; spi_master_xfer_complete(state.fetch.xfersize); return; } /* Trigger a transfer */ SPDR = 0xFF; break; } } int8_t spi_master_xmit(const void *buf, uint16_t nr_bytes) { if (state.current_xfer != SPI_MSTR_IDLE) return -1; state.current_xfer = SPI_MSTR_XMIT; state.xmit.buf = buf; state.xmit.nr_bytes = nr_bytes; state.xmit.count = 0; /* Send the first byte. */ spi_master_xmit_next_byte(); return 0; } int8_t spi_master_fetch(void *buf, uint16_t size) { if (state.current_xfer != SPI_MSTR_IDLE) return -1; state.current_xfer = SPI_MSTR_FETCH; state.fetch.buf = buf; state.fetch.bufsize = size; state.fetch.xfersize = 0; state.fetch.count = 0; /* Trigger a transfer */ SPDR = 0xFF; return 0; } void spi_master_init(void) { state.current_xfer = SPI_MSTR_IDLE; /* SPI master mode 0 with IRQ enabled. * Prescaler is 4. */ PORTB |= (1 << 2/*SS*/); DDRB |= (1 << 2/*SS*/) | (1 << 3/*MOSI*/) | (1 << 5/*SCK*/); DDRB &= ~(1 << 4/*MISO*/); SPCR = (1 << SPE) | (1 << SPIE) | (1 << MSTR) /*| (1 << SPR0) | (1 << SPR1)*/; PORTB &= ~(1 << 2/*SS*/); /* Wait for the slave to init its SPI engine. */ mdelay(200); (void)SPSR; /* clear state */ (void)SPDR; /* clear state */ }