From cff54ceef2c904a72511ce4f73c66d93eb3516ca Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Mon, 29 Nov 2010 23:07:55 +0100 Subject: digital/avr/modules/twi: implement USI slave, closes #142 --- digital/avr/modules/twi/Makefile.module | 2 +- digital/avr/modules/twi/test/usi/Makefile | 12 + digital/avr/modules/twi/test/usi/avrconfig.h | 57 +++++ digital/avr/modules/twi/test/usi/test_twi_usi.c | 49 ++++ digital/avr/modules/twi/twi_usi.avr.c | 287 ++++++++++++++++++++++++ 5 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 digital/avr/modules/twi/test/usi/Makefile create mode 100644 digital/avr/modules/twi/test/usi/avrconfig.h create mode 100644 digital/avr/modules/twi/test/usi/test_twi_usi.c create mode 100644 digital/avr/modules/twi/twi_usi.avr.c (limited to 'digital/avr/modules') diff --git a/digital/avr/modules/twi/Makefile.module b/digital/avr/modules/twi/Makefile.module index 136203a9..0f90a8dc 100644 --- a/digital/avr/modules/twi/Makefile.module +++ b/digital/avr/modules/twi/Makefile.module @@ -1 +1 @@ -twi_SOURCES = twi.c twi_hard.avr.c twi_soft.avr.c twi.host.c +twi_SOURCES = twi.c twi_hard.avr.c twi_soft.avr.c twi_usi.avr.c twi.host.c diff --git a/digital/avr/modules/twi/test/usi/Makefile b/digital/avr/modules/twi/test/usi/Makefile new file mode 100644 index 00000000..e37c3f8c --- /dev/null +++ b/digital/avr/modules/twi/test/usi/Makefile @@ -0,0 +1,12 @@ +BASE = ../../../.. +AVR_PROGS = test_twi_usi +test_twi_usi_SOURCES = test_twi_usi.c +MODULES = twi +CONFIGFILE = avrconfig.h +# atmega8, atmega8535, atmega128... +AVR_MCU = attiny85 +# -O2 : speed +# -Os : size +OPTIMIZE = -Os + +include $(BASE)/make/Makefile.gen diff --git a/digital/avr/modules/twi/test/usi/avrconfig.h b/digital/avr/modules/twi/test/usi/avrconfig.h new file mode 100644 index 00000000..183064e0 --- /dev/null +++ b/digital/avr/modules/twi/test/usi/avrconfig.h @@ -0,0 +1,57 @@ +#ifndef avrconfig_h +#define avrconfig_h +/* avrconfig.h */ +/* avr.twi - TWI AVR module. {{{ + * + * Copyright (C) 2010 Nicolas Schodet + * + * APBTeam: + * Web: http://apbteam.org/ + * Email: team AT apbteam DOT org + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * }}} */ + +/* utils */ +/** AVR Frequency : 1000000, 1843200, 2000000, 3686400, 4000000, 7372800, + * 8000000, 11059200, 14745600, 16000000, 18432000, 20000000. */ +#define AC_FREQ 1000000 + +/* twi - TWI module. */ +/** Driver to implement TWI: HARD, SOFT, or USI. */ +#define AC_TWI_DRIVER USI +/** Do not use interrupts. */ +#define AC_TWI_NO_INTERRUPT 1 +/** TWI frequency, should really be 100 kHz. */ +#define AC_TWI_FREQ 100000 +/** Enable slave part. */ +#define AC_TWI_SLAVE_ENABLE 1 +/** Enable master part. */ +#define AC_TWI_MASTER_ENABLE 0 +/** Use polled slave mode: received data is stored in a buffer which can be + * polled using twi_slave_poll. */ +#define AC_TWI_SLAVE_POLLED 1 +/** Slave reception callback to be defined by the user when not in polled + * mode. */ +#undef AC_TWI_SLAVE_RECV +/** Use internal pull up. */ +#define AC_TWI_PULL_UP 0 +/** Slave reception buffer size. */ +#define AC_TWI_SLAVE_RECV_BUFFER_SIZE 16 +/** Slave transmission buffer size. */ +#define AC_TWI_SLAVE_SEND_BUFFER_SIZE 16 + +#endif /* avrconfig_h */ diff --git a/digital/avr/modules/twi/test/usi/test_twi_usi.c b/digital/avr/modules/twi/test/usi/test_twi_usi.c new file mode 100644 index 00000000..d55456a9 --- /dev/null +++ b/digital/avr/modules/twi/test/usi/test_twi_usi.c @@ -0,0 +1,49 @@ +/* test_twi_usi.c */ +/* avr.twi - TWI AVR module. {{{ + * + * Copyright (C) 2010 Nicolas Schodet + * + * APBTeam: + * Web: http://apbteam.org/ + * Email: team AT apbteam DOT org + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * }}} */ +#include "common.h" +#include "modules/twi/twi.h" +#include "io.h" + +int +main (int argc, char **argv) +{ + /* Initialize TWI. */ + twi_init (0x04); + while (42) + { + /* Simulate interrupts. */ + twi_update (); + /* Check for data. */ + uint8_t data[AC_TWI_SLAVE_RECV_BUFFER_SIZE]; + uint8_t data_len; + data_len = twi_slave_poll (data, AC_TWI_SLAVE_RECV_BUFFER_SIZE); + if (data_len) + { + /* Echo them back.*/ + twi_slave_update (data, data_len); + } + } +} + diff --git a/digital/avr/modules/twi/twi_usi.avr.c b/digital/avr/modules/twi/twi_usi.avr.c new file mode 100644 index 00000000..682bf3e9 --- /dev/null +++ b/digital/avr/modules/twi/twi_usi.avr.c @@ -0,0 +1,287 @@ +/* twi_usi.avr.c - USI implementation. */ +/* avr.twi - TWI AVR module. {{{ + * + * Copyright (C) 2010 Nicolas Schodet + * + * APBTeam: + * Web: http://apbteam.org/ + * Email: team AT apbteam DOT org + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * }}} */ +#include "common.h" +#include "twi.h" + +#if TWI_DRIVER == TWI_DRIVER_USI + +# include "io.h" + +# include + +# if AC_TWI_PULL_UP +# error "twi: pull up not supported by USI driver" +# endif +# if AC_TWI_MASTER_ENABLE +# error "twi: master mode not supported by USI driver" +# endif + +#if defined (__AVR_ATtiny25__) \ + || defined (__AVR_ATtiny45__) \ + || defined (__AVR_ATtiny85__) +# define SDA_IO B, 0 +# define SCL_IO B, 2 +#else +# error "twi: not defined for this chip" +#endif + +#if AC_TWI_NO_INTERRUPT +# define HANDLE_START() static void handle_start (void) +# define HANDLE_OVERFLOW() static void handle_overflow (void) +# define IF_INTR(x) 0 +#else +# define HANDLE_START() SIGNAL (USI_START_vect) +# define HANDLE_OVERFLOW() SIGNAL (USI_OVF_vect) +# define IF_INTR(x) (x) +#endif + +/** Value to be used for USISR to clear all flags, except start condition + * which is cleared only if CLEAR_START is 1, and to set the counter so that + * it overflows after BITS bits tranceived. */ +#define USISR_SET(clear_start, bits) \ + (((clear_start) << USISIF) \ + | _BV (USIOIF) | _BV (USIPF) | _BV (USIDC) \ + | (((bits) ? (16 - (bits) * 2) : 0) << USICNT0)) + +/** Value to be used for USICR when waiting for start condition only. */ +#define USICR_START_SET \ + (IF_INTR (_BV (USISIE)) | _BV (USIWM1) | _BV (USICS1)) + +/** Value to be used for USICR when waiting for start condition or counter + * overflow. */ +#define USICR_START_OR_OVERFLOW_SET \ + (IF_INTR (_BV (USISIE) | _BV (USIOIE)) | _BV (USIWM1) | _BV (USIWM0) \ + | _BV (USICS1)) + +/** USI slave state, see below for state explanation. */ +enum +{ + TWI_USI_WAIT_ADDRESS, + TWI_USI_RECV, + TWI_USI_RECV_SEND_ACK, + TWI_USI_SEND_CHECK_ACK, + TWI_USI_SEND, + TWI_USI_SEND_RECV_ACK, +}; + +/** Module context. */ +struct twi_usi_t +{ + /** Slave address. */ + uint8_t address; + /** Current state, only used on counter overflow. */ + uint8_t state; + /** Reception buffer. */ + uint8_t recv_buffer[AC_TWI_SLAVE_RECV_BUFFER_SIZE]; + /** Reception buffer current size. */ + uint8_t recv_buffer_size; + /** Transmission buffer. */ + uint8_t send_buffer[AC_TWI_SLAVE_SEND_BUFFER_SIZE]; + /** Transmission buffer current size. */ + uint8_t send_buffer_size; + /** Index of last transmitted byte in transmission buffer. */ + uint8_t send_buffer_index; +}; + +/** Global context. */ +static struct twi_usi_t twi_usi_global; +#define ctx twi_usi_global + +void +twi_init (uint8_t addr) +{ + /* Reset context. */ + ctx.address = addr; + ctx.recv_buffer_size = 0; + ctx.send_buffer_size = 0; + /* Set IO (SDA input, SCL driven (for clock stretching)), enable USI. */ + IO_SET (SDA_IO); + IO_SET (SCL_IO); + USISR = USISR_SET (1, 0); + USICR = USICR_START_SET; + IO_INPUT (SDA_IO); + IO_OUTPUT (SCL_IO); +} + +void +twi_uninit (void) +{ + /* Stop USI, set IO to input. */ + IO_INPUT (SDA_IO); + IO_INPUT (SCL_IO); + USICR = 0; + IO_CLR (SDA_IO); + IO_CLR (SCL_IO); +} + +void +twi_slave_update (const uint8_t *buffer, uint8_t size) +{ +#if !AC_TWI_NO_INTERRUPT +# warning "twi: driver not safe using interrupts" +#endif + memcpy (ctx.send_buffer, buffer, size); + ctx.send_buffer_size = size; +} + +/** Handle start condition reception. */ +HANDLE_START () +{ + /* Stop driving SDA (start condition acts as a reset). */ + IO_INPUT (SDA_IO); + /* Wait until the start condition is finished. */ + while (!IO_GET (SDA_IO) && IO_GET (SCL_IO)) + ; + /* Prepare for address reception. */ + ctx.state = TWI_USI_WAIT_ADDRESS; + USICR = USICR_START_OR_OVERFLOW_SET; + USISR = USISR_SET (1, 8); +} + +/** Handle USI counter overflow. */ +HANDLE_OVERFLOW () +{ + uint8_t sr, send; + switch (ctx.state) + { + case TWI_USI_WAIT_ADDRESS: + /* Start condition has been received, check if this is our address, + * send ACK and go to receiver or sender state. */ + if ((USIDR & 0xfe) == ctx.address) + { + send = USIDR & 1; + /* Send ACK. */ + USIDR = 0; + IO_OUTPUT (SDA_IO); + USISR = USISR_SET (0, 1); + /* Next state. */ + if (send) + { + ctx.send_buffer_index = 0; + ctx.state = TWI_USI_SEND; + } + else + { + ctx.recv_buffer_size = 0; + ctx.state = TWI_USI_RECV; + } + } + else + { + USICR = USICR_START_SET; + USISR = USISR_SET (0, 0); + } + break; + /* Receiver mode. */ + case TWI_USI_RECV: + /* ACK has been transmitted, receive data. If we are here, there is + * room left to receive this data. */ + IO_INPUT (SDA_IO); + USISR = USISR_SET (0, 8); + ctx.state = TWI_USI_RECV_SEND_ACK; + /* Poll for STOP condition detection. + * Here, the master will clock SCL to send data, or will send a stop + * condition, wait for one of those events. */ + do + sr = USISR; + while (!(sr & _BV (USIPF)) + && ((sr >> USICNT0) & 0xf) < 16 - 7 * 2); + /* On STOP condition stop here. */ + if (sr & _BV (USIPF)) + { + USICR = USICR_START_SET; + USISR = USISR_SET (0, 0); + AC_TWI_SLAVE_RECV (ctx.recv_buffer, ctx.recv_buffer_size); + } + break; + case TWI_USI_RECV_SEND_ACK: + /* Data has been received, send ACK if there is room left. */ + ctx.recv_buffer[ctx.recv_buffer_size++] = USIDR; + if (ctx.recv_buffer_size < AC_TWI_SLAVE_RECV_BUFFER_SIZE) + { + USIDR = 0; + IO_OUTPUT (SDA_IO); + USISR = USISR_SET (0, 1); + ctx.state = TWI_USI_RECV; + } + else + { + USICR = USICR_START_SET; + USISR = USISR_SET (0, 0); + AC_TWI_SLAVE_RECV (ctx.recv_buffer, ctx.recv_buffer_size); + } + break; + /* Transmitter mode. */ + case TWI_USI_SEND_CHECK_ACK: + /* Check received ACK, send if master wants to continue. */ + if (USIDR) + { + /* NACK received, stop here. */ + USICR = USICR_START_SET; + USISR = USISR_SET (0, 0); + break; + } + /* no break */ + case TWI_USI_SEND: + /* ACK has been transmitted or received, send next data. */ + if (ctx.send_buffer_index < ctx.send_buffer_size) + { + USIDR = ctx.send_buffer[ctx.send_buffer_index++]; + IO_OUTPUT (SDA_IO); + USISR = USISR_SET (0, 8); + ctx.state = TWI_USI_SEND_RECV_ACK; + } + else + { + /* No data to send, stop here. */ + IO_INPUT (SDA_IO); + USICR = USICR_START_SET; + USISR = USISR_SET (0, 0); + } + break; + case TWI_USI_SEND_RECV_ACK: + /* Data has been sent, receive ACK. */ + USIDR = 0; + IO_INPUT (SDA_IO); + USISR = USISR_SET (0, 1); + ctx.state = TWI_USI_SEND_CHECK_ACK; + break; + } +} + +# if AC_TWI_NO_INTERRUPT + +void +twi_update (void) +{ + if (USISR & _BV (USISIF)) + handle_start (); + if (USISR & _BV (USIOIF)) + handle_overflow (); +} + +# endif /* AC_TWI_NO_INTERRUPT */ + +#endif /* TWI_DRIVER == TWI_DRIVER_USI */ -- cgit v1.2.3