123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663 |
- /********************************************************************************
-
- USI TWI Slave driver.
-
- Created by Donald R. Blake. donblake at worldnet.att.net
- Adapted by Jochen Toppe, jochen.toppe at jtoee.com
-
- ---------------------------------------------------------------------------------
-
- Created from Atmel source files for Application Note AVR312: Using the USI Module
- as an I2C slave.
-
- 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.
-
- ---------------------------------------------------------------------------------
-
- Change Activity:
-
- Date Description
- ------ -------------
- 16 Mar 2007 Created.
- 27 Mar 2007 Added support for ATtiny261, 461 and 861.
- 26 Apr 2007 Fixed ACK of slave address on a read.
- 04 Jul 2007 Fixed USISIF in ATtiny45 def
- 12 Dev 2009 Added callback functions for data requests
- 06 Feb 2015 Minor change to allow mutli-byte requestFrom() from master.
- 10 Feb 2015 Simplied RX/TX buffer code and allowed use of full buffer.
- 12 Dec 2016 Added support for ATtiny167
-
- ********************************************************************************/
-
-
- /********************************************************************************
- includes
- ********************************************************************************/
- #include <stddef.h>
- #include <avr/io.h>
- #include <avr/interrupt.h>
-
- #include "usiTwiSlave.h"
- //#include "../common/util.h"
-
- //request_func_t _onTwiDataRequest = NULL;
- request_func_t usi_onRequestPtr = NULL;
- receive_func_t usi_onReceiverPtr = NULL;
-
- /********************************************************************************
- device dependent defines
- ********************************************************************************/
-
- #if defined( __AVR_ATtiny167__ )
- # define DDR_USI DDRB
- # define PORT_USI PORTB
- # define PIN_USI PINB
- # define PORT_USI_SDA PB0
- # define PORT_USI_SCL PB2
- # define PIN_USI_SDA PINB0
- # define PIN_USI_SCL PINB2
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
- #endif
-
- #if defined( __AVR_ATtiny2313__ )
- # define DDR_USI DDRB
- # define PORT_USI PORTB
- # define PIN_USI PINB
- # define PORT_USI_SDA PB5
- # define PORT_USI_SCL PB7
- # define PIN_USI_SDA PINB5
- # define PIN_USI_SCL PINB7
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
- #endif
-
- #if defined(__AVR_ATtiny84__) | \
- defined(__AVR_ATtiny44__)
- # define DDR_USI DDRA
- # define PORT_USI PORTA
- # define PIN_USI PINA
- # define PORT_USI_SDA PORTA6
- # define PORT_USI_SCL PORTA4
- # define PIN_USI_SDA PINA6
- # define PIN_USI_SCL PINA4
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVF_vect
- #endif
-
- #if defined( __AVR_ATtiny25__ ) | \
- defined( __AVR_ATtiny45__ ) | \
- defined( __AVR_ATtiny85__ )
- # define DDR_USI DDRB
- # define PORT_USI PORTB
- # define PIN_USI PINB
- # define PORT_USI_SDA PB0
- # define PORT_USI_SCL PB2
- # define PIN_USI_SDA PINB0
- # define PIN_USI_SCL PINB2
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVF_vect
- #endif
-
- #if defined( __AVR_ATtiny26__ )
- # define DDR_USI DDRB
- # define PORT_USI PORTB
- # define PIN_USI PINB
- # define PORT_USI_SDA PB0
- # define PORT_USI_SCL PB2
- # define PIN_USI_SDA PINB0
- # define PIN_USI_SCL PINB2
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_STRT_vect
- # define USI_OVERFLOW_VECTOR USI_OVF_vect
- #endif
-
- #if defined( __AVR_ATtiny261__ ) | \
- defined( __AVR_ATtiny461__ ) | \
- defined( __AVR_ATtiny861__ )
- # define DDR_USI DDRB
- # define PORT_USI PORTB
- # define PIN_USI PINB
- # define PORT_USI_SDA PB0
- # define PORT_USI_SCL PB2
- # define PIN_USI_SDA PINB0
- # define PIN_USI_SCL PINB2
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVF_vect
- #endif
-
- #if defined( __AVR_ATmega165__ ) | \
- defined( __AVR_ATmega325__ ) | \
- defined( __AVR_ATmega3250__ ) | \
- defined( __AVR_ATmega645__ ) | \
- defined( __AVR_ATmega6450__ ) | \
- defined( __AVR_ATmega329__ ) | \
- defined( __AVR_ATmega3290__ )
- # define DDR_USI DDRE
- # define PORT_USI PORTE
- # define PIN_USI PINE
- # define PORT_USI_SDA PE5
- # define PORT_USI_SCL PE4
- # define PIN_USI_SDA PINE5
- # define PIN_USI_SCL PINE4
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
- #endif
-
- #if defined( __AVR_ATmega169__ )
- # define DDR_USI DDRE
- # define PORT_USI PORTE
- # define PIN_USI PINE
- # define PORT_USI_SDA PE5
- # define PORT_USI_SCL PE4
- # define PIN_USI_SDA PINE5
- # define PIN_USI_SCL PINE4
- # define USI_START_COND_INT USISIF
- # define USI_START_VECTOR USI_START_vect
- # define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
- #endif
-
-
-
- /********************************************************************************
-
- functions implemented as macros
-
- ********************************************************************************/
-
- #define SET_USI_TO_SEND_ACK( ) \
- { \
- /* prepare ACK */ \
- USIDR = 0; \
- /* set SDA as output */ \
- DDR_USI |= ( 1 << PORT_USI_SDA ); \
- /* clear all interrupt flags, except Start Cond */ \
- USISR = \
- ( 0 << USI_START_COND_INT ) | \
- ( 1 << USIOIF ) | ( 1 << USIPF ) | \
- ( 1 << USIDC )| \
- /* set USI counter to shift 1 bit */ \
- ( 0x0E << USICNT0 ); \
- }
-
- #define SET_USI_TO_READ_ACK( ) \
- { \
- /* set SDA as input */ \
- DDR_USI &= ~( 1 << PORT_USI_SDA ); \
- /* prepare ACK */ \
- USIDR = 0; \
- /* clear all interrupt flags, except Start Cond */ \
- USISR = \
- ( 0 << USI_START_COND_INT ) | \
- ( 1 << USIOIF ) | \
- ( 1 << USIPF ) | \
- ( 1 << USIDC ) | \
- /* set USI counter to shift 1 bit */ \
- ( 0x0E << USICNT0 ); \
- }
-
- #define SET_USI_TO_TWI_START_CONDITION_MODE( ) \
- { \
- USICR = \
- /* enable Start Condition Interrupt, disable Overflow Interrupt */ \
- ( 1 << USISIE ) | ( 0 << USIOIE ) | \
- /* set USI in Two-wire mode, no USI Counter overflow hold */ \
- ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | \
- /* Shift Register Clock Source = External, positive edge */ \
- /* 4-Bit Counter Source = external, both edges */ \
- ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | \
- /* no toggle clock-port pin */ \
- ( 0 << USITC ); \
- USISR = \
- /* clear all interrupt flags, except Start Cond */ \
- ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
- ( 1 << USIDC ) | ( 0x0 << USICNT0 ); \
- }
-
- #define SET_USI_TO_SEND_DATA( ) \
- { \
- /* set SDA as output */ \
- DDR_USI |= ( 1 << PORT_USI_SDA ); \
- /* clear all interrupt flags, except Start Cond */ \
- USISR = \
- ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \
- ( 1 << USIDC) | \
- /* set USI to shift out 8 bits */ \
- ( 0x0 << USICNT0 ); \
- }
-
- #define SET_USI_TO_READ_DATA( ) \
- { \
- /* set SDA as input */ \
- DDR_USI &= ~( 1 << PORT_USI_SDA ); \
- /* clear all interrupt flags, except Start Cond */ \
- USISR = \
- ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | \
- ( 1 << USIPF ) | ( 1 << USIDC ) | \
- /* set USI to shift out 8 bits */ \
- ( 0x0 << USICNT0 ); \
- }
-
- #define USI_RECEIVE_CALLBACK() \
- { \
- if (usi_onReceiverPtr) \
- { \
- if (usiTwiAmountDataInReceiveBuffer()) \
- { \
- usi_onReceiverPtr(usiTwiAmountDataInReceiveBuffer()); \
- } \
- } \
- }
-
- #define ONSTOP_USI_RECEIVE_CALLBACK() \
- { \
- if (USISR & ( 1 << USIPF )) \
- { \
- USI_RECEIVE_CALLBACK(); \
- } \
- }
-
-
- #define USI_REQUEST_CALLBACK() \
- { \
- USI_RECEIVE_CALLBACK(); \
- if(usi_onRequestPtr) usi_onRequestPtr(); \
- }
-
- /********************************************************************************
-
- typedef's
-
- ********************************************************************************/
-
- typedef enum
- {
- USI_SLAVE_CHECK_ADDRESS = 0x00,
- USI_SLAVE_SEND_DATA = 0x01,
- USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA = 0x02,
- USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA = 0x03,
- USI_SLAVE_REQUEST_DATA = 0x04,
- USI_SLAVE_GET_DATA_AND_SEND_ACK = 0x05
- } overflowState_t;
-
-
-
- /********************************************************************************
-
- local variables
-
- ********************************************************************************/
-
- static uint8_t slaveAddress;
- static volatile overflowState_t overflowState;
-
-
- static uint8_t rxBuf[ TWI_RX_BUFFER_SIZE ];
- static volatile uint8_t rxHead;
- static volatile uint8_t rxTail;
- static volatile uint8_t rxCount;
-
- static uint8_t txBuf[ TWI_TX_BUFFER_SIZE ];
- static volatile uint8_t txHead;
- static volatile uint8_t txTail;
- static volatile uint8_t txCount;
-
-
-
- /********************************************************************************
-
- local functions
-
- ********************************************************************************/
-
-
-
- // flushes the TWI buffers
-
- static
- void
- flushTwiBuffers(
- void
- )
- {
- rxTail = 0;
- rxHead = 0;
- rxCount = 0;
- txTail = 0;
- txHead = 0;
- txCount = 0;
- } // end flushTwiBuffers
-
-
-
- /********************************************************************************
-
- public functions
-
- ********************************************************************************/
-
-
-
- // initialise USI for TWI slave mode
-
- void
- usiTwiSlaveInit(
- uint8_t ownAddress
- )
- {
-
- flushTwiBuffers( );
-
- slaveAddress = ownAddress;
-
- // In Two Wire mode (USIWM1, USIWM0 = 1X), the slave USI will pull SCL
- // low when a start condition is detected or a counter overflow (only
- // for USIWM1, USIWM0 = 11). This inserts a wait state. SCL is released
- // by the ISRs (USI_START_vect and USI_OVERFLOW_vect).
-
- // Set SCL and SDA as output
- DDR_USI |= ( 1 << PORT_USI_SCL ) | ( 1 << PORT_USI_SDA );
-
- // set SCL high
- PORT_USI |= ( 1 << PORT_USI_SCL );
-
- // set SDA high
- PORT_USI |= ( 1 << PORT_USI_SDA );
-
- // Set SDA as input
- DDR_USI &= ~( 1 << PORT_USI_SDA );
-
- USICR =
- // enable Start Condition Interrupt
- ( 1 << USISIE ) |
- // disable Overflow Interrupt
- ( 0 << USIOIE ) |
- // set USI in Two-wire mode, no USI Counter overflow hold
- ( 1 << USIWM1 ) | ( 0 << USIWM0 ) |
- // Shift Register Clock Source = external, positive edge
- // 4-Bit Counter Source = external, both edges
- ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
- // no toggle clock-port pin
- ( 0 << USITC );
-
- // clear all interrupt flags and reset overflow counter
-
- USISR = ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | ( 1 << USIDC );
-
- } // end usiTwiSlaveInit
-
-
- bool usiTwiDataInTransmitBuffer(void)
- {
-
- // return 0 (false) if the receive buffer is empty
- return txCount;
-
- } // end usiTwiDataInTransmitBuffer
-
-
- // put data in the transmission buffer, wait if buffer is full
-
- void
- usiTwiTransmitByte(
- uint8_t data
- )
- {
-
- // kpl uint8_t tmphead;
-
- // wait for free space in buffer
- while ( txCount == TWI_TX_BUFFER_SIZE) ;
-
- // store data in buffer
- txBuf[ txHead ] = data;
- txHead = ( txHead + 1 ) & TWI_TX_BUFFER_MASK;
- txCount++;
-
- } // end usiTwiTransmitByte
-
-
-
-
-
- // return a byte from the receive buffer, wait if buffer is empty
-
- uint8_t
- usiTwiReceiveByte(
- void
- )
- {
- uint8_t rtn_byte;
-
- // wait for Rx data
- while ( !rxCount );
-
- rtn_byte = rxBuf [ rxTail ];
- // calculate buffer index
- rxTail = ( rxTail + 1 ) & TWI_RX_BUFFER_MASK;
- rxCount--;
-
- // return data from the buffer.
- return rtn_byte;
-
- } // end usiTwiReceiveByte
-
-
-
- uint8_t usiTwiAmountDataInReceiveBuffer(void)
- {
- return rxCount;
- }
-
-
-
-
- /********************************************************************************
-
- USI Start Condition ISR
-
- ********************************************************************************/
-
- ISR( USI_START_VECTOR )
- {
-
- /*
- // This triggers on second write, but claims to the callback there is only *one* byte in buffer
- ONSTOP_USI_RECEIVE_CALLBACK();
- */
- /*
- // This triggers on second write, but claims to the callback there is only *one* byte in buffer
- USI_RECEIVE_CALLBACK();
- */
-
-
- // set default starting conditions for new TWI package
- overflowState = USI_SLAVE_CHECK_ADDRESS;
-
- // set SDA as input
- DDR_USI &= ~( 1 << PORT_USI_SDA );
-
- // wait for SCL to go low to ensure the Start Condition has completed (the
- // start detector will hold SCL low ) - if a Stop Condition arises then leave
- // the interrupt to prevent waiting forever - don't use USISR to test for Stop
- // Condition as in Application Note AVR312 because the Stop Condition Flag is
- // going to be set from the last TWI sequence
- while (
- // SCL his high
- ( PIN_USI & ( 1 << PIN_USI_SCL ) ) &&
- // and SDA is low
- !( ( PIN_USI & ( 1 << PIN_USI_SDA ) ) )
- );
-
-
- if ( !( PIN_USI & ( 1 << PIN_USI_SDA ) ) )
- {
-
- // a Stop Condition did not occur
-
- USICR =
- // keep Start Condition Interrupt enabled to detect RESTART
- ( 1 << USISIE ) |
- // enable Overflow Interrupt
- ( 1 << USIOIE ) |
- // set USI in Two-wire mode, hold SCL low on USI Counter overflow
- ( 1 << USIWM1 ) | ( 1 << USIWM0 ) |
- // Shift Register Clock Source = External, positive edge
- // 4-Bit Counter Source = external, both edges
- ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
- // no toggle clock-port pin
- ( 0 << USITC );
-
- }
- else
- {
- // a Stop Condition did occur
-
- USICR =
- // enable Start Condition Interrupt
- ( 1 << USISIE ) |
- // disable Overflow Interrupt
- ( 0 << USIOIE ) |
- // set USI in Two-wire mode, no USI Counter overflow hold
- ( 1 << USIWM1 ) | ( 0 << USIWM0 ) |
- // Shift Register Clock Source = external, positive edge
- // 4-Bit Counter Source = external, both edges
- ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) |
- // no toggle clock-port pin
- ( 0 << USITC );
-
- } // end if
-
- USISR =
- // clear interrupt flags - resetting the Start Condition Flag will
- // release SCL
- ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) |
- ( 1 << USIPF ) |( 1 << USIDC ) |
- // set USI to sample 8 bits (count 16 external SCL pin toggles)
- ( 0x0 << USICNT0);
-
-
- } // end ISR( USI_START_VECTOR )
-
-
-
- /********************************************************************************
-
- USI Overflow ISR
-
- Handles all the communication.
-
- Only disabled when waiting for a new Start Condition.
-
- ********************************************************************************/
-
- ISR( USI_OVERFLOW_VECTOR )
- {
-
- switch ( overflowState )
- {
-
- // Address mode: check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK,
- // else reset USI
- case USI_SLAVE_CHECK_ADDRESS:
- if ( ( USIDR == 0 ) || ( ( USIDR >> 1 ) == slaveAddress) )
- {
- if ( USIDR & 0x01 )
- {
- USI_REQUEST_CALLBACK();
- overflowState = USI_SLAVE_SEND_DATA;
- }
- else
- {
- overflowState = USI_SLAVE_REQUEST_DATA;
- } // end if
- SET_USI_TO_SEND_ACK( );
- }
- else
- {
- SET_USI_TO_TWI_START_CONDITION_MODE( );
- }
- break;
-
- // Master write data mode: check reply and goto USI_SLAVE_SEND_DATA if OK,
- // else reset USI
- case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA:
- if ( USIDR )
- {
- // if NACK, the master does not want more data
- SET_USI_TO_TWI_START_CONDITION_MODE( );
- return;
- }
- // from here we just drop straight into USI_SLAVE_SEND_DATA if the
- // master sent an ACK
-
- // copy data from buffer to USIDR and set USI to shift byte
- // next USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA
- case USI_SLAVE_SEND_DATA:
- // Get data from Buffer
- if ( txCount )
- {
- USIDR = txBuf[ txTail ];
- txTail = ( txTail + 1 ) & TWI_TX_BUFFER_MASK;
- txCount--;
- }
- else
- {
- // the buffer is empty
- SET_USI_TO_READ_ACK( ); // This might be neccessary sometimes see http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=805227#805227
- SET_USI_TO_TWI_START_CONDITION_MODE( );
- return;
- } // end if
- overflowState = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA;
- SET_USI_TO_SEND_DATA( );
- break;
-
- // set USI to sample reply from master
- // next USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA
- case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA:
- overflowState = USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA;
- SET_USI_TO_READ_ACK( );
- break;
-
- // Master read data mode: set USI to sample data from master, next
- // USI_SLAVE_GET_DATA_AND_SEND_ACK
- case USI_SLAVE_REQUEST_DATA:
- overflowState = USI_SLAVE_GET_DATA_AND_SEND_ACK;
- SET_USI_TO_READ_DATA( );
- break;
-
- // copy data from USIDR and send ACK
- // next USI_SLAVE_REQUEST_DATA
- case USI_SLAVE_GET_DATA_AND_SEND_ACK:
- // put data into buffer
- // check buffer size
- if ( rxCount < TWI_RX_BUFFER_SIZE )
- {
- rxBuf[ rxHead ] = USIDR;
- rxHead = ( rxHead + 1 ) & TWI_RX_BUFFER_MASK;
- rxCount++;
- } else {
- // overrun
- // drop data
- }
- // next USI_SLAVE_REQUEST_DATA
- overflowState = USI_SLAVE_REQUEST_DATA;
- SET_USI_TO_SEND_ACK( );
- break;
-
- } // end switch
-
- } // end ISR( USI_OVERFLOW_VECTOR )
|