1
0
mirror of https://github.com/SMFSW/cI2C synced 2024-11-25 03:04:21 +01:00

v0.2: first commit

This commit is contained in:
SMFSW 2017-01-16 19:22:03 +01:00
parent 67ecd8ce83
commit 588c8106a3
9 changed files with 3429 additions and 21 deletions

2408
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,13 @@
MIT License The MIT License (MIT)
Copyright (c) 2017 SMFSW (Sebastien Bizien)
Copyright (c) 2017 SMFSW
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

507
ci2c.c Normal file
View File

@ -0,0 +1,507 @@
/*!\file ci2c.c
** \author SMFSW
** \version 0.2
** \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 isSetBitReg(v, b) ((v & (1 << b)) != 0)
//#define isClrBitReg(v, b) ((v & (1 << b)) == 0)
#define setBitReg(v, b) v |= (1 << b)
#define clrBitReg(v, b) v &= (uint8_t) (~(1 << b))
#define invBitReg(v, b) v ^= (1 << b)
static struct {
struct {
I2C_SPEED speed; //!< i2c bus speed
uint8_t retries; //!< i2c message retries when fail
uint16_t timeout; //!< i2c timeout (ms)
} cfg;
uint16_t start_wait;
bool busy;
} i2c = { {0, DEF_CI2C_NB_RETRIES, DEF_CI2C_TIMEOUT }, 0, false };
// Needed prototypes
static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes);
static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes);
/*! \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, I2C_rd, 0);
I2C_slave_set_rw_func(slave, I2C_wr, 1);
slave->reg_addr = 0;
slave->status = I2C_OK;
}
/*! \brief Redirect slave I2C read/write function (if needed for advanced use)
*! \param [in] slave - pointer to the I2C slave structure to init
*! \param [in] func - pointer to read/write function to affect
*! \param [in] rw - 0 = read function, 1 = write function
*! \return nothing
*/
void I2C_slave_set_rw_func(I2C_SLAVE * slave, void * func, bool rw)
{
ci2c_fct_ptr * pfc = (ci2c_fct_ptr*) (rw ? &slave->cfg.wr : &slave->cfg.rd);
*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)
*/
inline 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)
*/
inline 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;
if (reg_sz > I2C_16B_REG) { return false; }
else { return true; }
}
/*! \brief Set I2C current register address
*! \param [in, out] slave - pointer to the I2C slave structure
*! \param [in] reg_addr - register address
*! \return nothing
*/
static inline void I2C_slave_set_reg_addr(I2C_SLAVE * slave, uint16_t reg_addr)
{
slave->reg_addr = reg_addr;
}
/*! \brief Get I2C slave address
*! \param [in] slave - pointer to the I2C slave structure
*! \return I2C slave address
*/
inline uint8_t I2C_slave_get_addr(I2C_SLAVE * slave)
{
return slave->cfg.addr;
}
/*! \brief Get I2C register map size (for access)
*! \param [in] slave - pointer to the I2C slave structure
*! \return register map using 16bits if true (1Byte otherwise)
*/
inline bool I2C_slave_get_reg_size(I2C_SLAVE * slave)
{
return slave->cfg.reg_size;
}
/*! \brief Get I2C current register address (addr may passed this way in procedures if contigous accesses)
*! \param [in] slave - pointer to the I2C slave structure
*! \return current register map address
*/
inline uint16_t I2C_slave_get_reg_addr(I2C_SLAVE * slave)
{
return slave->reg_addr;
}
/*! \brief Enable I2c module on arduino board (including pull-ups,
*! enabling of ACK, and setting clock frequency)
*! \param [in] speed - I2C bus speed in KHz
*! \return nothing
*/
void I2C_init(uint16_t speed)
{
// Set SDA and SCL to ports
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__)
setBitReg(PORTC, 4);
setBitReg(PORTC, 5);
#else
setBitReg(PORTD, 0);
setBitReg(PORTD, 1);
#endif
(void) I2C_set_speed(speed);
}
/*! \brief I2C bus reset (Release SCL and SDA lines and re-enable module)
*! \return nothing
*/
void I2C_reset(void)
{
TWCR = 0;
setBitReg(TWCR, TWEA);
setBitReg(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 ? I2C_SLOW : (speed > I2C_FAST ? I2C_SLOW : speed));
// Ensure i2c module is disabled
clrBitReg(TWCR, TWEN);
// Set prescaler and clock frequency
clrBitReg(TWSR, TWPS0);
clrBitReg(TWSR, TWPS1);
TWBR = ((F_CPU / (i2c.cfg.speed * 1000)) - 16) / 2; // TODO: check speed and make it a param
// re-enable module
I2C_reset();
return i2c.cfg.speed == speed ? true : false;
}
/*! \brief Change I2C ack timeout
*! \param [in] timeout - I2C ack timeout (500 ms max)
*! \return true if change is successful (false otherwise)
*/
inline bool I2C_set_timeout(uint16_t timeout)
{
i2c.cfg.timeout = timeout > 500 ? 500 : timeout;
return i2c.cfg.timeout == timeout ? true : false;
}
/*! \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)
*/
inline bool I2C_set_retries(uint8_t retries)
{
i2c.cfg.retries = retries > 8 ? 8 : retries;
return i2c.cfg.retries == retries ? true : false;
}
/*! \brief Get I2C busy status
*! \return true if busy
*/
inline 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] nb_bytes - indicates how many bytes of data to write
*! \param [in] wr - 0 = read, 1 = write operation
*! \return I2C_STATUS status of write attempt
*/
static I2C_STATUS I2C_comm(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes, bool wr)
{
uint8_t retry = i2c.cfg.retries;
bool ack = false;
ci2c_fct_ptr fc = (ci2c_fct_ptr) (wr ? slave->cfg.wr : slave->cfg.rd);
if (I2C_is_busy()) { return slave->status = I2C_BUSY; }
i2c.busy = true;
ack = fc(slave, reg_addr, data, nb_bytes);
while ((!ack) && (retry != 0)) // If com not successful, retry some more times
{
delay(5);
ack = fc(slave, reg_addr, data, nb_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 to init
*! \param [in] reg_addr - register address in register map
*! \param [in] data - pointer to the first byte of a block of data to write
*! \param [in] nb_bytes - indicates how many bytes of data to write
*! \return I2C_STATUS status of write attempt
*/
inline I2C_STATUS I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes)
{
return I2C_comm(slave, reg_addr, data, nb_bytes, 1);
}
/*! \brief This inline is a wrapper to I2C_write in case of contigous operations
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] data - pointer to the first byte of a block of data to write
*! \param [in] nb_bytes - indicates how many bytes of data to write
*! \return I2C_STATUS status of write attempt
*/
inline I2C_STATUS I2C_write_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes)
{
// TODO: implement read next so that it doesn't have to send start register address again
return I2C_write(slave, slave->reg_addr, data, nb_bytes);
}
/*! \brief This function reads data from the address specified and stores this
*! data in the area provided by the pointer.
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] reg_addr - register address in register map
*! \param [in, out] data - pointer to the first byte of a block of data to read
*! \param [in] nb_bytes - indicates how many bytes of data to read
*! \return I2C_STATUS status of read attempt
*/
inline I2C_STATUS I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes)
{
return I2C_comm(slave, reg_addr, data, nb_bytes, 0);
}
/*! \brief This inline is a wrapper to I2C_read in case of contigous operations
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] data - pointer to the first byte of a block of data to read
*! \param [in] nb_bytes - indicates how many bytes of data to read
*! \return I2C_STATUS status of read attempt
*/
inline I2C_STATUS I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes)
{
// TODO: implement read next so that it doesn't have to send start register address again
return I2C_read(slave, slave->reg_addr, data, nb_bytes);
}
/*! \brief Start i2c_timeout timer
*! \return nothing
*/
static inline void I2C_start_timeout(void)
{
i2c.start_wait = (uint16_t) millis();
}
/*! \brief Test i2c_timeout
*! \return true if i2c_timeout occured (false otherwise)
*/
static inline uint8_t 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 I2C address
*! \param [in] sl_addr - I2C slave address
*! \return true if I2C slave address sent acknowledged (false otherwise)
*/
bool I2C_sndAddr(uint8_t sl_addr)
{
TWDR = sl_addr;
I2C_start_timeout();
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)))
{ if (I2C_timeout()) { I2C_reset(); return false; } }
if ((TWI_STATUS == MT_SLA_ACK) || (TWI_STATUS == MR_SLA_ACK)) { return true; }
if ((TWI_STATUS == MT_SLA_NACK) || (TWI_STATUS == MR_SLA_NACK)) { I2C_stop(); }
else { I2C_reset(); }
return false;
}
/*! \brief Send byte on bus
*! \param [in] dat - data to be sent
*! \return true if data sent acknowledged (false otherwise)
*/
bool I2C_snd8(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_rcv8(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; }
if (((TWI_STATUS == MR_DATA_NACK) && (!ack)) || ((TWI_STATUS == MR_DATA_ACK) && (ack))) { return true; }
else { return false; }
}
/*! \brief This procedure calls appropriate functions to perform a proper send transaction on I2C bus.
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] reg_addr - register address in register map
*! \param [in] data - pointer to the first byte of a block of data to write
*! \param [in] nb_bytes - indicates how many bytes of data to write
*! \return Boolean indicating success/fail of write attempt
*/
static bool I2C_wr(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes)
{
uint16_t ct_w;
(void) I2C_slave_set_reg_addr(slave, reg_addr);
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; }
if (slave->cfg.reg_size)
{
if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used
{
if (I2C_snd8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_snd8((uint8_t) reg_addr) == false) { return false; }
}
for (ct_w = 0; ct_w < nb_bytes; ct_w++)
{
if (I2C_snd8(*(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 to init
*! \param [in] reg_addr - register address in register map
*! \param [in, out] data - pointer to the first byte of a block of data to read
*! \param [in] nb_bytes - indicates how many bytes of data to read
*! \return Boolean indicating success/fail of read attempt
*/
static bool I2C_rd(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes)
{
uint16_t ct_r;
(void) I2C_slave_set_reg_addr(slave, reg_addr);
if (nb_bytes == 0) { nb_bytes++; }
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; }
if (slave->cfg.reg_size)
{
if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used
{
if (I2C_snd8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_snd8((uint8_t) reg_addr) == false) { return false; }
if (I2C_start() == false) { return false; }
if (I2C_sndAddr((slave->cfg.addr << 1) | 0x01) == false) { return false; }
}
for (ct_r = 0; ct_r < nb_bytes; ct_r++)
{
if (ct_r == (nb_bytes - 1))
{
if (I2C_rcv8(false) == false) { return false; }
}
else
{
if (I2C_rcv8(true) == false) { return false; }
}
*data++ = TWDR;
slave->reg_addr++;
}
if (I2C_stop() == false) { return false; }
return true;
}

229
ci2c.h Normal file
View File

@ -0,0 +1,229 @@
/*!\file ci2c.h
** \author SMFSW
** \version 0.2
** \copyright MIT SMFSW (2017)
** \brief arduino i2c in plain c declarations
**/
/****************************************************************/
#ifndef __CI2C_H__
#define __CI2C_H__ "v0.2"
/****************************************************************/
#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_SPEED 100000
#define DEF_CI2C_NB_RETRIES 3
#define DEF_CI2C_TIMEOUT 100
typedef enum __attribute__((__packed__)) enI2C_STATUS {
I2C_OK = 0x00,
I2C_BUSY,
I2C_NACK
} I2C_STATUS;
typedef enum __attribute__((__packed__)) enI2C_SPEED {
I2C_SLOW = 100, //!< I2C Slow speed (100KHz)
I2C_LOW = 400, //!< I2C Low speed (400KHz)
I2C_FAST = 1000 //!< I2C Fast mode (1MHz)
} I2C_SPEED;
typedef enum __attribute__((__packed__)) enI2C_INT_SIZE {
I2C_NO_REG = 0x00, //!< Internal adress registers not applicable for slave
I2C_8B_REG, //!< Slave internal adress registers space is 8bits wide
I2C_16B_REG //!< Slave internal adress registers space is 16bits wide
} I2C_INT_SIZE;
typedef bool (*ci2c_fct_ptr) (const void*, uint16_t, uint8_t*, uint16_t); //!< i2c read/write function typedef
typedef struct __attribute__((__packed__)) StructI2CSlave {
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 = read function, 1 = write function
*! \return nothing
*/
extern void I2C_slave_set_rw_func(I2C_SLAVE * slave, void * func, bool 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 inline bool I2C_slave_set_addr(I2C_SLAVE * slave, uint8_t sl_addr) __attribute__((__always_inline__));
/*! \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 inline bool I2C_slave_set_reg_size(I2C_SLAVE * slave, I2C_INT_SIZE reg_sz) __attribute__((__always_inline__));
/*! \brief Get I2C slave address
*! \param [in] slave - pointer to the I2C slave structure
*! \return I2C slave address
*/
extern inline uint8_t I2C_slave_get_addr(I2C_SLAVE * slave) __attribute__((__always_inline__));
/*! \brief Get I2C register map size (for access)
*! \param [in] slave - pointer to the I2C slave structure
*! \return register map using 16bits if true (1Byte otherwise)
*/
extern inline bool I2C_slave_get_reg_size(I2C_SLAVE * slave) __attribute__((__always_inline__));
/*! \brief Get I2C current register address (addr may passed this way in procedures if contigous accesses)
*! \param [in] slave - pointer to the I2C slave structure
*! \return current register map address
*/
extern inline uint16_t I2C_slave_get_reg_addr(I2C_SLAVE * slave) __attribute__((__always_inline__));
/*************************/
/*** 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 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 inline 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 inline bool I2C_set_retries(uint8_t retries);
/*! \brief Get I2C busy status
*! \return true if busy
*/
extern inline 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 to init
*! \param [in] reg_addr - register address in register map
*! \param [in] data - pointer to the first byte of a block of data to write
*! \param [in] nb_bytes - indicates how many bytes of data to write
*! \return I2C_STATUS status of write attempt
*/
extern inline I2C_STATUS I2C_write(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes);
/*! \brief This inline is a wrapper to I2C_write in case of contigous operations
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] data - pointer to the first byte of a block of data to write
*! \param [in] nb_bytes - indicates how many bytes of data to write
*! \return I2C_STATUS status of write attempt
*/
extern inline I2C_STATUS I2C_write_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_bytes);
/*! \brief This function reads data from the address specified and stores this
*! data in the area provided by the pointer.
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] reg_addr - register address in register map
*! \param [in, out] data - pointer to the first byte of a block of data to read
*! \param [in] nb_bytes - indicates how many bytes of data to read
*! \return I2C_STATUS status of read attempt
*/
extern inline I2C_STATUS I2C_read(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes);
/*! \brief This inline is a wrapper to I2C_read in case of contigous operations
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] data - pointer to the first byte of a block of data to read
*! \param [in] nb_bytes - indicates how many bytes of data to read
*! \return I2C_STATUS status of read attempt
*/
extern inline I2C_STATUS I2C_read_next(I2C_SLAVE * slave, uint8_t * data, uint16_t nb_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 I2C address
*! \param [in] sl_addr - I2C slave address
*! \return true if I2C chip address sent acknowledged (false otherwise)
*/
extern bool I2C_sndAddr(uint8_t sl_addr);
/*! \brief Send byte on bus
*! \param [in] dat - data to be sent
*! \return true if data sent acknowledged (false otherwise)
*/
extern bool I2C_snd8(uint8_t dat);
/*! \brief Receive byte from bus
*! \param [in] ack - true if wait for ack
*! \return true if data reception acknowledged (false otherwise)
*/
extern uint8_t I2C_rcv8(bool ack);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,127 @@
/*
Master i2c (advanced)
Redirecting write & read slave functions in setup (to custom functions following template)
Read and Write operations are then called using the same functions
This example code is in the public domain.
created Jan 12 2017
latest mod Jan 16 2017
by SMFSW
*/
#include <ci2c.h>
I2C_SLAVE FRAM;
void setup() {
Serial.begin(115200); // start serial for output
I2C_init(I2C_LOW); // init with low speed (400KHz)
I2C_slave_init(&FRAM, 0x50, I2C_16B_REG);
I2C_slave_set_rw_func(&FRAM, I2C_rd_advanced, 0);
I2C_slave_set_rw_func(&FRAM, I2C_wr_advanced, 1);
}
void loop() {
const uint16_t reg_addr = 0;
uint8_t str[7];
memset(&str, 0xEE, sizeof(str));
I2C_read(&FRAM, reg_addr, &str[0], sizeof(str)); // Addr 0, 2bytes Addr size, str, read chars for size of str
Serial.println();
for (uint8_t i = 0; i < sizeof(str); i++)
{
Serial.print(str[i], HEX); // receive a byte as character
Serial.print(" ");
}
delay(5000);
}
/*! \brief This procedure calls appropriate functions to perform a proper send transaction on I2C bus.
*!
*! \param [in, out] slave - pointer to the I2C slave structure to init
*! \param [in] reg_addr - register address in register map
*! \param [in] data - pointer to the first byte of a block of data to write
*! \param [in] nb_bytes - indicates how many bytes of data to write
*! \return Boolean indicating success/fail of write attempt
*/
static bool I2C_wr_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes)
{
uint16_t ct_w;
slave->reg_addr = reg_addr;
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; }
if (slave->cfg.reg_size)
{
if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used
{
if (I2C_snd8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_snd8((uint8_t) reg_addr) == false) { return false; }
}
for (ct_w = 0; ct_w < nb_bytes; ct_w++)
{
if (I2C_snd8(*(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 to init
*! \param [in] reg_addr - register address in register map
*! \param [in, out] data - pointer to the first byte of a block of data to read
*! \param [in] nb_bytes - indicates how many bytes of data to read
*! \return Boolean indicating success/fail of read attempt
*/
static bool I2C_rd_advanced(I2C_SLAVE * slave, uint16_t reg_addr, uint8_t * data, uint16_t nb_bytes)
{
uint16_t ct_r;
slave->reg_addr = reg_addr;
if (nb_bytes == 0) { nb_bytes++; }
if (I2C_start() == false) { return false; }
if (I2C_sndAddr(slave->cfg.addr << 1) == false) { return false; }
if (slave->cfg.reg_size)
{
if (slave->cfg.reg_size >= I2C_16B_REG) // if size >2, 16bit address is used
{
if (I2C_snd8((uint8_t) (reg_addr >> 8)) == false) { return false; }
}
if (I2C_snd8((uint8_t) reg_addr) == false) { return false; }
if (I2C_start() == false) { return false; }
if (I2C_sndAddr((slave->cfg.addr << 1) | 0x01) == false) { return false; }
}
for (ct_r = 0; ct_r < nb_bytes; ct_r++)
{
if (ct_r == (nb_bytes - 1))
{
if (I2C_rcv8(false) == false) { return false; }
}
else
{
if (I2C_rcv8(true) == false) { return false; }
}
*data++ = TWDR;
slave->reg_addr++;
}
if (I2C_stop() == false) { return false; }
return true;
}

View File

@ -0,0 +1,37 @@
/*
Master i2c read
Read some bytes in FRAM
This example code is in the public domain.
created Jan 12 2017
latest mod Jan 16 2017
by SMFSW
*/
#include <ci2c.h>
I2C_SLAVE FRAM;
void setup() {
Serial.begin(115200); // start serial for output
I2C_init(I2C_LOW); // init with low speed (400KHz)
I2C_slave_init(&FRAM, 0x50, I2C_16B_REG);
}
void loop() {
const uint16_t reg_addr = 0;
uint8_t str[7];
memset(&str, 0xEE, sizeof(str));
I2C_read(&FRAM, reg_addr, &str[0], sizeof(str)); // Addr 0, 2bytes Addr size, str, read chars for size of str
Serial.println();
for (uint8_t i = 0; i < sizeof(str); i++)
{
Serial.print(str[i], HEX); // receive a byte as character
Serial.print(" ");
}
delay(5000);
}

View File

@ -0,0 +1,45 @@
/*
Master i2c write
Write some bytes to FRAM and compare them
with what's read afterwards
This example code is in the public domain.
created Jan 12 2017
latest mod Jan 16 2017
by SMFSW
*/
#include <ci2c.h>
I2C_SLAVE FRAM;
void setup() {
Serial.begin(115200); // start serial for output
I2C_init(I2C_LOW); // init with low speed (400KHz)
I2C_slave_init(&FRAM, 0x50, I2C_16B_REG);
}
void loop() {
const uint16_t reg_addr = 0xF000;
uint8_t str[7] = { 0x55, 20, 30, 40, 50, 60, 0xAA };
bool match = true;
uint8_t str_out[7];
memset(&str_out, 0xEE, sizeof(str));
I2C_write(&FRAM, reg_addr, &str[0], sizeof(str)); // Addr 0, 2bytes Addr size, str, read chars for size of str
Serial.println();
I2C_read(&FRAM, reg_addr, &str_out[0], sizeof(str));
for (uint8_t i = 0; i < sizeof(str_out); i++)
{
if (str[i] != str_out[i]) { match = false; }
Serial.print(str_out[i], HEX); // receive a byte as character
Serial.print(" ");
}
Serial.print("WRITE ");
Serial.print(match ? "OK" : "NOK");
while(1);
}

49
keywords.txt Normal file
View File

@ -0,0 +1,49 @@
#######################################
# Syntax Coloring Map For cI2C library
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
I2C_STATUS KEYWORD1
I2C_INT_SIZE KEYWORD1
I2C_SLAVE KEYWORD1
ci2c_fct_ptr KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
I2C_slave_init KEYWORD2
I2C_slave_set_addr KEYWORD2
I2C_slave_set_rw_func KEYWORD2
I2C_slave_set_reg_size KEYWORD2
I2C_slave_get_addr KEYWORD2
I2C_slave_get_reg_size KEYWORD2
I2C_slave_get_reg_addr KEYWORD2
I2C_init KEYWORD2
I2C_set_timeout KEYWORD2
I2C_set_retries KEYWORD2
I2C_set_speed KEYWORD2
I2C_is_busy KEYWORD2
I2C_write KEYWORD2
I2C_write_next KEYWORD2
I2C_read KEYWORD2
I2C_read_next KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################
I2C_OK LITERAL1
I2C_BUSY LITERAL1
I2C_NACK LITERAL1
I2C_LOW LITERAL1
I2C_SLOW LITERAL1
I2C_FAST LITERAL1
I2C_NO_REG LITERAL1
I2C_8B_REG LITERAL1
I2C_16B_REG LITERAL1

10
library.properties Normal file
View File

@ -0,0 +1,10 @@
name=cI2C
version=0.2
author=SMFSW
maintainer=SMFSW
sentence=Arduino Hardware Master I2C for AVR (in plain c)
paragraph=Hardware I2C library for AVR µcontrollers (lib intended for Master I2C protocols development in c, for easier ports to other µcontrollers)
category=Communication
url=http://playground.arduino.cc/code/CI2C
architectures=avr