| /***************************************************************************//** |
| * @file em_i2c.h |
| * @brief Inter-integrated circuit (I2C) peripheral API |
| * @version 5.6.0 |
| ******************************************************************************* |
| * # License |
| * <b>Copyright 2016 Silicon Laboratories, Inc. www.silabs.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: Silicon Labs has no |
| * obligation to support this Software. Silicon Labs 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. |
| * |
| * Silicon Labs 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 "em_device.h" |
| #if defined(I2C_COUNT) && (I2C_COUNT > 0) |
| |
| #include <stdbool.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /***************************************************************************//** |
| * @addtogroup emlib |
| * @{ |
| ******************************************************************************/ |
| |
| /***************************************************************************//** |
| * @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 |
| * @note |
| * Due to chip characteristics, max value is somewhat reduced. |
| */ |
| #if defined(_SILICON_LABS_32B_SERIES_0) \ |
| && (defined(_EFM32_GECKO_FAMILY) \ |
| || defined(_EFM32_TINY_FAMILY) \ |
| || defined(_EFM32_ZERO_FAMILY) \ |
| || defined(_EFM32_HAPPY_FAMILY)) |
| #define I2C_FREQ_STANDARD_MAX 93000 |
| #elif defined(_SILICON_LABS_32B_SERIES_0) \ |
| && (defined(_EFM32_GIANT_FAMILY) \ |
| || defined(_EFM32_WONDER_FAMILY)) |
| #define I2C_FREQ_STANDARD_MAX 92000 |
| #elif defined(_SILICON_LABS_32B_SERIES_1) |
| // None of the chips on this platform has been characterized on this parameter. |
| // Use same value as on Wonder until further notice. |
| #define I2C_FREQ_STANDARD_MAX 92000 |
| #elif defined(_SILICON_LABS_32B_SERIES_2) |
| #define I2C_FREQ_STANDARD_MAX 100000 |
| #else |
| #error "Unknown device family." |
| #endif |
| |
| /** |
| * @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 392157 |
| |
| /** |
| * @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.12us, 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 987167 |
| |
| /** |
| * @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 initialization 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 configured reference clock will 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 configuration for I2C initialization structure. */ |
| #define I2C_INIT_DEFAULT \ |
| { \ |
| true, /* Enable when initialization done. */ \ |
| true, /* Set to master mode. */ \ |
| 0, /* Use currently configured reference clock. */ \ |
| I2C_FREQ_STANDARD_MAX, /* Set to standard rate assuring being */ \ |
| /* within I2C specification. */ \ |
| 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 |
| * (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 freqRef, |
| uint32_t freqScl, |
| I2C_ClockHLR_TypeDef i2cMode); |
| 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 bitwise 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) |
| { |
| #if defined (I2C_HAS_SET_CLEAR) |
| i2c->IF_CLR = flags; |
| #else |
| i2c->IFC = flags; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @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) |
| { |
| #if defined (I2C_HAS_SET_CLEAR) |
| i2c->IEN_CLR = flags; |
| #else |
| i2c->IEN &= ~(flags); |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Enable one or more I2C interrupts. |
| * |
| * @note |
| * Depending on the use, a pending interrupt may already be set prior to |
| * enabling the interrupt. To ignore a pending interrupt, consider using |
| * I2C_IntClear() prior to enabling the interrupt. |
| * |
| * @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) |
| { |
| #if defined (I2C_HAS_SET_CLEAR) |
| i2c->IEN_SET = flags; |
| #else |
| i2c->IEN |= flags; |
| #endif |
| } |
| |
| /***************************************************************************//** |
| * @brief |
| * Get pending I2C interrupt flags. |
| * |
| * @note |
| * 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 |
| * Get enabled and pending I2C interrupt flags. |
| * Useful for handling more interrupt sources in the same interrupt handler. |
| * |
| * @note |
| * Interrupt flags are not cleared by the use of this function. |
| * |
| * @param[in] i2c |
| * Pointer to I2C peripheral register block. |
| * |
| * @return |
| * Pending and enabled I2C interrupt sources |
| * Return value is the bitwise AND of |
| * - the enabled interrupt sources in I2Cn_IEN and |
| * - the pending interrupt flags I2Cn_IF |
| ******************************************************************************/ |
| __STATIC_INLINE uint32_t I2C_IntGetEnabled(I2C_TypeDef *i2c) |
| { |
| uint32_t ien; |
| |
| ien = i2c->IEN; |
| return i2c->IF & ien; |
| } |
| |
| /***************************************************************************//** |
| * @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) |
| { |
| #if defined (I2C_HAS_SET_CLEAR) |
| i2c->IF_SET = flags; |
| #else |
| i2c->IFS = flags; |
| #endif |
| } |
| |
| 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 emlib) */ |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif /* defined(I2C_COUNT) && (I2C_COUNT > 0) */ |
| #endif /* EM_I2C_H */ |