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:
parent
588c8106a3
commit
4728577666
43
Doxyfile
Normal file → Executable file
43
Doxyfile
Normal file → Executable 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
|
||||
|
26
README.md
26
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
|
23
Release Notes.txt
Executable file
23
Release Notes.txt
Executable 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
406
ci2c.c
Normal file → Executable 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_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++;
|
||||
}
|
||||
|
230
ci2c.h
Normal file → Executable file
230
ci2c.h
Normal file → Executable 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
115
examples/ci2c_advanced/ci2c_advanced.ino
Normal file → Executable 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_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;
|
||||
}
|
||||
|
16
examples/ci2c_master_read/ci2c_master_read.ino
Normal file → Executable file
16
examples/ci2c_master_read/ci2c_master_read.ino
Normal file → Executable 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
12
examples/ci2c_master_write/ci2c_master_write.ino
Normal file → Executable 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
7
keywords.txt
Normal file → Executable 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
6
library.properties
Normal file → Executable 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
|
||||
|
Loading…
Reference in New Issue
Block a user