1
0
mirror of https://github.com/SMFSW/cI2C synced 2024-06-29 23:22:57 +02:00

Compare commits

..

25 Commits
v0.6 ... 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
10 changed files with 2919 additions and 434 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

@ -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.6 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
@ -748,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.
@ -770,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
@ -935,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
@ -1729,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

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,37 @@
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: v0.6 12 Jul 2017:
- compliance with Arduino v1.5+ IDE source located in src subfolder - compliance with Arduino v1.5+ IDE source located in src subfolder

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,5 +1,5 @@
name=cI2C name=cI2C
version=0.6 version=1.3
author=SMFSW <xgarmanboziax@gmail.com> author=SMFSW <xgarmanboziax@gmail.com>
maintainer=SMFSW <xgarmanboziax@gmail.com> maintainer=SMFSW <xgarmanboziax@gmail.com>
sentence=Arduino Hardware I2C for AVR (in plain c) sentence=Arduino Hardware I2C for AVR (in plain c)

View File

@ -1,18 +1,13 @@
/*!\file ci2c.c /*!\file ci2c.c
** \author SMFSW ** \author SMFSW
** \version 0.6 ** \copyright MIT SMFSW (2017-2018)
** \copyright MIT SMFSW (2017)
** \brief arduino master i2c in plain c code ** \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: 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: 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" #include "ci2c.h"
#define START 0x08 #define START 0x08
@ -35,13 +30,13 @@
#define clrRegBit(r, b) r &= (uint8_t) (~(1 << b)) //!< clear 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 invRegBit(r, b) r ^= (1 << b) //!< invert bit \b b in register \b r
/*! \struct i2c /*!\struct i2c
* \brief static ci2c bus config and control parameters ** \brief static ci2c bus config and control parameters
*/ **/
static struct { static struct {
/*! \struct cfg /*!\struct cfg
* \brief ci2c bus parameters ** \brief ci2c bus parameters
*/ **/
struct { struct {
I2C_SPEED speed; //!< i2c bus speed I2C_SPEED speed; //!< i2c bus speed
uint8_t retries; //!< i2c message retries when fail uint8_t retries; //!< i2c message retries when fail
@ -53,80 +48,78 @@ static struct {
// Needed prototypes // Needed prototypes
static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes); 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, uint16_t reg_addr, uint8_t * data, 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 /*!\brief Init an I2C slave structure for cMI2C communication
* \param [in] slave - pointer to the I2C slave structure to init ** \param [in] slave - pointer to the I2C slave structure to init
* \param [in] sl_addr - I2C slave address ** \param [in] sl_addr - I2C slave address
* \param [in] reg_sz - internal register map size ** \param [in] reg_sz - internal register map size
* \return nothing ** \return nothing
*/ **/
void I2C_slave_init(I2C_SLAVE * slave, uint8_t sl_addr, I2C_INT_SIZE reg_sz) 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_addr(slave, sl_addr);
(void) I2C_slave_set_reg_size(slave, reg_sz); (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_wr, I2C_WRITE);
I2C_slave_set_rw_func(slave, (ci2c_fct_ptr) I2C_rd, I2C_READ); I2C_slave_set_rw_func(slave, (ci2c_fct_ptr) I2C_rd, I2C_READ);
slave->reg_addr = 0; 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; slave->status = I2C_OK;
} }
/*! \brief Redirect slave I2C read/write function (if needed for advanced use) /*!\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] slave - pointer to the I2C slave structure to init
* \param [in] func - pointer to read/write function to affect ** \param [in] func - pointer to read/write function to affect
* \param [in] rw - 0 = write function, 1 = read function ** \param [in] rw - 0 = write function, 1 = read function
* \return nothing ** \return nothing
*/ **/
void I2C_slave_set_rw_func(I2C_SLAVE * slave, ci2c_fct_ptr func, I2C_RW rw) 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); ci2c_fct_ptr * pfc = (ci2c_fct_ptr*) (rw ? &slave->cfg.rd : &slave->cfg.wr);
*pfc = func; *pfc = func;
} }
/*! \brief Change I2C slave address /*!\brief Change I2C slave address
* \param [in, out] slave - pointer to the I2C slave structure to init ** \param [in, out] slave - pointer to the I2C slave structure to init
* \param [in] sl_addr - I2C slave address ** \param [in] sl_addr - I2C slave address
* \return true if new address set (false if address is >7Fh) ** \return true if new address set (false if address is >7Fh)
*/ **/
bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr) bool I2C_slave_set_addr(I2C_SLAVE * slave, const uint8_t sl_addr)
{ {
if (sl_addr > 0x7F) { return false; } if (sl_addr > 0x7F) { return false; }
slave->cfg.addr = sl_addr; slave->cfg.addr = sl_addr;
return true; return true;
} }
/*! \brief Change I2C registers map size (for access) /*!\brief Change I2C registers map size (for access)
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_sz - internal register map size ** \param [in] reg_sz - internal register map size
* \return true if new size is correct (false otherwise and set to 16bit by default) ** \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) 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; slave->cfg.reg_size = reg_sz > I2C_16B_REG ? I2C_16B_REG : reg_sz;
return !(reg_sz > I2C_16B_REG); return !(reg_sz > I2C_16B_REG);
} }
/*! \brief Set I2C current register address /*!\brief Set I2C current register address
* \attribute inline ** \attribute inline
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address ** \param [in] reg_addr - register address
* \return nothing ** \return nothing
*/ **/
static inline void __attribute__((__always_inline__)) 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, const uint16_t reg_addr) {
{ slave->reg_addr = reg_addr; }
slave->reg_addr = reg_addr;
}
/*! \brief Enable I2c module on arduino board (including pull-ups, /*!\brief Enable I2c module on arduino board (including pull-ups,
* enabling of ACK, and setting clock frequency) * enabling of ACK, and setting clock frequency)
* \param [in] speed - I2C bus speed in KHz ** \param [in] speed - I2C bus speed in KHz
* \return nothing ** \return nothing
*/ **/
void I2C_init(uint16_t speed) void I2C_init(const uint16_t speed)
{ {
// Set SDA and SCL to ports with pull-ups // Set SDA and SCL to ports with pull-ups
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
@ -140,9 +133,9 @@ void I2C_init(uint16_t speed)
(void) I2C_set_speed(speed); (void) I2C_set_speed(speed);
} }
/*! \brief Disable I2c module on arduino board (releasing pull-ups, and TWI control) /*!\brief Disable I2c module on arduino board (releasing pull-ups, and TWI control)
* \return nothing ** \return nothing
*/ **/
void I2C_uninit() void I2C_uninit()
{ {
// Release SDA and SCL ports pull-ups // Release SDA and SCL ports pull-ups
@ -158,9 +151,9 @@ void I2C_uninit()
} }
/*! \brief I2C bus reset (Release SCL and SDA lines and re-enable module) /*!\brief I2C bus reset (Release SCL and SDA lines and re-enable module)
* \return nothing ** \return nothing
*/ **/
void I2C_reset(void) void I2C_reset(void)
{ {
TWCR = 0; TWCR = 0;
@ -168,68 +161,66 @@ void I2C_reset(void)
setRegBit(TWCR, TWEN); setRegBit(TWCR, TWEN);
} }
/*! \brief Change I2C frequency /*!\brief Change I2C frequency
* \param [in] speed - I2C speed in kHz (max 1MHz) ** \param [in] speed - I2C speed in KHz (max 400KHz on avr)
* \return true if change is successful (false otherwise) ** \return Configured bus speed
*/ **/
bool I2C_set_speed(uint16_t 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_HS) ? (uint16_t) I2C_STD : 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 clrRegBit(TWCR, TWEN); // Ensure i2c module is disabled
// Set prescaler and clock frequency // Set prescaler and clock frequency
clrRegBit(TWSR, TWPS0); clrRegBit(TWSR, TWPS0);
clrRegBit(TWSR, TWPS1); clrRegBit(TWSR, TWPS1);
TWBR = ((F_CPU / (i2c.cfg.speed * 1000)) - 16) / 2; TWBR = (((F_CPU / 1000) / i2c.cfg.speed) - 16) / 2;
I2C_reset(); // re-enable module I2C_reset(); // re-enable module
return (i2c.cfg.speed == speed); return i2c.cfg.speed;
} }
/*! \brief Change I2C ack timeout /*!\brief Change I2C ack timeout
* \param [in] timeout - I2C ack timeout (500 ms max) ** \param [in] timeout - I2C ack timeout (500 ms max)
* \return true if change is successful (false otherwise) ** \return Configured timeout
*/ **/
bool I2C_set_timeout(uint16_t timeout) uint16_t I2C_set_timeout(const uint16_t timeout)
{ {
static const uint16_t max_timeout = 500; static const uint16_t max_timeout = 500;
i2c.cfg.timeout = (timeout > max_timeout) ? max_timeout : timeout; i2c.cfg.timeout = (timeout > max_timeout) ? max_timeout : timeout;
return (i2c.cfg.timeout == timeout); return i2c.cfg.timeout;
} }
/*! \brief Change I2C message retries (in case of failure) /*!\brief Change I2C message retries (in case of failure)
* \param [in] retries - I2C number of retries (max of 8) ** \param [in] retries - I2C number of retries (max of 8)
* \return true if change is successful (false otherwise) ** \return Configured number of retries
*/ **/
bool I2C_set_retries(uint8_t retries) uint8_t I2C_set_retries(const uint8_t retries)
{ {
static const uint16_t max_retries = 8; static const uint16_t max_retries = 8;
i2c.cfg.retries = (retries > max_retries) ? max_retries : retries; i2c.cfg.retries = (retries > max_retries) ? max_retries : retries;
return (i2c.cfg.retries == retries); return i2c.cfg.retries;
} }
/*! \brief Get I2C busy status /*!\brief Get I2C busy status
* \return true if busy ** \return true if busy
*/ **/
bool I2C_is_busy(void) bool I2C_is_busy(void) {
{ return i2c.busy; }
return i2c.busy;
}
/*! \brief This function reads or writes the provided data to/from the address specified. /*!\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 * 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 * 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, out] slave - pointer to the I2C slave structure to init
* \param [in] reg_addr - register address in register map ** \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] 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] bytes - indicates how many bytes of data to write
* \param [in] rw - 0 = write, 1 = read operation ** \param [in] rw - 0 = write, 1 = read operation
* \return I2C_STATUS status of write attempt ** \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) 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; uint8_t retry = i2c.cfg.retries;
bool ack = false; bool ack = false;
@ -241,7 +232,7 @@ static I2C_STATUS I2C_comm(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data,
ack = fc(slave, reg_addr, data, bytes); ack = fc(slave, reg_addr, data, bytes);
while ((!ack) && (retry != 0)) // If com not successful, retry some more times while ((!ack) && (retry != 0)) // If com not successful, retry some more times
{ {
delay(5); delay(1);
ack = fc(slave, reg_addr, data, bytes); ack = fc(slave, reg_addr, data, bytes);
retry--; retry--;
} }
@ -250,53 +241,45 @@ static I2C_STATUS I2C_comm(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data,
return slave->status = ack ? I2C_OK : I2C_NACK; return slave->status = ack ? I2C_OK : I2C_NACK;
} }
/*! \brief This function writes the provided data to the address specified. /*!\brief This function writes the provided data to the address specified.
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address in register map ** \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] 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] bytes - indicates how many bytes of data to write
* \return I2C_STATUS status of write attempt ** \return I2C_STATUS status of write attempt
*/ **/
I2C_STATUS I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) 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); }
return I2C_comm(slave, reg_addr, data, bytes, I2C_WRITE);
}
/*! \brief This function reads data from the address specified and stores this /*!\brief This function reads data from the address specified and stores this
* data in the area provided by the pointer. * data in the area provided by the pointer.
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address in register map ** \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, 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 ** \param [in] bytes - indicates how many bytes of data to read
* \return I2C_STATUS status of read attempt ** \return I2C_STATUS status of read attempt
*/ **/
I2C_STATUS I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) 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); }
return I2C_comm(slave, reg_addr, data, bytes, I2C_READ);
}
/*! \brief Start i2c_timeout timer /*!\brief Start i2c_timeout timer
* \attribute inline ** \attribute inline
* \return nothing ** \return nothing
*/ **/
static inline void __attribute__((__always_inline__)) I2C_start_timeout(void) static inline void __attribute__((__always_inline__)) I2C_start_timeout(void) {
{ i2c.start_wait = (uint16_t) millis(); }
i2c.start_wait = (uint16_t) millis();
}
/*! \brief Test i2c_timeout /*!\brief Test i2c_timeout
* \attribute inline ** \attribute inline
* \return true if i2c_timeout occured (false otherwise) ** \return true if i2c_timeout occured (false otherwise)
*/ **/
static inline uint8_t __attribute__((__always_inline__)) I2C_timeout(void) static inline uint8_t __attribute__((__always_inline__)) I2C_timeout(void) {
{ return (((uint16_t) millis() - i2c.start_wait) >= i2c.cfg.timeout); }
return (((uint16_t) millis() - i2c.start_wait) >= i2c.cfg.timeout);
}
/*! \brief Send start condition /*!\brief Send start condition
* \return true if start condition acknowledged (false otherwise) ** \return true if start condition acknowledged (false otherwise)
*/ **/
bool I2C_start(void) bool I2C_start(void)
{ {
I2C_start_timeout(); I2C_start_timeout();
@ -312,9 +295,9 @@ bool I2C_start(void)
return false; return false;
} }
/*! \brief Send stop condition /*!\brief Send stop condition
* \return true if stop condition acknowledged (false otherwise) ** \return true if stop condition acknowledged (false otherwise)
*/ **/
bool I2C_stop(void) bool I2C_stop(void)
{ {
I2C_start_timeout(); I2C_start_timeout();
@ -327,11 +310,11 @@ bool I2C_stop(void)
return true; return true;
} }
/*! \brief Send byte on bus /*!\brief Send byte on bus
* \param [in] dat - data to be sent ** \param [in] dat - data to be sent
* \return true if data sent acknowledged (false otherwise) ** \return true if data sent acknowledged (false otherwise)
*/ **/
bool I2C_wr8(uint8_t dat) bool I2C_wr8(const uint8_t dat)
{ {
TWDR = dat; TWDR = dat;
@ -350,11 +333,11 @@ bool I2C_wr8(uint8_t dat)
return false; return false;
} }
/*! \brief Receive byte from bus /*!\brief Receive byte from bus
* \param [in] ack - true if wait for ack ** \param [in] ack - true if wait for ack
* \return true if data reception acknowledged (false otherwise) ** \return true if data reception acknowledged (false otherwise)
*/ **/
uint8_t I2C_rd8(bool ack) uint8_t I2C_rd8(const bool ack)
{ {
I2C_start_timeout(); I2C_start_timeout();
@ -369,12 +352,12 @@ uint8_t I2C_rd8(bool ack)
return ((((TWI_STATUS == MR_DATA_NACK) && (!ack)) || ((TWI_STATUS == MR_DATA_ACK) && (ack))) ? true : false); return ((((TWI_STATUS == MR_DATA_NACK) && (!ack)) || ((TWI_STATUS == MR_DATA_ACK) && (ack))) ? true : false);
} }
/*! \brief Send I2C address /*!\brief Send I2C address
* \param [in] slave - pointer to the I2C slave structure ** \param [in] slave - pointer to the I2C slave structure
* \param [in] rw - read/write transaction ** \param [in] rw - read/write transaction
* \return true if I2C chip address sent acknowledged (false otherwise) ** \return true if I2C chip address sent acknowledged (false otherwise)
*/ **/
bool I2C_sndAddr(I2C_SLAVE * slave, I2C_RW rw) bool I2C_sndAddr(I2C_SLAVE * slave, const I2C_RW rw)
{ {
TWDR = (slave->cfg.addr << 1) | rw; TWDR = (slave->cfg.addr << 1) | rw;
@ -394,21 +377,23 @@ bool I2C_sndAddr(I2C_SLAVE * slave, I2C_RW rw)
} }
/*! \brief This procedure calls appropriate functions to perform a proper send transaction on I2C bus. /*!\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, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address in register map ** \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] 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] 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
*/ **/
static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) static bool I2C_wr(I2C_SLAVE * slave, const uint16_t reg_addr, uint8_t * data, const uint16_t bytes)
{ {
(void) I2C_slave_set_reg_addr(slave, 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
{ {
(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 (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; }
@ -428,21 +413,21 @@ 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. /*!\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, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address in register map ** \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, 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 ** \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
*/ **/
static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t bytes) 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); (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_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,8 +1,8 @@
/*!\file ci2c.h /*!\file ci2c.h
** \author SMFSW ** \author SMFSW
** \version 0.6 ** \copyright MIT SMFSW (2017-2018)
** \copyright MIT SMFSW (2017)
** \brief arduino i2c in plain c declarations ** \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__ #ifndef __CI2C_H__
@ -25,49 +25,49 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C"{ extern "C" {
#endif #endif
#define DEF_CI2C_NB_RETRIES 3 //!< Default cI2C transaction retries #define DEF_CI2C_NB_RETRIES 3 //!< Default cI2C transaction retries
#define DEF_CI2C_TIMEOUT 100 //!< Default cI2C timeout #define DEF_CI2C_TIMEOUT 100 //!< Default cI2C timeout
/*! \enum enI2C_RW /*!\enum enI2C_RW
* \brief I2C RW bit enumeration ** \brief I2C RW bit enumeration
* \attribute packed enum ** \attribute packed enum
*/ **/
typedef enum __attribute__((__packed__)) enI2C_RW { typedef enum __attribute__((__packed__)) enI2C_RW {
I2C_WRITE = 0, //!< I2C rw bit (write) I2C_WRITE = 0, //!< I2C rw bit (write)
I2C_READ //!< I2C rw bit (read) I2C_READ //!< I2C rw bit (read)
} I2C_RW; } I2C_RW;
/*! \enum enI2C_SPEED /*!\enum enI2C_SPEED
* \brief I2C bus speed ** \brief I2C bus speed
* \attribute packed enum ** \attribute packed enum
*/ **/
typedef enum __attribute__((__packed__)) enI2C_SPEED { typedef enum __attribute__((__packed__)) enI2C_SPEED {
I2C_STD = 100, //!< I2C Standard (100KHz) I2C_STD = 100, //!< I2C Standard (100KHz)
I2C_FM = 400, //!< I2C Fast Mode (400KHz) I2C_FM = 400, //!< I2C Fast Mode (400KHz)
I2C_FMP = 1000, //!< I2C Fast mode + (1MHz) 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) I2C_HS = 3400 //!< I2C High Speed (3.4MHz): will set speed to Fast Mode (up to 400KHz on avr)
} I2C_SPEED; } I2C_SPEED;
/*! \enum enI2C_STATUS /*!\enum enI2C_STATUS
* \brief I2C slave status ** \brief I2C slave status
* \attribute packed enum ** \attribute packed enum
*/ **/
typedef enum __attribute__((__packed__)) enI2C_STATUS { typedef enum __attribute__((__packed__)) enI2C_STATUS {
I2C_OK = 0x00, //!< I2C OK I2C_OK = 0x00, //!< I2C OK
I2C_BUSY, //!< I2C Bus busy I2C_BUSY, //!< I2C Bus busy
I2C_NACK //!< I2C Not Acknowledge I2C_NACK //!< I2C Not Acknowledge
} I2C_STATUS; } I2C_STATUS;
/*! \enum enI2C_INT_SIZE /*!\enum enI2C_INT_SIZE
* \brief I2C slave internal address registers size ** \brief I2C slave internal address registers size
* \attribute packed enum ** \attribute packed enum
*/ **/
typedef enum __attribute__((__packed__)) enI2C_INT_SIZE { typedef enum __attribute__((__packed__)) enI2C_INT_SIZE {
I2C_NO_REG = 0x00, //!< Internal address registers not applicable for slave I2C_NO_REG = 0x00, //!< Internal address registers not applicable for slave
I2C_8B_REG, //!< Slave internal address registers space is 8bits wide I2C_8B_REG, //!< Slave internal address registers space is 8bits wide
@ -75,16 +75,17 @@ typedef enum __attribute__((__packed__)) enI2C_INT_SIZE {
} I2C_INT_SIZE; } I2C_INT_SIZE;
typedef bool (*ci2c_fct_ptr) (const void*, uint16_t, uint8_t*, uint16_t); //!< i2c read/write function pointer typedef 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 /*!\struct StructI2CSlave
* \attribute packed struct ** \brief ci2c slave config and control parameters
*/ ** \attribute packed struct
**/
typedef struct __attribute__((__packed__)) StructI2CSlave { typedef struct __attribute__((__packed__)) StructI2CSlave {
/*! \struct cfg /*!\struct cfg
* \brief ci2c slave parameters ** \brief ci2c slave parameters
*/ **/
struct { struct {
uint8_t addr; //!< Slave address uint8_t addr; //!< Slave address
I2C_INT_SIZE reg_size; //!< Slave internal registers size I2C_INT_SIZE reg_size; //!< Slave internal registers size
@ -100,183 +101,177 @@ typedef struct __attribute__((__packed__)) StructI2CSlave {
/*** I2C SLAVE FUNCTIONS ***/ /*** I2C SLAVE FUNCTIONS ***/
/***************************/ /***************************/
/*! \brief Init an I2C slave structure for cMI2C communication /*!\brief Init an I2C slave structure for cMI2C communication
* \param [in] slave - pointer to the I2C slave structure to init ** \param [in] slave - pointer to the I2C slave structure to init
* \param [in] sl_addr - I2C slave address ** \param [in] sl_addr - I2C slave address
* \param [in] reg_sz - internal register map size ** \param [in] reg_sz - internal register map size
* \return nothing ** \return nothing
*/ **/
extern void I2C_slave_init(I2C_SLAVE * slave, uint8_t sl_addr, I2C_INT_SIZE reg_sz); 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) /*!\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] slave - pointer to the I2C slave structure to init
* \param [in] func - pointer to read/write function to affect ** \param [in] func - pointer to read/write function to affect
* \param [in] rw - 0 = write function, 1 = read function ** \param [in] rw - 0 = write function, 1 = read function
* \return nothing ** \return nothing
*/ **/
extern void I2C_slave_set_rw_func(I2C_SLAVE * slave, ci2c_fct_ptr func, I2C_RW rw); void I2C_slave_set_rw_func(I2C_SLAVE * slave, const ci2c_fct_ptr func, const I2C_RW rw);
/*! \brief Change I2C slave address /*!\brief Change I2C slave address
* \param [in, out] slave - pointer to the I2C slave structure to init ** \param [in, out] slave - pointer to the I2C slave structure to init
* \param [in] sl_addr - I2C slave address ** \param [in] sl_addr - I2C slave address
* \return true if new address set (false if address is >7Fh) ** \return true if new address set (false if address is >7Fh)
*/ **/
extern bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr); bool I2C_slave_set_addr(I2C_SLAVE * slave, const uint8_t sl_addr);
/*! \brief Change I2C registers map size (for access) /*!\brief Change I2C registers map size (for access)
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_sz - internal register map size ** \param [in] reg_sz - internal register map size
* \return true if new size is correct (false otherwise and set to 16bit by default) ** \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); bool I2C_slave_set_reg_size(I2C_SLAVE * slave, const I2C_INT_SIZE reg_sz);
/*! \brief Get I2C slave address /*!\brief Get I2C slave address
* \attribute inline ** \attribute inline
* \param [in] slave - pointer to the I2C slave structure ** \param [in] slave - pointer to the I2C slave structure
* \return I2C slave address ** \return I2C slave address
*/ **/
inline uint8_t __attribute__((__always_inline__)) I2C_slave_get_addr(I2C_SLAVE * slave) inline uint8_t __attribute__((__always_inline__)) I2C_slave_get_addr(const I2C_SLAVE * slave) {
{ return slave->cfg.addr; }
return slave->cfg.addr;
}
/*! \brief Get I2C register map size (for access) /*!\brief Get I2C register map size (for access)
* \attribute inline ** \attribute inline
* \param [in] slave - pointer to the I2C slave structure ** \param [in] slave - pointer to the I2C slave structure
* \return register map using 16bits if true (1Byte otherwise) ** \return register map using 16bits if true (1Byte otherwise)
*/ **/
inline bool __attribute__((__always_inline__)) I2C_slave_get_reg_size(I2C_SLAVE * slave) inline bool __attribute__((__always_inline__)) I2C_slave_get_reg_size(const I2C_SLAVE * slave) {
{ return slave->cfg.reg_size; }
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; }
/*! \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 ***/ /*** I2C BUS FUNCTIONS ***/
/*************************/ /*************************/
/*! \brief Enable I2c module on arduino board (including pull-ups, /*!\brief Enable I2c module on arduino board (including pull-ups,
* enabling of ACK, and setting clock frequency) * enabling of ACK, and setting clock frequency)
* \param [in] speed - I2C bus speed in KHz ** \param [in] speed - I2C bus speed in KHz
* \return nothing ** \return nothing
*/ **/
extern void I2C_init(uint16_t speed); void I2C_init(const uint16_t speed);
/*! \brief Disable I2c module on arduino board (releasing pull-ups, and TWI control) /*!\brief Disable I2c module on arduino board (releasing pull-ups, and TWI control)
* \return nothing ** \return nothing
*/ **/
extern void I2C_uninit(); void I2C_uninit();
/*! \brief Change I2C frequency /*!\brief Change I2C frequency
* \param [in] speed - I2C bus speed in KHz ** \param [in] speed - I2C bus speed in KHz (max 400KHz on AVR)
* \return true if change is successful (false otherwise) ** \return Configured bus speed
*/ **/
extern bool I2C_set_speed(uint16_t speed); uint16_t I2C_set_speed(const uint16_t speed);
/*! \brief Change I2C ack timeout /*!\brief Change I2C ack timeout
* \param [in] timeout - I2C ack timeout (500 ms max) ** \param [in] timeout - I2C ack timeout (500 ms max)
* \return true if change is successful (false otherwise) ** \return Configured timeout
*/ **/
extern bool I2C_set_timeout(uint16_t timeout); uint16_t I2C_set_timeout(const uint16_t timeout);
/*! \brief Change I2C message retries (in case of failure) /*!\brief Change I2C message retries (in case of failure)
* \param [in] retries - I2C number of retries (max of 8) ** \param [in] retries - I2C number of retries (max of 8)
* \return true if change is successful (false otherwise) ** \return Configured number of retries
*/ **/
extern bool I2C_set_retries(uint8_t retries); uint8_t I2C_set_retries(const uint8_t retries);
/*! \brief Get I2C busy status /*!\brief Get I2C busy status
* \return true if busy ** \return true if busy
*/ **/
extern bool I2C_is_busy(void); bool I2C_is_busy(void);
/*! \brief This function writes the provided data to the address specified. /*!\brief This function writes the provided data to the address specified.
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address in register map ** \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] 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] bytes - indicates how many bytes of data to write
* \return I2C_STATUS status of write attempt ** \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); 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 /*!\brief This inline is a wrapper to I2C_write in case of contigous operations
* \attribute inline ** \attribute inline
* \param [in, out] slave - pointer to the I2C slave structure ** \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] 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] bytes - indicates how many bytes of data to write
* \return I2C_STATUS status of write attempt ** \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) 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); }
// 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 /*!\brief This function reads data from the address specified and stores this
* data in the area provided by the pointer. * data in the area provided by the pointer.
* \param [in, out] slave - pointer to the I2C slave structure ** \param [in, out] slave - pointer to the I2C slave structure
* \param [in] reg_addr - register address in register map ** \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, 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 ** \param [in] bytes - indicates how many bytes of data to read
* \return I2C_STATUS status of read attempt ** \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); 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 /*!\brief This inline is a wrapper to I2C_read in case of contigous operations
* \attribute inline ** \attribute inline
* \param [in, out] slave - pointer to the I2C slave structure ** \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] data - pointer to the first byte of a block of data to read
* \param [in] bytes - indicates how many bytes of data to read ** \param [in] bytes - indicates how many bytes of data to read
* \return I2C_STATUS status of read attempt ** \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) 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); }
// 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 ***/ /*** cI2C LOW LEVEL FUNCTIONS ***/
/*** THAT MAY BE USEFUL FOR DVPT ***/ /*** THAT MAY BE USEFUL FOR DVPT ***/
/***********************************/ /***********************************/
/*! \brief I2C bus reset (Release SCL and SDA lines and re-enable module) /*!\brief I2C bus reset (Release SCL and SDA lines and re-enable module)
* \return nothing ** \return nothing
*/ **/
extern void I2C_reset(void); void I2C_reset(void);
/*! \brief Send start condition
* \return true if start condition acknowledged (false otherwise) /*!\brief Send start condition
*/ ** \return true if start condition acknowledged (false otherwise)
extern bool I2C_start(void); **/
/*! \brief Send stop condition bool I2C_start(void);
* \return true if stop condition acknowledged (false otherwise)
*/ /*!\brief Send stop condition
extern bool I2C_stop(void); ** \return true if stop condition acknowledged (false otherwise)
/*! \brief Send byte on bus **/
* \param [in] dat - data to be sent bool I2C_stop(void);
* \return true if data sent acknowledged (false otherwise)
*/ /*!\brief Send byte on bus
extern bool I2C_wr8(uint8_t dat); ** \param [in] dat - data to be sent
/*! \brief Receive byte from bus ** \return true if data sent acknowledged (false otherwise)
* \param [in] ack - true if wait for ack **/
* \return true if data reception acknowledged (false otherwise) bool I2C_wr8(const uint8_t dat);
*/
extern uint8_t I2C_rd8(bool ack); /*!\brief Receive byte from bus
/*! \brief Send I2C address ** \param [in] ack - true if wait for ack
* \param [in] slave - pointer to the I2C slave structure ** \return true if data reception acknowledged (false otherwise)
* \param [in] rw - read/write transaction **/
* \return true if I2C chip address sent acknowledged (false otherwise) uint8_t I2C_rd8(const bool ack);
*/
extern bool I2C_sndAddr(I2C_SLAVE * slave, I2C_RW rw); /*!\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 #ifdef __cplusplus