Parcourir la source

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
Natalia il y a 5 ans
Parent
commit
ab61e9c03a

+ 461 - 140
app/driver/i2c_master.c

@@ -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);
-        i2c_master_wait(5);
     }
+    // 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

+ 9 - 71
app/include/driver/i2c_master.h

@@ -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])
-
-// #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
+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);
+
+#endif //__I2C_MASTER_H__

+ 16 - 0
app/include/user_config.h

@@ -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

+ 13 - 6
app/modules/i2c.c

@@ -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)
+  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 );
-  platform_i2c_send_start( 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 ) },

+ 6 - 0
app/platform/cpu_esp8266.h

@@ -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
 

+ 13 - 15
app/platform/platform.c

@@ -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();
+int platform_i2c_send_byte(unsigned id, uint8_t data ){
+  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);
 }
 
 // *****************************************************************************

+ 3 - 1
app/platform/platform.h

@@ -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 );

+ 110 - 25
docs/modules/i2c.md

@@ -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
@@ -39,11 +79,11 @@ id  = 0
 sda = 1
 scl = 2
 
--- initialize i2c, set pin1 as sda, set pin2 as scl
-i2c.setup(id, sda, scl, i2c.SLOW)
+-- initialize i2c, set pin 1 as sda, set pin 2 as scl
+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,29 +96,51 @@ 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))
 ```
 
-####See also
+#### See also
 [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
+#### Parameters
+- `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.
 
-####See also
+#### 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)
 
 ## i2c.start()
@@ -88,12 +150,12 @@ Send an I²C start condition.
 `i2c.start(id)`
 
 #### Parameters
-`id` always 0
+`id` bus number
 
 #### Returns
 `nil`
 
-####See also
+#### See also
 [i2c.read()](#i2cread)
 
 ## i2c.stop()
@@ -102,23 +164,23 @@ Send an I²C stop condition.
 #### Syntax
 `i2c.stop(id)`
 
-####Parameters
-`id` always 0
+#### Parameters
+`id` bus number
 
 #### Returns
 `nil`
 
-####See also
+#### See also
 [i2c.read()](#i2cread)
 
 ## i2c.write()
 Write data to I²C bus. Data items can be multiple numbers, strings or Lua tables.
 
-####Syntax
+#### Syntax
 `i2c.write(id, data1[, data2[, ..., datan]])`
 
-####Parameters
-- `id` always 0
+#### Parameters
+- `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