From 4085ae3ddfbbf10c8ccbd3dccd43452c40a1fe40 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 13 Mar 2015 12:24:52 +0200 Subject: Add bootloader, nfclib and sensorlib --- sensorlib/i2cm_drv.c | 2256 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2256 insertions(+) create mode 100644 sensorlib/i2cm_drv.c (limited to 'sensorlib/i2cm_drv.c') diff --git a/sensorlib/i2cm_drv.c b/sensorlib/i2cm_drv.c new file mode 100644 index 0000000..1e603e2 --- /dev/null +++ b/sensorlib/i2cm_drv.c @@ -0,0 +1,2256 @@ +//***************************************************************************** +// +// i2cm_drv.c - Interrupt-driven I2C master driver. +// +// Copyright (c) 2012-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#include "inc/hw_i2c.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/i2c.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "sensorlib/i2cm_drv.h" + +//***************************************************************************** +// +//! \addtogroup i2cm_drv_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +// The states in the interrupt handler state machine. +// +//***************************************************************************** +#define STATE_IDLE 0 +#define STATE_WRITE_NEXT 1 +#define STATE_WRITE_FINAL 2 +#define STATE_WRITE_PAUSE 3 +#define STATE_READ_ONE 4 +#define STATE_READ_FIRST 5 +#define STATE_READ_NEXT 6 +#define STATE_READ_FINAL 7 +#define STATE_READ_PAUSE 8 +#define STATE_READ_WAIT 9 +#define STATE_CALLBACK 10 + +//***************************************************************************** +// +// The states in the I2C read-modify-write state machine. +// +//***************************************************************************** +#define I2CM_RMW_STATE_IDLE 0 +#define I2CM_RMW_STATE_READ 1 +#define I2CM_RMW_STATE_WRITE 2 + +//***************************************************************************** +// +//! Writes data to an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8Data is a pointer to the data buffer to be written. +//! \param ui16Count is the number of bytes to be written. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C write to the queue of commands to be sent. If +//! successful, the I2C write is then performed in the background using the +//! interrupt handler. When the write is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of the data buffer contains the I2C address of the device to +//! access, and the remaining \e ui16Count bytes contain the data to be written +//! to the device. The \e ui16Count parameter can be zero if there are no +//! bytes to be written. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMWrite(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8Data, uint_fast16_t ui16Count, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Reads data from an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8WriteData is a pointer to the data buffer to be written. +//! \param ui16WriteCount is the number of bytes to be written. +//! \param pui8ReadData is a pointer to the buffer to be filled with the read +//! data. +//! \param ui16ReadCount is the number of bytes to be read. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C read to the queue of commands to be sent. If +//! successful, the I2C read is then performed in the background using the +//! interrupt handler. When the read is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of \e pui8WriteData contains the I2C address of the device +//! to access, the next \e ui16WriteCount bytes contains the data to be written +//! to the device. The data read from the device is written into the first +//! \e ui16ReadCount bytes of \e pui8ReadData. The \e ui16WriteCount or +//! \e ui16ReadCount parameters can be zero if there are no bytes to be read or +//! written. The write bytes are sent to the device first, and then the read +//! bytes are read from the device afterward. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMRead(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, + uint_fast16_t ui16WriteCount, + uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Writes data in batches to an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8Data is a pointer to the data buffer to be written. +//! \param ui16Count is the number of bytes to be written. +//! \param ui16BatchSize is the number of bytes in each write batch. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C write to the queue of commands to be sent. If +//! successful, the I2C write is then performed in the background using the +//! interrupt handler. When the write is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of the data buffer contains the I2C address of the device to +//! access, and the remaining \e ui16Count bytes contain the data to be written +//! to the device. The \e ui16Count parameter can be zero if there are no +//! bytes to be written. +//! +//! The data is written in batches of \e ui16WriteBatchSize. The callback +//! function is called after each batch is written, and I2CMTransferResume() +//! must be called when the next batch should be written. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMWriteBatched(tI2CMInstance *psInst, + uint_fast8_t ui8Addr, + const uint8_t *pui8Data, + uint_fast16_t ui16Count, + uint_fast16_t ui16BatchSize, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Reads data in batches from an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8WriteData is a pointer to the data buffer to be written. +//! \param ui16WriteCount is the number of bytes to be written. +//! \param ui16WriteBatchSize is the number of bytes in each write batch. +//! \param pui8ReadData is a pointer to the buffer to be filled with the read +//! data. +//! \param ui16ReadCount is the number of bytes to be read. +//! \param ui16ReadBatchSize is the number of bytes in each read batch. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C read to the queue of commands to be sent. If +//! successful, the I2C read is then performed in the background using the +//! interrupt handler. When the read is complete, the callback function, if +//! provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of \e pui8WriteData contains the I2C address of the device +//! to access, the next \e ui16WriteCount bytes contains the data to be written +//! to the device. The data read from the device is written into the first +//! \e ui16ReadCount bytes of \e pui8ReadData. The \e ui16WriteCount or +//! \e ui16ReadCount parameters can be zero if there are no bytes to be read or +//! written. The write bytes are sent to the device first, and then the read +//! bytes are read from the device afterward. +//! +//! The data is written in batches of \e ui16WriteBatchSize. The callback +//! function is called after each batch is written, and I2CMTransferResume() +//! must be called when the next batch should be written. +//! +//! The data is read in batches of \e ui16ReadBatchSize. The callback function +//! is called after each batch is read, and I2CMTransferResume() must be called +//! when the next batch should be read. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMReadBatched(tI2CMInstance *psInst, + uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, + uint_fast16_t ui16WriteCount, + uint_fast16_t ui16WriteBatchSize, + uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, + uint_fast16_t ui16ReadBatchSize, + tSensorCallback pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +//! Performs a read-modify-write of 16 bits of big-endian data in an I2C +//! device. +//! +//! \param psInst is a pointer to the read-modify-write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param ui16Mask is the mask indicating the register bits that should be +//! maintained. +//! \param ui16Value is the value indicating the new value for the register +//! bits that are not maintained. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read-modify-write transaction of 16 bits of +//! big-endian data in an I2C device. The modify portion of the operation is +//! performed by AND-ing the register value with \e ui16Mask and then OR-ing +//! the result with \e ui16Value. When the read-modify-write is complete, the +//! callback function, if provided, is called in the context of the I2C master +//! interrupt handler. +//! +//! If the mask (in \e ui16Mask) is zero, then none of the bits in the current +//! register value are maintained. In this case, the read portion of the +//! read-modify-write is bypassed, and the new register value (in \e ui16Value) +//! is directly written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +// The extern here provides a non-inline definition for this function to handle +// the case where the compiler chooses not to inline the function (which is a +// valid choice for the compiler to make). +// +//***************************************************************************** +extern uint_fast8_t I2CMReadModifyWrite16BE(tI2CMReadModifyWrite16 *psInst, + tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, + uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, + tSensorCallback *pfnCallback, + void *pvCallbackData); + +//***************************************************************************** +// +// This function handles the idle state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateIdle(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Do nothing if there is not another transfer in the queue. + // + if(psInst->ui8ReadPtr == psInst->ui8WritePtr) + { + return; + } + + // + // See if there is any data to be written. + // + if(pCommand->ui16WriteCount != 0) + { + // + // Set the slave address and indicate a write. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, false); + + // + // Place the first data byte to be written in the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, pCommand->pui8WriteData[0]); + + // + // See if there is just a single byte to be written and no bytes to be + // read. + // + if((pCommand->ui16WriteCount == 1) && (pCommand->ui16ReadCount == 0)) + { + // + // Perform a single byte send. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_SINGLE_SEND); + + // + // The next state is the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + + // + // Otherwise, see if there is just a single byte to be written and at + // least one byte to be read. + // + else if(pCommand->ui16WriteCount == 1) + { + // + // Perform a single send, writing the first byte as the only byte. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to read. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 1) ? + STATE_READ_ONE : STATE_READ_FIRST); + } + + // + // Otherwise, there is more than one byte to be written. + // + else + { + // + // Start the burst cycle, writing the first byte. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_START); + + // + // Set the index to indicate that the first byte has been + // transmitted. + // + psInst->ui16Index = 1; + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to write. + // + psInst->ui8State = ((pCommand->ui16WriteCount != 2) ? + STATE_WRITE_NEXT : STATE_WRITE_FINAL); + } + } + else + { + // + // Set the slave address and indicate a read. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, true); + + // + // Set the index to indicate that the first byte is being read. + // + psInst->ui16Index = 0; + + // + // See if there is just a single byte to be read. + // + if(pCommand->ui16ReadCount == 1) + { + // + // Perform a single byte read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; + } + else + { + // + // Start the burst receive. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_START); + + // + // Set the next state appropriately. If the read count is two, the + // next state must finish the transaction. If it is greater than + // two, the burst read must be continued. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 2) ? + STATE_READ_FINAL : STATE_READ_NEXT); + } + } +} + +//***************************************************************************** +// +// This function handles the write next state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateWriteNext(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // See if the write batch has been sent. + // + if(psInst->ui16Index == pCommand->ui16WriteBatchSize) + { + // + // Move to the write pause state. + // + psInst->ui8State = STATE_WRITE_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_DONE); + } + } + else + { + // + // Write the next byte to the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, + pCommand->pui8WriteData[psInst->ui16Index]); + psInst->ui16Index++; + + // + // Continue the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // If there is one byte left, set the next state to the final write + // state. + // + if((pCommand->ui16WriteCount - psInst->ui16Index) == 1) + { + psInst->ui8State = STATE_WRITE_FINAL; + } + } +} + +//***************************************************************************** +// +// This function handles the write final state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateWriteFinal(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // See if the write batch has been sent. + // + if(psInst->ui16Index == pCommand->ui16WriteBatchSize) + { + // + // Move to the write pause state. + // + psInst->ui8State = STATE_WRITE_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_DONE); + } + } + else + { + // + // Write the final byte to the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, + pCommand->pui8WriteData[psInst->ui16Index]); + + // + // See if there is data to be read after this byte is written. + // + if(pCommand->ui16ReadCount == 0) + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_FINISH); + + // + // The next state is the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + else + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to read. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 1) ? + STATE_READ_ONE : STATE_READ_FIRST); + } + } +} + +//***************************************************************************** +// +// This function handles the write pause state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateWritePause(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Decrement the write count by the batch size. + // + pCommand->ui16WriteCount -= pCommand->ui16WriteBatchSize; + + // + // Write the next byte to the data register. + // + MAP_I2CMasterDataPut(psInst->ui32Base, pCommand->pui8WriteData[0]); + + // + // Set the index to indicate that the first byte has been transmitted. + // + psInst->ui16Index = 1; + + // + // See if there is more than one byte left to be written. + // + if((pCommand->ui16WriteCount - psInst->ui16Index) == 0) + { + // + // See if there is data to be read after this byte is written. + // + if(pCommand->ui16ReadCount == 0) + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_FINISH); + + // + // The next state is the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + else + { + // + // Finish the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // Set the next state of the interrupt state machine based on the + // number of bytes to read. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 1) ? + STATE_READ_ONE : STATE_READ_FIRST); + } + } + else + { + // + // Continue the burst write. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_BURST_SEND_CONT); + + // + // The next state is the write next state. + // + if((pCommand->ui16WriteCount - psInst->ui16Index) == 1) + { + psInst->ui8State = STATE_WRITE_FINAL; + } + else + { + psInst->ui8State = STATE_WRITE_NEXT; + } + } +} + +//***************************************************************************** +// +// This function handles the read one state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadOne(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, true); + + // + // Perform a single byte read. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_SINGLE_RECEIVE); + + // + // Set the index to indicate that the first byte is being read. + // + psInst->ui16Index = 0; + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; +} + +//***************************************************************************** +// +// This function handles the read first state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadFirst(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Put the I2C master into receive mode. + // + MAP_I2CMasterSlaveAddrSet(psInst->ui32Base, pCommand->ui8Addr, true); + + // + // Start the burst receive. + // + MAP_I2CMasterControl(psInst->ui32Base, I2C_MASTER_CMD_BURST_RECEIVE_START); + + // + // Set the index to indicate that the first byte is being read. + // + psInst->ui16Index = 0; + + // + // Set the next state appropriately. If the count is greater than two it + // is the middle of the burst read. If exactly two, the next state must + // finish the transaction. + // + psInst->ui8State = ((pCommand->ui16ReadCount == 2) ? + STATE_READ_FINAL : STATE_READ_NEXT); +} + +//***************************************************************************** +// +// This function handles the read next state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadNext(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Read the received character. + // + pCommand->pui8ReadData[psInst->ui16Index] = + MAP_I2CMasterDataGet(psInst->ui32Base); + psInst->ui16Index++; + + // + // See if the read batch has been filled. + // + if(psInst->ui16Index == pCommand->ui16ReadBatchSize) + { + // + // Move to the read pause state. + // + psInst->ui8State = STATE_READ_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_READY); + } + } + else + { + // + // Continue the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + // + // If there are two characters left to be read, make the next state be + // the end of burst read state. + // + if((pCommand->ui16ReadCount - psInst->ui16Index) == 2) + { + psInst->ui8State = STATE_READ_FINAL; + } + } +} + +//***************************************************************************** +// +// This function handles the read final state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadFinal(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Read the received character. + // + pCommand->pui8ReadData[psInst->ui16Index] = + MAP_I2CMasterDataGet(psInst->ui32Base); + psInst->ui16Index++; + + // + // See if the read batch has been filled. + // + if(psInst->ui16Index == pCommand->ui16ReadBatchSize) + { + // + // Move to the read pause state. + // + psInst->ui8State = STATE_READ_PAUSE; + + // + // Call the callback function. + // + if(pCommand->pfnCallback) + { + pCommand->pfnCallback(pCommand->pvCallbackData, + I2CM_STATUS_BATCH_READY); + } + } + else + { + // + // Finish the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; + } +} + +//***************************************************************************** +// +// This function handles the read pause state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadPause(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Decrement the read count by the batch size. + // + pCommand->ui16ReadCount -= pCommand->ui16ReadBatchSize; + + // + // Reset the read index. + // + psInst->ui16Index = 0; + + // + // See if there is more than one byte left to be read. + // + if((pCommand->ui16ReadCount - psInst->ui16Index) == 1) + { + // + // Finish the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_FINISH); + + // + // The next state is the wait for final read state. + // + psInst->ui8State = STATE_READ_WAIT; + } + else + { + // + // Continue the burst read. + // + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_RECEIVE_CONT); + + // + // Determine the next state based on the number of bytes left to read. + // + if((pCommand->ui16ReadCount - psInst->ui16Index) == 2) + { + psInst->ui8State = STATE_READ_FINAL; + } + else + { + psInst->ui8State = STATE_READ_NEXT; + } + } +} + +//***************************************************************************** +// +// This function handles the read wait state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateReadWait(tI2CMInstance *psInst, tI2CMCommand *pCommand) +{ + // + // Read the received character. + // + pCommand->pui8ReadData[psInst->ui16Index] = + MAP_I2CMasterDataGet(psInst->ui32Base); + + // + // The state machine is now in the callback state. + // + psInst->ui8State = STATE_CALLBACK; +} + +//***************************************************************************** +// +// This function handles the callback state of the I2C master state machine. +// +//***************************************************************************** +static void +I2CMStateCallback(tI2CMInstance *psInst, tI2CMCommand *pCommand, + uint32_t ui32Status) +{ + tSensorCallback *pfnCallback; + void *pvCallbackData; + + // + // Save the callback information. + // + pfnCallback = pCommand->pfnCallback; + pvCallbackData = pCommand->pvCallbackData; + + // + // This command has been completed, so increment the read pointer. + // + psInst->ui8ReadPtr++; + if(psInst->ui8ReadPtr == NUM_I2CM_COMMANDS) + { + psInst->ui8ReadPtr = 0; + } + + // + // If there is a callback function then call it now. + // + if(pfnCallback) + { + // + // Convert the status from the I2C driver into the I2C master + // driver status. + // + if((ui32Status & (I2C_MCS_ARBLST | I2C_MCS_ERROR)) == 0) + { + ui32Status = I2CM_STATUS_SUCCESS; + } + else if(ui32Status & I2C_MCS_ARBLST) + { + ui32Status = I2CM_STATUS_ARB_LOST; + } + else if(ui32Status & I2C_MCS_ADRACK) + { + ui32Status = I2CM_STATUS_ADDR_NACK; + } + else if(ui32Status & I2C_MCS_DATACK) + { + ui32Status = I2CM_STATUS_DATA_NACK; + } + else + { + ui32Status = I2CM_STATUS_ERROR; + } + + // + // Call the callback function. + // + pfnCallback(pvCallbackData, ui32Status); + } + + // + // The state machine is now idle. + // + psInst->ui8State = STATE_IDLE; +} + +//***************************************************************************** +// +//! Handles I2C master interrupts. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! +//! This function performs the processing required in response to an I2C +//! interrupt. The application-supplied interrupt handler should call this +//! function with the correct instance data in response to the I2C interrupt. +//! +//! \return None. +// +//***************************************************************************** +void +I2CMIntHandler(tI2CMInstance *psInst) +{ + tI2CMCommand *pCommand; + uint32_t ui32Status; + + // + // Clear the I2C interrupt. + // + MAP_I2CMasterIntClear(psInst->ui32Base); + ui32Status = HWREG(psInst->ui32Base + I2C_O_MCS); + + // + // Get a pointer to the current command. + // + pCommand = &(psInst->pCommands[psInst->ui8ReadPtr]); + + // + // See if an error occurred during the last transaction. + // + if((ui32Status & (I2C_MCS_ERROR | I2C_MCS_ARBLST)) && + (psInst->ui8State != STATE_IDLE)) + { + // + // An error occurred, so halt the I2C transaction. The error stop + // command for send and receive is identical, so it does not matter + // which one is used here. Only issue the stop if the bus is busy. + // + if(ui32Status & I2C_MCS_BUSBSY) + { + MAP_I2CMasterControl(psInst->ui32Base, + I2C_MASTER_CMD_BURST_SEND_ERROR_STOP); + } + + // + // Move to the callback state. + // + psInst->ui8State = STATE_CALLBACK; + } + + // + // Loop forever. Most states will return when they have completed their + // action. However, a few states require multi-state processing, so those + // states will break and this loop repeated. + // + while(1) + { + // + // Determine what to do based on the current state. + // + switch(psInst->ui8State) + { + // + // The idle state. + // + case STATE_IDLE: + { + // + // Handle the idle state. + // + I2CMStateIdle(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the middle of a burst write. + // + case STATE_WRITE_NEXT: + { + // + // Handle the write next state. + // + I2CMStateWriteNext(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the final write of a burst sequence. + // + case STATE_WRITE_FINAL: + { + // + // Handle the write final state. + // + I2CMStateWriteFinal(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for a paused write. + // + case STATE_WRITE_PAUSE: + { + // + // Handle the write pause state. + // + I2CMStateWritePause(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for a single byte read. + // + case STATE_READ_ONE: + { + // + // Handle the read one state. + // + I2CMStateReadOne(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the start of a burst read. + // + case STATE_READ_FIRST: + { + // + // Handle the read first state. + // + I2CMStateReadFirst(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the middle of a burst read. + // + case STATE_READ_NEXT: + { + // + // Handle the read next state. + // + I2CMStateReadNext(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for the end of a burst read. + // + case STATE_READ_FINAL: + { + // + // Handle the read final state. + // + I2CMStateReadFinal(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // The state for a paused read. + // + case STATE_READ_PAUSE: + { + // + // Handle the read pause state. + // + I2CMStateReadPause(psInst, pCommand); + + // + // This state is done and the next state should be handled at + // the next interrupt. + // + return; + } + + // + // This state is for the final read of a single or burst read. + // + case STATE_READ_WAIT: + { + // + // Handle the read wait state. + // + I2CMStateReadWait(psInst, pCommand); + + // + // This state is done and the next state needs to be handled + // immediately. + // + break; + } + + // + // This state is for providing the transaction complete callback. + // + case STATE_CALLBACK: + { + // + // Handle the callback state. + // + I2CMStateCallback(psInst, pCommand, ui32Status); + + // + // If an error occurred, this state is done. The completion of + // the error handling stop condition above, if issued, will + // cause the next state to be processed. + // + if((ui32Status & (I2C_MCS_ERROR | I2C_MCS_ARBLST)) && + (ui32Status & I2C_MCS_BUSBSY)) + { + return; + } + + // + // Update the pointer to the current command. + // + pCommand = &(psInst->pCommands[psInst->ui8ReadPtr]); + + // + // This state is done and the next state needs to be handled + // immediately. + // + break; + } + } + } +} + +//***************************************************************************** +// +//! Initializes the I2C master driver. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui32Base is the base address of the I2C module. +//! \param ui8Int is the interrupt number for the I2C module. +//! \param ui8TxDMA is the uDMA channel number used for transmitting data to +//! the I2C module. +//! \param ui8RxDMA is the uDMA channel number used for receiving data from +//! the I2C module. +//! \param ui32Clock is the clock frequency of the input clock to the I2C +//! module. +//! +//! This function prepares both the I2C master module and driver for operation, +//! and must be the first I2C master driver function called for each I2C master +//! instance. It is assumed that the application has enabled the I2C module, +//! configured the I2C pins, and provided an I2C interrupt handler that calls +//! I2CMIntHandler(). +//! +//! The uDMA module cannot be used at present to transmit/receive data, so the +//! \e ui8TxDMA and \e ui8RxDMA parameters are unused. They are reserved for +//! future use and should be set to 0xff in order to ensure future +//! compatibility. +//! +//! \return None. +// +//***************************************************************************** +void +I2CMInit(tI2CMInstance *psInst, uint32_t ui32Base, uint_fast8_t ui8Int, + uint_fast8_t ui8TxDMA, uint_fast8_t ui8RxDMA, uint32_t ui32Clock) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT((ui32Base == I2C0_BASE) || (ui32Base == I2C1_BASE) || + (ui32Base == I2C2_BASE) || (ui32Base == I2C3_BASE) || + (ui32Base == I2C4_BASE) || (ui32Base == I2C5_BASE) || + (ui32Base == I2C6_BASE) || (ui32Base == I2C7_BASE) || + (ui32Base == I2C8_BASE) || (ui32Base == I2C9_BASE)); + ASSERT(ui8Int); + ASSERT(ui32Clock); + + // + // Initialize the state structure. + // + psInst->ui32Base = ui32Base; + psInst->ui8Int = ui8Int; + psInst->ui8TxDMA = ui8TxDMA; + psInst->ui8RxDMA = ui8RxDMA; + psInst->ui8State = STATE_IDLE; + psInst->ui8ReadPtr = 0; + psInst->ui8WritePtr = 0; + + // + // Initialize the I2C master module. + // + MAP_I2CMasterInitExpClk(ui32Base, ui32Clock, true); + + // + // Enable the I2C interrupt. + // + MAP_IntEnable(ui8Int); + MAP_I2CMasterIntEnableEx(ui32Base, I2C_MASTER_INT_DATA); +} + +//***************************************************************************** +// +//! Sends a command to an I2C device. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param pui8WriteData is a pointer to the data buffer to be written. +//! \param ui16WriteCount is the number of bytes to be written. +//! \param ui16WriteBatchSize is the number of bytes in each write batch. +//! \param pui8ReadData is a pointer to the buffer to be filled with the read +//! data. +//! \param ui16ReadCount is the number of bytes to be read. +//! \param ui16ReadBatchSize is the number of bytes to be read in each batch. +//! \param pfnCallback is the function to be called when the transfer has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function adds an I2C command to the queue of commands to be sent. If +//! successful, the I2C command is then transferred in the background using the +//! interrupt handler. When the transfer is complete, the callback function, +//! if provided, is called in the context of the I2C master interrupt handler. +//! +//! The first byte of \e pui8WriteData contains the I2C address of the device +//! to access, the next \e ui16WriteCount bytes contains the data to be written +//! to the device. The data read from the device is written into the first +//! \e ui16ReadCount bytes of \e pui8ReadData. The \e ui16WriteCount or +//! \e ui16ReadCount parameters can be zero if there are no bytes to be read or +//! written. The write bytes are sent to the device first, and then the read +//! bytes are read from the device afterward. +//! +//! If \e ui16WriteBatchSize is less than \e ui16WriteCount, the write portion +//! of the transfer is broken up into as many \e ui16WriteBatchSize batches as +//! required to write \e ui16WriteCount bytes. After each batch, the callback +//! function is called with an \b I2CM_STATUS_BATCH_DONE status, and the +//! transfer is paused (with the I2C bus held). The transfer is resumed when +//! I2CMTransferResume() is called. This procedure can be used to perform very +//! large writes without requiring all the data be available at once, at the +//! expense of tying up the I2C bus for the extended duration of the transfer. +//! +//! If \e ui16ReadBatchSize is less than \e ui16ReadCount, the read portion of +//! the transfer is broken up into as many \e ui16ReadBatchSize batches as +//! required to read \e ui16ReadCount bytes. After each batch, the callback +//! function is called with an \b I2CM_STATUS_BATCH_READY status, and the +//! transfer is paused (with the I2C bus held). The transfer is resumed when +//! I2CMTransferResume() is called. This procedure can be used to perform very +//! large reads without requiring a large SRAM buffer, at the expense of tying +//! up the I2C bus for the extended duration of the transfer. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMCommand(tI2CMInstance *psInst, uint_fast8_t ui8Addr, + const uint8_t *pui8WriteData, uint_fast16_t ui16WriteCount, + uint_fast16_t ui16WriteBatchSize, uint8_t *pui8ReadData, + uint_fast16_t ui16ReadCount, uint_fast16_t ui16ReadBatchSize, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + uint_fast8_t ui8Next, ui8Enabled; + tI2CMCommand *pCommand; + + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(pui8WriteData || !ui16WriteCount); + ASSERT(!ui16WriteCount || (ui16WriteBatchSize > 0)); + ASSERT(pui8ReadData || !ui16ReadCount); + ASSERT(!ui16ReadCount || (ui16ReadBatchSize > 0)); + + // + // Disable the I2C interrupt. + // + if(MAP_IntIsEnabled(psInst->ui8Int)) + { + ui8Enabled = 1; + MAP_IntDisable(psInst->ui8Int); + } + else + { + ui8Enabled = 0; + } + + // + // Compute the new value of the write pointer (after this command is added + // to the queue). + // + ui8Next = psInst->ui8WritePtr + 1; + if(ui8Next == NUM_I2CM_COMMANDS) + { + ui8Next = 0; + } + + // + // Return a failure if the command queue is full. + // + if(psInst->ui8ReadPtr == ui8Next) + { + if(ui8Enabled) + { + MAP_IntEnable(psInst->ui8Int); + } + return(0); + } + + // + // Get a pointer to the command structure. + // + pCommand = &(psInst->pCommands[psInst->ui8WritePtr]); + + // + // Fill in the command structure with the details of this command. + // + pCommand->ui8Addr = ui8Addr; + pCommand->pui8WriteData = pui8WriteData; + pCommand->ui16WriteCount = ui16WriteCount; + pCommand->ui16WriteBatchSize = ui16WriteBatchSize; + pCommand->pui8ReadData = pui8ReadData; + pCommand->ui16ReadCount = ui16ReadCount; + pCommand->ui16ReadBatchSize = ui16ReadBatchSize; + pCommand->pfnCallback = pfnCallback; + pCommand->pvCallbackData = pvCallbackData; + + // + // Update the write pointer. + // + psInst->ui8WritePtr = ui8Next; + + // + // See if the state machine is idle. + // + if(psInst->ui8State == STATE_IDLE) + { + // + // Generate a fake I2C interrupt, which will commence the I2C transfer. + // + IntTrigger(psInst->ui8Int); + } + + // + // Re-enable the I2C master interrupt. + // + if(ui8Enabled) + { + MAP_IntEnable(psInst->ui8Int); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +//! Resumes an I2C transfer. +//! +//! \param psInst is a pointer to the I2C master instance data. +//! \param pui8Data is a pointer to the buffer to be used for the next batch of +//! data. +//! +//! This function resumes an I2C transfer that has been paused via the use of +//! the write or read batch size capability. +//! +//! \return Returns 1 if the transfer was resumed and 0 if there was not a +//! paused transfer to resume. +// +//***************************************************************************** +uint_fast8_t +I2CMTransferResume(tI2CMInstance *psInst, uint8_t *pui8Data) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(pui8Data); + + // + // Return an error if there is not a paused transfer. + // + if((psInst->ui8State != STATE_WRITE_PAUSE) && + (psInst->ui8State != STATE_READ_PAUSE)) + { + return(0); + } + + // + // Save the pointer for the next buffer. + // + if(psInst->ui8State == STATE_WRITE_PAUSE) + { + psInst->pCommands[psInst->ui8ReadPtr].pui8WriteData = pui8Data; + } + else + { + psInst->pCommands[psInst->ui8ReadPtr].pui8ReadData = pui8Data; + } + + // + // Trigger the I2C interrupt, resuming the transfer. + // + IntTrigger(psInst->ui8Int); + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of of a +// read-modify-write operation of 8 bits of data have completed. +// +//***************************************************************************** +static void +I2CMReadModifyWrite8Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMReadModifyWrite8 *psInst; + + // + // Convert the instance data into a pointer to a tI2CMReadModifyWrite8 + // structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result ina callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = I2CM_RMW_STATE_IDLE; + } + + // + // Determine the current state of the I2C master read-modify-write state + // machine. + // + switch(psInst->ui8State) + { + // + // The read portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_READ: + { + // + // Modify the register data that was just read. + // + psInst->pui8Buffer[1] = ((psInst->pui8Buffer[1] & + psInst->ui8Mask) | psInst->ui8Value); + + // + // Write the data back to the device. + // + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Buffer, + 2, I2CMReadModifyWrite8Callback, psInst); + + // + // Move to the wait for write state. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Done. + // + break; + } + + // + // The write portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_WRITE: + { + // + // Move to the idle state. + // + psInst->ui8State = I2CM_RMW_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == I2CM_RMW_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a read-modify-write of 8 bits of data in an I2C device. +//! +//! \param psInst is a pointer to the read-modify-write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param ui8Mask is the mask indicating the register bits that should be +//! maintained. +//! \param ui8Value is the value indicating the new value for the register bits +//! that are not maintained. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read-modify-write transaction of 8 bits of data +//! in an I2C device. The modify portion of the operation is performed by +//! AND-ing the register value with \e ui8Mask and then OR-ing the result with +//! \e ui8Value. When the read-modify-write is complete, the callback +//! function, if provided, is called in the context of the I2C master interrupt +//! handler. +//! +//! If the mask (in \e ui8Mask) is zero, then none of the bits in the current +//! register value are maintained. In this case, the read portion of the +//! read-modify-write is bypassed, and the new register value (in \e ui8Value) +//! is directly written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMReadModifyWrite8(tI2CMReadModifyWrite8 *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + uint_fast8_t ui8Mask, uint_fast8_t ui8Value, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the read-modify-write structure with the details of this + // request. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8Addr; + psInst->ui8Mask = ui8Mask; + psInst->ui8Value = ui8Value; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Construct the I2C command to access the requested register. + // + psInst->pui8Buffer[0] = ui8Reg; + + // + // See if this is a write or a read-modify-write. + // + if(ui8Mask == 0) + { + // + // Set the state to waiting for the write portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Set the new register value in the command buffer. + // + psInst->pui8Buffer[1] = ui8Value; + + // + // Add the write command to the I2C master queue. + // + if(I2CMWrite(psI2CInst, ui8Addr, psInst->pui8Buffer, 2, + I2CMReadModifyWrite8Callback, psInst) == 0) + { + return(0); + } + } + else + { + // + // Set the state to waiting for the read portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_READ; + + // + // Add the read command to the I2C master queue. + // + if(I2CMRead(psI2CInst, ui8Addr, psInst->pui8Buffer, 1, + psInst->pui8Buffer + 1, 1, I2CMReadModifyWrite8Callback, + psInst) == 0) + { + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a +// read-modify-write operation of 16 bits of little-endian data have completed. +// +//***************************************************************************** +static void +I2CMReadModifyWrite16LECallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMReadModifyWrite16 *psInst; + uint16_t ui16Value; + + // + // Convert the instance data into a pointer to a tI2CMReadModifyWrite16 + // structure. + // + psInst = pvCallbackData; + + // + // If the I2C master driver encountered a failure, force the state machine + // to the idle state (which will also result ina callback to propagate the + // error). + // + if(ui8Status != I2CM_STATUS_SUCCESS) + { + psInst->ui8State = I2CM_RMW_STATE_IDLE; + } + + // + // Determine the current state of the I2C master read-modify-write state + // machine. + // + switch(psInst->ui8State) + { + // + // The read portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_READ: + { + // + // Modify the register data that was just read. + // + ui16Value = (psInst->pui8Buffer[2] << 8) | psInst->pui8Buffer[1]; + ui16Value = (ui16Value & psInst->ui16Mask) | psInst->ui16Value; + psInst->pui8Buffer[1] = ui16Value & 0xff; + psInst->pui8Buffer[2] = (ui16Value >> 8) & 0xff; + + // + // Write the data back to the device. + // + I2CMWrite(psInst->psI2CInst, psInst->ui8Addr, psInst->pui8Buffer, + 3, I2CMReadModifyWrite16LECallback, psInst); + + // + // Move to the wait for write state. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Done. + // + break; + } + + // + // The write portion of the read-modify-write has completed. + // + case I2CM_RMW_STATE_WRITE: + { + // + // Move to the idle state. + // + psInst->ui8State = I2CM_RMW_STATE_IDLE; + + // + // Done. + // + break; + } + } + + // + // See if the state machine is now idle and there is a callback function. + // + if((psInst->ui8State == I2CM_RMW_STATE_IDLE) && psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a read-modify-write of 16 bits of little-endian data in an I2C +//! device. +//! +//! \param psInst is a pointer to the read-modify-write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param ui16Mask is the mask indicating the register bits that should be +//! maintained. +//! \param ui16Value is the value indicating the new value for the register +//! bits that are not maintained. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read-modify-write transaction of 16 bits of +//! little-endian data in an I2C device. The modify portion of the operation +//! is performed by AND-ing the register value with \e ui16Mask and then OR-ing +//! the result with \e ui16Value. When the read-modify-write is complete, the +//! callback function, if provided, is called in the context of the I2C master +//! interrupt handler. +//! +//! If the mask (in \e ui16Mask) is zero, then none of the bits in the current +//! register value are maintained. In this case, the read portion of the +//! read-modify-write is bypassed, and the new register value (in \e ui16Value) +//! is directly written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMReadModifyWrite16LE(tI2CMReadModifyWrite16 *psInst, + tI2CMInstance *psI2CInst, uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, uint_fast16_t ui16Mask, + uint_fast16_t ui16Value, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the read-modify-write structure with the details of this + // request. + // + psInst->psI2CInst = psI2CInst; + psInst->ui8Addr = ui8Addr; + psInst->ui16Mask = ui16Mask; + psInst->ui16Value = ui16Value; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Construct the I2C command to access the requested register. + // + psInst->pui8Buffer[0] = ui8Reg; + + // + // See if this is a write or a read-modify-write. + // + if(ui16Mask == 0) + { + // + // Set the state to waiting for the write portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_WRITE; + + // + // Set the new register value in the command buffer. + // + psInst->pui8Buffer[1] = ui16Value & 0xff; + psInst->pui8Buffer[2] = (ui16Value >> 8) & 0xff; + + // + // Add the write command to the I2C master queue. + // + if(I2CMWrite(psI2CInst, ui8Addr, psInst->pui8Buffer, 3, + I2CMReadModifyWrite16LECallback, psInst) == 0) + { + return(0); + } + } + else + { + // + // Set the state to waiting for the read portion of the + // read-modify-write. + // + psInst->ui8State = I2CM_RMW_STATE_READ; + + // + // Add the read command to the I2C master queue. + // + if(I2CMRead(psI2CInst, ui8Addr, psInst->pui8Buffer, 1, + psInst->pui8Buffer + 1, 2, I2CMReadModifyWrite16LECallback, + psInst) == 0) + { + return(0); + } + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a +// write operation of 8-bit data have completed. +// +//***************************************************************************** +static void +I2CMWrite8Callback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMWrite8 *psInst; + + // + // Convert the instance data into a pointer to a tI2CMWrite8 structure. + // + psInst = pvCallbackData; + + // + // See if the current batch is done and more data is needed. + // + if(ui8Status == I2CM_STATUS_BATCH_DONE) + { + // + // Place the next two bytes into the write buffer. + // + psInst->pui8Buffer[0] = psInst->pui8Data[0]; + if(psInst->ui16Count > 1) + { + psInst->pui8Buffer[1] = psInst->pui8Data[1]; + } + + // + // Advance past the next two bytes of the input buffer. + // + psInst->pui8Data += 2; + psInst->ui16Count -= 2; + + // + // Resume the batched write. + // + I2CMTransferResume(psInst->psI2CInst, psInst->pui8Buffer); + } + + // + // The transfer has completed, or an error has occurred. In both cases, + // see if there is a callback function. + // + else if(psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a write of 8-bit data to an I2C device. +//! +//! \param psInst is a pointer to the 8-bit write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param pui8Data is a pointer to the register data to be written. +//! \param ui16Count is the number of register values to be written. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a write transaction of 8-bit data to an I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMWrite8(tI2CMWrite8 *psInst, tI2CMInstance *psI2CInst, uint_fast8_t ui8Addr, + uint_fast8_t ui8Reg, const uint8_t *pui8Data, + uint_fast16_t ui16Count, tSensorCallback *pfnCallback, + void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the write structure with the details of this request. + // + psInst->psI2CInst = psI2CInst; + psInst->pui8Data = pui8Data + 1; + psInst->ui16Count = ui16Count - 1; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Initiate the I2C write to this device. + // + psInst->pui8Buffer[0] = ui8Reg; + psInst->pui8Buffer[1] = pui8Data[0]; + if(I2CMWriteBatched(psI2CInst, ui8Addr, psInst->pui8Buffer, ui16Count + 1, + 2, I2CMWrite8Callback, psInst) == 0) + { + // + // The I2C write failed, so return a failure. + // + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a read +// operation of 16-bit big-endian data have completed. +// +//***************************************************************************** +static void +I2CMRead16BECallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMRead16BE *psInst; + uint8_t ui8Temp; + + // + // Convert the instance data into a pointer to a tI2CMRead16BE structure. + // + psInst = pvCallbackData; + + // + // See if the transaction completed successfully. + // + if(ui8Status == I2CM_STATUS_SUCCESS) + { + // + // Loop through the 16-bit values read from the I2C device. + // + while(psInst->ui16Count--) + { + // + // Byte swap this value. + // + ui8Temp = psInst->pui8Data[0]; + psInst->pui8Data[0] = psInst->pui8Data[1]; + psInst->pui8Data[1] = ui8Temp; + + // + // Skip to the next value. + // + psInst->pui8Data += 2; + } + } + + // + // See if there is a callback function. + // + if(psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a read of 16-bit big-endian data from an I2C device. +//! +//! \param psInst is a pointer to the 16-bit big-endian read instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param pui16Data is a pointer to the buffer to be filled with the register +//! data. +//! \param ui16Count is the number of 16-bit register values to be read. +//! \param pfnCallback is the function to be called when the read has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a read transaction of 16-bit big-endian data from +//! an I2C device. The data is provided by the device in big-endian format and +//! is byte-swapped as it is read from the I2C device, returning the data in +//! little-endian format. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMRead16BE(tI2CMRead16BE *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the read structure with the details of this request. + // + psInst->psI2CInst = psI2CInst; + psInst->pui8Data = (uint8_t *)pui16Data; + psInst->ui16Count = ui16Count; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Initiate the I2C write to this device. + // + psInst->pui8Data[0] = ui8Reg; + if(I2CMRead(psI2CInst, ui8Addr, psInst->pui8Data, 1, psInst->pui8Data, + ui16Count * 2, I2CMRead16BECallback, psInst) == 0) + { + // + // The I2C write failed, so return a failure. + // + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// The callback function that is called when I2C transactions as part of a +// write operation of 16-bit big-endian data have completed. +// +//***************************************************************************** +static void +I2CMWrite16BECallback(void *pvCallbackData, uint_fast8_t ui8Status) +{ + tI2CMWrite16BE *psInst; + + // + // Convert the instance data into a pointer to a tI2CMWrite16BE structure. + // + psInst = pvCallbackData; + + // + // See if the current batch is done and more data is needed. + // + if(ui8Status == I2CM_STATUS_BATCH_DONE) + { + // + // Place the next two bytes into the write buffer. + // + psInst->pui8Buffer[0] = psInst->pui8Data[0]; + if(psInst->ui16Count > 1) + { + psInst->pui8Buffer[1] = psInst->pui8Data[3]; + } + + // + // Advance past the next two bytes of the input buffer. + // + psInst->pui8Data += 2; + psInst->ui16Count--; + + // + // Resume the batched write. + // + I2CMTransferResume(psInst->psI2CInst, psInst->pui8Buffer); + } + + // + // The transfer has completed, or an error has occurred. In both cases, + // see if there is a callback function. + // + else if(psInst->pfnCallback) + { + // + // Call the application-supplied callback function. + // + psInst->pfnCallback(psInst->pvCallbackData, ui8Status); + } +} + +//***************************************************************************** +// +//! Performs a write of 16-bit big-endian data to an I2C device. +//! +//! \param psInst is a pointer to the 16-bit big-endian write instance data. +//! \param psI2CInst is a pointer to the I2C master instance data. +//! \param ui8Addr is the address of the I2C device to access. +//! \param ui8Reg is the register in the I2C device to access. +//! \param pui16Data is a pointer to the register data to be written. +//! \param ui16Count is the number of 16-bit register values to be written. +//! \param pfnCallback is the function to be called when the write has +//! completed (can be \b NULL if a callback is not required). +//! \param pvCallbackData is a pointer that is passed to the callback function. +//! +//! This function initiates a write transaction of 16-bit big-endian data to an +//! I2C device. The data in the buffer is provided in little-endian format and +//! is byte-swapped as it is being written to the I2C device. +//! +//! \return Returns 1 if the command was successfully added to the queue and 0 +//! if it was not. +// +//***************************************************************************** +uint_fast8_t +I2CMWrite16BE(tI2CMWrite16BE *psInst, tI2CMInstance *psI2CInst, + uint_fast8_t ui8Addr, uint_fast8_t ui8Reg, + const uint16_t *pui16Data, uint_fast16_t ui16Count, + tSensorCallback *pfnCallback, void *pvCallbackData) +{ + // + // Check the arguments. + // + ASSERT(psInst); + ASSERT(psI2CInst); + + // + // Fill in the write structure with the details of this request. + // + psInst->psI2CInst = psI2CInst; + psInst->pui8Data = (const uint8_t *)pui16Data; + psInst->ui16Count = ui16Count; + psInst->pfnCallback = pfnCallback; + psInst->pvCallbackData = pvCallbackData; + + // + // Initiate the I2C write to this device. + // + psInst->pui8Buffer[0] = ui8Reg; + psInst->pui8Buffer[1] = psInst->pui8Data[1]; + if(I2CMWriteBatched(psI2CInst, ui8Addr, psInst->pui8Buffer, + (ui16Count * 2) + 1, 2, I2CMWrite16BECallback, + psInst) == 0) + { + // + // The I2C write failed, so return a failure. + // + return(0); + } + + // + // Success. + // + return(1); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1