/* ---------------------------------------------------------------------------- | |
* SAM Software Package License | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2015, 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 "peripherals/twid_legacy.h" | |
#include "peripherals/xdmad.h" | |
#include "cortex-a/cp15.h" | |
#include "trace.h" | |
#include <assert.h> | |
/*---------------------------------------------------------------------------- | |
* Definition | |
*----------------------------------------------------------------------------*/ | |
#define TWITIMEOUTMAX 0xfffff | |
static struct _xdmad_cfg twi_dmaCfg; | |
static struct _xdmad_channel *dmaWriteChannel; | |
static struct _xdmad_channel *dmaReadChannel; | |
static struct _xdmad_desc_view1 dmaWriteLinkList[1]; | |
static struct _xdmad_desc_view1 dmaReadLinkList[1]; | |
/*---------------------------------------------------------------------------- | |
* Types | |
*----------------------------------------------------------------------------*/ | |
/** TWI driver callback function.*/ | |
typedef void (*TwiCallback) (struct _async *); | |
/** \brief TWI asynchronous transfer descriptor.*/ | |
struct _async_twi { | |
volatile uint32_t status; /** Asynchronous transfer status. */ | |
TwiCallback callback; /** Callback function to invoke when transfer completes or fails.*/ | |
uint8_t *pData; /** Pointer to the data buffer.*/ | |
uint32_t num; /** Total number of bytes to transfer.*/ | |
uint32_t transferred; /** Number of already transferred bytes.*/ | |
}; | |
//struct _async_twi async_twi ; | |
/*---------------------------------------------------------------------------- | |
* Global functions | |
*----------------------------------------------------------------------------*/ | |
/** | |
* \brief Initializes a TWI DMA Read channel. | |
*/ | |
static void twid_dma_initialize_read(uint8_t TWI_ID) | |
{ | |
/* Allocate a XDMA channel, Read accesses into TWI_THR */ | |
dmaReadChannel = xdmad_allocate_channel(TWI_ID, XDMAD_PERIPH_MEMORY); | |
if (!dmaReadChannel) { | |
printf("-E- Can't allocate XDMA channel\n\r"); | |
} | |
xdmad_prepare_channel(dmaReadChannel); | |
} | |
/** | |
* \brief Initializes a TWI DMA write channel. | |
*/ | |
static void twid_dma_initialize_write(uint8_t TWI_ID) | |
{ | |
/* Allocate a XDMA channel, Write accesses into TWI_THR */ | |
dmaWriteChannel = xdmad_allocate_channel(XDMAD_PERIPH_MEMORY, TWI_ID); | |
if (!dmaWriteChannel) { | |
printf("-E- Can't allocate XDMA channel\n\r"); | |
} | |
xdmad_prepare_channel(dmaWriteChannel); | |
} | |
/** | |
* \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(struct _twid* pTwid, Twi * pTwi) | |
{ | |
trace_debug("twid_initialize()\n\r"); | |
assert(pTwid != NULL); | |
assert(pTwi != NULL); | |
/* Initialize driver. */ | |
pTwid->pTwi = pTwi; | |
pTwid->pTransfer = 0; | |
/* Initialize XDMA driver instance with polling mode */ | |
// ************* need to be updated XDMAD_Initialize(&twi_dma, 1); | |
} | |
/** | |
* \brief Configure xDMA write linker list for TWI transfer. | |
*/ | |
static void _xdma_configure_write(uint8_t * buf, uint32_t len, uint8_t twi_id) | |
{ | |
uint32_t i; | |
uint32_t xdmaCndc, Thr; | |
Twi* twi = (Twi*)get_twi_addr_from_id(twi_id); | |
Thr = (uint32_t) & (TWI0->TWI_THR); | |
if (twi) | |
Thr = (uint32_t) & (twi->TWI_THR); | |
for (i = 0; i < 1; i++) { | |
dmaWriteLinkList[i].ublock_size = XDMA_UBC_NVIEW_NDV1 | |
| ((i == len - 1) ? 0 : XDMA_UBC_NDE_FETCH_EN) | |
| len; | |
dmaWriteLinkList[i].src_addr = & buf[i]; | |
dmaWriteLinkList[i].dest_addr = (void*)Thr; | |
if (i == len - 1) | |
dmaWriteLinkList[i].next_desc = 0; | |
else | |
dmaWriteLinkList[i].next_desc = | |
& dmaWriteLinkList[i + 1]; | |
} | |
twi_dmaCfg.cfg.uint32_value = XDMAC_CC_TYPE_PER_TRAN | |
| XDMAC_CC_MBSIZE_SINGLE | |
| XDMAC_CC_DSYNC_MEM2PER | |
| XDMAC_CC_CSIZE_CHK_1 | |
| XDMAC_CC_DWIDTH_BYTE | |
| XDMAC_CC_SIF_AHB_IF0 | |
| XDMAC_CC_DIF_AHB_IF1 | |
| XDMAC_CC_SAM_INCREMENTED_AM | |
| XDMAC_CC_DAM_FIXED_AM | |
| | |
XDMAC_CC_PERID(get_peripheral_xdma_channel(twi_id, XDMAC0, true)); | |
xdmaCndc = | |
XDMAC_CNDC_NDVIEW_NDV1 | XDMAC_CNDC_NDE_DSCR_FETCH_EN | | |
XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED | | |
XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED; | |
cp15_coherent_dcache_for_dma((uint32_t) & dmaWriteLinkList, | |
((uint32_t) & dmaWriteLinkList + | |
sizeof (*dmaWriteLinkList) * len)); | |
xdmad_configure_transfer(dmaWriteChannel, &twi_dmaCfg, | |
xdmaCndc, dmaWriteLinkList); | |
} | |
/** | |
* \brief Configure xDMA read linker list for TWI transfer. | |
*/ | |
static void _xdma_configure_read(uint8_t * buf, uint32_t len, uint8_t twi_id) | |
{ | |
uint32_t i; | |
uint32_t xdmaCndc, Rhr; | |
Twi* twi = get_twi_addr_from_id(twi_id); | |
Rhr = (uint32_t) & (TWI0->TWI_RHR); | |
if (twi) { | |
Rhr = twi->TWI_RHR; | |
} | |
/* if (twi_id == ID_TWI1) { */ | |
/* Rhr = (uint32_t) & (TWI1->TWI_RHR); */ | |
/* } */ | |
/* if (twi_id == ID_TWI2) { */ | |
/* Rhr = (uint32_t) & (TWI2->TWI_RHR); */ | |
/* } */ | |
for (i = 0; i < 1; i++) { | |
dmaReadLinkList[i].ublock_size = XDMA_UBC_NVIEW_NDV1 | |
| ((i == len - 1) ? 0 : XDMA_UBC_NDE_FETCH_EN) | |
| len; | |
dmaReadLinkList[i].src_addr = (void*)Rhr; | |
dmaReadLinkList[i].dest_addr = & buf[i]; | |
if (i == len - 1) | |
dmaReadLinkList[i].next_desc = 0; | |
else | |
dmaReadLinkList[i].next_desc = | |
& dmaReadLinkList[i + 1]; | |
} | |
twi_dmaCfg.cfg.uint32_value = XDMAC_CC_TYPE_PER_TRAN | |
| XDMAC_CC_MBSIZE_SINGLE | |
| XDMAC_CC_DSYNC_PER2MEM | |
| XDMAC_CC_CSIZE_CHK_1 | |
| XDMAC_CC_DWIDTH_BYTE | |
| XDMAC_CC_SIF_AHB_IF1 | |
| XDMAC_CC_DIF_AHB_IF0 | |
| XDMAC_CC_SAM_FIXED_AM | |
| XDMAC_CC_DAM_INCREMENTED_AM | |
| | |
XDMAC_CC_PERID(get_peripheral_xdma_channel(twi_id, XDMAC0, false)); | |
xdmaCndc = | |
XDMAC_CNDC_NDVIEW_NDV1 | XDMAC_CNDC_NDE_DSCR_FETCH_EN | | |
XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED | | |
XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED; | |
cp15_coherent_dcache_for_dma((uint32_t) & dmaReadLinkList, | |
((uint32_t) & dmaReadLinkList + | |
sizeof (*dmaReadLinkList) * len)); | |
xdmad_configure_transfer(dmaReadChannel, &twi_dmaCfg, xdmaCndc, | |
dmaReadLinkList); | |
} | |
/** | |
* \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(struct _twid* pTwid) | |
{ | |
uint32_t status; | |
struct _async_twi* pTransfer; | |
Twi *pTwi; | |
assert(pTwid != NULL); | |
pTransfer = (struct _async_twi *) pTwid->pTransfer; | |
assert(pTransfer != NULL); | |
pTwi = pTwid->pTwi; | |
assert(pTwi != NULL); | |
/* Retrieve interrupt status */ | |
status = twi_get_masked_status(pTwi); | |
/* Byte received */ | |
if (TWI_STATUS_RXRDY(status)) { | |
pTransfer->pData[pTransfer->transferred] = twi_read_byte(pTwi); | |
pTransfer->transferred++; | |
/* check for transfer finish */ | |
if (pTransfer->transferred == pTransfer->num) { | |
twi_enable_it(pTwi, TWI_IDR_RXRDY); | |
twi_enable_it(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_enable_it(pTwi, TWI_IDR_TXRDY); | |
twi_enable_it(pTwi, TWI_IER_TXCOMP); | |
twi_send_stop_condition(pTwi); | |
} | |
/* Bytes remaining */ | |
else { | |
twi_write_byte(pTwi, | |
pTransfer->pData[pTransfer->transferred]); | |
pTransfer->transferred++; | |
} | |
} | |
/* Transfer complete */ | |
else if (TWI_STATUS_TXCOMP(status)) { | |
twi_enable_it(pTwi, TWI_IDR_TXCOMP); | |
pTransfer->status = 0; | |
if (pTransfer->callback) { | |
pTransfer->callback((struct _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(struct _twid* pTwid, uint8_t address, uint32_t iaddress, | |
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync) | |
{ | |
Twi *pTwi; | |
struct _async_twi* pTransfer; | |
uint32_t timeout = 0; | |
uint32_t i = 0; | |
uint32_t status; | |
assert(pTwid != NULL); | |
pTwi = pTwid->pTwi; | |
pTransfer = (struct _async_twi*) 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 = (struct _async_twi*) pAsync; | |
pTransfer->status = ASYNC_STATUS_PENDING; | |
pTransfer->pData = pData; | |
pTransfer->num = num; | |
pTransfer->transferred = 0; | |
/* Enable read interrupt and start the transfer */ | |
twi_enable_it(pTwi, TWI_IER_RXRDY); | |
twi_start_read(pTwi, address, iaddress, isize); | |
} | |
/* Synchronous transfer */ | |
else { | |
/* Start read */ | |
twi_start_read(pTwi, address, iaddress, isize); | |
if (num != 1) { | |
status = twi_get_status(pTwi); | |
if (status & TWI_SR_NACK) | |
trace_error("TWID NACK error\n\r"); | |
timeout = 0; | |
while (!(status & TWI_SR_RXRDY) | |
&& (++timeout < TWITIMEOUTMAX)) { | |
status = twi_get_status(pTwi); | |
//trace_error("TWID status %x\n\r",twi_get_status(pTwi)); | |
} | |
pData[0] = twi_read_byte(pTwi); | |
for (i = 1; i < num - 1; i++) { | |
status = twi_get_status(pTwi); | |
if (status & TWI_SR_NACK) | |
trace_error("TWID NACK error\n\r"); | |
timeout = 0; | |
while (!(status & TWI_SR_RXRDY) | |
&& (++timeout < TWITIMEOUTMAX)) { | |
status = twi_get_status(pTwi); | |
//trace_error("TWID status %x\n\r",twi_get_status(pTwi)); | |
} | |
pData[i] = twi_read_byte(pTwi); | |
} | |
} | |
twi_stop(pTwi); | |
status = twi_get_status(pTwi); | |
if (status & TWI_SR_NACK) | |
trace_error("TWID NACK error\n\r"); | |
timeout = 0; | |
while (!(status & TWI_SR_RXRDY) && (++timeout < TWITIMEOUTMAX)) { | |
status = twi_get_status(pTwi); | |
//trace_error("TWID status %x\n\r",twi_get_status(pTwi)); | |
} | |
pData[i] = twi_read_byte(pTwi); | |
timeout = 0; | |
status = twi_get_status(pTwi); | |
while (!(status & TWI_SR_TXCOMP) && (++timeout < TWITIMEOUTMAX)) { | |
status = twi_get_status(pTwi); | |
//trace_error("TWID status %x\n\r",twi_get_status(pTwi)); | |
} | |
} | |
return 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. | |
* \param twi_id TWI ID for TWI0, TWI1, TWI2. | |
* \return 0 if the transfer has been started; otherwise returns a TWI error code. | |
*/ | |
uint8_t twid_dma_read(struct _twid* pTwid, uint8_t address, uint32_t iaddress, | |
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync, uint8_t twi_id) | |
{ | |
Twi *pTwi; | |
struct _async_twi* pTransfer; | |
uint32_t timeout = 0; | |
uint32_t status; | |
assert(pTwid != NULL); | |
pTwi = pTwid->pTwi; | |
pTransfer = (struct _async_twi*) 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 = (struct _async_twi*) pAsync; | |
pTransfer->status = ASYNC_STATUS_PENDING; | |
pTransfer->pData = pData; | |
pTransfer->num = num; | |
pTransfer->transferred = 0; | |
/* Enable read interrupt and start the transfer */ | |
twi_enable_it(pTwi, TWI_IER_RXRDY); | |
twi_start_read(pTwi, address, iaddress, isize); | |
} | |
/* Synchronous transfer */ | |
else { | |
twid_dma_initialize_read(twi_id); | |
_xdma_configure_read(pData, num, twi_id); | |
/* Start read */ | |
xdmad_start_transfer(dmaReadChannel); | |
twi_start_read(pTwi, address, iaddress, isize); | |
while ((!xdmad_is_transfer_done(dmaReadChannel)) | |
&& (++timeout < TWITIMEOUTMAX)) | |
xdmad_poll(); | |
xdmad_stop_transfer(dmaReadChannel); | |
status = twi_get_status(pTwi); | |
timeout = 0; | |
while (!(status & TWI_SR_RXRDY) | |
&& (++timeout < TWITIMEOUTMAX)) ; | |
twi_stop(pTwi); | |
twi_read_byte(pTwi); | |
status = twi_get_status(pTwi); | |
timeout = 0; | |
while (!(status & TWI_SR_RXRDY) | |
&& (++timeout < TWITIMEOUTMAX)) ; | |
twi_read_byte(pTwi); | |
status = twi_get_status(pTwi); | |
timeout = 0; | |
while (!(status & TWI_SR_TXCOMP) | |
&& (++timeout < TWITIMEOUTMAX)) ; | |
if (timeout == TWITIMEOUTMAX) { | |
trace_error("TWID Timeout Read\n\r"); | |
} | |
xdmad_free_channel(dmaReadChannel); | |
} | |
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. | |
* \param twi_id TWI ID for TWI0, TWI1, TWI2. | |
* \return 0 if the transfer has been started; otherwise returns a TWI error code. | |
*/ | |
uint8_t twid_dma_write(struct _twid* pTwid, uint8_t address, uint32_t iaddress, | |
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync, uint8_t twi_id) | |
{ | |
Twi *pTwi = pTwid->pTwi; | |
struct _async_twi* 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); | |
pTransfer = (struct _async_twi *) pTwid->pTransfer; | |
// 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 = (struct _async_twi*) pAsync; | |
pTransfer->status = ASYNC_STATUS_PENDING; | |
pTransfer->pData = pData; | |
pTransfer->num = num; | |
pTransfer->transferred = 1; | |
/* Enable write interrupt and start the transfer */ | |
twi_start_write(pTwi, address, iaddress, isize, *pData); | |
twi_enable_it(pTwi, TWI_IER_TXRDY); | |
} | |
/* Synchronous transfer */ | |
else { | |
cp15_coherent_dcache_for_dma((uint32_t) pData, (uint32_t) pData); | |
twid_dma_initialize_write(twi_id); | |
_xdma_configure_write(pData, num, twi_id); | |
/* Set slave address and number of internal address bytes. */ | |
pTwi->TWI_MMR = 0; | |
pTwi->TWI_MMR = (isize << 8) | (address << 16); | |
/* Set internal address bytes. */ | |
pTwi->TWI_IADR = 0; | |
pTwi->TWI_IADR = iaddress; | |
xdmad_start_transfer(dmaWriteChannel); | |
while (!xdmad_is_transfer_done(dmaWriteChannel)) | |
xdmad_poll(); | |
xdmad_stop_transfer(dmaWriteChannel); | |
status = twi_get_status(pTwi); | |
timeout = 0; | |
while (!(status & TWI_SR_TXRDY) && (timeout++ < TWITIMEOUTMAX)) { | |
status = twi_get_status(pTwi); | |
} | |
if (timeout == TWITIMEOUTMAX) { | |
trace_error("TWID Timeout TXRDY\n\r"); | |
} | |
/* Send a STOP condition */ | |
twi_stop(pTwi); | |
status = twi_get_status(pTwi); | |
timeout = 0; | |
while (!(status & TWI_SR_TXCOMP) && (++timeout < TWITIMEOUTMAX)) { | |
status = twi_get_status(pTwi); | |
} | |
if (timeout == TWITIMEOUTMAX) { | |
trace_error("TWID Timeout Write\n\r"); | |
} | |
cp15_invalidate_dcache_for_dma((uint32_t) pData, (uint32_t) (pData)); | |
xdmad_free_channel(dmaWriteChannel); | |
} | |
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(struct _twid* pTwid, uint8_t address, uint32_t iaddress, | |
uint8_t isize, uint8_t * pData, uint32_t num, struct _async * pAsync) | |
{ | |
Twi *pTwi = pTwid->pTwi; | |
struct _async_twi* 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); | |
pTransfer = (struct _async_twi *) pTwid->pTransfer; | |
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 = (struct _async_twi*) pAsync; | |
pTransfer->status = ASYNC_STATUS_PENDING; | |
pTransfer->pData = pData; | |
pTransfer->num = num; | |
pTransfer->transferred = 1; | |
/* Enable write interrupt and start the transfer */ | |
twi_start_write(pTwi, address, iaddress, isize, *pData); | |
twi_enable_it(pTwi, TWI_IER_TXRDY); | |
} | |
/* Synchronous transfer */ | |
else { | |
// Start write | |
twi_start_write(pTwi, address, iaddress, isize, *pData++); | |
num--; | |
if (singleTransfer) { | |
/* Send a STOP condition */ | |
twi_send_stop_condition(pTwi); | |
} | |
status = twi_get_status(pTwi); | |
if (status & TWI_SR_NACK) | |
trace_error("TWID NACK error\n\r"); | |
while (!(status & TWI_SR_TXRDY) && (timeout++ < TWITIMEOUTMAX)) { | |
status = twi_get_status(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_write_byte(pTwi, *pData++); | |
status = twi_get_status(pTwi); | |
if (status & TWI_SR_NACK) | |
trace_error("TWID NACK error\n\r"); | |
while (!(status & TWI_SR_TXRDY) | |
&& (timeout++ < TWITIMEOUTMAX)) { | |
status = twi_get_status(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_send_stop_condition(pTwi); | |
} | |
while (!twi_is_transfer_complete(pTwi) | |
&& (++timeout < TWITIMEOUTMAX)) ; | |
if (timeout == TWITIMEOUTMAX) { | |
trace_error("TWID Timeout TC2\n\r"); | |
} | |
} | |
return 0; | |
} |