I2C sw driver with support of multiple buses, Slow, Fast, FastPlus, and user-defined speed selection (#2465)

* I2C driver speed-up, i2c.SLOW, i2c.FAST and user-defined speed selection

* - Multiple buses (up to 10) with different speeds on each bus
- Standard(Slow, 100kHz), Fast(400kHz) and FastPlus(1MHz) modes or an 
arbitrary clock speed
- Sharing SDA line over multiple I²C buses to save available pins
- GPIO16 pin can be used as SCL pin, but it does not support clock 
stretching and selected bus will be limited to FAST speed.

* Dynamic memory allocation, error checks, simplification, timing tweaks.

* Separated the code of old driver for better compatibility and simplicity

* Change of driver interface

* Add bus status check in setup(); simplify getDC(); remove unnesessary lines in ACK read/write

* Fix for moved doc file and trailing whitespaces
This commit is contained in:
Natalia 2019-04-05 07:56:11 +03:00 committed by Marcel Stör
parent 3f5ae99e42
commit ab61e9c03a
8 changed files with 630 additions and 257 deletions

View File

@ -1,20 +1,421 @@
/******************************************************************************
* Copyright 2013-2014 Espressif Systems (Wuxi)
/*
* ESPRESSIF MIT License
*
* FileName: i2c_master.c
* Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Description: i2c master API
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
* it is free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* Modification history:
* 2014/3/12, v1.0 create this file.
*******************************************************************************/
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* Rework of original driver: Natalia Sorokina <sonaux@gmail.com>, 2018
*/
#include "../libc/c_stdlib.h"
#include "ets_sys.h"
#include "osapi.h"
#include "gpio.h"
#include "user_interface.h"
#include "cpu_esp8266.h"
#include "pin_map.h"
#include "user_config.h"
#include "driver/i2c_master.h"
#include "pin_map.h"
#ifndef I2C_MASTER_OLD_VERSION
/******************************************************************************
* NEW driver
* Enabled if I2C_MASTER_OLD_VERSION is not defined in user_config.h
*******************************************************************************/
// Supports multiple i2c buses
// I2C speed in range 25kHz - 550kHz (25kHz - 1MHz if CPU at 160MHz)
// If GPIO16 is used as SCL then speed is limited to 25kHz - 400kHz
// Speed is defined for every bus separately
// enable use GPIO16 (D0) pin as SCL line
#ifdef I2C_MASTER_GPIO16_ENABLE
#define IS_PIN16(n) ((n)==16)
// CPU_CYCLES_BETWEEN_DELAYS describes how much cpu cycles code runs
// between i2c_master_setDC() calls if delay is zero and i2c_master_set_DC_delay()
// is not being called. This is not exact value, but proportional with length of code.
// Increasing the value results in less delay and faster i2c clock speed.
#define CPU_CYCLES_BETWEEN_DELAYS 80
// CPU_CYCLES_GPIO16 is added to CPU_CYCLES_BETWEEN_DELAYS,
// as RTC-related IO takes much more time than standard GPIOs.
// Increasing the value results in less delay and faster i2c clock speed for GPIO16.
#define CPU_CYCLES_GPIO16 90
#else
// If GPIO16 support is not enabled, remove GPIO16-related code during compile
// and change timing constants.
#define IS_PIN16(n) (0)
#define CPU_CYCLES_BETWEEN_DELAYS 74
#endif //I2C_MASTER_GPIO16_ENABLE
#define MIN_SPEED 25000
#define MAX_NUMBER_OF_I2C NUM_I2C
typedef struct {
uint8 last_SDA;
uint8 last_SCL;
uint8 pin_SDA;
uint8 pin_SCL;
uint32 pin_SDA_SCL_mask;
uint32 pin_SDA_mask;
uint32 pin_SCL_mask;
uint32 speed;
sint16 cycles_delay;
} i2c_master_state_t;
static i2c_master_state_t *i2c[MAX_NUMBER_OF_I2C];
/******************************************************************************
* FunctionName : i2c_master_set_DC_delay
* Description : Internal used function - calculate delay for i2c_master_setDC
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
i2c_master_set_DC_delay(uint16 id)
{
// [cpu cycles per half SCL clock period] - [cpu cycles that code takes to run]
i2c[id]->cycles_delay = system_get_cpu_freq() * 500000 / i2c[id]->speed - CPU_CYCLES_BETWEEN_DELAYS;
#ifdef I2C_MASTER_GPIO16_ENABLE
if(IS_PIN16(i2c[id]->pin_SCL)){ //if GPIO16
i2c[id]->cycles_delay -= CPU_CYCLES_GPIO16; //decrease delay
}
#endif //I2C_MASTER_GPIO16_ENABLE
if(i2c[id]->cycles_delay < 0){
i2c[id]->cycles_delay = 0;
}
}
/******************************************************************************
* FunctionName : i2c_master_wait_cpu_cycles
* Description : Internal used function - wait for given count of cpu cycles
* Parameters : sint16 cycles_delay
* Returns : NONE
*******************************************************************************/
static inline void i2c_master_wait_cpu_cycles(sint16 cycles_delay)
{
uint32 cycles_start;
uint32 cycles_curr;
// uses special 'ccount' register which is increased every CPU cycle
// to make precise delay
asm volatile("rsr %0, ccount":"=a"(cycles_start));
do{
asm volatile("rsr %0, ccount":"=a"(cycles_curr));
} while (cycles_curr - cycles_start < cycles_delay);
}
/******************************************************************************
* FunctionName : i2c_master_wait_gpio_SCL_high
* Description : Internal used function - wait until SCL line in a high state
(slave device may hold SCL line low until it is ready to proceed)
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
static inline void i2c_master_wait_gpio_SCL_high(uint16 id)
{
// retrieves bitmask of all GPIOs from memory-mapped gpio register and exits if SCL bit is set
// equivalent, but slow variant:
// while(!(READ_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS) & i2c[id]->pin_SCL_mask)) {};
// even slower: while (!(gpio_input_get() & i2c[id]->pin_SCL_mask)) {};
asm volatile("l_wait:"
"l16ui %0, %[gpio_in_addr], 0;" //read gpio state into register %0
"memw;" //wait for read completion
"bnall %0, %[gpio_SCL_mask], l_wait;" // test if SCL bit not set
::[gpio_SCL_mask] "r" (i2c[id]->pin_SCL_mask),
[gpio_in_addr] "r" (PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS)
);
}
/******************************************************************************
* FunctionName : i2c_master_setDC
* Description : Internal used function -
* set i2c SDA and SCL bit value for half clock cycle
* Parameters : bus id, uint8 SDA, uint8 SCL
* Returns : NONE
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
i2c_master_setDC(uint16 id, uint8 SDA, uint8 SCL)
{
uint32 this_SDA_SCL_set_mask;
uint32 this_SDA_SCL_clear_mask;
i2c[id]->last_SDA = SDA;
i2c[id]->last_SCL = SCL;
if(i2c[id]->cycles_delay > 0){
i2c_master_wait_cpu_cycles(i2c[id]->cycles_delay);
}
if (IS_PIN16(i2c[id]->pin_SCL)){ //GPIO16 wired differently, it has it's own register address
WRITE_PERI_REG(RTC_GPIO_OUT, SCL); // write SCL value
if(1 == SDA){
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, i2c[id]->pin_SDA_mask); //SDA = 1
}else{
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, i2c[id]->pin_SDA_mask); // SDA = 0
}
if(1 == SCL){ //clock stretching, GPIO16 version
while(!(READ_PERI_REG(RTC_GPIO_IN_DATA) & 1)) {}; //read SCL value until SCL goes high
}else{
// dummy read operation and empty CPU cycles to maintain equal times for low and high state
READ_PERI_REG(RTC_GPIO_IN_DATA) & 1; asm volatile("nop;nop;nop;nop;");
}
}
else{
this_SDA_SCL_set_mask = (SDA << i2c[id]->pin_SDA) | (SCL << i2c[id]->pin_SCL);
this_SDA_SCL_clear_mask = i2c[id]->pin_SDA_SCL_mask ^ this_SDA_SCL_set_mask;
GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, this_SDA_SCL_clear_mask);
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, this_SDA_SCL_set_mask);
if(1 == SCL) { //clock stretching
i2c_master_wait_gpio_SCL_high(id);
}else{
asm volatile("nop;nop;nop;"); // empty CPU cycles to maintain equal times for low and high state
}
}
}
/******************************************************************************
* FunctionName : i2c_master_getDC
* Description : Internal used function -
* get i2c SDA bit value
* Parameters : bus id
* Returns : uint8 - SDA bit value
*******************************************************************************/
static inline uint8 ICACHE_FLASH_ATTR
i2c_master_getDC(uint16 id)
{
return (READ_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS) >> i2c[id]->pin_SDA) & 1;
}
/******************************************************************************
* FunctionName : i2c_master_configured
* Description : checks if i2c bus is configured
* Parameters : bus id
* Returns : boolean value, true if configured
*******************************************************************************/
bool ICACHE_FLASH_ATTR
i2c_master_configured(uint16 id){
return !(NULL == i2c[id]);
}
/******************************************************************************
* FunctionName : i2c_master_init
* Description : initialize I2C bus to enable i2c operations
(reset state of all slave devices)
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_init(uint16 id)
{
uint8 i;
i2c_master_setDC(id, 1, 0);
// when SCL = 0, toggle SDA to clear up
i2c_master_setDC(id, 0, 0) ;
i2c_master_setDC(id, 1, 0) ;
// set data_cnt to max value
for (i = 0; i < 28; i++) {
i2c_master_setDC(id, 1, 0);
i2c_master_setDC(id, 1, 1);
}
// reset all
i2c_master_stop(id);
return;
}
/******************************************************************************
* FunctionName : i2c_master_setup
* Description : Initializes and configures the driver on given bus ID
* Parameters : bus id
* Returns : configured speed
*******************************************************************************/
uint32 ICACHE_FLASH_ATTR
i2c_master_setup(uint16 id, uint8 sda, uint8 scl, uint32 speed)
{
if(NULL == i2c[id]){
i2c[id] = (i2c_master_state_t*) c_malloc(sizeof(i2c_master_state_t));
}
if(NULL == i2c[id]){ // if malloc failed
return 0;
}
i2c[id]->last_SDA = 1; //default idle state
i2c[id]->last_SCL = 1;
i2c[id]->pin_SDA = pin_num[sda];
i2c[id]->pin_SCL = pin_num[scl];
i2c[id]->pin_SDA_mask = 1 << i2c[id]->pin_SDA;
i2c[id]->pin_SCL_mask = 1 << i2c[id]->pin_SCL;
i2c[id]->pin_SDA_SCL_mask = i2c[id]->pin_SDA_mask | i2c[id]->pin_SCL_mask;
i2c[id]->speed = speed;
i2c[id]->cycles_delay = 0;
if(i2c[id]->speed < MIN_SPEED){
i2c[id]->speed = MIN_SPEED;
}
i2c_master_set_DC_delay(id); // recalibrate clock
ETS_GPIO_INTR_DISABLE(); //disable gpio interrupts
if (IS_PIN16(i2c[id]->pin_SCL)){ //if GPIO16
CLEAR_PERI_REG_MASK(PAD_XPD_DCDC_CONF, 0x43); //disable all functions for XPD_DCDC
SET_PERI_REG_MASK(PAD_XPD_DCDC_CONF, 0x1); // select function RTC_GPIO0 for pin XPD_DCDC
CLEAR_PERI_REG_MASK(RTC_GPIO_CONF, 0x1); //mux configuration for out enable
SET_PERI_REG_MASK(RTC_GPIO_ENABLE, 0x1); //out enable
SET_PERI_REG_MASK(RTC_GPIO_OUT, 0x1); // set SCL high
}
else{
PIN_FUNC_SELECT(pin_mux[scl], pin_func[scl]);
SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR + GPIO_PIN_ADDR(GPIO_ID_PIN(i2c[id]->pin_SCL)),
GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //open drain
gpio_output_set(i2c[id]->pin_SCL_mask, 0, i2c[id]->pin_SCL_mask, 0); //enable and set high
}
PIN_FUNC_SELECT(pin_mux[sda], pin_func[sda]);
SET_PERI_REG_MASK(PERIPHS_GPIO_BASEADDR + GPIO_PIN_ADDR(GPIO_ID_PIN(i2c[id]->pin_SDA)),
GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //open drain
gpio_output_set(i2c[id]->pin_SDA_mask, 0, i2c[id]->pin_SDA_mask, 0); //enable and set high
ETS_GPIO_INTR_ENABLE(); //enable gpio interrupts
if (! (gpio_input_get() ^ i2c[id]->pin_SCL_mask)){ //SCL is in low state, bus failure
return 0;
}
i2c_master_init(id);
if (! (gpio_input_get() ^ i2c[id]->pin_SDA_mask)){ //SDA is in low state, bus failure
return 0;
}
return i2c[id]->speed;
}
/******************************************************************************
* FunctionName : i2c_master_start
* Description : set i2c to send state
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_start(uint16 id)
{
i2c_master_set_DC_delay(id); // recalibrate clock
i2c_master_setDC(id, 1, i2c[id]->last_SCL);
i2c_master_setDC(id, 1, 1);
i2c_master_setDC(id, 0, 1);
}
/******************************************************************************
* FunctionName : i2c_master_stop
* Description : set i2c to stop sending state
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_stop(uint16 id)
{
i2c_master_setDC(id, 0, i2c[id]->last_SCL);
i2c_master_setDC(id, 0, 1);
i2c_master_setDC(id, 1, 1);
}
/******************************************************************************
* FunctionName : i2c_master_readByte
* Description : read Byte from i2c bus
* Parameters : bus id
* Returns : uint8 - readed value
*******************************************************************************/
uint8 ICACHE_FLASH_ATTR
i2c_master_readByte(uint16 id, sint16 ack)
{
uint8 retVal = 0;
uint8 k;
sint8 i;
//invert and clamp ACK to 0/1, because ACK == 1 for i2c means SDA in low state
uint8 ackLevel = (ack ? 0 : 1);
i2c_master_setDC(id, i2c[id]->last_SDA, 0);
i2c_master_setDC(id, 1, 0);
for (i = 7; i >= 0; i--) {
i2c_master_setDC(id, 1, 1);
k = i2c_master_getDC(id);
i2c_master_setDC(id, 1, 0); // unnecessary in last iteration
k <<= i;
retVal |= k;
}
// set ACK
i2c_master_setDC(id, ackLevel, 0);
i2c_master_setDC(id, ackLevel, 1);
i2c_master_setDC(id, 1, 0);
return retVal;
}
/******************************************************************************
* FunctionName : i2c_master_writeByte
* Description : write wrdata value(one byte) into i2c
* Parameters : bus id, uint8 wrdata - write value
* Returns : NONE
*******************************************************************************/
uint8 ICACHE_FLASH_ATTR
i2c_master_writeByte(uint16 id, uint8 wrdata)
{
uint8 dat;
sint8 i;
uint8 retVal;
i2c_master_setDC(id, i2c[id]->last_SDA, 0);
for (i = 7; i >= 0; i--) {
dat = (wrdata >> i) & 1;
i2c_master_setDC(id, dat, 0);
i2c_master_setDC(id, dat, 1);
}
//get ACK
i2c_master_setDC(id, 1, 0);
i2c_master_setDC(id, 1, 1);
retVal = i2c_master_getDC(id);
i2c_master_setDC(id, 1, 0);
return ! retVal;
}
#else // if defined I2C_MASTER_OLD_VERSION
/******************************************************************************
* OLD driver
* Enabled when I2C_MASTER_OLD_VERSION is defined in user_config.h
*******************************************************************************/
#define I2C_MASTER_SDA_MUX (pin_mux[sda])
#define I2C_MASTER_SCL_MUX (pin_mux[scl])
#define I2C_MASTER_SDA_GPIO (pinSDA)
#define I2C_MASTER_SCL_GPIO (pinSCL)
#define I2C_MASTER_SDA_FUNC (pin_func[sda])
#define I2C_MASTER_SCL_FUNC (pin_func[scl])
#define I2C_MASTER_SDA_HIGH_SCL_HIGH() \
gpio_output_set(1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define I2C_MASTER_SDA_HIGH_SCL_LOW() \
gpio_output_set(1<<I2C_MASTER_SDA_GPIO, 1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define I2C_MASTER_SDA_LOW_SCL_HIGH() \
gpio_output_set(1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define I2C_MASTER_SDA_LOW_SCL_LOW() \
gpio_output_set(0, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define i2c_master_wait os_delay_us
#define I2C_MASTER_SPEED 100000
#define I2C_MASTER_BUS_ID 0
LOCAL uint8 m_nLastSDA;
LOCAL uint8 m_nLastSCL;
@ -26,8 +427,7 @@ LOCAL uint8 pinSCL = 15;
* FunctionName : i2c_master_setDC
* Description : Internal used function -
* set i2c SDA and SCL bit value for half clk cycle
* Parameters : uint8 SDA
* uint8 SCL
* Parameters : uint8 SDA, uint8 SCL
* Returns : NONE
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
@ -49,11 +449,13 @@ i2c_master_setDC(uint8 SDA, uint8 SCL)
} else {
I2C_MASTER_SDA_HIGH_SCL_HIGH();
}
if(1 == SCL) {
do {
sclLevel = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SCL_GPIO));
} while(sclLevel == 0);
}
i2c_master_wait(5);
}
/******************************************************************************
@ -64,7 +466,7 @@ i2c_master_setDC(uint8 SDA, uint8 SCL)
* Returns : uint8 - SDA bit value
*******************************************************************************/
LOCAL uint8 ICACHE_FLASH_ATTR
i2c_master_getDC(void)
i2c_master_getDC()
{
uint8 sda_out;
sda_out = GPIO_INPUT_GET(GPIO_ID_PIN(I2C_MASTER_SDA_GPIO));
@ -74,53 +476,52 @@ i2c_master_getDC(void)
/******************************************************************************
* FunctionName : i2c_master_init
* Description : initilize I2C bus to enable i2c operations
* Parameters : NONE
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_init(void)
i2c_master_init(uint16 id)
{
uint8 i;
i2c_master_setDC(1, 0);
i2c_master_wait(5);
// when SCL = 0, toggle SDA to clear up
i2c_master_setDC(0, 0) ;
i2c_master_wait(5);
i2c_master_setDC(1, 0) ;
i2c_master_wait(5);
// set data_cnt to max value
for (i = 0; i < 28; i++) {
i2c_master_setDC(1, 0);
i2c_master_wait(5); // sda 1, scl 0
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
}
// reset all
i2c_master_stop();
i2c_master_stop(I2C_MASTER_BUS_ID);
return;
}
uint8 i2c_master_get_pinSDA(){
return pinSDA;
}
uint8 i2c_master_get_pinSCL(){
return pinSCL;
/******************************************************************************
* FunctionName : i2c_master_configured
* Description : checks if i2c bus is configured
* Parameters : bus id
* Returns : boolean value, true if configured
*******************************************************************************/
bool ICACHE_FLASH_ATTR
i2c_master_configured(uint16 id)
{
return true;
}
/******************************************************************************
* FunctionName : i2c_master_gpio_init
* FunctionName : i2c_master_setup
* Description : config SDA and SCL gpio to open-drain output mode,
* mux and gpio num defined in i2c_master.h
* Parameters : NONE
* Returns : NONE
* Parameters : bus id, uint8 sda, uint8 scl, uint32 speed
* Returns : configured speed
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_gpio_init(uint8 sda, uint8 scl)
uint32 ICACHE_FLASH_ATTR
i2c_master_setup(uint16 id, uint8 sda, uint8 scl, uint32 speed)
{
pinSDA = pin_num[sda];
pinSCL = pin_num[scl];
@ -141,152 +542,61 @@ i2c_master_gpio_init(uint8 sda, uint8 scl)
ETS_GPIO_INTR_ENABLE() ;
// ETS_INTR_UNLOCK();
i2c_master_init();
i2c_master_init(I2C_MASTER_BUS_ID);
return I2C_MASTER_SPEED;
}
/******************************************************************************
* FunctionName : i2c_master_start
* Description : set i2c to send state
* Parameters : NONE
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_start(void)
i2c_master_start(uint16 id)
{
i2c_master_setDC(1, m_nLastSCL);
i2c_master_wait(5);
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
i2c_master_setDC(0, 1);
i2c_master_wait(5); // sda 0, scl 1
}
/******************************************************************************
* FunctionName : i2c_master_stop
* Description : set i2c to stop sending state
* Parameters : NONE
* Parameters : bus id
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_stop(void)
i2c_master_stop(uint16 id)
{
i2c_master_wait(5);
i2c_master_setDC(0, m_nLastSCL);
i2c_master_wait(5); // sda 0
i2c_master_setDC(0, 1);
i2c_master_wait(5); // sda 0, scl 1
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
}
/******************************************************************************
* FunctionName : i2c_master_setAck
* Description : set ack to i2c bus as level value
* Parameters : uint8 level - 0 or 1
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_setAck(uint8 level)
{
i2c_master_setDC(m_nLastSDA, 0);
i2c_master_wait(5);
i2c_master_setDC(level, 0);
i2c_master_wait(5); // sda level, scl 0
i2c_master_setDC(level, 1);
i2c_master_wait(8); // sda level, scl 1
i2c_master_setDC(level, 0);
i2c_master_wait(5); // sda level, scl 0
i2c_master_setDC(1, 0);
i2c_master_wait(5);
}
/******************************************************************************
* FunctionName : i2c_master_getAck
* Description : confirm if peer send ack
* Parameters : NONE
* Returns : uint8 - ack value, 0 or 1
*******************************************************************************/
uint8 ICACHE_FLASH_ATTR
i2c_master_getAck(void)
{
uint8 retVal;
i2c_master_setDC(m_nLastSDA, 0);
i2c_master_wait(5);
i2c_master_setDC(1, 0);
i2c_master_wait(5);
i2c_master_setDC(1, 1);
i2c_master_wait(5);
retVal = i2c_master_getDC();
i2c_master_wait(5);
i2c_master_setDC(1, 0);
i2c_master_wait(5);
return retVal;
}
/******************************************************************************
* FunctionName : i2c_master_checkAck
* Description : get dev response
* Parameters : NONE
* Returns : true : get ack ; false : get nack
*******************************************************************************/
bool ICACHE_FLASH_ATTR
i2c_master_checkAck(void)
{
if(i2c_master_getAck()){
return FALSE;
}else{
return TRUE;
}
}
/******************************************************************************
* FunctionName : i2c_master_send_ack
* Description : response ack
* Parameters : NONE
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_send_ack(void)
{
i2c_master_setAck(0x0);
}
/******************************************************************************
* FunctionName : i2c_master_send_nack
* Description : response nack
* Parameters : NONE
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_send_nack(void)
{
i2c_master_setAck(0x1);
}
/******************************************************************************
* FunctionName : i2c_master_readByte
* Description : read Byte from i2c bus
* Parameters : NONE
* Parameters : bus id
* Returns : uint8 - readed value
*******************************************************************************/
uint8 ICACHE_FLASH_ATTR
i2c_master_readByte(void)
i2c_master_readByte(uint16 id, sint16 ack)
{
uint8 retVal = 0;
uint8 k, i;
uint8 ackLevel = (ack ? 0 : 1);
i2c_master_wait(5);
i2c_master_setDC(m_nLastSDA, 0);
i2c_master_wait(5); // sda 1, scl 0
for (i = 0; i < 8; i++) {
i2c_master_wait(5);
i2c_master_setDC(1, 0);
i2c_master_wait(5); // sda 1, scl 0
i2c_master_setDC(1, 1);
i2c_master_wait(5); // sda 1, scl 1
k = i2c_master_getDC();
i2c_master_wait(5);
@ -300,40 +610,51 @@ i2c_master_readByte(void)
}
i2c_master_setDC(1, 0);
i2c_master_wait(5); // sda 1, scl 0
// set ACK
i2c_master_setDC(m_nLastSDA, 0);
i2c_master_setDC(ackLevel, 0);
i2c_master_setDC(ackLevel, 1);
i2c_master_wait(3);
i2c_master_setDC(ackLevel, 0);
i2c_master_setDC(1, 0);
return retVal;
}
/******************************************************************************
* FunctionName : i2c_master_writeByte
* Description : write wrdata value(one byte) into i2c
* Parameters : uint8 wrdata - write value
* Parameters : bus id, uint8 wrdata - write value
* Returns : NONE
*******************************************************************************/
void ICACHE_FLASH_ATTR
i2c_master_writeByte(uint8 wrdata)
uint8 ICACHE_FLASH_ATTR
i2c_master_writeByte(uint16 id, uint8 wrdata)
{
uint8 dat;
sint8 i;
uint8 retVal;
i2c_master_wait(5);
i2c_master_setDC(m_nLastSDA, 0);
i2c_master_wait(5);
for (i = 7; i >= 0; i--) {
dat = wrdata >> i;
i2c_master_setDC(dat, 0);
i2c_master_wait(5);
i2c_master_setDC(dat, 1);
i2c_master_wait(5);
if (i == 0) {
i2c_master_wait(3); ////
}
i2c_master_setDC(dat, 0);
}
// get ACK
i2c_master_setDC(m_nLastSDA, 0);
i2c_master_setDC(1, 0);
i2c_master_setDC(1, 1);
retVal = i2c_master_getDC();
i2c_master_wait(5);
i2c_master_setDC(1, 0);
return ! retVal;
}
}
#endif

View File

@ -1,74 +1,12 @@
#ifndef __I2C_MASTER_H__
#define __I2C_MASTER_H__
#define I2C_MASTER_SDA_MUX (pin_mux[sda])
#define I2C_MASTER_SCL_MUX (pin_mux[scl])
#define I2C_MASTER_SDA_GPIO (pinSDA)
#define I2C_MASTER_SCL_GPIO (pinSCL)
#define I2C_MASTER_SDA_FUNC (pin_func[sda])
#define I2C_MASTER_SCL_FUNC (pin_func[scl])
uint32 i2c_master_setup(uint16 id, uint8 sda, uint8 scl, uint32 speed);
void i2c_master_init(uint16 id);
bool i2c_master_configured(uint16 id);
void i2c_master_stop(uint16 id);
void i2c_master_start(uint16 id);
uint8 i2c_master_readByte(uint16 id, sint16 ack);
uint8 i2c_master_writeByte(uint16 id, uint8 wrdata);
// #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U
// #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTDO_U
// #define I2C_MASTER_SDA_GPIO 2
// #define I2C_MASTER_SCL_GPIO 15
// #define I2C_MASTER_SDA_FUNC FUNC_GPIO2
// #define I2C_MASTER_SCL_FUNC FUNC_GPIO15
// #define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U
// #define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_MTMS_U
// #define I2C_MASTER_SDA_GPIO 2
// #define I2C_MASTER_SCL_GPIO 14
// #define I2C_MASTER_SDA_FUNC FUNC_GPIO2
// #define I2C_MASTER_SCL_FUNC FUNC_GPIO14
//#define I2C_MASTER_SDA_MUX PERIPHS_IO_MUX_GPIO2_U
//#define I2C_MASTER_SCL_MUX PERIPHS_IO_MUX_GPIO0_U
//#define I2C_MASTER_SDA_GPIO 2
//#define I2C_MASTER_SCL_GPIO 0
//#define I2C_MASTER_SDA_FUNC FUNC_GPIO2
//#define I2C_MASTER_SCL_FUNC FUNC_GPIO0
#if 0
#define I2C_MASTER_GPIO_SET(pin) \
gpio_output_set(1<<pin,0,1<<pin,0)
#define I2C_MASTER_GPIO_CLR(pin) \
gpio_output_set(0,1<<pin,1<<pin,0)
#define I2C_MASTER_GPIO_OUT(pin,val) \
if(val) I2C_MASTER_GPIO_SET(pin);\
else I2C_MASTER_GPIO_CLR(pin)
#endif
#define I2C_MASTER_SDA_HIGH_SCL_HIGH() \
gpio_output_set(1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define I2C_MASTER_SDA_HIGH_SCL_LOW() \
gpio_output_set(1<<I2C_MASTER_SDA_GPIO, 1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define I2C_MASTER_SDA_LOW_SCL_HIGH() \
gpio_output_set(1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
#define I2C_MASTER_SDA_LOW_SCL_LOW() \
gpio_output_set(0, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 1<<I2C_MASTER_SDA_GPIO | 1<<I2C_MASTER_SCL_GPIO, 0)
void i2c_master_gpio_init(uint8 sda, uint8 scl);
void i2c_master_init(void);
#define i2c_master_wait os_delay_us
void i2c_master_stop(void);
void i2c_master_start(void);
void i2c_master_setAck(uint8 level);
uint8 i2c_master_getAck(void);
uint8 i2c_master_readByte(void);
void i2c_master_writeByte(uint8 wrdata);
bool i2c_master_checkAck(void);
void i2c_master_send_ack(void);
void i2c_master_send_nack(void);
uint8 i2c_master_get_pinSDA();
uint8 i2c_master_get_pinSCL();
#endif
#endif //__I2C_MASTER_H__

View File

@ -156,6 +156,22 @@
#define ENDUSER_SETUP_AP_SSID "SetupGadget"
// I2C software driver partially supports use of GPIO16 (D0) pin for SCL line.
// GPIO16 does not support open-drain mode and works in push-pull mode,
// so clock stretching will not be possible, because circuit in slave device that
// supposed to drive SCL low during stretching will not be capable to hold SCL low.
// Also I2C speed will be limited to no more than 400000 Hz (FAST mode).
// This define is does not have an effect on an old driver (see I2C_MASTER_OLD_VERSION).
//#define I2C_MASTER_GPIO16_ENABLE
// For compatibility reasons you can switch to old version of I2C software driver.
// It does not support changing speed, have only one bus id = 0, does not support GPIO16
// and works only in Standard(slow) mode with clock speed around 50kHz.
#define I2C_MASTER_OLD_VERSION
// The following sections are only relevent for those developers who are
// developing modules or core Lua changes and configure how extra diagnostics
// are enabled in the firmware. These should only be configured if you are

View File

@ -15,13 +15,16 @@ static int i2c_setup( lua_State *L )
MOD_CHECK_ID( gpio, sda );
MOD_CHECK_ID( gpio, scl );
if(scl==0 || sda==0)
return luaL_error( L, "no i2c for D0" );
if ( sda == 0 )
return luaL_error( L, "i2c SDA on D0 is not supported" );
s32 speed = ( s32 )luaL_checkinteger( L, 4 );
if ( speed <= 0 )
return luaL_error( L, "wrong arg range" );
lua_pushinteger( L, platform_i2c_setup( id, sda, scl, (u32)speed ) );
speed = platform_i2c_setup( id, sda, scl, (u32)speed );
if ( speed == 0 )
return luaL_error( L, "failed to initialize i2c %d", id );
lua_pushinteger( L, speed );
return 1;
}
@ -31,7 +34,10 @@ static int i2c_start( lua_State *L )
unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( i2c, id );
if (platform_i2c_configured( id ) )
platform_i2c_send_start( id );
else
luaL_error( L, "i2c %d is not configured", id );
return 0;
}
@ -147,7 +153,8 @@ static const LUA_REG_TYPE i2c_map[] = {
{ LSTRKEY( "address" ), LFUNCVAL( i2c_address ) },
{ LSTRKEY( "write" ), LFUNCVAL( i2c_write ) },
{ LSTRKEY( "read" ), LFUNCVAL( i2c_read ) },
//{ LSTRKEY( "FAST" ), LNUMVAL( PLATFORM_I2C_SPEED_FAST ) },
{ LSTRKEY( "FASTPLUS" ), LNUMVAL( PLATFORM_I2C_SPEED_FASTPLUS ) },
{ LSTRKEY( "FAST" ), LNUMVAL( PLATFORM_I2C_SPEED_FAST ) },
{ LSTRKEY( "SLOW" ), LNUMVAL( PLATFORM_I2C_SPEED_SLOW ) },
{ LSTRKEY( "TRANSMITTER" ), LNUMVAL( PLATFORM_I2C_DIRECTION_TRANSMITTER ) },
{ LSTRKEY( "RECEIVER" ), LNUMVAL( PLATFORM_I2C_DIRECTION_RECEIVER ) },

View File

@ -15,7 +15,13 @@
#define NUM_PWM GPIO_PIN_NUM
#define NUM_ADC 1
#define NUM_CAN 0
#ifndef I2C_MASTER_OLD_VERSION
#define NUM_I2C 10
#else
#define NUM_I2C 1
#endif //I2C_MASTER_OLD_VERSION
#define NUM_OW GPIO_PIN_NUM
#define NUM_TMR 7

View File

@ -735,16 +735,19 @@ uint32_t platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t spe
platform_gpio_mode(sda, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); // inside this func call platform_pwm_close
platform_gpio_mode(scl, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP); // disable gpio interrupt first
i2c_master_gpio_init(sda, scl);
return PLATFORM_I2C_SPEED_SLOW;
return i2c_master_setup(id, sda, scl, speed);
}
bool platform_i2c_configured( unsigned id ){
return i2c_master_configured(id);
}
void platform_i2c_send_start( unsigned id ){
i2c_master_start();
i2c_master_start(id);
}
void platform_i2c_send_stop( unsigned id ){
i2c_master_stop();
i2c_master_stop(id);
}
int platform_i2c_send_address( unsigned id, uint16_t address, int direction ){
@ -754,22 +757,17 @@ int platform_i2c_send_address( unsigned id, uint16_t address, int direction ){
PLATFORM_I2C_DIRECTION_RECEIVER == 1 ) ) {
direction = ( direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ) ? 0 : 1;
}
i2c_master_writeByte( (uint8_t) ((address << 1) | direction ));
// Low-level returns nack (0=acked); we return ack (1=acked).
return ! i2c_master_getAck();
return i2c_master_writeByte(id,
(uint8_t) ((address << 1) + (direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ? 0 : 1))
);
}
int platform_i2c_send_byte(unsigned id, uint8_t data ){
i2c_master_writeByte(data);
// Low-level returns nack (0=acked); we return ack (1=acked).
return ! i2c_master_getAck();
return i2c_master_writeByte(id, data);
}
int platform_i2c_recv_byte( unsigned id, int ack ){
uint8_t r = i2c_master_readByte();
i2c_master_setAck( !ack );
return r;
return i2c_master_readByte(id, ack);
}
// *****************************************************************************

View File

@ -235,7 +235,8 @@ void platform_sigma_delta_set_target( uint8_t target );
enum
{
PLATFORM_I2C_SPEED_SLOW = 100000,
PLATFORM_I2C_SPEED_FAST = 400000
PLATFORM_I2C_SPEED_FAST = 400000,
PLATFORM_I2C_SPEED_FASTPLUS = 1000000
};
// I2C direction
@ -251,6 +252,7 @@ static inline int platform_i2c_exists( unsigned id ) { return id < NUM_I2C; }
static inline int platform_i2c_exists( unsigned id ) { return 0; }
#endif
uint32_t platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed );
bool platform_i2c_configured( unsigned id );
void platform_i2c_send_start( unsigned id );
void platform_i2c_send_stop( unsigned id );
int platform_i2c_send_address( unsigned id, uint16_t address, int direction );

View File

@ -2,16 +2,56 @@
| Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ |
| 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [i2c.c](../../app/modules/i2c.c)|
| 2018-08-30 | [Natalia Sorokina](https://github.com/sonaux) | | [i2c_master.c](../../app/driver/i2c_master.c)|
I²C (I2C, IIC) is a serial 2-wire bus for communicating with various devices. Also known as SMBus or TWI, though SMBus have some additions to the I2C protocol.
ESP8266 chip does not have hardware I²C, so module uses software I²C driver.
It can be set up on any GPIO pins including GPIO16 (see below).
This module supports:
- Master mode
- Multiple buses (up to 10) with different speeds on each bus
- Standard(Slow, 100kHz), Fast(400kHz) and FastPlus(1MHz) modes or an arbitrary clock speed
- Clock stretching (slow slave device can tell the master to wait)
- Sharing SDA line over multiple I²C buses to save available pins
- GPIO16 pin can be used as SCL pin, but selected bus will be limited to not more than FAST speed.
HIGH-speed mode (3.5MHz clock) and 10-bit addressing scheme is not supported.
You have to call `i2c.setup` on a given I²C bus at least once before communicating to any device connected to that bus, otherwise you will get an error.
I²C bus designed to work in open-drain mode, so it needs pull-up resistors 1k - 10k on SDA and SCL lines. Though many peripheral modules have pull-up resistors onboard and will work without additional external resistors.
Hint for using many identical devices with same address:
Many devices allow to choose between 2 I²C addresses via pin or soldered 0 Ohm resistor.
If address change is not an option or you need to use more than 2 similar devices, you can use different I²C buses.
Initialize them once by calling `i2c.setup` with different bus numbers and pins, then refer to each device by bus id and device address.
SCL pins should be different, SDA can be shared on one pin.
Note that historically many NodeMCU drivers and modules assumed that only a single I²C bus with id 0 is available, so it is always safer to start with id 0 as first bus in your code.
If your device driver functions do not have I²C bus id as an input parameter and/or not built with Lua OOP principles then most probably device will be accessible through bus id 0 only and must be connected to its pins.
To enable new driver comment line `#define I2C_MASTER_OLD_VERSION` in `user_config.h`
To enable support for GPIO16 (D0) uncomment line `#define I2C_MASTER_GPIO16_ENABLED` in `user_config.h`
GPIO16 does not support open-drain mode and works in push-pull mode. That may lead to communication errors when slave device tries to stretch SCL clock but unable to hold SCL line low. If that happens, try setting lower I²C speed.
!!! caution
If your module reboots when trying to use GPIO16 pin, then it is wired to RESET pin to support deep sleep mode and you cannot use GPIO16 for I²C bus or other purposes.
## i2c.address()
Setup I²C address and read/write mode for the next transfer.
On I²C bus every device is addressed by 7-bit number. Address for the particular device can be found in its datasheet.
#### Syntax
`i2c.address(id, device_addr, direction)`
#### Parameters
- `id` always 0
- `device_addr` 7-bit device address, remember that [in I²C `device_addr` represents the upper 7 bits](http://www.nxp.com/documents/user_manual/UM10204.pdf#page=13) followed by a single `direction` bit
- `id` bus number
- `device_addr` 7-bit device address. Remember that [in I²C `device_addr` represents the upper 7 bits](http://www.nxp.com/documents/user_manual/UM10204.pdf#page=13) followed by a single `direction` bit. Sometimes device address is advertised as 8-bit value, then you should divide it by 2 to get 7-bit value.
- `direction` `i2c.TRANSMITTER` for writing mode , `i2c. RECEIVER` for reading mode
#### Returns
@ -27,7 +67,7 @@ Read data for variable number of bytes.
`i2c.read(id, len)`
#### Parameters
- `id` always 0
- `id` bus number
- `len` number of data bytes
#### Returns
@ -40,10 +80,10 @@ sda = 1
scl = 2
-- initialize i2c, set pin 1 as sda, set pin 2 as scl
i2c.setup(id, sda, scl, i2c.SLOW)
i2c.setup(id, sda, scl, i2c.FAST)
-- user defined function: read from reg_addr content of dev_addr
function read_reg(dev_addr, reg_addr)
-- user defined function: read 1 byte of data from device
function read_reg(id, dev_addr, reg_addr)
i2c.start(id)
i2c.address(id, dev_addr, i2c.TRANSMITTER)
i2c.write(id, reg_addr)
@ -56,7 +96,7 @@ function read_reg(dev_addr, reg_addr)
end
-- get content of register 0xAA of device 0x77
reg = read_reg(0x77, 0xAA)
reg = read_reg(id, 0x77, 0xAA)
print(string.byte(reg))
```
@ -64,20 +104,42 @@ print(string.byte(reg))
[i2c.write()](#i2cwrite)
## i2c.setup()
Initialize the I²C module.
Initialize the I²C bus with the selected bus number, pins and speed.
#### Syntax
`i2c.setup(id, pinSDA, pinSCL, speed)`
#### Parameters
- `id` always 0
- `id` 0~9, bus number
- `pinSDA` 1~12, IO index
- `pinSCL` 1~12, IO index
- `speed` only `i2c.SLOW` supported
- `pinSCL` 0~12, IO index
- `speed` `i2c.SLOW` (100kHz), `i2c.FAST` (400kHz), `i2c.FASTPLUS` (1MHz) or any clock frequency in range of 25000-1000000 Hz.
FASTPLUS mode results in 600kHz I2C clock speed at default 80MHz CPU frequency. To get 1MHz I2C clock speed change CPU frequency to 160MHz with function `node.setcpufreq(node.CPU160MHZ)`.
#### Returns
`speed` the selected speed
`speed` the selected speed, `0` if bus initialization error.
#### Example
```lua
i2c0 = {
id = 0,
sda = 1,
scl = 0,
speed = i2c.FAST
}
i2c1 = {
id = 1,
sda = 1,
scl = 2,
speed = i2c.FASTPLUS
}
-- initialize i2c bus 0
i2c0.speed = i2c.setup(i2c0.id, i2c0.sda, i2c0.scl, i2c0.speed)
-- initialize i2c bus 1 with shared SDA on pin 1
node.setcpufreq(node.CPU160MHZ) -- to support FASTPLUS speed
i2c1.speed = i2c.setup(i2c1.id, i2c1.sda, i2c1.scl, i2c1.speed)
print("i2c bus 0 speed: ", i2c0.speed, "i2c bus 1 speed: ", i2c1.speed)
```
#### See also
[i2c.read()](#i2cread)
@ -88,7 +150,7 @@ Send an I²C start condition.
`i2c.start(id)`
#### Parameters
`id` always 0
`id` bus number
#### Returns
`nil`
@ -103,7 +165,7 @@ Send an I²C stop condition.
`i2c.stop(id)`
#### Parameters
`id` always 0
`id` bus number
#### Returns
`nil`
@ -118,7 +180,7 @@ Write data to I²C bus. Data items can be multiple numbers, strings or Lua table
`i2c.write(id, data1[, data2[, ..., datan]])`
#### Parameters
- `id` always 0
- `id` bus number
- `data` data can be numbers, string or Lua table.
#### Returns
@ -126,7 +188,30 @@ Write data to I²C bus. Data items can be multiple numbers, strings or Lua table
#### Example
```lua
i2c.write(0, "hello", "world")
id = 0
sda = 1
scl = 2
-- initialize i2c, set pin 1 as sda, set pin 2 as scl
i2c.setup(id, sda, scl, i2c.FAST)
-- user defined function: write some data to device
-- with address dev_addr starting from reg_addr
function write_reg(id, dev_addr, reg_addr, data)
i2c.start(id)
i2c.address(id, dev_addr, i2c.TRANSMITTER)
i2c.write(id, reg_addr)
c = i2c.write(id, data)
i2c.stop(id)
return c
end
-- set register with address 0x45 of device 0x77 with value 1
count = write_reg(id, 0x77, 0x45, 1)
print(count, " bytes written")
-- write text into i2c EEPROM starting with memory address 0
count = write_reg(id, 0x50, 0, "Sample")
print(count, " bytes written")
```
#### See also