/* This file has been prepared for Doxygen automatic documentation generation.*/ /*! \file ********************************************************************* * * \brief * XMEGA TWI slave driver source file. * * This file contains the function implementations the XMEGA TWI slave * driver. * * The driver is not intended for size and/or speed critical code, since * most functions are just a few lines of code, and the function call * overhead would decrease code performance. The driver is intended for * rapid prototyping and documentation purposes for getting started with * the XMEGA TWI slave module. * * For size and/or speed critical code, it is recommended to copy the * function contents directly into your application instead of making * a function call. * * Several functions use the following construct: * "some_register = ... | (some_parameter ? SOME_BIT_bm : 0) | ..." * Although the use of the ternary operator ( if ? then : else ) is * discouraged, in some occasions the operator makes it possible to write * pretty clean and neat code. In this driver, the construct is used to * set or not set a configuration bit based on a boolean input parameter, * such as the "some_parameter" in the example above. * * \par Application note: * AVR1308: Using the XMEGA TWI * * \par Documentation * For comprehensive code documentation, supported compilers, compiler * settings and supported devices see readme.html * * \author * Atmel Corporation: http://www.atmel.com \n * Support email: avr@atmel.com * * $Revision: 2660 $ * $Date: 2009-08-11 12:28:58 +0200 (ti, 11 aug 2009) $ \n * * Copyright (c) 2008, Atmel Corporation All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of ATMEL may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include "twi_slave_driver.h" /*! \brief Initalizes TWI slave driver structure. * * Initialize the instance of the TWI Slave and set the appropriate values. * * \param twi The TWI_Slave_t struct instance. * \param module Pointer to the TWI module. * \param processDataFunction Pointer to the function that handles incoming data. */ void TWI_SlaveInitializeDriver(TWI_Slave_t *twi, TWI_t *module, TWI_SlaveProc processDataFunction) { twi->interface = module; twi->Process_Data = processDataFunction; twi->bytesReceived = 0; twi->bytesSent = 0; twi->status = TWIS_STATUS_READY; twi->result = TWIS_RESULT_UNKNOWN; twi->abort = false; } /*! \brief Initialize the TWI module. * * Enables interrupts on address recognition and data available. * Remember to enable interrupts globally from the main application. * * \param twi The TWI_Slave_t struct instance. * \param address Slave address for this module. * \param intLevel Interrupt level for the TWI slave interrupt handler. */ void TWI_SlaveInitializeModule(TWI_Slave_t *twi, uint8_t address, TWI_SLAVE_INTLVL_t intLevel, uint8_t *recvData, uint8_t bytesMaxRecv ) { twi->bytesMaxRecv = bytesMaxRecv; twi->recvData = recvData; twi->interface->SLAVE.CTRLA = intLevel | TWI_SLAVE_DIEN_bm | TWI_SLAVE_APIEN_bm | TWI_SLAVE_ENABLE_bm; twi->interface->SLAVE.ADDR = (address << 1); } /*! \brief Common TWI slave interrupt service routine. * * Handles all TWI transactions and responses to address match, data reception, * data transmission, bus error and data collision. * * \param twi The TWI_Slave_t struct instance. */ void TWI_SlaveInterruptHandler(TWI_Slave_t *twi) { uint8_t const currentStatus = twi->interface->SLAVE.STATUS; /* If bus error. */ if (currentStatus & TWI_SLAVE_BUSERR_bm) { twi->bytesReceived = 0; twi->bytesSent = 0; twi->result = TWIS_RESULT_BUS_ERROR; twi->status = TWIS_STATUS_READY; } else /* If transmit collision. */ if (currentStatus & TWI_SLAVE_COLL_bm) { twi->bytesReceived = 0; twi->bytesSent = 0; twi->result = TWIS_RESULT_TRANSMIT_COLLISION; twi->status = TWIS_STATUS_READY; } else /* If address match. */ if ((currentStatus & TWI_SLAVE_APIF_bm) && (currentStatus & TWI_SLAVE_AP_bm)) { TWI_SlaveAddressMatchHandler(twi); } else /* If stop (only enabled through slave read transaction). */ if (currentStatus & TWI_SLAVE_APIF_bm) { TWI_SlaveStopHandler(twi); } else /* If data interrupt. */ if (currentStatus & TWI_SLAVE_DIF_bm) { TWI_SlaveDataHandler(twi); } /* If unexpected state. */ else { TWI_SlaveTransactionFinished(twi, TWIS_RESULT_FAIL); } } /*! \brief TWI address match interrupt handler. * * Prepares TWI module for transaction when an address match occures. * * \param twi The TWI_Slave_t struct instance. */ void TWI_SlaveAddressMatchHandler(TWI_Slave_t *twi) { /* If application signalling need to abort (error occured). */ if (twi->abort) { twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; TWI_SlaveTransactionFinished(twi, TWIS_RESULT_ABORTED); twi->abort = false; } else { twi->status = TWIS_STATUS_BUSY; twi->result = TWIS_RESULT_UNKNOWN; /* Disable stop interrupt. */ uint8_t currentCtrlA = twi->interface->SLAVE.CTRLA; twi->interface->SLAVE.CTRLA = currentCtrlA & ~TWI_SLAVE_PIEN_bm; twi->bytesReceived = 0; twi->bytesSent = 0; /* Send ACK, wait for data interrupt. */ twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; } } /*! \brief TWI stop condition interrupt handler. * * \param twi The TWI_Slave_t struct instance. */ void TWI_SlaveStopHandler(TWI_Slave_t *twi) { /* Disable stop interrupt. */ uint8_t currentCtrlA = twi->interface->SLAVE.CTRLA; twi->interface->SLAVE.CTRLA = currentCtrlA & ~TWI_SLAVE_PIEN_bm; /* Clear APIF, according to flowchart don't ACK or NACK */ uint8_t currentStatus = twi->interface->SLAVE.STATUS; twi->interface->SLAVE.STATUS = currentStatus | TWI_SLAVE_APIF_bm; TWI_SlaveTransactionFinished(twi, TWIS_RESULT_OK); } /*! \brief TWI data interrupt handler. * * Calls the appropriate slave read or write handler. * * \param twi The TWI_Slave_t struct instance. */ void TWI_SlaveDataHandler(TWI_Slave_t *twi) { if( twi->interface->SLAVE.STATUS & TWI_SLAVE_DIR_bm ) { TWI_SlaveWriteHandler(twi); } else { TWI_SlaveReadHandler(twi); } } /*! \brief TWI slave read interrupt handler. * * Handles TWI slave read transactions and responses. * * \param twi The TWI_Slave_t struct instance. */ void TWI_SlaveReadHandler(TWI_Slave_t *twi) { uint8_t const bytesMaxRecv = twi->bytesMaxRecv; /* Enable stop interrupt. */ uint8_t currentCtrlA = twi->interface->SLAVE.CTRLA; twi->interface->SLAVE.CTRLA = currentCtrlA | TWI_SLAVE_PIEN_bm; /* If free space in buffer. */ if( twi->bytesReceived < bytesMaxRecv ) { /* Fetch data */ uint8_t data = twi->interface->SLAVE.DATA; twi->recvData[twi->bytesReceived] = data; /* Process data. */ twi->Process_Data(twi); twi->bytesReceived++; /* If application signalling need to abort (error occured), * complete transaction and wait for next START. Otherwise * send ACK and wait for data interrupt. */ if( twi->abort ) { twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; TWI_SlaveTransactionFinished(twi, TWIS_RESULT_ABORTED); twi->abort = false; } else { twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; } } /* If buffer overflow, send NACK and wait for next START. Set * result buffer overflow. */ else { twi->interface->SLAVE.CTRLB = TWI_SLAVE_ACKACT_bm | TWI_SLAVE_CMD_COMPTRANS_gc; TWI_SlaveTransactionFinished(twi, TWIS_RESULT_BUFFER_OVERFLOW); } } /*! \brief TWI slave write interrupt handler. * * Handles TWI slave write transactions and responses. * * \param twi The TWI_Slave_t struct instance. */ void TWI_SlaveWriteHandler(TWI_Slave_t *twi) { uint8_t const bytesToSend = twi->bytesToSend; /* If NACK, slave write transaction finished. */ if ((twi->bytesSent > 0) && (twi->interface->SLAVE.STATUS & TWI_SLAVE_RXACK_bm)) { twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; TWI_SlaveTransactionFinished(twi, TWIS_RESULT_OK); } /* If ACK, master expects more data. */ else { if( twi->bytesSent < bytesToSend ) { uint8_t data = twi->sendData[twi->bytesSent]; twi->interface->SLAVE.DATA = data; twi->bytesSent++; /* Send data, wait for data interrupt. */ twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; } /* If buffer overflow. */ else { twi->interface->SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc; TWI_SlaveTransactionFinished(twi, TWIS_RESULT_BUFFER_OVERFLOW); } } } /*! \brief TWI transaction finished function. * * Prepares module for new transaction. * * \param twi The TWI_Slave_t struct instance. * \param result The result of the transaction. */ void TWI_SlaveTransactionFinished(TWI_Slave_t *twi, uint8_t result) { twi->result = result; twi->status = TWIS_STATUS_READY; }