diff --git a/Doxyfile b/Doxyfile old mode 100644 new mode 100755 index ec45186..9128473 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.10 +# Doxyfile 1.8.11 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Arduino Hardware I2C for AVR (in plain c)" +PROJECT_NAME = "Arduino Hardware I2C for AVR (plain c)" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.2 +PROJECT_NUMBER = 0.3 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Arduino Hardware I2C for AVR (in plain c) documentation" +PROJECT_BRIEF = "Arduino Hardware I2C for AVR (plain c) documentation" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -229,7 +229,8 @@ TAB_SIZE = 4 # newlines. ALIASES = "issue=\xrefitem issue \"Issue\" \"Issues List\"" \ - "isr=\xrefitem isr \"Interrupt\" \"ISR List\"" + "isr=\xrefitem isr \"Interrupt\" \"ISR List\"" \ + "attribute=\xrefitem attribute \"GCC Attributes\" \"GCC Attributes List\"" # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" @@ -740,6 +741,12 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -788,8 +795,8 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, -# *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. FILE_PATTERNS = *.c \ *.cpp \ @@ -878,6 +885,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -887,6 +898,10 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = @@ -1004,7 +1019,7 @@ VERBATIM_HEADERS = YES # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. +# generated with the -Duse-libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO @@ -1748,6 +1763,14 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1979,7 +2002,7 @@ ENABLE_PREPROCESSING = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -2020,7 +2043,7 @@ INCLUDE_FILE_PATTERNS = *.h # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = ARDUINO=10506 \ - DBG_SEQTIMER + DOXY=1 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md index 9504de2..b996cd9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,28 @@ # cI2C Arduino Hardware I2C for AVR (plain c) +Hardware I2C library for AVR µcontrollers (lib intended for I2C protocols development in c, for easier ports to other µcontrollers) + +notes: +- cI2C is written in plain c (intentionally) +- cI2C does not use any interrupt (yet, but soon will have to) +- cI2C is designed to act as bus Master (Slave mode will be considered in future releases) +- cI2C is set to work on AVR targets only + -> port to SAM &(|) ESP8266 targets would have to be considered, yet the lib is not multi-cores approach designed + +Usage: +refer to Doxygen generated documentation & example sketches + +examples included: +following examples should work with any I2C EEPROM/FRAM with address 0x50 +(yet function to get Chip ID are device dependant (and will probably only work on FUJITSU devices)) +ci2c_master_write.ino: Write some bytes to FRAM and compare them with what's read afterwards +ci2c_master_read.ino: Read some bytes in FRAM +ci2c_advanced.ino: Redirecting slave write & read functions (to custom functions following typedef) + + +Doxygen doc can be generated for the library using doxyfile + +Feel free to share your thoughts @ xgarmanboziax@gmail.com about: + - issues encountered + - optimisations + - improvements & new functionalities \ No newline at end of file diff --git a/Release Notes.txt b/Release Notes.txt new file mode 100755 index 0000000..6c1769f --- /dev/null +++ b/Release Notes.txt @@ -0,0 +1,23 @@ +Arduino Hardware I2C for AVR (plain c) +2017-2017 SMFSW + +Feel free to share your thoughts @ xgarmanboziax@gmail.com about: + - issues encountered + - optimisations + - improvements & new functionalities + +------------ + +** Actual: +v0.3 22 Jan 2017: +- used function pointer in function parameters for convenience +- fixed read bug for devices without register address +- refactored rw booleans with enum instead (implied logic change) +- I2C_sndAddr function parameters changed +- added I2C_uninit function to release i2c bus +- refactoring & optimisations +- doxygen pass without warnings/errors now +- examples updated to test more of the library + +v0.2 16 Jan 2017: +- First release diff --git a/ci2c.c b/ci2c.c old mode 100644 new mode 100755 index 0ce2275..6459366 --- a/ci2c.c +++ b/ci2c.c @@ -1,12 +1,13 @@ /*!\file ci2c.c ** \author SMFSW -** \version 0.2 +** \version 0.3 ** \copyright MIT SMFSW (2017) ** \brief arduino master i2c in plain c code **/ // TODO: add interrupt vector / callback for it operations (if not too messy) // TODO: consider interrupts at least for RX when slave (and TX when master) + // TODO: change contigous r/w operations so it doesn't send internal address again // TODO: split functions & headers @@ -27,63 +28,73 @@ #define LOST_ARBTRTN 0x38 #define TWI_STATUS (TWSR & 0xF8) -//#define isSetBitReg(v, b) ((v & (1 << b)) != 0) -//#define isClrBitReg(v, b) ((v & (1 << b)) == 0) +//#define isSetRegBit(r, b) ((r & (1 << b)) != 0) +//#define isClrRegBit(r, b) ((r & (1 << b)) == 0) -#define setBitReg(v, b) v |= (1 << b) -#define clrBitReg(v, b) v &= (uint8_t) (~(1 << b)) -#define invBitReg(v, b) v ^= (1 << b) +#define setRegBit(r, b) r |= (1 << b) //!< set bit \b b in register \b r +#define clrRegBit(r, b) r &= (uint8_t) (~(1 << b)) //!< clear bit \b b in register \b r +#define invRegBit(r, b) r ^= (1 << b) //!< invert bit \b b in register \b r +#define binEval(exp) ((exp) ? true : false) //!< boolean evaluation of expression \b exp +#define nbinEval(exp) (!binEval(exp)) //!< complemented boolean evaluation of expression \b exp + +/*! \struct i2c + * \brief static ci2c bus config and control parameters + */ static struct { + /*! \struct cfg + * \brief ci2c bus parameters + */ struct { I2C_SPEED speed; //!< i2c bus speed uint8_t retries; //!< i2c message retries when fail uint16_t timeout; //!< i2c timeout (ms) } cfg; - uint16_t start_wait; - bool busy; + uint16_t start_wait; //!< time start waiting for acknowledge + bool busy; //!< true if already busy (in case of interrupts implementation) } i2c = { {0, DEF_CI2C_NB_RETRIES, DEF_CI2C_TIMEOUT }, 0, false }; // Needed prototypes -static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes); -static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes); +static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes); +static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes); /*! \brief Init an I2C slave structure for cMI2C communication - *! \param [in] slave - pointer to the I2C slave structure to init - *! \param [in] sl_addr - I2C slave address - *! \param [in] reg_sz - internal register map size - *! \return nothing + * \param [in] slave - pointer to the I2C slave structure to init + * \param [in] sl_addr - I2C slave address + * \param [in] reg_sz - internal register map size + * \return nothing */ void I2C_slave_init(I2C_SLAVE * slave, uint8_t sl_addr, I2C_INT_SIZE reg_sz) { (void) I2C_slave_set_addr(slave, sl_addr); (void) I2C_slave_set_reg_size(slave, reg_sz); - I2C_slave_set_rw_func(slave, I2C_rd, 0); - I2C_slave_set_rw_func(slave, I2C_wr, 1); + I2C_slave_set_rw_func(slave, I2C_wr, I2C_WRITE); + I2C_slave_set_rw_func(slave, I2C_rd, I2C_READ); slave->reg_addr = 0; slave->status = I2C_OK; } /*! \brief Redirect slave I2C read/write function (if needed for advanced use) - *! \param [in] slave - pointer to the I2C slave structure to init - *! \param [in] func - pointer to read/write function to affect - *! \param [in] rw - 0 = read function, 1 = write function - *! \return nothing + * \param [in] slave - pointer to the I2C slave structure to init + * \param [in] func - pointer to read/write function to affect + * \param [in] rw - 0 = write function, 1 = read function + * \return nothing */ -void I2C_slave_set_rw_func(I2C_SLAVE * slave, void * func, bool rw) +void I2C_slave_set_rw_func(I2C_SLAVE * slave, ci2c_fct_ptr func, I2C_RW rw) { - ci2c_fct_ptr * pfc = (ci2c_fct_ptr*) (rw ? &slave->cfg.wr : &slave->cfg.rd); + ci2c_fct_ptr * pfc = (ci2c_fct_ptr*) (rw ? &slave->cfg.rd : &slave->cfg.wr); *pfc = func; } /*! \brief Change I2C slave address - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] sl_addr - I2C slave address - *! \return true if new address set (false if address is >7Fh) + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure to init + * \param [in] sl_addr - I2C slave address + * \return true if new address set (false if address is >7Fh) */ -inline bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr) +inline bool __attribute__((__always_inline__)) I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr) { if (sl_addr > 0x7F) { return false; } slave->cfg.addr = sl_addr; @@ -91,160 +102,182 @@ inline bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr) } /*! \brief Change I2C registers map size (for access) - *! \param [in, out] slave - pointer to the I2C slave structure - *! \param [in] reg_sz - internal register map size - *! \return true if new size is correct (false otherwise and set to 16bit by default) + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_sz - internal register map size + * \return true if new size is correct (false otherwise and set to 16bit by default) */ -inline bool I2C_slave_set_reg_size(I2C_SLAVE * slave, I2C_INT_SIZE reg_sz) +inline bool __attribute__((__always_inline__)) I2C_slave_set_reg_size(I2C_SLAVE * slave, I2C_INT_SIZE reg_sz) { slave->cfg.reg_size = reg_sz > I2C_16B_REG ? I2C_16B_REG : reg_sz; - if (reg_sz > I2C_16B_REG) { return false; } - else { return true; } + return !(reg_sz > I2C_16B_REG); } /*! \brief Set I2C current register address - *! \param [in, out] slave - pointer to the I2C slave structure - *! \param [in] reg_addr - register address - *! \return nothing + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address + * \return nothing */ -static inline void I2C_slave_set_reg_addr(I2C_SLAVE * slave, uint16_t reg_addr) +static inline void __attribute__((__always_inline__)) I2C_slave_set_reg_addr(I2C_SLAVE * slave, uint16_t reg_addr) { slave->reg_addr = reg_addr; } /*! \brief Get I2C slave address - *! \param [in] slave - pointer to the I2C slave structure - *! \return I2C slave address + * \attribute inline + * \param [in] slave - pointer to the I2C slave structure + * \return I2C slave address */ -inline uint8_t I2C_slave_get_addr(I2C_SLAVE * slave) +inline uint8_t __attribute__((__always_inline__)) I2C_slave_get_addr(I2C_SLAVE * slave) { return slave->cfg.addr; } /*! \brief Get I2C register map size (for access) - *! \param [in] slave - pointer to the I2C slave structure - *! \return register map using 16bits if true (1Byte otherwise) + * \attribute inline + * \param [in] slave - pointer to the I2C slave structure + * \return register map using 16bits if true (1Byte otherwise) */ -inline bool I2C_slave_get_reg_size(I2C_SLAVE * slave) +inline bool __attribute__((__always_inline__)) I2C_slave_get_reg_size(I2C_SLAVE * slave) { return slave->cfg.reg_size; } /*! \brief Get I2C current register address (addr may passed this way in procedures if contigous accesses) - *! \param [in] slave - pointer to the I2C slave structure - *! \return current register map address + * \attribute inline + * \param [in] slave - pointer to the I2C slave structure + * \return current register map address */ -inline uint16_t I2C_slave_get_reg_addr(I2C_SLAVE * slave) +inline uint16_t __attribute__((__always_inline__)) I2C_slave_get_reg_addr(I2C_SLAVE * slave) { return slave->reg_addr; } /*! \brief Enable I2c module on arduino board (including pull-ups, - *! enabling of ACK, and setting clock frequency) - *! \param [in] speed - I2C bus speed in KHz - *! \return nothing + * enabling of ACK, and setting clock frequency) + * \param [in] speed - I2C bus speed in KHz + * \return nothing */ void I2C_init(uint16_t speed) { - // Set SDA and SCL to ports + // Set SDA and SCL to ports with pull-ups #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) - setBitReg(PORTC, 4); - setBitReg(PORTC, 5); + setRegBit(PORTC, 4); + setRegBit(PORTC, 5); #else - setBitReg(PORTD, 0); - setBitReg(PORTD, 1); + setRegBit(PORTD, 0); + setRegBit(PORTD, 1); #endif (void) I2C_set_speed(speed); } +/*! \brief Disable I2c module on arduino board (releasing pull-ups, and TWI control) + * \return nothing + */ +void I2C_uninit() +{ + // Release SDA and SCL ports pull-ups + #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) + clrRegBit(PORTC, 4); + clrRegBit(PORTC, 5); + #else + clrRegBit(PORTD, 0); + clrRegBit(PORTD, 1); + #endif + + TWCR = 0; +} + + /*! \brief I2C bus reset (Release SCL and SDA lines and re-enable module) - *! \return nothing + * \return nothing */ void I2C_reset(void) { TWCR = 0; - setBitReg(TWCR, TWEA); - setBitReg(TWCR, TWEN); + setRegBit(TWCR, TWEA); + setRegBit(TWCR, TWEN); } /*! \brief Change I2C frequency - *! \param [in] speed - I2C speed in kHz (max 1MHz) - *! \return true if change is successful (false otherwise) + * \param [in] speed - I2C speed in kHz (max 1MHz) + * \return true if change is successful (false otherwise) */ bool I2C_set_speed(uint16_t speed) { - i2c.cfg.speed = (I2C_SPEED) (speed == 0 ? I2C_SLOW : (speed > I2C_FAST ? I2C_SLOW : speed)); + i2c.cfg.speed = (I2C_SPEED) ((speed == 0) ? I2C_SLOW : ((speed > I2C_FAST) ? I2C_SLOW : speed)); - // Ensure i2c module is disabled - clrBitReg(TWCR, TWEN); + clrRegBit(TWCR, TWEN); // Ensure i2c module is disabled // Set prescaler and clock frequency - clrBitReg(TWSR, TWPS0); - clrBitReg(TWSR, TWPS1); - TWBR = ((F_CPU / (i2c.cfg.speed * 1000)) - 16) / 2; // TODO: check speed and make it a param + clrRegBit(TWSR, TWPS0); + clrRegBit(TWSR, TWPS1); + TWBR = ((F_CPU / (i2c.cfg.speed * 1000)) - 16) / 2; - // re-enable module - I2C_reset(); + I2C_reset(); // re-enable module - return i2c.cfg.speed == speed ? true : false; + return (i2c.cfg.speed == speed); } /*! \brief Change I2C ack timeout - *! \param [in] timeout - I2C ack timeout (500 ms max) - *! \return true if change is successful (false otherwise) + * \param [in] timeout - I2C ack timeout (500 ms max) + * \return true if change is successful (false otherwise) */ -inline bool I2C_set_timeout(uint16_t timeout) +bool I2C_set_timeout(uint16_t timeout) { - i2c.cfg.timeout = timeout > 500 ? 500 : timeout; - return i2c.cfg.timeout == timeout ? true : false; + static const uint16_t max_timeout = 500; + i2c.cfg.timeout = (timeout > max_timeout) ? max_timeout : timeout; + return (i2c.cfg.timeout == timeout); } /*! \brief Change I2C message retries (in case of failure) - *! \param [in] retries - I2C number of retries (max of 8) - *! \return true if change is successful (false otherwise) + * \param [in] retries - I2C number of retries (max of 8) + * \return true if change is successful (false otherwise) */ -inline bool I2C_set_retries(uint8_t retries) +bool I2C_set_retries(uint8_t retries) { - i2c.cfg.retries = retries > 8 ? 8 : retries; - return i2c.cfg.retries == retries ? true : false; + static const uint16_t max_retries = 8; + i2c.cfg.retries = (retries > max_retries) ? max_retries : retries; + return (i2c.cfg.retries == retries); } /*! \brief Get I2C busy status - *! \return true if busy + * \attribute inline + * \return true if busy */ -inline bool I2C_is_busy(void) +inline bool __attribute__((__always_inline__)) I2C_is_busy(void) { return i2c.busy; } /*! \brief This function reads or writes the provided data to/from the address specified. - *! If anything in the write process is not successful, then it will be - *! repeated up till 3 more times (default). If still not successful, returns NACK - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \param [in] wr - 0 = read, 1 = write operation - *! \return I2C_STATUS status of write attempt + * If anything in the write process is not successful, then it will be repeated + * up till 3 more times (default). If still not successful, returns NACK + * \param [in, out] slave - pointer to the I2C slave structure to init + * \param [in] reg_addr - register address in register map + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \param [in] rw - 0 = write, 1 = read operation + * \return I2C_STATUS status of write attempt */ -static I2C_STATUS I2C_comm(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes, bool wr) +static I2C_STATUS I2C_comm(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes, I2C_RW rw) { uint8_t retry = i2c.cfg.retries; bool ack = false; - ci2c_fct_ptr fc = (ci2c_fct_ptr) (wr ? slave->cfg.wr : slave->cfg.rd); + ci2c_fct_ptr fc = (ci2c_fct_ptr) (rw ? slave->cfg.rd : slave->cfg.wr); if (I2C_is_busy()) { return slave->status = I2C_BUSY; } i2c.busy = true; - ack = fc(slave, reg_addr, data, nb_bytes); + ack = fc(slave, reg_addr, data, bytes); while ((!ack) && (retry != 0)) // If com not successful, retry some more times { delay(5); - ack = fc(slave, reg_addr, data, nb_bytes); + ack = fc(slave, reg_addr, data, bytes); retry--; } @@ -253,77 +286,79 @@ static I2C_STATUS I2C_comm(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, } /*! \brief This function writes the provided data to the address specified. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \return I2C_STATUS status of write attempt + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \return I2C_STATUS status of write attempt */ -inline I2C_STATUS I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes) +inline I2C_STATUS __attribute__((__always_inline__)) I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) { - return I2C_comm(slave, reg_addr, data, nb_bytes, 1); + return I2C_comm(slave, reg_addr, data, bytes, I2C_WRITE); } /*! \brief This inline is a wrapper to I2C_write in case of contigous operations - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \return I2C_STATUS status of write attempt + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \return I2C_STATUS status of write attempt */ -inline I2C_STATUS I2C_write_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes) +inline I2C_STATUS __attribute__((__always_inline__)) I2C_write_next(I2C_SLAVE * slave, uint8_t * data, uint16_t bytes) { // TODO: implement read next so that it doesn't have to send start register address again - return I2C_write(slave, slave->reg_addr, data, nb_bytes); + return I2C_write(slave, slave->reg_addr, data, bytes); } /*! \brief This function reads data from the address specified and stores this - *! data in the area provided by the pointer. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in, out] data - pointer to the first byte of a block of data to read - *! \param [in] nb_bytes - indicates how many bytes of data to read - *! \return I2C_STATUS status of read attempt + * data in the area provided by the pointer. + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in, out] data - pointer to the first byte of a block of data to read + * \param [in] bytes - indicates how many bytes of data to read + * \return I2C_STATUS status of read attempt */ -inline I2C_STATUS I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes) +inline I2C_STATUS __attribute__((__always_inline__)) I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) { - return I2C_comm(slave, reg_addr, data, nb_bytes, 0); + return I2C_comm(slave, reg_addr, data, bytes, I2C_READ); } /*! \brief This inline is a wrapper to I2C_read in case of contigous operations - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] data - pointer to the first byte of a block of data to read - *! \param [in] nb_bytes - indicates how many bytes of data to read - *! \return I2C_STATUS status of read attempt + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] data - pointer to the first byte of a block of data to read + * \param [in] bytes - indicates how many bytes of data to read + * \return I2C_STATUS status of read attempt */ -inline I2C_STATUS I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes) +inline I2C_STATUS __attribute__((__always_inline__)) I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16_t bytes) { // TODO: implement read next so that it doesn't have to send start register address again - return I2C_read(slave, slave->reg_addr, data, nb_bytes); + return I2C_read(slave, slave->reg_addr, data, bytes); } /*! \brief Start i2c_timeout timer - *! \return nothing + * \attribute inline + * \return nothing */ -static inline void I2C_start_timeout(void) +static inline void __attribute__((__always_inline__)) I2C_start_timeout(void) { i2c.start_wait = (uint16_t) millis(); } /*! \brief Test i2c_timeout - *! \return true if i2c_timeout occured (false otherwise) + * \attribute inline + * \return true if i2c_timeout occured (false otherwise) */ -static inline uint8_t I2C_timeout(void) +static inline uint8_t __attribute__((__always_inline__)) I2C_timeout(void) { return (((uint16_t) millis() - i2c.start_wait) >= i2c.cfg.timeout); } /*! \brief Send start condition - *! \return true if start condition acknowledged (false otherwise) + * \return true if start condition acknowledged (false otherwise) */ bool I2C_start(void) { @@ -341,7 +376,7 @@ bool I2C_start(void) } /*! \brief Send stop condition - *! \return true if stop condition acknowledged (false otherwise) + * \return true if stop condition acknowledged (false otherwise) */ bool I2C_stop(void) { @@ -355,32 +390,9 @@ bool I2C_stop(void) return true; } -/*! \brief Send I2C address - *! \param [in] sl_addr - I2C slave address - *! \return true if I2C slave address sent acknowledged (false otherwise) - */ -bool I2C_sndAddr(uint8_t sl_addr) -{ - TWDR = sl_addr; - - I2C_start_timeout(); - - TWCR = (1 << TWINT) | (1 << TWEN); - - while (!(TWCR & (1 << TWINT))) - { if (I2C_timeout()) { I2C_reset(); return false; } } - - if ((TWI_STATUS == MT_SLA_ACK) || (TWI_STATUS == MR_SLA_ACK)) { return true; } - - if ((TWI_STATUS == MT_SLA_NACK) || (TWI_STATUS == MR_SLA_NACK)) { I2C_stop(); } - else { I2C_reset(); } - - return false; -} - /*! \brief Send byte on bus - *! \param [in] dat - data to be sent - *! \return true if data sent acknowledged (false otherwise) + * \param [in] dat - data to be sent + * \return true if data sent acknowledged (false otherwise) */ bool I2C_snd8(uint8_t dat) { @@ -402,8 +414,8 @@ bool I2C_snd8(uint8_t dat) } /*! \brief Receive byte from bus - *! \param [in] ack - true if wait for ack - *! \return true if data reception acknowledged (false otherwise) + * \param [in] ack - true if wait for ack + * \return true if data reception acknowledged (false otherwise) */ uint8_t I2C_rcv8(bool ack) { @@ -417,26 +429,47 @@ uint8_t I2C_rcv8(bool ack) if (TWI_STATUS == LOST_ARBTRTN) { I2C_reset(); return false; } - if (((TWI_STATUS == MR_DATA_NACK) && (!ack)) || ((TWI_STATUS == MR_DATA_ACK) && (ack))) { return true; } - else { return false; } + return ((((TWI_STATUS == MR_DATA_NACK) && (!ack)) || ((TWI_STATUS == MR_DATA_ACK) && (ack))) ? true : false); +} + +/*! \brief Send I2C address + * \param [in] slave - pointer to the I2C slave structure + * \param [in] rw - read/write transaction + * \return true if I2C chip address sent acknowledged (false otherwise) + */ +bool I2C_sndAddr(I2C_SLAVE * slave, I2C_RW rw) +{ + TWDR = (slave->cfg.addr << 1) | rw; + + I2C_start_timeout(); + + TWCR = (1 << TWINT) | (1 << TWEN); + + while (!(TWCR & (1 << TWINT))) + { if (I2C_timeout()) { I2C_reset(); return false; } } + + if ((TWI_STATUS == MT_SLA_ACK) || (TWI_STATUS == MR_SLA_ACK)) { return true; } + + if ((TWI_STATUS == MT_SLA_NACK) || (TWI_STATUS == MR_SLA_NACK)) { I2C_stop(); } + else { I2C_reset(); } + + return false; } /*! \brief This procedure calls appropriate functions to perform a proper send transaction on I2C bus. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \return Boolean indicating success/fail of write attempt + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \return Boolean indicating success/fail of write attempt */ -static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes) +static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) { - uint16_t ct_w; (void) I2C_slave_set_reg_addr(slave, reg_addr); if (I2C_start() == false) { return false; } - if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; } + if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; } if (slave->cfg.reg_size) { if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used @@ -446,7 +479,7 @@ static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_ if (I2C_snd8((uint8_t) reg_addr) == false) { return false; } } - for (ct_w = 0; ct_w < nb_bytes; ct_w++) + for (uint16_t cnt = 0; cnt < bytes; cnt++) { if (I2C_snd8(*(data++)) == false) { return false; } slave->reg_addr++; @@ -459,43 +492,34 @@ static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_ /*! \brief This procedure calls appropriate functions to perform a proper receive transaction on I2C bus. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in, out] data - pointer to the first byte of a block of data to read - *! \param [in] nb_bytes - indicates how many bytes of data to read - *! \return Boolean indicating success/fail of read attempt + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in, out] data - pointer to the first byte of a block of data to read + * \param [in] bytes - indicates how many bytes of data to read + * \return Boolean indicating success/fail of read attempt */ -static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes) +static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) { - uint16_t ct_r; (void) I2C_slave_set_reg_addr(slave, reg_addr); - if (nb_bytes == 0) { nb_bytes++; } + if (bytes == 0) { bytes = 1; } - if (I2C_start() == false) { return false; } - if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; } - if (slave->cfg.reg_size) + if (slave->cfg.reg_size) // If start register has to be sent first { + if (I2C_start() == false) { return false; } + if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; } if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used { if (I2C_snd8((uint8_t) (reg_addr >> 8)) == false) { return false; } } if (I2C_snd8((uint8_t) reg_addr) == false) { return false; } - if (I2C_start() == false) { return false; } - if (I2C_sndAddr((slave->cfg.addr << 1) | 0x01) == false) { return false; } } + if (I2C_start() == false) { return false; } + if (I2C_sndAddr(slave, I2C_READ) == false) { return false; } - for (ct_r = 0; ct_r < nb_bytes; ct_r++) + for (uint16_t cnt = 0; cnt < bytes; cnt++) { - if (ct_r == (nb_bytes - 1)) - { - if (I2C_rcv8(false) == false) { return false; } - } - else - { - if (I2C_rcv8(true) == false) { return false; } - } + if (I2C_rcv8((cnt == (bytes - 1)) ? false : true) == false) { return false; } *data++ = TWDR; slave->reg_addr++; } diff --git a/ci2c.h b/ci2c.h old mode 100644 new mode 100755 index 8d710fe..c4e2af3 --- a/ci2c.h +++ b/ci2c.h @@ -1,14 +1,19 @@ /*!\file ci2c.h ** \author SMFSW -** \version 0.2 +** \version 0.3 ** \copyright MIT SMFSW (2017) ** \brief arduino i2c in plain c declarations **/ /****************************************************************/ #ifndef __CI2C_H__ - #define __CI2C_H__ "v0.2" + #define __CI2C_H__ "v0.3" /****************************************************************/ +#if defined(DOXY) + // Define gcc __attribute__ as void when Doxygen runs + #define __attribute__(a) //!< GCC attribute (ignored by Doxygen) +#endif + #if (ARDUINO >= 100) #include #else @@ -23,16 +28,24 @@ extern "C"{ #endif -#define DEF_CI2C_SPEED 100000 -#define DEF_CI2C_NB_RETRIES 3 -#define DEF_CI2C_TIMEOUT 100 +#define DEF_CI2C_NB_RETRIES 3 //!< Default cI2C transaction retries +#define DEF_CI2C_TIMEOUT 100 //!< Default cI2C timeout -typedef enum __attribute__((__packed__)) enI2C_STATUS { - I2C_OK = 0x00, - I2C_BUSY, - I2C_NACK -} I2C_STATUS; +/*! \enum enI2C_RW + * \brief I2C RW bit enumeration + * \attribute packed enum + */ +typedef enum __attribute__((__packed__)) enI2C_RW { + I2C_WRITE = 0, //!< I2C rw bit (write) + I2C_READ //!< I2C rw bit (read) +} I2C_RW; + + +/*! \enum enI2C_SPEED + * \brief I2C bus speed + * \attribute packed enum + */ typedef enum __attribute__((__packed__)) enI2C_SPEED { I2C_SLOW = 100, //!< I2C Slow speed (100KHz) I2C_LOW = 400, //!< I2C Low speed (400KHz) @@ -40,16 +53,37 @@ typedef enum __attribute__((__packed__)) enI2C_SPEED { } I2C_SPEED; +/*! \enum enI2C_STATUS + * \brief I2C slave status + * \attribute packed enum + */ +typedef enum __attribute__((__packed__)) enI2C_STATUS { + I2C_OK = 0x00, //!< I2C OK + I2C_BUSY, //!< I2C Bus busy + I2C_NACK //!< I2C Not Acknowledge +} I2C_STATUS; + +/*! \enum enI2C_INT_SIZE + * \brief I2C slave internal address registers size + * \attribute packed enum + */ typedef enum __attribute__((__packed__)) enI2C_INT_SIZE { - I2C_NO_REG = 0x00, //!< Internal adress registers not applicable for slave - I2C_8B_REG, //!< Slave internal adress registers space is 8bits wide - I2C_16B_REG //!< Slave internal adress registers space is 16bits wide + I2C_NO_REG = 0x00, //!< Internal address registers not applicable for slave + I2C_8B_REG, //!< Slave internal address registers space is 8bits wide + I2C_16B_REG //!< Slave internal address registers space is 16bits wide } I2C_INT_SIZE; -typedef bool (*ci2c_fct_ptr) (const void*, uint16_t, uint8_t*, uint16_t); //!< i2c read/write function typedef +typedef bool (*ci2c_fct_ptr) (const void*, uint16_t, uint8_t*, uint16_t); //!< i2c read/write function pointer typedef +/*! \struct StructI2CSlave + * \brief ci2c slave config and control parameters + * \attribute packed struct + */ typedef struct __attribute__((__packed__)) StructI2CSlave { + /*! \struct cfg + * \brief ci2c slave parameters + */ struct { uint8_t addr; //!< Slave address I2C_INT_SIZE reg_size; //!< Slave internal registers size @@ -66,52 +100,57 @@ typedef struct __attribute__((__packed__)) StructI2CSlave { /***************************/ /*! \brief Init an I2C slave structure for cMI2C communication - *! \param [in] slave - pointer to the I2C slave structure to init - *! \param [in] sl_addr - I2C slave address - *! \param [in] reg_sz - internal register map size - *! \return nothing + * \param [in] slave - pointer to the I2C slave structure to init + * \param [in] sl_addr - I2C slave address + * \param [in] reg_sz - internal register map size + * \return nothing */ extern void I2C_slave_init(I2C_SLAVE * slave, uint8_t sl_addr, I2C_INT_SIZE reg_sz); /*! \brief Redirect slave I2C read/write function (if needed for advanced use) - *! \param [in] slave - pointer to the I2C slave structure to init - *! \param [in] func - pointer to read/write function to affect - *! \param [in] rw - 0 = read function, 1 = write function - *! \return nothing + * \param [in] slave - pointer to the I2C slave structure to init + * \param [in] func - pointer to read/write function to affect + * \param [in] rw - 0 = write function, 1 = read function + * \return nothing */ -extern void I2C_slave_set_rw_func(I2C_SLAVE * slave, void * func, bool rw); +extern void I2C_slave_set_rw_func(I2C_SLAVE * slave, ci2c_fct_ptr func, I2C_RW rw); /*! \brief Change I2C slave address - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] sl_addr - I2C slave address - *! \return true if new address set (false if address is >7Fh) + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure to init + * \param [in] sl_addr - I2C slave address + * \return true if new address set (false if address is >7Fh) */ -extern inline bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr) __attribute__((__always_inline__)); +extern inline bool __attribute__((__always_inline__)) I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr); /*! \brief Change I2C registers map size (for access) - *! \param [in, out] slave - pointer to the I2C slave structure - *! \param [in] reg_sz - internal register map size - *! \return true if new size is correct (false otherwise and set to 16bit by default) + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_sz - internal register map size + * \return true if new size is correct (false otherwise and set to 16bit by default) */ -extern inline bool I2C_slave_set_reg_size(I2C_SLAVE * slave, I2C_INT_SIZE reg_sz) __attribute__((__always_inline__)); +extern inline bool __attribute__((__always_inline__)) I2C_slave_set_reg_size(I2C_SLAVE * slave, I2C_INT_SIZE reg_sz); /*! \brief Get I2C slave address - *! \param [in] slave - pointer to the I2C slave structure - *! \return I2C slave address + * \attribute inline + * \param [in] slave - pointer to the I2C slave structure + * \return I2C slave address */ -extern inline uint8_t I2C_slave_get_addr(I2C_SLAVE * slave) __attribute__((__always_inline__)); +extern inline uint8_t __attribute__((__always_inline__)) I2C_slave_get_addr(I2C_SLAVE * slave); /*! \brief Get I2C register map size (for access) - *! \param [in] slave - pointer to the I2C slave structure - *! \return register map using 16bits if true (1Byte otherwise) + * \attribute inline + * \param [in] slave - pointer to the I2C slave structure + * \return register map using 16bits if true (1Byte otherwise) */ -extern inline bool I2C_slave_get_reg_size(I2C_SLAVE * slave) __attribute__((__always_inline__)); +extern inline bool __attribute__((__always_inline__)) I2C_slave_get_reg_size(I2C_SLAVE * slave); /*! \brief Get I2C current register address (addr may passed this way in procedures if contigous accesses) - *! \param [in] slave - pointer to the I2C slave structure - *! \return current register map address + * \attribute inline + * \param [in] slave - pointer to the I2C slave structure + * \return current register map address */ -extern inline uint16_t I2C_slave_get_reg_addr(I2C_SLAVE * slave) __attribute__((__always_inline__)); +extern inline uint16_t __attribute__((__always_inline__)) I2C_slave_get_reg_addr(I2C_SLAVE * slave); /*************************/ @@ -119,74 +158,80 @@ extern inline uint16_t I2C_slave_get_reg_addr(I2C_SLAVE * slave) __attribute__(( /*************************/ /*! \brief Enable I2c module on arduino board (including pull-ups, - *! enabling of ACK, and setting clock frequency) - *! \param [in] speed - I2C bus speed in KHz - *! \return nothing + * enabling of ACK, and setting clock frequency) + * \param [in] speed - I2C bus speed in KHz + * \return nothing */ extern void I2C_init(uint16_t speed); +/*! \brief Disable I2c module on arduino board (releasing pull-ups, and TWI control) + * \return nothing + */ +extern void I2C_uninit(); + /*! \brief Change I2C frequency - *! \param [in] speed - I2C bus speed in KHz - *! \return true if change is successful (false otherwise) + * \param [in] speed - I2C bus speed in KHz + * \return true if change is successful (false otherwise) */ extern bool I2C_set_speed(uint16_t speed); /*! \brief Change I2C ack timeout - *! \param [in] timeout - I2C ack timeout (500 ms max) - *! \return true if change is successful (false otherwise) + * \param [in] timeout - I2C ack timeout (500 ms max) + * \return true if change is successful (false otherwise) */ -extern inline bool I2C_set_timeout(uint16_t timeout); +extern bool I2C_set_timeout(uint16_t timeout); /*! \brief Change I2C message retries (in case of failure) - *! \param [in] retries - I2C number of retries (max of 8) - *! \return true if change is successful (false otherwise) + * \param [in] retries - I2C number of retries (max of 8) + * \return true if change is successful (false otherwise) */ -extern inline bool I2C_set_retries(uint8_t retries); +extern bool I2C_set_retries(uint8_t retries); /*! \brief Get I2C busy status - *! \return true if busy + * \attribute inline + * \return true if busy */ -extern inline bool I2C_is_busy(void); +extern inline bool __attribute__((__always_inline__)) I2C_is_busy(void); /*! \brief This function writes the provided data to the address specified. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \return I2C_STATUS status of write attempt + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \return I2C_STATUS status of write attempt */ -extern inline I2C_STATUS I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes); +extern inline I2C_STATUS __attribute__((__always_inline__)) I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes); /*! \brief This inline is a wrapper to I2C_write in case of contigous operations - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \return I2C_STATUS status of write attempt + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \return I2C_STATUS status of write attempt */ -extern inline I2C_STATUS I2C_write_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes); +extern inline I2C_STATUS __attribute__((__always_inline__)) I2C_write_next(I2C_SLAVE * slave, uint8_t * data, uint16_t bytes); /*! \brief This function reads data from the address specified and stores this - *! data in the area provided by the pointer. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in, out] data - pointer to the first byte of a block of data to read - *! \param [in] nb_bytes - indicates how many bytes of data to read - *! \return I2C_STATUS status of read attempt + * data in the area provided by the pointer. + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in, out] data - pointer to the first byte of a block of data to read + * \param [in] bytes - indicates how many bytes of data to read + * \return I2C_STATUS status of read attempt */ -extern inline I2C_STATUS I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes); +extern inline I2C_STATUS __attribute__((__always_inline__)) I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes); /*! \brief This inline is a wrapper to I2C_read in case of contigous operations - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] data - pointer to the first byte of a block of data to read - *! \param [in] nb_bytes - indicates how many bytes of data to read - *! \return I2C_STATUS status of read attempt + * \attribute inline + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] data - pointer to the first byte of a block of data to read + * \param [in] bytes - indicates how many bytes of data to read + * \return I2C_STATUS status of read attempt */ -extern inline I2C_STATUS I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes); +extern inline I2C_STATUS __attribute__((__always_inline__)) I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16_t bytes); /***********************************/ @@ -194,32 +239,33 @@ extern inline I2C_STATUS I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16 /*** THAT MAY BE USEFUL FOR DVPT ***/ /***********************************/ /*! \brief I2C bus reset (Release SCL and SDA lines and re-enable module) - *! \return nothing + * \return nothing */ extern void I2C_reset(void); /*! \brief Send start condition - *! \return true if start condition acknowledged (false otherwise) + * \return true if start condition acknowledged (false otherwise) */ extern bool I2C_start(void); /*! \brief Send stop condition - *! \return true if stop condition acknowledged (false otherwise) + * \return true if stop condition acknowledged (false otherwise) */ extern bool I2C_stop(void); -/*! \brief Send I2C address - *! \param [in] sl_addr - I2C slave address - *! \return true if I2C chip address sent acknowledged (false otherwise) - */ -extern bool I2C_sndAddr(uint8_t sl_addr); /*! \brief Send byte on bus - *! \param [in] dat - data to be sent - *! \return true if data sent acknowledged (false otherwise) + * \param [in] dat - data to be sent + * \return true if data sent acknowledged (false otherwise) */ extern bool I2C_snd8(uint8_t dat); /*! \brief Receive byte from bus - *! \param [in] ack - true if wait for ack - *! \return true if data reception acknowledged (false otherwise) + * \param [in] ack - true if wait for ack + * \return true if data reception acknowledged (false otherwise) */ extern uint8_t I2C_rcv8(bool ack); +/*! \brief Send I2C address + * \param [in] slave - pointer to the I2C slave structure + * \param [in] rw - read/write transaction + * \return true if I2C chip address sent acknowledged (false otherwise) + */ +extern bool I2C_sndAddr(I2C_SLAVE * slave, I2C_RW rw); #ifdef __cplusplus diff --git a/examples/ci2c_advanced/ci2c_advanced.ino b/examples/ci2c_advanced/ci2c_advanced.ino old mode 100644 new mode 100755 index cffdae1..c7be260 --- a/examples/ci2c_advanced/ci2c_advanced.ino +++ b/examples/ci2c_advanced/ci2c_advanced.ino @@ -1,38 +1,53 @@ /* Master i2c (advanced) - Redirecting write & read slave functions in setup (to custom functions following template) + Redirecting slave write & read functions in setup (to custom functions following typedef) Read and Write operations are then called using the same functions + Function to get Chip ID are device dependant (and will probably only work on FUJITSU devices) This example code is in the public domain. created Jan 12 2017 - latest mod Jan 16 2017 + latest mod Jan 22 2017 by SMFSW */ #include -I2C_SLAVE FRAM; +const uint8_t blank = 0xEE; // blank tab filling value for test + +I2C_SLAVE FRAM; // slave declaration void setup() { + uint8_t str[3]; + memset(&str, blank, sizeof(str)); + Serial.begin(115200); // start serial for output I2C_init(I2C_LOW); // init with low speed (400KHz) I2C_slave_init(&FRAM, 0x50, I2C_16B_REG); - I2C_slave_set_rw_func(&FRAM, I2C_rd_advanced, 0); - I2C_slave_set_rw_func(&FRAM, I2C_wr_advanced, 1); + I2C_slave_set_rw_func(&FRAM, I2C_wr_advanced, I2C_WRITE); + I2C_slave_set_rw_func(&FRAM, I2C_rd_advanced, I2C_READ); + + I2C_get_chip_id(&FRAM, &str[0]); + + Serial.println(); + //for (uint8_t i = 0; i < sizeof(str); i++) { Serial.print(str[i], HEX); } // print hex values + Serial.print("\nManufacturer ID: "); + Serial.print((str[0] << 4) + (str[1] >> 4), HEX); + Serial.print("\nProduct ID: "); + Serial.print(((str[1] & 0x0F) << 8) + str[2], HEX); } void loop() { const uint16_t reg_addr = 0; uint8_t str[7]; - memset(&str, 0xEE, sizeof(str)); - - I2C_read(&FRAM, reg_addr, &str[0], sizeof(str)); // Addr 0, 2bytes Addr size, str, read chars for size of str + memset(&str, blank, sizeof(str)); + + I2C_read(&FRAM, reg_addr, &str[0], sizeof(str)); // FRAM, Addr 0, str, read chars for size of str Serial.println(); for (uint8_t i = 0; i < sizeof(str); i++) { - Serial.print(str[i], HEX); // receive a byte as character + Serial.print(str[i], HEX); // print hex values Serial.print(" "); } @@ -41,21 +56,18 @@ void loop() { /*! \brief This procedure calls appropriate functions to perform a proper send transaction on I2C bus. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in] data - pointer to the first byte of a block of data to write - *! \param [in] nb_bytes - indicates how many bytes of data to write - *! \return Boolean indicating success/fail of write attempt + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in] data - pointer to the first byte of a block of data to write + * \param [in] bytes - indicates how many bytes of data to write + * \return Boolean indicating success/fail of write attempt */ -static bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes) +bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) { - uint16_t ct_w; - slave->reg_addr = reg_addr; if (I2C_start() == false) { return false; } - if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; } + if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; } if (slave->cfg.reg_size) { if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used @@ -65,7 +77,7 @@ static bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data if (I2C_snd8((uint8_t) reg_addr) == false) { return false; } } - for (ct_w = 0; ct_w < nb_bytes; ct_w++) + for (uint16_t cnt = 0; cnt < bytes; cnt++) { if (I2C_snd8(*(data++)) == false) { return false; } slave->reg_addr++; @@ -78,44 +90,34 @@ static bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data /*! \brief This procedure calls appropriate functions to perform a proper receive transaction on I2C bus. - *! - *! \param [in, out] slave - pointer to the I2C slave structure to init - *! \param [in] reg_addr - register address in register map - *! \param [in, out] data - pointer to the first byte of a block of data to read - *! \param [in] nb_bytes - indicates how many bytes of data to read - *! \return Boolean indicating success/fail of read attempt + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in] reg_addr - register address in register map + * \param [in, out] data - pointer to the first byte of a block of data to read + * \param [in] bytes - indicates how many bytes of data to read + * \return Boolean indicating success/fail of read attempt */ -static bool I2C_rd_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes) +bool I2C_rd_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) { - uint16_t ct_r; - slave->reg_addr = reg_addr; - if (nb_bytes == 0) { nb_bytes++; } + if (bytes == 0) { bytes = 1; } - if (I2C_start() == false) { return false; } - if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; } if (slave->cfg.reg_size) { + if (I2C_start() == false) { return false; } + if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; } if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used { if (I2C_snd8((uint8_t) (reg_addr >> 8)) == false) { return false; } } if (I2C_snd8((uint8_t) reg_addr) == false) { return false; } - if (I2C_start() == false) { return false; } - if (I2C_sndAddr((slave->cfg.addr << 1) | 0x01) == false) { return false; } } + if (I2C_start() == false) { return false; } + if (I2C_sndAddr(slave, I2C_READ) == false) { return false; } - for (ct_r = 0; ct_r < nb_bytes; ct_r++) + for (uint16_t cnt = 0; cnt < bytes; cnt++) { - if (ct_r == (nb_bytes - 1)) - { - if (I2C_rcv8(false) == false) { return false; } - } - else - { - if (I2C_rcv8(true) == false) { return false; } - } + if (I2C_rcv8((cnt == (bytes - 1)) ? false : true) == false) { return false; } *data++ = TWDR; slave->reg_addr++; } @@ -125,3 +127,32 @@ static bool I2C_rd_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data return true; } + +/*! \brief This procedure calls appropriate functions to get chip ID of FUJITSU devices. + * \param [in, out] slave - pointer to the I2C slave structure + * \param [in, out] data - pointer to the first byte of a block of data to read + * \return Boolean indicating success/fail of read attempt + */ +bool I2C_get_chip_id(I2C_SLAVE * slave, uint8_t * data) +{ + const uint16_t bytes = 3; + I2C_SLAVE FRAM_ID; + + I2C_slave_init(&FRAM_ID, 0xF8 >> 1, I2C_16B_REG); // Dummy slave init for I2C_sndAddr + + if (I2C_start() == false) { return false; } + if (I2C_sndAddr(&FRAM_ID, I2C_WRITE) == false) { return false; } + if (I2C_snd8(slave->cfg.addr << 1) == false) { return false; } + if (I2C_start() == false) { return false; } + if (I2C_sndAddr(&FRAM_ID, I2C_READ) == false) { return false; } + + for (uint16_t cnt = 0; cnt < bytes; cnt++) + { + if (I2C_rcv8((cnt == (bytes - 1)) ? false : true) == false) { return false; } + *data++ = TWDR; + } + + if (I2C_stop() == false) { return false; } + + return true; +} diff --git a/examples/ci2c_master_read/ci2c_master_read.ino b/examples/ci2c_master_read/ci2c_master_read.ino old mode 100644 new mode 100755 index a5cd1e4..5ba9fe3 --- a/examples/ci2c_master_read/ci2c_master_read.ino +++ b/examples/ci2c_master_read/ci2c_master_read.ino @@ -5,13 +5,15 @@ This example code is in the public domain. created Jan 12 2017 - latest mod Jan 16 2017 + latest mod Jan 22 2017 by SMFSW */ #include -I2C_SLAVE FRAM; +const uint8_t blank = 0xEE; // blank tab filling value for test + +I2C_SLAVE FRAM; // slave declaration void setup() { Serial.begin(115200); // start serial for output @@ -21,15 +23,17 @@ void setup() { void loop() { const uint16_t reg_addr = 0; - uint8_t str[7]; - memset(&str, 0xEE, sizeof(str)); + uint8_t str[8]; + const uint8_t half_sz = sizeof(str) / 2; + memset(&str, blank, sizeof(str)); - I2C_read(&FRAM, reg_addr, &str[0], sizeof(str)); // Addr 0, 2bytes Addr size, str, read chars for size of str + I2C_read(&FRAM, reg_addr, &str[0], half_sz); // FRAM, Addr 0, str, read chars for size of half str + I2C_read_next(&FRAM, &str[half_sz], half_sz); Serial.println(); for (uint8_t i = 0; i < sizeof(str); i++) { - Serial.print(str[i], HEX); // receive a byte as character + Serial.print(str[i], HEX); // print hex values Serial.print(" "); } diff --git a/examples/ci2c_master_write/ci2c_master_write.ino b/examples/ci2c_master_write/ci2c_master_write.ino old mode 100644 new mode 100755 index 33391b7..9f4cdc4 --- a/examples/ci2c_master_write/ci2c_master_write.ino +++ b/examples/ci2c_master_write/ci2c_master_write.ino @@ -6,13 +6,15 @@ This example code is in the public domain. created Jan 12 2017 - latest mod Jan 16 2017 + latest mod Jan 22 2017 by SMFSW */ #include -I2C_SLAVE FRAM; +const uint8_t blank = 0xEE; // blank tab filling value for test + +I2C_SLAVE FRAM; // slave declaration void setup() { Serial.begin(115200); // start serial for output @@ -25,9 +27,9 @@ void loop() { uint8_t str[7] = { 0x55, 20, 30, 40, 50, 60, 0xAA }; bool match = true; uint8_t str_out[7]; - memset(&str_out, 0xEE, sizeof(str)); + memset(&str_out, blank, sizeof(str)); - I2C_write(&FRAM, reg_addr, &str[0], sizeof(str)); // Addr 0, 2bytes Addr size, str, read chars for size of str + I2C_write(&FRAM, reg_addr, &str[0], sizeof(str)); // FRAM, Addr 0, str, read chars for size of str Serial.println(); @@ -35,7 +37,7 @@ void loop() { for (uint8_t i = 0; i < sizeof(str_out); i++) { if (str[i] != str_out[i]) { match = false; } - Serial.print(str_out[i], HEX); // receive a byte as character + Serial.print(str_out[i], HEX); // print hex values Serial.print(" "); } Serial.print("WRITE "); diff --git a/keywords.txt b/keywords.txt old mode 100644 new mode 100755 index e1498d4..199cd73 --- a/keywords.txt +++ b/keywords.txt @@ -22,6 +22,7 @@ I2C_slave_get_reg_size KEYWORD2 I2C_slave_get_reg_addr KEYWORD2 I2C_init KEYWORD2 +I2C_uninit KEYWORD2 I2C_set_timeout KEYWORD2 I2C_set_retries KEYWORD2 I2C_set_speed KEYWORD2 @@ -38,6 +39,8 @@ I2C_read_next KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### +I2C_READ LITERAL1 +I2C_WRITE LITERAL1 I2C_OK LITERAL1 I2C_BUSY LITERAL1 I2C_NACK LITERAL1 @@ -47,3 +50,7 @@ I2C_FAST LITERAL1 I2C_NO_REG LITERAL1 I2C_8B_REG LITERAL1 I2C_16B_REG LITERAL1 +I2C_READ LITERAL1 + +DEF_CI2C_NB_RETRIES LITERAL1 +DEF_CI2C_TIMEOUT LITERAL1 \ No newline at end of file diff --git a/library.properties b/library.properties old mode 100644 new mode 100755 index e178c6c..7b15f63 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=cI2C -version=0.2 +version=0.3 author=SMFSW maintainer=SMFSW -sentence=Arduino Hardware Master I2C for AVR (in plain c) -paragraph=Hardware I2C library for AVR µcontrollers (lib intended for Master I2C protocols development in c, for easier ports to other µcontrollers) +sentence=Arduino Hardware I2C for AVR (in plain c) +paragraph=Hardware I2C library for AVR µcontrollers (lib intended for I2C protocols development in c, for easier ports to other µcontrollers) category=Communication url=http://playground.arduino.cc/code/CI2C architectures=avr