1
0
mirror of https://github.com/SMFSW/cI2C synced 2024-06-27 14:12:59 +02:00

Compare commits

...

26 Commits
v0.5 ... master

Author SHA1 Message Date
SMFSW
d31cca271e Updated to travis-ci.com status 2018-12-12 20:54:31 +01:00
SMFSW
e94f51f762 Removed deprecated sudo from .travis.yml 2018-12-11 19:53:01 +01:00
SMFSW
dbe9dc0a61 typo in ReleaseNotes.md 2018-06-17 18:23:58 +02:00
SMFSW
e6d3d2a8ae v1.3: Delay between retries set to 1ms 2018-05-27 15:57:06 +02:00
SMFSW
b9babcdde0 Added dedicated Doxyfile for Travis CI and updated .travis.yml to use graphviz 2018-05-04 17:43:37 +02:00
SMFSW
b079e7997d Doxyfile updated for Travis CI 2018-05-04 12:30:50 +02:00
SMFSW
3277748c49 Updated Doxyfile & .travis.yml 2018-05-04 02:23:19 +02:00
SMFSW
de152ccc5f Updated .travis.yml 2018-05-04 01:46:31 +02:00
SMFSW
fa20634862 Updated .travis.yml 2018-05-04 01:29:09 +02:00
SMFSW
4ce72eeb34 Upadated Doxyfile and .travis.yml for documentation generation 2018-05-04 00:34:44 +02:00
SMFSW
669f2c787b Updated README.md 2018-05-04 00:16:21 +02:00
SMFSW
7122bfd2de Updated README.md 2018-05-03 21:24:01 +02:00
SMFSW
66ceef30b4 Updated README.md 2018-05-03 21:15:08 +02:00
SMFSW
7a2af3d743 README.md updated 2018-05-03 20:57:08 +02:00
SMFSW
668e2ebcf5 Release Notes.md renamed to ReleaseNotes.md 2018-05-03 20:06:58 +02:00
SMFSW
93e2ffcccc Release Notes.md renamed to ReleaseNotes.md 2018-05-03 20:05:10 +02:00
SMFSW
dc8c19cb7a v1.3: Updated README.md 2018-05-03 19:33:34 +02:00
SMFSW
ee3b56f52e v1.3: updated README.md for Travis 2018-05-03 19:22:12 +02:00
SMFSW
a3ed48b1f1 v1.3: Release Notes is now markdown 2018-05-03 19:14:57 +02:00
SMFSW
0e091c3f95 updated .travis.yml 2018-05-03 19:02:27 +02:00
SMFSW
4fcef282b6 v1.3: Added Travis CI support and removed doxygen version anchors in sources 2018-05-03 18:42:08 +02:00
SMFSW
63be41402d Doxyfile 2018-03-15 01:38:58 +01:00
SMFSW
acfcfec7aa v1.2: No internal address transmission when reading/writing to next internal address (auto-increment feature of most I2C devices) 2017-11-30 22:40:42 +01:00
SMFSW
a44abc72ec v1.1: fixed bus speed calc & returning configuration value applied instead of bool 2017-11-30 01:32:44 +01:00
SMFSW
ed7030cffc code review, changes following Wire lib updates & const for function parameters 2017-11-21 23:15:55 +01:00
SMFSW
035edd6d7f v0.6: compliance with Arduino v1.5+ IDE specs 2017-07-12 22:07:33 +02:00
12 changed files with 3328 additions and 825 deletions

4
.gitignore vendored Normal file → Executable file
View File

@ -27,3 +27,7 @@
*.exe *.exe
*.out *.out
*.app *.app
#Doxygen
doxygen_sqlite3.db
html

29
.travis.yml Executable file
View File

@ -0,0 +1,29 @@
language: c
addons:
apt:
packages:
- graphviz
# Blacklist
branches:
except:
- gh-pages
env:
global:
- PRETTYNAME="cI2C: Arduino Hardware I2C for AVR (in plain c)"
- GH_REPO_NAME: cI2C
- GH_REPO_REF: github.com/SMFSW/cI2C.git
- DOXYFILE: $TRAVIS_BUILD_DIR/Doxyfile.auto
before_install:
- source <(curl -SLs https://raw.githubusercontent.com/SMFSW/travis-ci-arduino/master/install.sh)
script:
- build_avr_platforms
# Generate and deploy documentation
after_success:
- source <(curl -SLs https://raw.githubusercontent.com/SMFSW/travis-ci-arduino/master/library_check.sh)
- source <(curl -SLs https://raw.githubusercontent.com/SMFSW/travis-ci-arduino/master/doxy_gen_and_deploy.sh)

View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.11 # Doxyfile 1.8.13
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -38,7 +38,7 @@ PROJECT_NAME = "Arduino Hardware I2C for AVR MCUs (plain c)"
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 0.5 PROJECT_NUMBER = 1.3
# Using the PROJECT_BRIEF tag one can provide an optional one line description # 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 # for a project that appears at the top of each page and should give viewer a
@ -244,7 +244,7 @@ TCL_SUBST =
# members will be omitted, etc. # members will be omitted, etc.
# The default value is: NO. # The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_FOR_C = YES
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored # Python sources only. Doxygen will then generate output that is more tailored
@ -295,6 +295,15 @@ EXTENSION_MAPPING = ino=C++
MARKDOWN_SUPPORT = YES MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
# When enabled doxygen tries to link words that correspond to documented # When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can # classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or # be prevented in individual cases by putting a % sign in front of the word or
@ -739,7 +748,7 @@ WARN_IF_DOC_ERROR = YES
# parameter documentation, but not about the absence of documentation. # parameter documentation, but not about the absence of documentation.
# The default value is: NO. # The default value is: NO.
WARN_NO_PARAMDOC = NO WARN_NO_PARAMDOC = YES
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. # a warning is encountered.
@ -761,7 +770,7 @@ WARN_FORMAT = "$file:$line: $text"
# messages should be written. If left blank the output is written to standard # messages should be written. If left blank the output is written to standard
# error (stderr). # error (stderr).
WARN_LOGFILE = WARN_LOGFILE = workdir/doxy.log
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the input files # Configuration options related to the input files
@ -773,7 +782,8 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = ./ INPUT = ./ \
./src
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -795,8 +805,8 @@ INPUT_ENCODING = UTF-8
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
FILE_PATTERNS = *.c \ FILE_PATTERNS = *.c \
*.cpp \ *.cpp \
@ -925,7 +935,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub # (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output. # and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = USE_MDFILE_AS_MAINPAGE = README.md
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to source browsing # Configuration options related to source browsing
@ -1719,7 +1729,7 @@ LATEX_EXTRA_FILES =
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = NO PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate # If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES, to get a # the PDF file directly from the LaTeX files. Set this option to YES, to get a
@ -2365,6 +2375,11 @@ DIAFILE_DIRS =
PLANTUML_JAR_PATH = PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by # When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block. # the !include statement in a plantuml block.

2446
Doxyfile.auto Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,12 @@
# cI2C # cI2C [![Build Status](https://travis-ci.com/SMFSW/cI2C.svg?branch=master)](https://travis-ci.com/SMFSW/cI2C)
Arduino Hardware I2C for AVR (plain c) Arduino Hardware I2C for AVR (plain c)
Hardware I2C library for AVR MCUs (lib intended for I2C protocols development in c, for easier ports to other MCUs) Hardware I2C library for AVR MCUs (lib intended for I2C protocols development in c, for easier ports to other MCUs)
## Library choice: ## Library choice
* cI2C library implements I2C bus for AVR tagets (Uno, Nano, Mega...)
* cI2C library implements I2C bus for AVR targets (Uno, Nano, Mega...)
* you may prefer this one when: * you may prefer this one when:
* working on AVR targets * working on AVR targets
* interrupts are not needed * interrupts are not needed
@ -16,64 +18,69 @@ Hardware I2C library for AVR MCUs (lib intended for I2C protocols development in
No refactoring is required when switching between **cI2C** & **WireWrapper** libs; No refactoring is required when switching between **cI2C** & **WireWrapper** libs;
Both libs share same Typedefs, Functions & Parameters. Both libs share same Typedefs, Functions & Parameters.
## Notes: ## Notes
* cI2C is written in plain c (intentionally) * cI2C is written in plain c (intentionally)
* cI2C does not use any interrupt (yet, but soon will have to) * 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 designed to act as bus Master (Slave mode will be considered in future releases)
* cI2C is set to work on AVR targets only * cI2C is set to work on AVR targets only
* for other targets, you may use **WireWrapper** instead (will be using Wire) * for other targets, you may use **WireWrapper** instead (will be using Wire)
## Usage: ## Usage
This library is intended to be able to work with multiple slaves connected on the same I2C bus. This library is intended to be able to work with multiple slaves connected on the same I2C bus.
Thus, the I2C bus and Slaves are defined separately. Thus, the I2C bus and Slaves are defined separately.
* On one hand, I2C bus has to be initialised with appropriate speed: * On one hand, I2C bus has to be initialized with appropriate speed:
* use I2C_init(speed): speed can be choosen from I2C_SPEED enum for convenience, or passing an integer as parameter * use `I2C_init(speed)`: speed can be chosen from `I2C_SPEED` enum for convenience, or passing an integer as parameter
* On the other hand, Slave(s) have to be defined and initialised too: * On the other hand, Slave(s) have to be defined and initialized too:
* use I2C_SLAVE typedef to declare slaves structs * use `I2C_SLAVE` typedef to declare slaves structs
* use I2C_slave_init(pSlave, addr, regsize) * use `I2C_slave_init(pSlave, addr, regsize)`
* **pSlave** is a pointer to the slave struct to initialise * `pSlave`: pointer to the slave struct to initialize
* **addr** is the slave I2C address (don't shift addr, lib takes care of that) * `addr`: slave I2C address (don't shift addr, lib takes care of that)
* **regsize** is the width of internal slave registers (to be choosen from I2C_INT_SIZE) * `regsize`: width of internal slave registers (to be chosen from `I2C_INT_SIZE`)
* in case you need to use custom R/W procedures for a particular slave: * in case you need to use custom R/W procedures for a particular slave:
* use I2C_slave_set_rw_func(pSlave, pFunc, rw) * use `I2C_slave_set_rw_func(pSlave, pFunc, rw)`
* **pSlave** is a pointer to the slave declaration to initialise * `pSlave`: pointer to the slave declaration to initialize
* **pFunc** is a pointer to the Read or Write bypass function * `pFunc`: pointer to the Read or Write bypass function
* **rw** can be choosen from I2C_RW enum (wr=0, rd=1) * `rw`: can be chosen from `I2C_RW` enum (wr=0, rd=1)
After all inits are done, the lib can basically be used this way: After all inits are done, the lib can basically be used this way:
* I2C_read(pSlave, regaddr, pData, bytes) * `I2C_read(pSlave, regaddr, pData, bytes)`
* **pSlave** is a pointer to the slave struct to read from * `pSlave`: pointer to the slave struct to read from
* **regaddr** is the start address to read from * `regaddr`: start address to read from
* **pData** is a pointer to the place where datas read will be stored * `pData`: pointer to the place where datas read will be stored
* **bytes** number of bytes to read from slave * `bytes`: number of bytes to read from slave
* returns true if read is ok, false otherwise * returns `true` if read is ok, `false` otherwise
* I2C_write(pSlave, regaddr, pData, bytes) * `I2C_write(pSlave, regaddr, pData, bytes)`
* **pSlave** is a pointer to the slave struct to write to * `pSlave`: pointer to the slave struct to write to
* **regaddr** is the start address to write to * `regaddr`: start address to write to
* **pData** is a pointer to the block of datas to write to slave * `pData`: pointer to the block of datas to write to slave
* **bytes** number of bytes to write to slave * `bytes`: number of bytes to write to slave
* returns true if write is ok, false otherwise * returns `true` if write is ok, `false` otherwise
## Examples included
## Examples included:
following examples should work with any I2C EEPROM/FRAM with address 0x50 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)) (yet function to get Chip ID are device dependent (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_write.ino](examples/ci2c_master_write/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_master_read.ino](examples/ci2c_master_read/ci2c_master_read.ino): Read some bytes in FRAM
* ci2c_advanced.ino: Redirecting slave write & read functions (to custom functions following typedef) * [ci2c_advanced.ino](examples/ci2c_advanced/ci2c_advanced.ino): Redirecting slave write & read functions (to custom functions following typedef)
Doxygen doc can be generated for the library using doxyfile ## Documentation
## Links: Doxygen doc can be generated using "Doxyfile".
Feel free to share your thoughts @ xgarmanboziax@gmail.com about:
* issues encountered See [generated documentation](https://smfsw.github.io/cI2C/)
* optimisations
* improvements & new functionalities ## Release Notes
See [release notes](ReleaseNotes.md)
## See also
**cI2C** **cI2C**
- https://github.com/SMFSW/cI2C * [cI2C github](https://github.com/SMFSW/cI2C) - C implementation of this library
- https://bitbucket.org/SMFSW/ci2c
**WireWrapper** **WireWrapper**
- https://github.com/SMFSW/WireWrapper * [WireWrapper github](https://github.com/SMFSW/WireWrapper) - Cpp implementation using Wire Wrapper
- https://bitbucket.org/SMFSW/wirewrapper

View File

@ -1,20 +1,40 @@
Arduino Hardware I2C for AVR (plain c) Arduino Hardware I2C for AVR (plain c)
2017-2017 SMFSW 2017-2018 SMFSW
- cI2C is set to work on AVR targets only - cI2C is set to work on AVR targets only
-> for other targets, you may use WireWrapper instead (will be using Wire) -> for other targets, you may use WireWrapper instead (will be using Wire)
-> cI2C & WireWrapper libs declare same structures & functions as seen from the outside -> cI2C & WireWrapper libs declare same structures & functions as seen from the outside
(switch between libs without changing anyhting but the include) (switch between libs without changing anything but the include)
Feel free to share your thoughts @ xgarmanboziax@gmail.com about: Feel free to share your thoughts @ xgarmanboziax@gmail.com about:
- issues encountered - issues encountered
- optimisations - optimizations
- improvements & new functionalities - improvements & new functionalities
------------ ------------
** Actual: ** Actual:
v1.3 13 May 2018:
- Delay between retries is now 1ms
- Adding support for unit tests and doxygen documentation generation with Travis CI
- Updated README.md
v1.2 30 Nov 2017:
- No internal address transmission when reading/writing to next internal address (make sure not to r/w last 16 address right just after init, otherwise make a dummy of address 0 just before)
v1.1 29 Nov 2017:
- Frequency calculation fix (thanks to TonyWilk)
- Set Frequency higher than Fast Mode (400KHz) will set bus to Fast Mode (frequency is up to 400KHz on AVR)
- I2C_set_xxx now returns values applied, not bool
v1.0 21 Nov 2017:
- Added const qualifier for function parameters
- Return from comm functions if bytes to R/W set to 0
v0.6 12 Jul 2017:
- compliance with Arduino v1.5+ IDE source located in src subfolder
v0.5 31 Jan 2017: v0.5 31 Jan 2017:
- refactored I2C_SPEED enum names for coherence with I2C specifications - refactored I2C_SPEED enum names for coherence with I2C specifications
- High Speed mode added in I2C_SPEED enum - High Speed mode added in I2C_SPEED enum

468
ci2c.c
View File

@ -1,468 +0,0 @@
/*!\file ci2c.c
** \author SMFSW
** \version 0.5
** \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
#include "ci2c.h"
#define START 0x08
#define REPEATED_START 0x10
#define MT_SLA_ACK 0x18
#define MT_SLA_NACK 0x20
#define MT_DATA_ACK 0x28
#define MT_DATA_NACK 0x30
#define MR_SLA_ACK 0x40
#define MR_SLA_NACK 0x48
#define MR_DATA_ACK 0x50
#define MR_DATA_NACK 0x58
#define LOST_ARBTRTN 0x38
#define TWI_STATUS (TWSR & 0xF8)
//#define isSetRegBit(r, b) ((r & (1 << b)) != 0)
//#define isClrRegBit(r, b) ((r & (1 << b)) == 0)
#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
/*! \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; //!< time start waiting for acknowledge
bool busy; //!< true if already busy (in case of interrupts implementation)
} i2c = { { (I2C_SPEED) 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 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
*/
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, (ci2c_fct_ptr) I2C_wr, I2C_WRITE);
I2C_slave_set_rw_func(slave, (ci2c_fct_ptr) 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 = write function, 1 = read function
* \return nothing
*/
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.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)
*/
bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr)
{
if (sl_addr > 0x7F) { return false; }
slave->cfg.addr = sl_addr;
return true;
}
/*! \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)
*/
bool 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;
return !(reg_sz > I2C_16B_REG);
}
/*! \brief Set I2C current register address
* \attribute inline
* \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address
* \return nothing
*/
static inline void __attribute__((__always_inline__)) I2C_slave_set_reg_addr(I2C_SLAVE * slave, uint16_t reg_addr)
{
slave->reg_addr = 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
*/
void I2C_init(uint16_t speed)
{
// Set SDA and SCL to ports with pull-ups
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
setRegBit(PORTC, 4);
setRegBit(PORTC, 5);
#else
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
*/
void I2C_reset(void)
{
TWCR = 0;
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)
*/
bool I2C_set_speed(uint16_t speed)
{
i2c.cfg.speed = (I2C_SPEED) ((speed == 0) ? (uint16_t) I2C_STD : ((speed > (uint16_t) I2C_HS) ? (uint16_t) I2C_STD : speed));
clrRegBit(TWCR, TWEN); // Ensure i2c module is disabled
// Set prescaler and clock frequency
clrRegBit(TWSR, TWPS0);
clrRegBit(TWSR, TWPS1);
TWBR = ((F_CPU / (i2c.cfg.speed * 1000)) - 16) / 2;
I2C_reset(); // re-enable module
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)
*/
bool I2C_set_timeout(uint16_t timeout)
{
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)
*/
bool I2C_set_retries(uint8_t retries)
{
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
*/
bool 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] 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 bytes, I2C_RW rw)
{
uint8_t retry = i2c.cfg.retries;
bool ack = false;
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, bytes);
while ((!ack) && (retry != 0)) // If com not successful, retry some more times
{
delay(5);
ack = fc(slave, reg_addr, data, bytes);
retry--;
}
i2c.busy = false;
return slave->status = ack ? I2C_OK : I2C_NACK;
}
/*! \brief This function writes the provided data to the address specified.
* \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
*/
I2C_STATUS I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes)
{
return I2C_comm(slave, reg_addr, data, bytes, I2C_WRITE);
}
/*! \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
* \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
*/
I2C_STATUS I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes)
{
return I2C_comm(slave, reg_addr, data, bytes, I2C_READ);
}
/*! \brief Start i2c_timeout timer
* \attribute inline
* \return nothing
*/
static inline void __attribute__((__always_inline__)) I2C_start_timeout(void)
{
i2c.start_wait = (uint16_t) millis();
}
/*! \brief Test i2c_timeout
* \attribute inline
* \return true if i2c_timeout occured (false otherwise)
*/
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)
*/
bool I2C_start(void)
{
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if ((TWI_STATUS == START) || (TWI_STATUS == REPEATED_START)) { return true; }
if (TWI_STATUS == LOST_ARBTRTN) { I2C_reset(); }
return false;
}
/*! \brief Send stop condition
* \return true if stop condition acknowledged (false otherwise)
*/
bool I2C_stop(void)
{
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while ((TWCR & (1 << TWSTO)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
return true;
}
/*! \brief Send byte on bus
* \param [in] dat - data to be sent
* \return true if data sent acknowledged (false otherwise)
*/
bool I2C_wr8(uint8_t dat)
{
TWDR = dat;
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if (TWI_STATUS == MT_DATA_ACK) { return true; }
if (TWI_STATUS == MT_DATA_NACK) { I2C_stop(); }
else { I2C_reset(); }
return false;
}
/*! \brief Receive byte from bus
* \param [in] ack - true if wait for ack
* \return true if data reception acknowledged (false otherwise)
*/
uint8_t I2C_rd8(bool ack)
{
I2C_start_timeout();
if (ack) { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); }
else { TWCR = (1 << TWINT) | (1 << TWEN); }
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if (TWI_STATUS == LOST_ARBTRTN) { I2C_reset(); 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
* \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 bytes)
{
(void) I2C_slave_set_reg_addr(slave, reg_addr);
if (I2C_start() == 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
{
if (I2C_wr8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_wr8((uint8_t) reg_addr) == false) { return false; }
}
for (uint16_t cnt = 0; cnt < bytes; cnt++)
{
if (I2C_wr8(*data++) == false) { return false; }
slave->reg_addr++;
}
if (I2C_stop() == false) { return false; }
return true;
}
/*! \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
* \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 bytes)
{
(void) I2C_slave_set_reg_addr(slave, reg_addr);
if (bytes == 0) { bytes = 1; }
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_wr8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_wr8((uint8_t) reg_addr) == false) { return false; }
}
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave, I2C_READ) == false) { return false; }
for (uint16_t cnt = 0; cnt < bytes; cnt++)
{
if (I2C_rd8((cnt == (bytes - 1)) ? false : true) == false) { return false; }
*data++ = TWDR;
slave->reg_addr++;
}
if (I2C_stop() == false) { return false; }
return true;
}

286
ci2c.h
View File

@ -1,286 +0,0 @@
/*!\file ci2c.h
** \author SMFSW
** \version 0.5
** \copyright MIT SMFSW (2017)
** \brief arduino i2c in plain c declarations
**/
/****************************************************************/
#ifndef __CI2C_H__
#define __CI2C_H__ "v0.5"
/****************************************************************/
#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
#include <WProgram.h>
#endif
#include <inttypes.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"{
#endif
#define DEF_CI2C_NB_RETRIES 3 //!< Default cI2C transaction retries
#define DEF_CI2C_TIMEOUT 100 //!< Default cI2C timeout
/*! \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_STD = 100, //!< I2C Standard (100KHz)
I2C_FM = 400, //!< I2C Fast Mode (400KHz)
I2C_FMP = 1000, //!< I2C Fast mode + (1MHz)
I2C_HS = 3400 //!< I2C High Speed (3.4MHz)
} 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 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 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
ci2c_fct_ptr wr; //!< Slave write function pointer
ci2c_fct_ptr rd; //!< Slave read function pointer
} cfg;
uint16_t reg_addr; //!< Internal current register address
I2C_STATUS status; //!< Status of the last communications
} I2C_SLAVE;
/***************************/
/*** I2C SLAVE FUNCTIONS ***/
/***************************/
/*! \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
*/
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 = write function, 1 = read function
* \return nothing
*/
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)
*/
extern 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)
*/
extern bool I2C_slave_set_reg_size(I2C_SLAVE * slave, I2C_INT_SIZE reg_sz);
/*! \brief Get I2C slave address
* \attribute inline
* \param [in] slave - pointer to the I2C slave structure
* \return I2C slave address
*/
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)
* \attribute inline
* \param [in] slave - pointer to the I2C slave structure
* \return register map using 16bits if true (1Byte otherwise)
*/
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)
* \attribute inline
* \param [in] slave - pointer to the I2C slave structure
* \return current register map address
*/
inline uint16_t __attribute__((__always_inline__)) I2C_slave_get_reg_addr(I2C_SLAVE * slave)
{
return slave->reg_addr;
}
/*************************/
/*** I2C BUS FUNCTIONS ***/
/*************************/
/*! \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
*/
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)
*/
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)
*/
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)
*/
extern bool I2C_set_retries(uint8_t retries);
/*! \brief Get I2C busy status
* \return true if busy
*/
extern bool 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
* \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 I2C_STATUS 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
* \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 __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, 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
* \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 I2C_STATUS 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
* \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 __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, bytes);
}
/***********************************/
/*** cI2C LOW LEVEL FUNCTIONS ***/
/*** THAT MAY BE USEFUL FOR DVPT ***/
/***********************************/
/*! \brief I2C bus reset (Release SCL and SDA lines and re-enable module)
* \return nothing
*/
extern void I2C_reset(void);
/*! \brief Send start condition
* \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)
*/
extern bool I2C_stop(void);
/*! \brief Send byte on bus
* \param [in] dat - data to be sent
* \return true if data sent acknowledged (false otherwise)
*/
extern bool I2C_wr8(uint8_t dat);
/*! \brief Receive byte from bus
* \param [in] ack - true if wait for ack
* \return true if data reception acknowledged (false otherwise)
*/
extern uint8_t I2C_rd8(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
}
#endif
#endif

View File

@ -7,7 +7,7 @@
This example code is in the public domain. This example code is in the public domain.
created Jan 12 2017 created Jan 12 2017
latest mod Jan 31 2017 latest mod Nov 30 2017
by SMFSW by SMFSW
*/ */
@ -24,8 +24,8 @@ void setup() {
Serial.begin(115200); // start serial for output Serial.begin(115200); // start serial for output
I2C_init(I2C_FM); // init with Fast Mode (400KHz) I2C_init(I2C_FM); // init with Fast Mode (400KHz)
I2C_slave_init(&FRAM, 0x50, I2C_16B_REG); I2C_slave_init(&FRAM, 0x50, I2C_16B_REG);
I2C_slave_set_rw_func(&FRAM, I2C_wr_advanced, I2C_WRITE); I2C_slave_set_rw_func(&FRAM, (ci2c_fct_ptr) I2C_wr_advanced, I2C_WRITE);
I2C_slave_set_rw_func(&FRAM, I2C_rd_advanced, I2C_READ); I2C_slave_set_rw_func(&FRAM, (ci2c_fct_ptr) I2C_rd_advanced, I2C_READ);
I2C_get_chip_id(&FRAM, &str[0]); I2C_get_chip_id(&FRAM, &str[0]);
@ -62,14 +62,16 @@ void loop() {
* \param [in] bytes - indicates how many bytes of data to write * \param [in] bytes - indicates how many bytes of data to write
* \return Boolean indicating success/fail of write attempt * \return Boolean indicating success/fail of write attempt
*/ */
bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) bool I2C_wr_advanced(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes)
{ {
slave->reg_addr = reg_addr; if (bytes == 0) { return false; }
if (I2C_start() == false) { return false; } if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; } if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; }
if (slave->cfg.reg_size) if ((slave->cfg.reg_size) && (reg_addr != slave->reg_addr)) // Don't send address if writing next
{ {
slave->reg_addr = reg_addr;
if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used
{ {
if (I2C_wr8((uint8_t) (reg_addr >> 8)) == false) { return false; } if (I2C_wr8((uint8_t) (reg_addr >> 8)) == false) { return false; }
@ -96,14 +98,14 @@ bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint1
* \param [in] bytes - indicates how many bytes of data to read * \param [in] bytes - indicates how many bytes of data to read
* \return Boolean indicating success/fail of read attempt * \return Boolean indicating success/fail of read attempt
*/ */
bool I2C_rd_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) bool I2C_rd_advanced(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes)
{ {
if (bytes == 0) { return false; }
if ((slave->cfg.reg_size) && (reg_addr != slave->reg_addr)) // Don't send address if reading next
{
slave->reg_addr = reg_addr; slave->reg_addr = reg_addr;
if (bytes == 0) { bytes = 1; }
if (slave->cfg.reg_size)
{
if (I2C_start() == false) { return false; } if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave, I2C_WRITE) == 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 (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used

View File

@ -1,7 +1,7 @@
name=cI2C name=cI2C
version=0.5 version=1.3
author=SMFSW author=SMFSW <xgarmanboziax@gmail.com>
maintainer=SMFSW maintainer=SMFSW <xgarmanboziax@gmail.com>
sentence=Arduino Hardware I2C for AVR (in plain c) sentence=Arduino Hardware I2C for AVR (in plain c)
paragraph=Hardware I2C library for AVR MCUs (lib intended for I2C protocols development in c, for easier ports to other MCUs) paragraph=Hardware I2C library for AVR MCUs (lib intended for I2C protocols development in c, for easier ports to other MCUs)
category=Communication category=Communication

453
src/ci2c.c Executable file
View File

@ -0,0 +1,453 @@
/*!\file ci2c.c
** \author SMFSW
** \copyright MIT SMFSW (2017-2018)
** \brief arduino master i2c in plain c code
** \warning Don't access (r/w) last 16b internal address byte alone right after init, this would lead to hazardous result (in such case, make a dummy read of addr 0 before)
**/
// 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)
#include "ci2c.h"
#define START 0x08
#define REPEATED_START 0x10
#define MT_SLA_ACK 0x18
#define MT_SLA_NACK 0x20
#define MT_DATA_ACK 0x28
#define MT_DATA_NACK 0x30
#define MR_SLA_ACK 0x40
#define MR_SLA_NACK 0x48
#define MR_DATA_ACK 0x50
#define MR_DATA_NACK 0x58
#define LOST_ARBTRTN 0x38
#define TWI_STATUS (TWSR & 0xF8)
//#define isSetRegBit(r, b) ((r & (1 << b)) != 0)
//#define isClrRegBit(r, b) ((r & (1 << b)) == 0)
#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
/*!\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; //!< time start waiting for acknowledge
bool busy; //!< true if already busy (in case of interrupts implementation)
} i2c = { { (I2C_SPEED) 0, DEF_CI2C_NB_RETRIES, DEF_CI2C_TIMEOUT }, 0, false };
// Needed prototypes
static bool I2C_wr(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes);
static bool I2C_rd(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const 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
**/
void I2C_slave_init(I2C_SLAVE * slave, const uint8_t sl_addr, const 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, (ci2c_fct_ptr) I2C_wr, I2C_WRITE);
I2C_slave_set_rw_func(slave, (ci2c_fct_ptr) I2C_rd, I2C_READ);
slave->reg_addr = (uint16_t) -1; // To be sure to send address on first access (warning: unless last 16b byte address is accessed alone)
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 = write function, 1 = read function
** \return nothing
**/
void I2C_slave_set_rw_func(I2C_SLAVE * slave, const ci2c_fct_ptr func, const I2C_RW rw)
{
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)
**/
bool I2C_slave_set_addr(I2C_SLAVE * slave, const uint8_t sl_addr)
{
if (sl_addr > 0x7F) { return false; }
slave->cfg.addr = sl_addr;
return true;
}
/*!\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)
**/
bool I2C_slave_set_reg_size(I2C_SLAVE * slave, const I2C_INT_SIZE reg_sz)
{
slave->cfg.reg_size = reg_sz > I2C_16B_REG ? I2C_16B_REG : reg_sz;
return !(reg_sz > I2C_16B_REG);
}
/*!\brief Set I2C current register address
** \attribute inline
** \param [in, out] slave - pointer to the I2C slave structure
** \param [in] reg_addr - register address
** \return nothing
**/
static inline void __attribute__((__always_inline__)) I2C_slave_set_reg_addr(I2C_SLAVE * slave, const uint16_t reg_addr) {
slave->reg_addr = 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
**/
void I2C_init(const uint16_t speed)
{
// Set SDA and SCL to ports with pull-ups
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
setRegBit(PORTC, 4);
setRegBit(PORTC, 5);
#else
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
**/
void I2C_reset(void)
{
TWCR = 0;
setRegBit(TWCR, TWEA);
setRegBit(TWCR, TWEN);
}
/*!\brief Change I2C frequency
** \param [in] speed - I2C speed in KHz (max 400KHz on avr)
** \return Configured bus speed
**/
uint16_t I2C_set_speed(const uint16_t speed)
{
i2c.cfg.speed = (I2C_SPEED) ((speed == 0) ? (uint16_t) I2C_STD : ((speed > (uint16_t) I2C_FM) ? (uint16_t) I2C_FM : speed));
clrRegBit(TWCR, TWEN); // Ensure i2c module is disabled
// Set prescaler and clock frequency
clrRegBit(TWSR, TWPS0);
clrRegBit(TWSR, TWPS1);
TWBR = (((F_CPU / 1000) / i2c.cfg.speed) - 16) / 2;
I2C_reset(); // re-enable module
return i2c.cfg.speed;
}
/*!\brief Change I2C ack timeout
** \param [in] timeout - I2C ack timeout (500 ms max)
** \return Configured timeout
**/
uint16_t I2C_set_timeout(const uint16_t timeout)
{
static const uint16_t max_timeout = 500;
i2c.cfg.timeout = (timeout > max_timeout) ? max_timeout : timeout;
return i2c.cfg.timeout;
}
/*!\brief Change I2C message retries (in case of failure)
** \param [in] retries - I2C number of retries (max of 8)
** \return Configured number of retries
**/
uint8_t I2C_set_retries(const uint8_t retries)
{
static const uint16_t max_retries = 8;
i2c.cfg.retries = (retries > max_retries) ? max_retries : retries;
return i2c.cfg.retries;
}
/*!\brief Get I2C busy status
** \return true if busy
**/
bool 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] 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, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes, const I2C_RW rw)
{
uint8_t retry = i2c.cfg.retries;
bool ack = false;
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, bytes);
while ((!ack) && (retry != 0)) // If com not successful, retry some more times
{
delay(1);
ack = fc(slave, reg_addr, data, bytes);
retry--;
}
i2c.busy = false;
return slave->status = ack ? I2C_OK : I2C_NACK;
}
/*!\brief This function writes the provided data to the address specified.
** \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
**/
I2C_STATUS I2C_write(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes) {
return I2C_comm(slave, reg_addr, data, bytes, I2C_WRITE); }
/*!\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
** \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
**/
I2C_STATUS I2C_read(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes) {
return I2C_comm(slave, reg_addr, data, bytes, I2C_READ); }
/*!\brief Start i2c_timeout timer
** \attribute inline
** \return nothing
**/
static inline void __attribute__((__always_inline__)) I2C_start_timeout(void) {
i2c.start_wait = (uint16_t) millis(); }
/*!\brief Test i2c_timeout
** \attribute inline
** \return true if i2c_timeout occured (false otherwise)
**/
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)
**/
bool I2C_start(void)
{
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if ((TWI_STATUS == START) || (TWI_STATUS == REPEATED_START)) { return true; }
if (TWI_STATUS == LOST_ARBTRTN) { I2C_reset(); }
return false;
}
/*!\brief Send stop condition
** \return true if stop condition acknowledged (false otherwise)
**/
bool I2C_stop(void)
{
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while ((TWCR & (1 << TWSTO)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
return true;
}
/*!\brief Send byte on bus
** \param [in] dat - data to be sent
** \return true if data sent acknowledged (false otherwise)
**/
bool I2C_wr8(const uint8_t dat)
{
TWDR = dat;
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if (TWI_STATUS == MT_DATA_ACK) { return true; }
if (TWI_STATUS == MT_DATA_NACK) { I2C_stop(); }
else { I2C_reset(); }
return false;
}
/*!\brief Receive byte from bus
** \param [in] ack - true if wait for ack
** \return true if data reception acknowledged (false otherwise)
**/
uint8_t I2C_rd8(const bool ack)
{
I2C_start_timeout();
if (ack) { TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); }
else { TWCR = (1 << TWINT) | (1 << TWEN); }
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if (TWI_STATUS == LOST_ARBTRTN) { I2C_reset(); 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, const 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
** \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, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes)
{
if (bytes == 0) { return false; }
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave, I2C_WRITE) == false) { return false; }
if ((slave->cfg.reg_size) && (reg_addr != slave->reg_addr)) // Don't send address if writing next
{
(void) I2C_slave_set_reg_addr(slave, reg_addr);
if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used
{
if (I2C_wr8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_wr8((uint8_t) reg_addr) == false) { return false; }
}
for (uint16_t cnt = 0; cnt < bytes; cnt++)
{
if (I2C_wr8(*data++) == false) { return false; }
slave->reg_addr++;
}
if (I2C_stop() == false) { return false; }
return true;
}
/*!\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
** \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, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes)
{
if (bytes == 0) { return false; }
if ((slave->cfg.reg_size) && (reg_addr != slave->reg_addr)) // Don't send address if reading next
{
(void) I2C_slave_set_reg_addr(slave, reg_addr);
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_wr8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_wr8((uint8_t) reg_addr) == false) { return false; }
}
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave, I2C_READ) == false) { return false; }
for (uint16_t cnt = 0; cnt < bytes; cnt++)
{
if (I2C_rd8((cnt == (bytes - 1)) ? false : true) == false) { return false; }
*data++ = TWDR;
slave->reg_addr++;
}
if (I2C_stop() == false) { return false; }
return true;
}

281
src/ci2c.h Executable file
View File

@ -0,0 +1,281 @@
/*!\file ci2c.h
** \author SMFSW
** \copyright MIT SMFSW (2017-2018)
** \brief arduino i2c in plain c declarations
** \warning Don't access (r/w) last 16b internal address byte alone right after init, this would lead to hazardous result (in such case, make a dummy read of addr 0 before)
**/
/****************************************************************/
#ifndef __CI2C_H__
#define __CI2C_H__
/****************************************************************/
#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
#include <WProgram.h>
#endif
#include <inttypes.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define DEF_CI2C_NB_RETRIES 3 //!< Default cI2C transaction retries
#define DEF_CI2C_TIMEOUT 100 //!< Default cI2C timeout
/*!\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_STD = 100, //!< I2C Standard (100KHz)
I2C_FM = 400, //!< I2C Fast Mode (400KHz)
I2C_FMP = 1000, //!< I2C Fast mode + (1MHz): will set speed to Fast Mode (up to 400KHz on avr)
I2C_HS = 3400 //!< I2C High Speed (3.4MHz): will set speed to Fast Mode (up to 400KHz on avr)
} 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 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) (void*, const uint16_t, uint8_t*, const 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
ci2c_fct_ptr wr; //!< Slave write function pointer
ci2c_fct_ptr rd; //!< Slave read function pointer
} cfg;
uint16_t reg_addr; //!< Internal current register address
I2C_STATUS status; //!< Status of the last communications
} I2C_SLAVE;
/***************************/
/*** I2C SLAVE FUNCTIONS ***/
/***************************/
/*!\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
**/
void I2C_slave_init(I2C_SLAVE * slave, const uint8_t sl_addr, const 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 = write function, 1 = read function
** \return nothing
**/
void I2C_slave_set_rw_func(I2C_SLAVE * slave, const ci2c_fct_ptr func, const 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)
**/
bool I2C_slave_set_addr(I2C_SLAVE * slave, const 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)
**/
bool I2C_slave_set_reg_size(I2C_SLAVE * slave, const I2C_INT_SIZE reg_sz);
/*!\brief Get I2C slave address
** \attribute inline
** \param [in] slave - pointer to the I2C slave structure
** \return I2C slave address
**/
inline uint8_t __attribute__((__always_inline__)) I2C_slave_get_addr(const I2C_SLAVE * slave) {
return slave->cfg.addr; }
/*!\brief Get I2C register map size (for access)
** \attribute inline
** \param [in] slave - pointer to the I2C slave structure
** \return register map using 16bits if true (1Byte otherwise)
**/
inline bool __attribute__((__always_inline__)) I2C_slave_get_reg_size(const I2C_SLAVE * slave) {
return slave->cfg.reg_size; }
/*!\brief Get I2C current register address (addr may passed this way in procedures if contigous accesses)
** \attribute inline
** \param [in] slave - pointer to the I2C slave structure
** \return current register map address
**/
inline uint16_t __attribute__((__always_inline__)) I2C_slave_get_reg_addr(const I2C_SLAVE * slave) {
return slave->reg_addr; }
/*************************/
/*** I2C BUS FUNCTIONS ***/
/*************************/
/*!\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
**/
void I2C_init(const uint16_t speed);
/*!\brief Disable I2c module on arduino board (releasing pull-ups, and TWI control)
** \return nothing
**/
void I2C_uninit();
/*!\brief Change I2C frequency
** \param [in] speed - I2C bus speed in KHz (max 400KHz on AVR)
** \return Configured bus speed
**/
uint16_t I2C_set_speed(const uint16_t speed);
/*!\brief Change I2C ack timeout
** \param [in] timeout - I2C ack timeout (500 ms max)
** \return Configured timeout
**/
uint16_t I2C_set_timeout(const uint16_t timeout);
/*!\brief Change I2C message retries (in case of failure)
** \param [in] retries - I2C number of retries (max of 8)
** \return Configured number of retries
**/
uint8_t I2C_set_retries(const uint8_t retries);
/*!\brief Get I2C busy status
** \return true if busy
**/
bool 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
** \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
**/
I2C_STATUS I2C_write(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes);
/*!\brief This inline is a wrapper to I2C_write in case of contigous operations
** \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 __attribute__((__always_inline__)) I2C_write_next(I2C_SLAVE * slave, uint8_t * data, const uint16_t 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
** \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
**/
I2C_STATUS I2C_read(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes);
/*!\brief This inline is a wrapper to I2C_read in case of contigous operations
** \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 __attribute__((__always_inline__)) I2C_read_next(I2C_SLAVE * slave, uint8_t * data, const uint16_t bytes) {
return I2C_read(slave, slave->reg_addr, data, bytes); }
/***********************************/
/*** cI2C LOW LEVEL FUNCTIONS ***/
/*** THAT MAY BE USEFUL FOR DVPT ***/
/***********************************/
/*!\brief I2C bus reset (Release SCL and SDA lines and re-enable module)
** \return nothing
**/
void I2C_reset(void);
/*!\brief Send start condition
** \return true if start condition acknowledged (false otherwise)
**/
bool I2C_start(void);
/*!\brief Send stop condition
** \return true if stop condition acknowledged (false otherwise)
**/
bool I2C_stop(void);
/*!\brief Send byte on bus
** \param [in] dat - data to be sent
** \return true if data sent acknowledged (false otherwise)
**/
bool I2C_wr8(const uint8_t dat);
/*!\brief Receive byte from bus
** \param [in] ack - true if wait for ack
** \return true if data reception acknowledged (false otherwise)
**/
uint8_t I2C_rd8(const 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)
**/
bool I2C_sndAddr(I2C_SLAVE * slave, const I2C_RW rw);
#ifdef __cplusplus
}
#endif
#endif