| /* ---------------------------------------------------------------------------- | |
| * SAM Software Package License | |
| * ---------------------------------------------------------------------------- | |
| * Copyright (c) 2014, 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: | |
| * | |
| * - Redistributions of source code must retain the above copyright notice, | |
| * this list of conditions and the disclaimer below. | |
| * | |
| * Atmel's name may not be used to endorse or promote products derived from | |
| * this software without specific prior written permission. | |
| * | |
| * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
| * 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. | |
| * ---------------------------------------------------------------------------- | |
| */ | |
| /** \file */ | |
| /*---------------------------------------------------------------------------- | |
| * Headers | |
| *----------------------------------------------------------------------------*/ | |
| #include "chip.h" | |
| #include <assert.h> | |
| /*---------------------------------------------------------------------------- | |
| * Definition | |
| *----------------------------------------------------------------------------*/ | |
| #define TWITIMEOUTMAX 0xfffff | |
| /*---------------------------------------------------------------------------- | |
| * Types | |
| *----------------------------------------------------------------------------*/ | |
| /** TWI driver callback function.*/ | |
| typedef void (*TwiCallback)(Async *); | |
| /** \brief TWI asynchronous transfer descriptor.*/ | |
| typedef struct _AsyncTwi { | |
| /** Asynchronous transfer status. */ | |
| volatile uint32_t status; | |
| // Callback function to invoke when transfer completes or fails.*/ | |
| TwiCallback callback; | |
| /** Pointer to the data buffer.*/ | |
| uint8_t *pData; | |
| /** Total number of bytes to transfer.*/ | |
| uint32_t num; | |
| /** Number of already transferred bytes.*/ | |
| uint32_t transferred; | |
| } AsyncTwi; | |
| /*---------------------------------------------------------------------------- | |
| * Global functions | |
| *----------------------------------------------------------------------------*/ | |
| /** | |
| * \brief Initializes a TWI driver instance, using the given TWI peripheral. | |
| * \note The peripheral must have been initialized properly before calling this function. | |
| * \param pTwid Pointer to the Twid instance to initialize. | |
| * \param pTwi Pointer to the TWI peripheral to use. | |
| */ | |
| void TWID_Initialize(Twid *pTwid, Twi *pTwi) | |
| { | |
| TRACE_DEBUG( "TWID_Initialize()\n\r" ) ; | |
| assert( pTwid != NULL ) ; | |
| assert( pTwi != NULL ) ; | |
| /* Initialize driver. */ | |
| pTwid->pTwi = pTwi; | |
| pTwid->pTransfer = 0; | |
| } | |
| /** | |
| * \brief Interrupt handler for a TWI peripheral. Manages asynchronous transfer | |
| * occuring on the bus. This function MUST be called by the interrupt service | |
| * routine of the TWI peripheral if asynchronous read/write are needed. | |
| * \param pTwid Pointer to a Twid instance. | |
| */ | |
| void TWID_Handler( Twid *pTwid ) | |
| { | |
| uint32_t status; | |
| AsyncTwi *pTransfer ; | |
| Twi *pTwi ; | |
| assert( pTwid != NULL ) ; | |
| pTransfer = (AsyncTwi*)pTwid->pTransfer ; | |
| assert( pTransfer != NULL ) ; | |
| pTwi = pTwid->pTwi ; | |
| assert( pTwi != NULL ) ; | |
| /* Retrieve interrupt status */ | |
| status = TWI_GetMaskedStatus(pTwi); | |
| /* Byte received */ | |
| if (TWI_STATUS_RXRDY(status)) { | |
| pTransfer->pData[pTransfer->transferred] = TWI_ReadByte(pTwi); | |
| pTransfer->transferred++; | |
| /* check for transfer finish */ | |
| if (pTransfer->transferred == pTransfer->num) { | |
| TWI_DisableIt(pTwi, TWI_IDR_RXRDY); | |
| TWI_EnableIt(pTwi, TWI_IER_TXCOMP); | |
| } | |
| /* Last byte? */ | |
| else if (pTransfer->transferred == (pTransfer->num - 1)) { | |
| TWI_Stop(pTwi); | |
| } | |
| } | |
| /* Byte sent*/ | |
| else if (TWI_STATUS_TXRDY(status)) { | |
| /* Transfer finished ? */ | |
| if (pTransfer->transferred == pTransfer->num) { | |
| TWI_DisableIt(pTwi, TWI_IDR_TXRDY); | |
| TWI_EnableIt(pTwi, TWI_IER_TXCOMP); | |
| TWI_SendSTOPCondition(pTwi); | |
| } | |
| /* Bytes remaining */ | |
| else { | |
| TWI_WriteByte(pTwi, pTransfer->pData[pTransfer->transferred]); | |
| pTransfer->transferred++; | |
| } | |
| } | |
| /* Transfer complete*/ | |
| else if (TWI_STATUS_TXCOMP(status)) { | |
| TWI_DisableIt(pTwi, TWI_IDR_TXCOMP); | |
| pTransfer->status = 0; | |
| if (pTransfer->callback) { | |
| pTransfer->callback((Async *)(void*) pTransfer); | |
| } | |
| pTwid->pTransfer = 0; | |
| } | |
| } | |
| /** | |
| * \brief Asynchronously reads data from a slave on the TWI bus. An optional | |
| * callback function is triggered when the transfer is complete. | |
| * \param pTwid Pointer to a Twid instance. | |
| * \param address TWI slave address. | |
| * \param iaddress Optional slave internal address. | |
| * \param isize Internal address size in bytes. | |
| * \param pData Data buffer for storing received bytes. | |
| * \param num Number of bytes to read. | |
| * \param pAsync Asynchronous transfer descriptor. | |
| * \return 0 if the transfer has been started; otherwise returns a TWI error code. | |
| */ | |
| uint8_t TWID_Read( | |
| Twid *pTwid, | |
| uint8_t address, | |
| uint32_t iaddress, | |
| uint8_t isize, | |
| uint8_t *pData, | |
| uint32_t num, | |
| Async *pAsync) | |
| { | |
| Twi *pTwi; | |
| AsyncTwi *pTransfer; | |
| uint32_t timeout = 0; | |
| uint32_t i = 0; | |
| uint32_t status; | |
| assert( pTwid != NULL ) ; | |
| pTwi = pTwid->pTwi; | |
| pTransfer = (AsyncTwi *) pTwid->pTransfer; | |
| assert( (address & 0x80) == 0 ) ; | |
| assert( (iaddress & 0xFF000000) == 0 ) ; | |
| assert( isize < 4 ) ; | |
| /* Check that no transfer is already pending*/ | |
| if (pTransfer) { | |
| TRACE_ERROR("TWID_Read: A transfer is already pending\n\r"); | |
| return TWID_ERROR_BUSY; | |
| } | |
| /* Asynchronous transfer*/ | |
| if (pAsync) { | |
| /* Update the transfer descriptor */ | |
| pTwid->pTransfer = pAsync; | |
| pTransfer = (AsyncTwi *) pAsync; | |
| pTransfer->status = ASYNC_STATUS_PENDING; | |
| pTransfer->pData = pData; | |
| pTransfer->num = num; | |
| pTransfer->transferred = 0; | |
| /* Enable read interrupt and start the transfer */ | |
| TWI_EnableIt(pTwi, TWI_IER_RXRDY); | |
| TWI_StartRead(pTwi, address, iaddress, isize); | |
| } | |
| /* Synchronous transfer*/ | |
| else { | |
| /* Start read*/ | |
| TWI_StartRead(pTwi, address, iaddress, isize); | |
| if (num != 1) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| if(status & TWI_SR_NACK) | |
| TRACE_ERROR("TWID NACK error\n\r"); | |
| timeout = 0; | |
| while( ! (status & TWI_SR_RXRDY) && (++timeout<TWITIMEOUTMAX)) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| //TRACE_ERROR("TWID status %x\n\r",TWI_GetStatus(pTwi)); | |
| } | |
| pData[0] = TWI_ReadByte(pTwi); | |
| for( i = 1; i < num - 1; i++) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| if(status & TWI_SR_NACK) | |
| TRACE_ERROR("TWID NACK error\n\r"); | |
| timeout = 0; | |
| while( ! (status & TWI_SR_RXRDY) && (++timeout<TWITIMEOUTMAX)) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| //TRACE_ERROR("TWID status %x\n\r",TWI_GetStatus(pTwi)); | |
| } | |
| pData[i] = TWI_ReadByte(pTwi); | |
| } | |
| } | |
| TWI_Stop(pTwi); | |
| status = TWI_GetStatus(pTwi); | |
| if(status & TWI_SR_NACK) | |
| TRACE_ERROR("TWID NACK error\n\r"); | |
| timeout = 0; | |
| while( ! (status & TWI_SR_RXRDY) && (++timeout<TWITIMEOUTMAX)) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| //TRACE_ERROR("TWID status %x\n\r",TWI_GetStatus(pTwi)); | |
| } | |
| pData[i] = TWI_ReadByte(pTwi); | |
| timeout = 0; | |
| status = TWI_GetStatus(pTwi); | |
| while( !(status & TWI_SR_TXCOMP) && (++timeout<TWITIMEOUTMAX)) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| //TRACE_ERROR("TWID status %x\n\r",TWI_GetStatus(pTwi)); | |
| } | |
| #if 0 | |
| /* Read all bytes, setting STOP before the last byte*/ | |
| while (num > 0) { | |
| /* Last byte ?*/ | |
| if (num == 1) { | |
| TWI_Stop(pTwi); | |
| } | |
| /* Wait for byte then read and store it*/ | |
| timeout = 0; | |
| while( !TWI_ByteReceived(pTwi) && (++timeout<TWITIMEOUTMAX) ); | |
| if (timeout == TWITIMEOUTMAX) { | |
| TRACE_ERROR("TWID Timeout BR\n\r"); | |
| } | |
| *pData++ = TWI_ReadByte(pTwi); | |
| num--; | |
| } | |
| /* Wait for transfer to be complete */ | |
| timeout = 0; | |
| while( !TWI_TransferComplete(pTwi) && (++timeout<TWITIMEOUTMAX) ); | |
| if (timeout == TWITIMEOUTMAX) { | |
| TRACE_ERROR("TWID Timeout TC\n\r"); | |
| } | |
| #endif | |
| } | |
| return 0; | |
| } | |
| /** | |
| * \brief Asynchronously sends data to a slave on the TWI bus. An optional callback | |
| * function is invoked whenever the transfer is complete. | |
| * \param pTwid Pointer to a Twid instance. | |
| * \param address TWI slave address. | |
| * \param iaddress Optional slave internal address. | |
| * \param isize Number of internal address bytes. | |
| * \param pData Data buffer for storing received bytes. | |
| * \param num Data buffer to send. | |
| * \param pAsync Asynchronous transfer descriptor. | |
| * \return 0 if the transfer has been started; otherwise returns a TWI error code. | |
| */ | |
| uint8_t TWID_Write( | |
| Twid *pTwid, | |
| uint8_t address, | |
| uint32_t iaddress, | |
| uint8_t isize, | |
| uint8_t *pData, | |
| uint32_t num, | |
| Async *pAsync) | |
| { | |
| Twi *pTwi = pTwid->pTwi; | |
| AsyncTwi *pTransfer = (AsyncTwi *) pTwid->pTransfer; | |
| uint32_t timeout = 0; | |
| uint32_t status; | |
| uint8_t singleTransfer = 0; | |
| assert( pTwi != NULL ) ; | |
| assert( (address & 0x80) == 0 ) ; | |
| assert( (iaddress & 0xFF000000) == 0 ) ; | |
| assert( isize < 4 ) ; | |
| if(num == 1) singleTransfer = 1; | |
| /* Check that no transfer is already pending */ | |
| if (pTransfer) { | |
| TRACE_ERROR("TWI_Write: A transfer is already pending\n\r"); | |
| return TWID_ERROR_BUSY; | |
| } | |
| /* Asynchronous transfer */ | |
| if (pAsync) { | |
| /* Update the transfer descriptor */ | |
| pTwid->pTransfer = pAsync; | |
| pTransfer = (AsyncTwi *) pAsync; | |
| pTransfer->status = ASYNC_STATUS_PENDING; | |
| pTransfer->pData = pData; | |
| pTransfer->num = num; | |
| pTransfer->transferred = 1; | |
| /* Enable write interrupt and start the transfer */ | |
| TWI_StartWrite(pTwi, address, iaddress, isize, *pData); | |
| TWI_EnableIt(pTwi, TWI_IER_TXRDY); | |
| } | |
| /* Synchronous transfer*/ | |
| else { | |
| // Start write | |
| TWI_StartWrite(pTwi, address, iaddress, isize, *pData++); | |
| num--; | |
| if (singleTransfer) { | |
| /* Send a STOP condition */ | |
| TWI_SendSTOPCondition(pTwi); | |
| } | |
| status = TWI_GetStatus(pTwi); | |
| if(status & TWI_SR_NACK) | |
| TRACE_ERROR("TWID NACK error\n\r"); | |
| while( !(status & TWI_SR_TXRDY) && (timeout++ < TWITIMEOUTMAX) ) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| } | |
| if (timeout == TWITIMEOUTMAX) { | |
| TRACE_ERROR("TWID Timeout BS\n\r"); | |
| } | |
| timeout = 0; | |
| /* Send all bytes */ | |
| while (num > 0) { | |
| /* Wait before sending the next byte */ | |
| timeout = 0; | |
| TWI_WriteByte(pTwi, *pData++); | |
| status = TWI_GetStatus(pTwi); | |
| if(status & TWI_SR_NACK) | |
| TRACE_ERROR("TWID NACK error\n\r"); | |
| while( !(status & TWI_SR_TXRDY) && (timeout++ < TWITIMEOUTMAX) ) | |
| { | |
| status = TWI_GetStatus(pTwi); | |
| } | |
| if (timeout == TWITIMEOUTMAX) { | |
| TRACE_ERROR("TWID Timeout BS\n\r"); | |
| } | |
| num--; | |
| } | |
| /* Wait for actual end of transfer */ | |
| timeout = 0; | |
| if (!singleTransfer) { | |
| /* Send a STOP condition */ | |
| TWI_SendSTOPCondition(pTwi); | |
| } | |
| while( !TWI_TransferComplete(pTwi) && (++timeout<TWITIMEOUTMAX) ); | |
| if (timeout == TWITIMEOUTMAX) { | |
| TRACE_ERROR("TWID Timeout TC2\n\r"); | |
| } | |
| } | |
| return 0; | |
| } | |