1
0
mirror of https://github.com/SMFSW/cI2C synced 2024-11-23 10:24:21 +01:00

v0.3: bugfixes and refactoring

This commit is contained in:
SMFSW 2017-01-22 18:32:00 +01:00
parent 588c8106a3
commit 4728577666
11 changed files with 536 additions and 350 deletions

43
Doxyfile Normal file → Executable file
View File

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

0
LICENSE Normal file → Executable file
View File

View File

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

23
Release Notes.txt Executable file
View File

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

406
ci2c.c Normal file → Executable file
View File

@ -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_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++;
}

230
ci2c.h Normal file → Executable file
View File

@ -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 <Arduino.h>
#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

115
examples/ci2c_advanced/ci2c_advanced.ino Normal file → Executable file
View File

@ -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 <ci2c.h>
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));
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], 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_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;
}

16
examples/ci2c_master_read/ci2c_master_read.ino Normal file → Executable file
View File

@ -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 <ci2c.h>
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(" ");
}

12
examples/ci2c_master_write/ci2c_master_write.ino Normal file → Executable file
View File

@ -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 <ci2c.h>
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 ");

7
keywords.txt Normal file → Executable file
View File

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

6
library.properties Normal file → Executable file
View File

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