Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /********************************************************************************
- USI TWI Slave driver.
- Created by Donald R. Blake
- donblake at worldnet.att.net
- ---------------------------------------------------------------------------------
- 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.
- ********************************************************************************/
- /********************************************************************************
- includes
- ********************************************************************************/
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #include "usiTwiSlave.h"
- /********************************************************************************
- device dependent defines
- ********************************************************************************/
- #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_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 USICIF
- # 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 ); \
- }
- /********************************************************************************
- 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 uint8_t txBuf[ TWI_TX_BUFFER_SIZE ];
- static volatile uint8_t txHead;
- static volatile uint8_t txTail;
- /********************************************************************************
- local functions
- ********************************************************************************/
- // flushes the TWI buffers
- static
- void
- flushTwiBuffers(
- void
- )
- {
- rxTail = 0;
- rxHead = 0;
- txTail = 0;
- txHead = 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
- // put data in the transmission buffer, wait if buffer is full
- void
- usiTwiTransmitByte(
- uint8_t data
- )
- {
- uint8_t tmphead;
- // calculate buffer index
- tmphead = ( txHead + 1 ) & TWI_TX_BUFFER_MASK;
- // wait for free space in buffer
- while ( tmphead == txTail );
- // store data in buffer
- txBuf[ tmphead ] = data;
- // store new index
- txHead = tmphead;
- } // end usiTwiTransmitByte
- // return a byte from the receive buffer, wait if buffer is empty
- uint8_t
- usiTwiReceiveByte(
- void
- )
- {
- // wait for Rx data
- while ( rxHead == rxTail );
- // calculate buffer index
- rxTail = ( rxTail + 1 ) & TWI_RX_BUFFER_MASK;
- // return data from the buffer.
- return rxBuf[ rxTail ];
- } // end usiTwiReceiveByte
- // check if there is data in the receive buffer
- bool
- usiTwiDataInReceiveBuffer(
- void
- )
- {
- // return 0 (false) if the receive buffer is empty
- return rxHead != rxTail;
- } // end usiTwiDataInReceiveBuffer
- /********************************************************************************
- USI Start Condition ISR
- ********************************************************************************/
- ISR( USI_START_VECTOR )
- {
- // 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 )
- {
- 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 ( txHead != txTail )
- {
- txTail = ( txTail + 1 ) & TWI_TX_BUFFER_MASK;
- USIDR = txBuf[ txTail ];
- }
- else
- {
- // the buffer is empty
- 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
- // Not necessary, but prevents warnings
- rxHead = ( rxHead + 1 ) & TWI_RX_BUFFER_MASK;
- rxBuf[ rxHead ] = USIDR;
- // next USI_SLAVE_REQUEST_DATA
- overflowState = USI_SLAVE_REQUEST_DATA;
- SET_USI_TO_SEND_ACK( );
- break;
- } // end switch
- } // end ISR( USI_OVERFLOW_VECTOR )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement