/***************************************************************************//** * @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 */