489 lines
17 KiB
C
489 lines
17 KiB
C
/***************************************************************************//**
|
|
* @file
|
|
* @brief Inter-intergrated circuit (I2C) peripheral API
|
|
* @author Energy Micro AS
|
|
* @version 3.0.0
|
|
*******************************************************************************
|
|
* @section License
|
|
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
|
|
*******************************************************************************
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software.
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
|
|
* obligation to support this Software. Energy Micro AS is providing the
|
|
* Software "AS IS", with no express or implied warranties of any kind,
|
|
* including, but not limited to, any implied warranties of merchantability
|
|
* or fitness for any particular purpose or warranties against infringement
|
|
* of any proprietary rights of a third party.
|
|
*
|
|
* Energy Micro AS will not be liable for any consequential, incidental, or
|
|
* special damages, or any other relief, or for any claim by any third party,
|
|
* arising from your use of this Software.
|
|
*
|
|
******************************************************************************/
|
|
#ifndef __EM_I2C_H
|
|
#define __EM_I2C_H
|
|
|
|
#include <stdbool.h>
|
|
#include "em_part.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup EM_Library
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup I2C
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
******************************* DEFINES ***********************************
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* @brief
|
|
* Standard mode max frequency assuming using 4:4 ratio for Nlow:Nhigh.
|
|
* @details
|
|
* From I2C specification: Min Tlow = 4.7us, min Thigh = 4.0us,
|
|
* max Trise=1.0us, max Tfall=0.3us. Since ratio is 4:4, have to use
|
|
* worst case value of Tlow or Thigh as base.
|
|
*
|
|
* 1/(Tlow + Thigh + 1us + 0.3us) = 1/(4.7 + 4.7 + 1.3)us = 93458Hz
|
|
*/
|
|
#define I2C_FREQ_STANDARD_MAX 93500
|
|
|
|
/**
|
|
* @brief
|
|
* Fast mode max frequency assuming using 6:3 ratio for Nlow:Nhigh.
|
|
* @details
|
|
* From I2C specification: Min Tlow = 1.3us, min Thigh = 0.6us,
|
|
* max Trise=0.3us, max Tfall=0.3us. Since ratio is 6:3, have to use
|
|
* worst case value of Tlow or 2xThigh as base.
|
|
*
|
|
* 1/(Tlow + Thigh + 0.3us + 0.3us) = 1/(1.3 + 0.65 + 0.6)us = 392157Hz
|
|
*/
|
|
#define I2C_FREQ_FAST_MAX 392500
|
|
|
|
|
|
/**
|
|
* @brief
|
|
* Fast mode+ max frequency assuming using 11:6 ratio for Nlow:Nhigh.
|
|
* @details
|
|
* From I2C specification: Min Tlow = 0.5us, min Thigh = 0.26us,
|
|
* max Trise=0.012us, max Tfall=0.12us. Since ratio is 11:6, have to use
|
|
* worst case value of Tlow or (11/6)xThigh as base.
|
|
*
|
|
* 1/(Tlow + Thigh + 0.12us + 0.12us) = 1/(0.5 + 0.273 + 0.24)us = 987167Hz
|
|
*/
|
|
#define I2C_FREQ_FASTPLUS_MAX 987500
|
|
|
|
|
|
/**
|
|
* @brief
|
|
* Indicate plain write sequence: S+ADDR(W)+DATA0+P.
|
|
* @details
|
|
* @li S - Start
|
|
* @li ADDR(W) - address with W/R bit cleared
|
|
* @li DATA0 - Data taken from buffer with index 0
|
|
* @li P - Stop
|
|
*/
|
|
#define I2C_FLAG_WRITE 0x0001
|
|
|
|
/**
|
|
* @brief
|
|
* Indicate plain read sequence: S+ADDR(R)+DATA0+P.
|
|
* @details
|
|
* @li S - Start
|
|
* @li ADDR(R) - address with W/R bit set
|
|
* @li DATA0 - Data read into buffer with index 0
|
|
* @li P - Stop
|
|
*/
|
|
#define I2C_FLAG_READ 0x0002
|
|
|
|
/**
|
|
* @brief
|
|
* Indicate combined write/read sequence: S+ADDR(W)+DATA0+Sr+ADDR(R)+DATA1+P.
|
|
* @details
|
|
* @li S - Start
|
|
* @li Sr - Repeated start
|
|
* @li ADDR(W) - address with W/R bit cleared
|
|
* @li ADDR(R) - address with W/R bit set
|
|
* @li DATAn - Data written from/read into buffer with index n
|
|
* @li P - Stop
|
|
*/
|
|
#define I2C_FLAG_WRITE_READ 0x0004
|
|
|
|
/**
|
|
* @brief
|
|
* Indicate write sequence using two buffers: S+ADDR(W)+DATA0+DATA1+P.
|
|
* @details
|
|
* @li S - Start
|
|
* @li ADDR(W) - address with W/R bit cleared
|
|
* @li DATAn - Data written from buffer with index n
|
|
* @li P - Stop
|
|
*/
|
|
#define I2C_FLAG_WRITE_WRITE 0x0008
|
|
|
|
/** Use 10 bit address. */
|
|
#define I2C_FLAG_10BIT_ADDR 0x0010
|
|
|
|
|
|
/*******************************************************************************
|
|
******************************** ENUMS ************************************
|
|
******************************************************************************/
|
|
|
|
/** Clock low to high ratio settings. */
|
|
typedef enum
|
|
{
|
|
i2cClockHLRStandard = _I2C_CTRL_CLHR_STANDARD, /**< Ratio is 4:4 */
|
|
i2cClockHLRAsymetric = _I2C_CTRL_CLHR_ASYMMETRIC, /**< Ratio is 6:3 */
|
|
i2cClockHLRFast = _I2C_CTRL_CLHR_FAST /**< Ratio is 11:3 */
|
|
} I2C_ClockHLR_TypeDef;
|
|
|
|
|
|
/** Return codes for single master mode transfer function. */
|
|
typedef enum
|
|
{
|
|
/* In progress code (>0) */
|
|
i2cTransferInProgress = 1, /**< Transfer in progress. */
|
|
|
|
/* Complete code (=0) */
|
|
i2cTransferDone = 0, /**< Transfer completed successfully. */
|
|
|
|
/* Transfer error codes (<0) */
|
|
i2cTransferNack = -1, /**< NACK received during transfer. */
|
|
i2cTransferBusErr = -2, /**< Bus error during transfer (misplaced START/STOP). */
|
|
i2cTransferArbLost = -3, /**< Arbitration lost during transfer. */
|
|
i2cTransferUsageFault = -4, /**< Usage fault. */
|
|
i2cTransferSwFault = -5 /**< SW fault. */
|
|
} I2C_TransferReturn_TypeDef;
|
|
|
|
|
|
/*******************************************************************************
|
|
******************************* STRUCTS ***********************************
|
|
******************************************************************************/
|
|
|
|
/** I2C initialization structure. */
|
|
typedef struct
|
|
{
|
|
/** Enable I2C peripheral when init completed. */
|
|
bool enable;
|
|
|
|
/** Set to master (true) or slave (false) mode */
|
|
bool master;
|
|
|
|
/**
|
|
* I2C reference clock assumed when configuring bus frequency setup.
|
|
* Set it to 0 if currently configurated reference clock shall be used
|
|
* This parameter is only applicable if operating in master mode.
|
|
*/
|
|
uint32_t refFreq;
|
|
|
|
/**
|
|
* (Max) I2C bus frequency to use. This parameter is only applicable
|
|
* if operating in master mode.
|
|
*/
|
|
uint32_t freq;
|
|
|
|
/** Clock low/high ratio control. */
|
|
I2C_ClockHLR_TypeDef clhr;
|
|
} I2C_Init_TypeDef;
|
|
|
|
/** Suggested default config for I2C init structure. */
|
|
#define I2C_INIT_DEFAULT \
|
|
{ true, /* Enable when init done */ \
|
|
true, /* Set to master mode */ \
|
|
0, /* Use currently configured reference clock */ \
|
|
I2C_FREQ_STANDARD_MAX, /* Set to standard rate assuring being */ \
|
|
/* within I2C spec */ \
|
|
i2cClockHLRStandard /* Set to use 4:4 low/high duty cycle */ \
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief
|
|
* Master mode transfer message structure used to define a complete
|
|
* I2C transfer sequence (from start to stop).
|
|
* @details
|
|
* The structure allows for defining the following types of sequences,
|
|
* please refer to defines for sequence details.
|
|
* @li #I2C_FLAG_READ - data read into buf[0].data
|
|
* @li #I2C_FLAG_WRITE - data written from buf[0].data
|
|
* @li #I2C_FLAG_WRITE_READ - data written from buf[0].data and read
|
|
* into buf[1].data
|
|
* @li #I2C_FLAG_WRITE_WRITE - data written from buf[0].data and
|
|
* buf[1].data
|
|
*/
|
|
typedef struct
|
|
{
|
|
/**
|
|
* @brief
|
|
* Address to use after (repeated) start.
|
|
* @details
|
|
* Layout details, A = address bit, X = don't care bit (set to 0):
|
|
* @li 7 bit address - use format AAAA AAAX.
|
|
* @li 10 bit address - use format XXXX XAAX AAAA AAAA
|
|
*/
|
|
uint16_t addr;
|
|
|
|
/** Flags defining sequence type and details, see I2C_FLAG_... defines. */
|
|
uint16_t flags;
|
|
|
|
/**
|
|
* Buffers used to hold data to send from or receive into depending
|
|
* on sequence type.
|
|
*/
|
|
struct
|
|
{
|
|
/** Buffer used for data to transmit/receive, must be @p len long. */
|
|
uint8_t *data;
|
|
|
|
/**
|
|
* Number of bytes in @p data to send or receive. Notice that when
|
|
* receiving data to this buffer, at least 1 byte must be received.
|
|
* Setting @p len to 0 in the receive case is considered a usage fault.
|
|
* Transmitting 0 bytes is legal, in which case only the address
|
|
* is transmitted after the start condition.
|
|
*/
|
|
uint16_t len;
|
|
} buf[2];
|
|
} I2C_TransferSeq_TypeDef;
|
|
|
|
|
|
/*******************************************************************************
|
|
***************************** PROTOTYPES **********************************
|
|
******************************************************************************/
|
|
|
|
uint32_t I2C_BusFreqGet(I2C_TypeDef *i2c);
|
|
void I2C_BusFreqSet(I2C_TypeDef *i2c,
|
|
uint32_t refFreq,
|
|
uint32_t freq,
|
|
I2C_ClockHLR_TypeDef type);
|
|
void I2C_Enable(I2C_TypeDef *i2c, bool enable);
|
|
void I2C_Init(I2C_TypeDef *i2c, const I2C_Init_TypeDef *init);
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Clear one or more pending I2C interrupts.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @param[in] flags
|
|
* Pending I2C interrupt source to clear. Use a bitwse logic OR combination of
|
|
* valid interrupt flags for the I2C module (I2C_IF_nnn).
|
|
******************************************************************************/
|
|
__STATIC_INLINE void I2C_IntClear(I2C_TypeDef *i2c, uint32_t flags)
|
|
{
|
|
i2c->IFC = flags;
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Disable one or more I2C interrupts.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @param[in] flags
|
|
* I2C interrupt sources to disable. Use a bitwise logic OR combination of
|
|
* valid interrupt flags for the I2C module (I2C_IF_nnn).
|
|
******************************************************************************/
|
|
__STATIC_INLINE void I2C_IntDisable(I2C_TypeDef *i2c, uint32_t flags)
|
|
{
|
|
i2c->IEN &= ~(flags);
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Enable one or more I2C interrupts.
|
|
*
|
|
* @note
|
|
* Depending on the use, a pending interrupt may already be set prior to
|
|
* enabling the interrupt. Consider using I2C_IntClear() prior to enabling
|
|
* if such a pending interrupt should be ignored.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @param[in] flags
|
|
* I2C interrupt sources to enable. Use a bitwise logic OR combination of
|
|
* valid interrupt flags for the I2C module (I2C_IF_nnn).
|
|
******************************************************************************/
|
|
__STATIC_INLINE void I2C_IntEnable(I2C_TypeDef *i2c, uint32_t flags)
|
|
{
|
|
i2c->IEN |= flags;
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Get pending I2C interrupt flags.
|
|
*
|
|
* @note
|
|
* The event bits are not cleared by the use of this function.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @return
|
|
* I2C interrupt sources pending. A bitwise logic OR combination of valid
|
|
* interrupt flags for the I2C module (I2C_IF_nnn).
|
|
******************************************************************************/
|
|
__STATIC_INLINE uint32_t I2C_IntGet(I2C_TypeDef *i2c)
|
|
{
|
|
return(i2c->IF);
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set one or more pending I2C interrupts from SW.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @param[in] flags
|
|
* I2C interrupt sources to set to pending. Use a bitwise logic OR combination
|
|
* of valid interrupt flags for the I2C module (I2C_IF_nnn).
|
|
******************************************************************************/
|
|
__STATIC_INLINE void I2C_IntSet(I2C_TypeDef *i2c, uint32_t flags)
|
|
{
|
|
i2c->IFS = flags;
|
|
}
|
|
|
|
void I2C_Reset(I2C_TypeDef *i2c);
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Get slave address used for I2C peripheral (when operating in slave mode).
|
|
*
|
|
* @details
|
|
* For 10 bit addressing mode, the address is split in two bytes, and only
|
|
* the first byte setting is fetched, effectively only controlling the 2 most
|
|
* significant bits of the 10 bit address. Full handling of 10 bit addressing
|
|
* in slave mode requires additional SW handling.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @return
|
|
* I2C slave address in use. The 7 most significant bits define the actual
|
|
* address, the least significant bit is reserved and always returned as 0.
|
|
******************************************************************************/
|
|
__STATIC_INLINE uint8_t I2C_SlaveAddressGet(I2C_TypeDef *i2c)
|
|
{
|
|
return((uint8_t)(i2c->SADDR));
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set slave address to use for I2C peripheral (when operating in slave mode).
|
|
*
|
|
* @details
|
|
* For 10 bit addressing mode, the address is split in two bytes, and only
|
|
* the first byte is set, effectively only controlling the 2 most significant
|
|
* bits of the 10 bit address. Full handling of 10 bit addressing in slave
|
|
* mode requires additional SW handling.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @param[in] addr
|
|
* I2C slave address to use. The 7 most significant bits define the actual
|
|
* address, the least significant bit is reserved and always set to 0.
|
|
******************************************************************************/
|
|
__STATIC_INLINE void I2C_SlaveAddressSet(I2C_TypeDef *i2c, uint8_t addr)
|
|
{
|
|
i2c->SADDR = (uint32_t)addr & 0xfe;
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Get slave address mask used for I2C peripheral (when operating in slave
|
|
* mode).
|
|
*
|
|
* @details
|
|
* The address mask defines how the comparator works. A bit position with
|
|
* value 0 means that the corresponding slave address bit is ignored during
|
|
* comparison (don't care). A bit position with value 1 means that the
|
|
* corresponding slave address bit must match.
|
|
*
|
|
* For 10 bit addressing mode, the address is split in two bytes, and only
|
|
* the mask for the first address byte is fetched, effectively only
|
|
* controlling the 2 most significant bits of the 10 bit address.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @return
|
|
* I2C slave address mask in use. The 7 most significant bits define the
|
|
* actual address mask, the least significant bit is reserved and always
|
|
* returned as 0.
|
|
******************************************************************************/
|
|
__STATIC_INLINE uint8_t I2C_SlaveAddressMaskGet(I2C_TypeDef *i2c)
|
|
{
|
|
return((uint8_t)(i2c->SADDRMASK));
|
|
}
|
|
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Set slave address mask used for I2C peripheral (when operating in slave
|
|
* mode).
|
|
*
|
|
* @details
|
|
* The address mask defines how the comparator works. A bit position with
|
|
* value 0 means that the corresponding slave address bit is ignored during
|
|
* comparison (don't care). A bit position with value 1 means that the
|
|
* corresponding slave address bit must match.
|
|
*
|
|
* For 10 bit addressing mode, the address is split in two bytes, and only
|
|
* the mask for the first address byte is set, effectively only controlling
|
|
* the 2 most significant bits of the 10 bit address.
|
|
*
|
|
* @param[in] i2c
|
|
* Pointer to I2C peripheral register block.
|
|
*
|
|
* @param[in] mask
|
|
* I2C slave address mask to use. The 7 most significant bits define the
|
|
* actual address mask, the least significant bit is reserved and should
|
|
* be 0.
|
|
******************************************************************************/
|
|
__STATIC_INLINE void I2C_SlaveAddressMaskSet(I2C_TypeDef *i2c, uint8_t mask)
|
|
{
|
|
i2c->SADDRMASK = (uint32_t)mask & 0xfe;
|
|
}
|
|
|
|
|
|
I2C_TransferReturn_TypeDef I2C_Transfer(I2C_TypeDef *i2c);
|
|
I2C_TransferReturn_TypeDef I2C_TransferInit(I2C_TypeDef *i2c,
|
|
I2C_TransferSeq_TypeDef *seq);
|
|
|
|
/** @} (end addtogroup I2C) */
|
|
/** @} (end addtogroup EM_Library) */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* __EM_I2C_H */
|