Spi-io.c

This is robostix/i2c-io/spi-io.c /* * Copyright (C) 2006 Andrei Rylin * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

/* * This code calls into ProcessCommand in i2c-io to provide [most of the] * functionality of the latter via SPI bus. * In addition, it implements an interface to get ADC samples using interrupts * and raise GPIO line when data are ready. */


 * 1) include 


 * 1) ifdef __AVR_LIBC_VERSION__
 * 2) include 
 * 3) else
 * 4) include 
 * 5) endif


 * 1) include 
 * 2) include 
 * 3) include 


 * 1) define DDR_SPI DDRB
 * 2) define DD_MISO DDB3
 * 3) define DD_MOSI DDB2
 * 4) define DD_SCK DDB1
 * 5) define DD_SS  DDB0


 * 1) include "i2c.h"
 * 2) include "i2c-io.h"
 * 3) include "Config.h"
 * 4) include "Crc8.h"

int ProcessCommand(I2C_Data_t *packet);

typedef uint8_t u8; typedef uint16_t u16;

/* * Our SPI packets start with one-byte packet length * followed by 'length' bytes plus trailing CRC. * CRC covers 'length'. */
 * 1) define MAX_PACKET_LENGTH 30

struct spi_dev { u8 state; u8 len; u8 tx; u8* ptr;

I2C_Data_t pkt; u8 pad[2];

u16 adc[8]; u16 *padc; };
 * 1) ifdef CONFIG_SPI_ADC
 * 1) endif


 * 1) define STATE_init   0
 * 2) define STATE_command 1
 * 3) define STATE_reply  2
 * 4) define STATE_error  3

/* * ATmega128 is single-buffered in output direction so it can easily * miss an opportunity to send a byte. Any SPI slave has * no "in-band" means of flow control (other than xon/xoff type). * So it's a hit-or-miss situation and we need to handle transmission * errors. * * Transmission errors are checked by CRC and to protect ourselves * from buffer overruns we also check packet length. All other * errors are [supposed to be] handled by higher layer(s). * On transmission error state becomes STATE_error and output * always gets SPI_NACK. To recover from a transmission error, * input is scanned for SPI_SYN. After that the first non-SPI_SYN * is expected to be length of a command packet. * * SPI_NULL is always placed on output when there's nothing else to send. * * Note that the three bytes defined below can't be mistaken * for the first byte of any packet (packet length). */
 * 1) define SPI_SYN 0x69
 * 2) define SPI_NACK 0x96
 * 3) define SPI_NULL 0xcc

static struct spi_dev __dev;

/* * i2c-io uses 1-10 for command byte, bootloader uses a few * of 0xBx and 0xCx. Let's grab some of the commands for ourselves. */
 * 1) ifdef CONFIG_SPI_ADC


 * 1) define SPI_CMD_FIRST    0x70
 * 2) define SPI_CMD_START_ADC 0x70
 * 3) define SPI_CMD_STOP_ADC 0x71
 * 4) define SPI_CMD_READ_ADC 0x72
 * 5) define SPI_CMD_LAST     0x72

static inline int handle_packet(struct spi_dev* dev) {   u8 cmd = dev->pkt.m_data[0]; if (cmd < SPI_CMD_FIRST || cmd > SPI_CMD_LAST) return ProcessCommand(&dev->pkt);

switch (cmd) { case SPI_CMD_START_ADC: PORTE &= ~(1<pkt.m_data, dev->adc, 16); PORTE &= ~(1<pkt)
 * 3) endif

static inline u8 do_crc8(const u8* buf, u8 n) { u8 crc = 0xff; while(n--) crc = Crc8(crc, *buf++); return crc; }

static inline void state_init(struct spi_dev* dev) {   dev->state = STATE_init; dev->tx = SPI_NULL; dev->ptr = &dev->pkt.m_len; }

static inline void state_reply(struct spi_dev* dev, int len) {   dev->state = STATE_reply; dev->pkt.m_len = len; dev->ptr = &dev->pkt.m_len; ++len; dev->ptr[len] = do_crc8(dev->ptr, len); dev->len = len+2; }

void spi_init_slave {   struct spi_dev* dev = &__dev; state_init(dev);

DDR_SPI |= (1<padc = dev->adc; ADMUX = 0; ADCSRA = (1<len = SPDR; dev->tx = SPI_NULL; if (dev->len <= MAX_PACKET_LENGTH) { dev->state = STATE_command; *dev->ptr++ = dev->len++; }   else if (dev->len != SPI_SYN) { dev->state = STATE_error; dev->tx = SPI_NACK; } }

static inline void spi_reply(struct spi_dev* dev) {   if (dev->len--) dev->tx = *dev->ptr++; else state_init(dev); }

static inline void spi_command(struct spi_dev* dev) {   *dev->ptr++ = SPDR; dev->len--; if (dev->len) dev->tx = SPI_NULL; else { /* command complete */ if (do_crc8(&dev->pkt.m_len, dev->pkt.m_len + 2)) { dev->tx = SPI_NACK; dev->state = STATE_error; } else { int len = handle_packet(dev); if (len>0 && len <= MAX_PACKET_LENGTH) { state_reply(dev, len); spi_reply(dev); }           else state_init(dev); }   } }

static inline void spi_error(struct spi_dev* dev) {   if (SPDR == SPI_SYN) state_init(dev); else dev->tx = SPI_NACK; }

SIGNAL(SIG_SPI) {   struct spi_dev* dev = &__dev;

#define SWITCH(x) switch(x) #define CASE(x) case STATE_##x: spi_##x(dev); return; /*    * gnu-ism - don't know if it's faster * but it's a bit bigger */   static const int offset[] = { &&_init   - &&_init, &&_command - &&_init, &&_reply  - &&_init, &&_error  - &&_init, };   #define SWITCH(x) goto *(&&_init + offset[x]); #define CASE(x) _##x: spi_##x(dev); return;
 * 1) if 1
 * 1) else
 * 1) endif

/* Our best chance - but not a guarantee - not to miss * an opportunity to transmit a byte is to do it asap, * then handle an incoming byte. That introduces a one-byte * lag, i.e. SPI master has to read an extra (SPI_NULL) byte * to get all reply data. */   SPDR = dev->tx; SWITCH(dev->state) { CASE(init) CASE(command) CASE(reply) CASE(error) } }

SIGNAL(SIG_ADC) {   struct spi_dev* dev = &__dev; *dev->padc++ = ADC; if ((ADMUX & 0x1f) < 7) { ADMUX++; ADCSRA = (ADCSRA & ~(1<padc = dev->adc; PORTE |= 1<<PE2; /* raise ATM IRQ */ } }
 * 1) ifdef CONFIG_SPI_ADC
 * 1) endif