Browse Source

add i2c module

devsaurus 7 years ago
parent
commit
381726f103

+ 1 - 0
components/driver_i2c/component.mk

@@ -0,0 +1 @@
+COMPONENT_ADD_INCLUDEDIRS:=include

+ 314 - 0
components/driver_i2c/i2c_sw_master.c

@@ -0,0 +1,314 @@
+/******************************************************************************
+ * Copyright 2013-2014 Espressif Systems (Wuxi)
+ *
+ * FileName: i2c_sw_master.c
+ *
+ * Description: i2c master API
+ *
+ * Modification history:
+ *     2014/3/12, v1.0 create this file.
+*******************************************************************************/
+#include "driver/gpio.h"
+
+#include "driver/i2c_sw_master.h"
+
+static uint8_t m_nLastSDA;
+static uint8_t m_nLastSCL;
+
+static uint8_t pinSDA;
+static uint8_t pinSCL;
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_setDC
+ * Description  : Internal used function -
+ *                    set i2c SDA and SCL bit value for half clk cycle
+ * Parameters   : uint8_t SDA
+ *                uint8_t SCL
+ * Returns      : NONE
+*******************************************************************************/
+static void i2c_sw_master_setDC(uint8_t SDA, uint8_t SCL)
+{
+  uint8_t sclLevel;
+
+  SDA &= 0x01;
+  SCL &= 0x01;
+  m_nLastSDA = SDA;
+  m_nLastSCL = SCL;
+
+  gpio_set_level(pinSDA, SDA);
+  gpio_set_level(pinSCL, SCL);
+  if(1 == SCL) {
+    do {
+      sclLevel = gpio_get_level(pinSCL);
+    } while(sclLevel == 0);
+  }
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_getDC
+ * Description  : Internal used function -
+ *                    get i2c SDA bit value
+ * Parameters   : NONE
+ * Returns      : uint8_t - SDA bit value
+*******************************************************************************/
+static uint8_t i2c_sw_master_getDC(void)
+{
+  return gpio_get_level(pinSDA);
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_init
+ * Description  : initilize I2C bus to enable i2c operations
+ * Parameters   : NONE
+ * Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_init(void)
+{
+  uint8_t i;
+
+  i2c_sw_master_setDC(1, 0);
+  i2c_sw_master_wait(5);
+
+  // when SCL = 0, toggle SDA to clear up
+  i2c_sw_master_setDC(0, 0);
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(1, 0);
+  i2c_sw_master_wait(5);
+
+  // set data_cnt to max value
+  for (i = 0; i < 28; i++) {
+    i2c_sw_master_setDC(1, 0);
+    i2c_sw_master_wait(5);	// sda 1, scl 0
+    i2c_sw_master_setDC(1, 1);
+    i2c_sw_master_wait(5);	// sda 1, scl 1
+  }
+
+  // reset all
+  i2c_sw_master_stop();
+  return;
+}
+
+uint8_t i2c_sw_master_get_pinSDA()
+{
+  return pinSDA;
+}
+
+uint8_t i2c_sw_master_get_pinSCL()
+{
+  return pinSCL;
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_gpio_init
+ * Description  : config SDA and SCL gpio to open-drain output mode,
+ *                mux and gpio num defined in i2c_sw_master.h
+ * Parameters   : NONE
+ * Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_gpio_init(uint8 sda, uint8 scl)
+{
+  pinSDA = sda;
+  pinSCL = scl;
+
+  gpio_config_t cfg;
+
+  cfg.pin_bit_mask = 1 << sda | 1 << scl;
+  cfg.mode = GPIO_MODE_INPUT_OUTPUT_OD;
+  cfg.pull_up_en = GPIO_PULLUP_ENABLE;
+  cfg.pull_down_en = GPIO_PULLDOWN_DISABLE;
+  cfg.intr_type = GPIO_INTR_DISABLE;
+
+  if (ESP_OK != gpio_config(&cfg))
+    return;
+
+  gpio_set_level(scl, 1);
+  gpio_set_level(sda, 1);
+
+  i2c_sw_master_init();
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_start
+ * Description  : set i2c to send state
+ * Parameters   : NONE
+ * Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_start(void)
+{
+  i2c_sw_master_setDC(1, m_nLastSCL);
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(1, 1);
+  i2c_sw_master_wait(5); // sda 1, scl 1
+  i2c_sw_master_setDC(0, 1);
+  i2c_sw_master_wait(5); // sda 0, scl 1
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_stop
+ * Description  : set i2c to stop sending state
+ * Parameters   : NONE
+ * Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_stop(void)
+{
+  i2c_sw_master_wait(5);
+
+  i2c_sw_master_setDC(0, m_nLastSCL);
+  i2c_sw_master_wait(5); // sda 0
+  i2c_sw_master_setDC(0, 1);
+  i2c_sw_master_wait(5); // sda 0, scl 1
+  i2c_sw_master_setDC(1, 1);
+  i2c_sw_master_wait(5); // sda 1, scl 1
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_setAck
+ * Description  : set ack to i2c bus as level value
+ * Parameters   : uint8_t level - 0 or 1
+ * Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_setAck(uint8_t level)
+{
+  i2c_sw_master_setDC(m_nLastSDA, 0);
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(level, 0);
+  i2c_sw_master_wait(5); // sda level, scl 0
+  i2c_sw_master_setDC(level, 1);
+  i2c_sw_master_wait(8); // sda level, scl 1
+  i2c_sw_master_setDC(level, 0);
+  i2c_sw_master_wait(5); // sda level, scl 0
+  i2c_sw_master_setDC(1, 0);
+  i2c_sw_master_wait(5);
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_getAck
+ * Description  : confirm if peer send ack
+ * Parameters   : NONE
+ * Returns      : uint8_t - ack value, 0 or 1
+*******************************************************************************/
+uint8_t i2c_sw_master_getAck(void)
+{
+  uint8_t retVal;
+  i2c_sw_master_setDC(m_nLastSDA, 0);
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(1, 0);
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(1, 1);
+  i2c_sw_master_wait(5);
+
+  retVal = i2c_sw_master_getDC();
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(1, 0);
+  i2c_sw_master_wait(5);
+
+  return retVal;
+}
+
+/******************************************************************************
+* FunctionName : i2c_sw_master_checkAck
+* Description  : get dev response
+* Parameters   : NONE
+* Returns      : true : get ack ; false : get nack
+*******************************************************************************/
+bool i2c_sw_master_checkAck(void)
+{
+  if (i2c_sw_master_getAck()) {
+    return false;
+  } else {
+    return true;
+  }
+}
+
+/******************************************************************************
+* FunctionName : i2c_sw_master_send_ack
+* Description  : response ack
+* Parameters   : NONE
+* Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_send_ack(void)
+{
+  i2c_sw_master_setAck(0x0);
+}
+
+/******************************************************************************
+* FunctionName : i2c_sw_master_send_nack
+* Description  : response nack
+* Parameters   : NONE
+* Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_send_nack(void)
+{
+  i2c_sw_master_setAck(0x1);
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_readByte
+ * Description  : read Byte from i2c bus
+ * Parameters   : NONE
+ * Returns      : uint8_t - readed value
+*******************************************************************************/
+uint8_t i2c_sw_master_readByte(void)
+{
+  uint8_t retVal = 0;
+  uint8_t k, i;
+
+  i2c_sw_master_wait(5);
+  i2c_sw_master_setDC(m_nLastSDA, 0);
+  i2c_sw_master_wait(5); // sda 1, scl 0
+
+  for (i = 0; i < 8; i++) {
+    i2c_sw_master_wait(5);
+    i2c_sw_master_setDC(1, 0);
+    i2c_sw_master_wait(5); // sda 1, scl 0
+    i2c_sw_master_setDC(1, 1);
+    i2c_sw_master_wait(5); // sda 1, scl 1
+
+    k = i2c_sw_master_getDC();
+    i2c_sw_master_wait(5);
+
+    if (i == 7) {
+      i2c_sw_master_wait(3);
+    }
+
+    k <<= (7 - i);
+    retVal |= k;
+  }
+
+  i2c_sw_master_setDC(1, 0);
+  i2c_sw_master_wait(5); // sda 1, scl 0
+
+  return retVal;
+}
+
+/******************************************************************************
+ * FunctionName : i2c_sw_master_writeByte
+ * Description  : write wrdata value(one byte) into i2c
+ * Parameters   : uint8_t wrdata - write value
+ * Returns      : NONE
+*******************************************************************************/
+void i2c_sw_master_writeByte(uint8_t wrdata)
+{
+  uint8_t dat;
+  int8_t i;
+
+  i2c_sw_master_wait(5);
+
+  i2c_sw_master_setDC(m_nLastSDA, 0);
+  i2c_sw_master_wait(5);
+
+  for (i = 7; i >= 0; i--) {
+    dat = wrdata >> i;
+    i2c_sw_master_setDC(dat, 0);
+    i2c_sw_master_wait(5);
+    i2c_sw_master_setDC(dat, 1);
+    i2c_sw_master_wait(5);
+
+    if (i == 0) {
+      i2c_sw_master_wait(3);
+    }
+
+    i2c_sw_master_setDC(dat, 0);
+    i2c_sw_master_wait(5);
+  }
+}

+ 26 - 0
components/driver_i2c/include/driver/i2c_sw_master.h

@@ -0,0 +1,26 @@
+#ifndef __I2C_SW_MASTER_H__
+#define __I2C_SW_MASTER_H__
+
+#include "rom/ets_sys.h"
+
+#define I2C_NUM_MAX 1
+
+void i2c_sw_master_gpio_init(uint8 sda, uint8 scl);
+void i2c_sw_master_init(void);
+
+#define i2c_sw_master_wait ets_delay_us
+void i2c_sw_master_stop(void);
+void i2c_sw_master_start(void);
+void i2c_sw_master_setAck(uint8_t level);
+uint8_t i2c_sw_master_getAck(void);
+uint8_t i2c_sw_master_readByte(void);
+void i2c_sw_master_writeByte(uint8_t wrdata);
+
+bool i2c_sw_master_checkAck(void);
+void i2c_sw_master_send_ack(void);
+void i2c_sw_master_send_nack(void);
+
+uint8_t i2c_sw_master_get_pinSDA();
+uint8_t i2c_sw_master_get_pinSCL();
+
+#endif

+ 6 - 0
components/modules/Kconfig

@@ -79,6 +79,12 @@ config LUA_MODULE_GPIO
   help
       Includes the GPIO module (recommended).
 
+config LUA_MODULE_I2C
+  bool "I2C module"
+  default "y"
+  help
+      Includes the I2C module (recommended).
+
 config LUA_MODULE_NET
   bool "Net module"
   default "y"

+ 161 - 0
components/modules/i2c.c

@@ -0,0 +1,161 @@
+// Module for interfacing with the I2C interface
+
+#include "module.h"
+#include "lauxlib.h"
+#include "platform.h"
+
+// Lua: i2c.setup( id, sda, scl, speed )
+static int i2c_setup( lua_State *L )
+{
+  unsigned id = luaL_checkinteger( L, 1 );
+  unsigned sda = luaL_checkinteger( L, 2 );
+  unsigned scl = luaL_checkinteger( L, 3 );
+
+  MOD_CHECK_ID( i2c, id );
+  MOD_CHECK_ID( gpio, sda );
+  MOD_CHECK_ID( gpio, scl );
+
+  int32_t speed = ( int32_t )luaL_checkinteger( L, 4 );
+  luaL_argcheck( L, speed > 0, 4, "wrong arg range" );
+  if (!platform_i2c_setup( id, sda, scl, (uint32_t)speed ))
+    luaL_error( L, "setup failed" );
+
+  return 0;
+}
+
+// Lua: i2c.start( id )
+static int i2c_start( lua_State *L )
+{
+  unsigned id = luaL_checkinteger( L, 1 );
+
+  MOD_CHECK_ID( i2c, id );
+  if (!platform_i2c_send_start( id ))
+    luaL_error( L, "command failed");
+
+  return 0;
+}
+
+// Lua: i2c.stop( id )
+static int i2c_stop( lua_State *L )
+{
+  unsigned id = luaL_checkinteger( L, 1 );
+
+  MOD_CHECK_ID( i2c, id );
+  if (!platform_i2c_send_stop( id ))
+    luaL_error( L, "command failed" );
+
+  return 0;
+}
+
+// Lua: status = i2c.address( id, address, direction )
+static int i2c_address( lua_State *L )
+{
+  unsigned id = luaL_checkinteger( L, 1 );
+  int address = luaL_checkinteger( L, 2 );
+  int direction = luaL_checkinteger( L, 3 );
+
+  MOD_CHECK_ID( i2c, id );
+  luaL_argcheck( L, address >= 0 && address <= 127, 2, "wrong arg range" );
+
+  int ret = platform_i2c_send_address( id, (uint16_t)address, direction, 1 );
+  lua_pushboolean( L,  ret > 0 );
+
+  return 1;
+}
+
+// Lua: wrote = i2c.write( id, data1, [data2], ..., [datan] )
+// data can be either a string, a table or an 8-bit number
+static int i2c_write( lua_State *L )
+{
+  unsigned id = luaL_checkinteger( L, 1 );
+  const char *pdata;
+  size_t datalen, i;
+  int numdata;
+  uint32_t wrote = 0;
+  unsigned argn;
+
+  MOD_CHECK_ID( i2c, id );
+  if( lua_gettop( L ) < 2 )
+    return luaL_error( L, "wrong arg type" );
+  for( argn = 2; argn <= lua_gettop( L ); argn ++ )
+  {
+    // lua_isnumber() would silently convert a string of digits to an integer
+    // whereas here strings are handled separately.
+    if( lua_type( L, argn ) == LUA_TNUMBER )
+    {
+      numdata = ( int )luaL_checkinteger( L, argn );
+      if( numdata < 0 || numdata > 255 )
+        return luaL_error( L, "wrong arg range" );
+      if( platform_i2c_send_byte( id, numdata, 1 ) != 1 )
+        break;
+      wrote ++;
+    }
+    else if( lua_istable( L, argn ) )
+    {
+      datalen = lua_objlen( L, argn );
+      for( i = 0; i < datalen; i ++ )
+      {
+        lua_rawgeti( L, argn, i + 1 );
+        numdata = ( int )luaL_checkinteger( L, -1 );
+        lua_pop( L, 1 );
+        if( numdata < 0 || numdata > 255 )
+          return luaL_error( L, "wrong arg range" );
+        if( platform_i2c_send_byte( id, numdata, 1 ) == 0 )
+          break;
+      }
+      wrote += i;
+      if( i < datalen )
+        break;
+    }
+    else
+    {
+      pdata = luaL_checklstring( L, argn, &datalen );
+      for( i = 0; i < datalen; i ++ )
+        if( platform_i2c_send_byte( id, pdata[ i ], 1 ) == 0 )
+          break;
+      wrote += i;
+      if( i < datalen )
+        break;
+    }
+  }
+  lua_pushinteger( L, wrote );
+  return 1;
+}
+
+// Lua: read = i2c.read( id, size )
+static int i2c_read( lua_State *L )
+{
+  unsigned id = luaL_checkinteger( L, 1 );
+  uint32_t size = ( uint32_t )luaL_checkinteger( L, 2 ), i;
+  luaL_Buffer b;
+  int data;
+
+  MOD_CHECK_ID( i2c, id );
+  if( size == 0 )
+    return 0;
+  luaL_buffinit( L, &b );
+  for( i = 0; i < size; i ++ )
+    if( ( data = platform_i2c_recv_byte( id, i < size - 1 ) ) == -1 )
+      break;
+    else
+      luaL_addchar( &b, ( char )data );
+  luaL_pushresult( &b );
+  return 1;
+}
+
+static const LUA_REG_TYPE i2c_map[] = {
+  { LSTRKEY( "setup" ),       LFUNCVAL( i2c_setup ) },
+  { LSTRKEY( "start" ),       LFUNCVAL( i2c_start ) },
+  { LSTRKEY( "stop" ),        LFUNCVAL( i2c_stop ) },
+  { LSTRKEY( "address" ),     LFUNCVAL( i2c_address ) },
+  { LSTRKEY( "write" ),       LFUNCVAL( i2c_write ) },
+  { LSTRKEY( "read" ),        LFUNCVAL( i2c_read ) },
+  { 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 ) },
+  {LNILKEY, LNILVAL}
+};
+
+
+NODEMCU_MODULE(I2C, "i2c", i2c_map, NULL);

+ 34 - 0
components/platform/include/platform.h

@@ -34,6 +34,13 @@ enum
 
 int platform_init (void);
 
+// *****************************************************************************
+// GPIO subsection
+
+int platform_gpio_exists( unsigned gpio );
+int platform_gpio_output_exists( unsigned gpio );
+
+
 // *****************************************************************************
 // UART subsection
 
@@ -80,6 +87,33 @@ uint8_t platform_sigma_delta_set_prescale( uint8_t channel, uint8_t prescale );
 uint8_t platform_sigma_delta_set_duty( uint8_t channel, int8_t duty );
 
 
+// *****************************************************************************
+// I2C platform interface
+
+// I2C speed
+enum
+{
+  PLATFORM_I2C_SPEED_SLOW = 100000,
+  PLATFORM_I2C_SPEED_FAST = 400000
+};
+
+// I2C direction
+enum
+{
+  PLATFORM_I2C_DIRECTION_TRANSMITTER,
+  PLATFORM_I2C_DIRECTION_RECEIVER
+};
+
+int platform_i2c_exists( unsigned id );
+int platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed );
+int platform_i2c_send_start( unsigned id );
+int platform_i2c_send_stop( unsigned id );
+int platform_i2c_send_address( unsigned id, uint16_t address, int direction, int ack_check_en );
+int platform_i2c_send_byte( unsigned id, uint8_t data, int ack_check_en );
+// ack_val: 1 = send ACK, 0 = send NACK
+int platform_i2c_recv_byte( unsigned id, int ack_val );
+
+
 // Internal flash erase/write functions
 
 uint32_t platform_flash_get_sector_of_address( uint32_t addr );

+ 154 - 0
components/platform/platform.c

@@ -8,6 +8,14 @@ int platform_init (void)
   return PLATFORM_OK;
 }
 
+
+// *****************************************************************************
+// GPIO subsection
+
+int platform_gpio_exists( unsigned gpio ) { return GPIO_IS_VALID_GPIO(gpio); }
+int platform_gpio_output_exists( unsigned gpio ) { return GPIO_IS_VALID_OUTPUT_GPIO(gpio); }
+
+
 // ****************************************************************************
 // UART
 
@@ -138,3 +146,149 @@ uint8_t IRAM_ATTR platform_sigma_delta_set_duty( uint8_t channel, int8_t duty )
 {
   return ESP_OK == sigmadelta_set_duty( channel, duty ) ? 1 : 0;
 }
+
+// *****************************************************************************
+// I2C platform interface
+
+#if 0
+// platform functions for the IDF I2C driver
+// they're currently deactivated because of https://github.com/espressif/esp-idf/issues/241
+// long-term goal is to use these instead of the SW driver in the #else branch
+
+#include "driver/i2c.h"
+int platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed ) {
+  i2c_config_t conf;
+  conf.mode = I2C_MODE_MASTER;
+  conf.sda_io_num = sda;
+  conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
+  conf.scl_io_num = scl;
+  conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
+  conf.master.clk_speed = speed;
+  if (ESP_OK != i2c_param_config( id, &conf ))
+    return 0;
+
+  if (ESP_OK != i2c_driver_install( id, conf.mode, 0, 0, 0 ))
+    return 0;
+
+  return 1;
+}
+
+int platform_i2c_send_start( unsigned id ) {
+  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+  i2c_master_start( cmd );
+  esp_err_t ret = i2c_master_cmd_begin( id, cmd, 1000 / portTICK_RATE_MS );
+  i2c_cmd_link_delete( cmd );
+
+  return ret == ESP_OK ? 1 : 0;
+}
+
+int platform_i2c_send_stop( unsigned id ) {
+  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+  i2c_master_stop( cmd );
+  esp_err_t ret = i2c_master_cmd_begin( id, cmd, 1000 / portTICK_RATE_MS );
+  i2c_cmd_link_delete( cmd );
+
+  return ret == ESP_OK ? 1 : 0;
+}
+
+int platform_i2c_send_address( unsigned id, uint16_t address, int direction, int ack_check_en ) {
+  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+
+  direction = ( direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ) ? 0 : 1;
+
+  i2c_master_write_byte( cmd, (uint8_t) ((address << 1) | direction ), ack_check_en );
+
+  esp_err_t ret = i2c_master_cmd_begin( id, cmd, 1000 / portTICK_RATE_MS );
+  i2c_cmd_link_delete( cmd );
+
+  // we return ack (1=acked).
+  if (ret == ESP_FAIL)
+    return 0;
+  else if (ret == ESP_OK)
+    return 1;
+  else
+    return -1;
+}
+
+int platform_i2c_send_byte( unsigned id, uint8_t data, int ack_check_en ) {
+  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+  i2c_master_write_byte( cmd, data, ack_check_en );
+
+  esp_err_t ret = i2c_master_cmd_begin( id, cmd, 1000 / portTICK_RATE_MS );
+  i2c_cmd_link_delete( cmd );
+
+  // we return ack (1=acked).
+  if (ret == ESP_FAIL)
+    return 0;
+  else if (ret == ESP_OK)
+    return 1;
+  else
+    return -1;
+}
+
+int platform_i2c_recv_byte( unsigned id, int ack_val ){
+  uint8_t data;
+  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+  i2c_master_read_byte( cmd, &data, ack_val > 0 ? 0 : 1 );
+
+  esp_err_t ret = i2c_master_cmd_begin( id, cmd, 1000 / portTICK_RATE_MS );
+  i2c_cmd_link_delete( cmd );
+
+  return ret == ESP_OK ? data : -1;
+}
+
+#else
+
+// platform functions for SW-based I2C driver
+// they work around the issue with the IDF driver
+// remove when functions for the IDF driver can be used instead
+
+#include "driver/i2c_sw_master.h"
+int platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed ){
+  if (!platform_gpio_output_exists(sda) || !platform_gpio_output_exists(scl))
+    return 0;
+
+  if (speed != PLATFORM_I2C_SPEED_SLOW)
+    return 0;
+
+  i2c_sw_master_gpio_init(sda, scl);
+  return 1;
+}
+
+int platform_i2c_send_start( unsigned id ){
+  i2c_sw_master_start();
+  return 1;
+}
+
+int platform_i2c_send_stop( unsigned id ){
+  i2c_sw_master_stop();
+  return 1;
+}
+
+int platform_i2c_send_address( unsigned id, uint16_t address, int direction, int ack_check_en ){
+  // Convert enum codes to R/w bit value.
+  // If TX == 0 and RX == 1, this test will be removed by the compiler
+  if ( ! ( PLATFORM_I2C_DIRECTION_TRANSMITTER == 0 &&
+           PLATFORM_I2C_DIRECTION_RECEIVER == 1 ) ) {
+    direction = ( direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ) ? 0 : 1;
+  }
+
+  i2c_sw_master_writeByte( (uint8_t) ((address << 1) | direction ));
+  // Low-level returns nack (0=acked); we return ack (1=acked).
+  return ! i2c_sw_master_getAck();
+}
+
+int platform_i2c_send_byte( unsigned id, uint8_t data, int ack_check_en ){
+  i2c_sw_master_writeByte(data);
+  // Low-level returns nack (0=acked); we return ack (1=acked).
+  return ! i2c_sw_master_getAck();
+}
+
+int platform_i2c_recv_byte( unsigned id, int ack ){
+  uint8_t r = i2c_sw_master_readByte();
+  i2c_sw_master_setAck( !ack );
+  return r;
+}
+#endif
+
+int platform_i2c_exists( unsigned id ) { return id < I2C_NUM_MAX; }

+ 133 - 0
docs/en/modules/i2c.md

@@ -0,0 +1,133 @@
+# I²C Module
+| Since  | Origin / Contributor  | Maintainer  | Source  |
+| :----- | :-------------------- | :---------- | :------ |
+| 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [i2c.c](../../../app/modules/i2c.c)|
+
+## i2c.address()
+Setup I²C address and read/write mode for the next transfer.
+
+#### Syntax
+`i2c.address(id, device_addr, direction)`
+
+#### Parameters
+- `id` always 0
+- `device_addr` device address
+- `direction` `i2c.TRANSMITTER` for writing mode , `i2c. RECEIVER` for reading mode
+
+#### Returns
+`true` if ack received, `false` if no ack received.
+
+#### See also
+[i2c.read()](#i2cread)
+
+## i2c.read()
+Read data for variable number of bytes.
+
+#### Syntax
+`i2c.read(id, len)`
+
+#### Parameters
+- `id` always 0
+- `len` number of data bytes
+
+#### Returns
+`string` of received data
+
+#### Example
+```lua
+id  = 0
+sda = 1
+scl = 2
+
+-- initialize i2c, set pin1 as sda, set pin2 as scl
+i2c.setup(id, sda, scl, i2c.SLOW)
+
+-- user defined function: read from reg_addr content of dev_addr
+function read_reg(dev_addr, reg_addr)
+    i2c.start(id)
+    i2c.address(id, dev_addr, i2c.TRANSMITTER)
+    i2c.write(id, reg_addr)
+    i2c.stop(id)
+    i2c.start(id)
+    i2c.address(id, dev_addr, i2c.RECEIVER)
+    c = i2c.read(id, 1)
+    i2c.stop(id)
+    return c
+end
+
+-- get content of register 0xAA of device 0x77
+reg = read_reg(0x77, 0xAA)
+print(string.byte(reg))
+```
+
+####See also
+[i2c.write()](#i2cwrite)
+
+## i2c.setup()
+Initialize the I²C module.
+
+#### Syntax
+`i2c.setup(id, pinSDA, pinSCL, speed)`
+
+####Parameters
+- `id` always 0
+- `pinSDA` 1~12, IO index
+- `pinSCL` 1~12, IO index
+- `speed` only `i2c.SLOW` supported
+
+#### Returns
+`speed` the selected speed
+
+####See also
+[i2c.read()](#i2cread)
+
+## i2c.start()
+Send an I²C start condition.
+
+#### Syntax
+`i2c.start(id)`
+
+#### Parameters
+`id` always 0
+
+#### Returns
+`nil`
+
+####See also
+[i2c.read()](#i2cread)
+
+## i2c.stop()
+Send an I²C stop condition.
+
+#### Syntax
+`i2c.stop(id)`
+
+####Parameters
+`id` always 0
+
+#### Returns
+`nil`
+
+####See also
+[i2c.read()](#i2cread)
+
+## i2c.write()
+Write data to I²C bus. Data items can be multiple numbers, strings or lua tables.
+
+####Syntax
+`i2c.write(id, data1[, data2[, ..., datan]])`
+
+####Parameters
+- `id` always 0
+- `data` data can be numbers, string or lua table.
+
+#### Returns
+`number` number of bytes written
+
+#### Example
+```lua
+i2c.write(0, "hello", "world")
+```
+
+#### See also
+[i2c.read()](#i2cread)

+ 1 - 0
mkdocs.yml

@@ -33,6 +33,7 @@ pages:
         - 'bit':         'en/modules/bit.md'
         - 'bthci':       'en/modules/bthci.md'
         - 'file':        'en/modules/file.md'
+        - 'i2c':         'en/modules/i2c.md'
         - 'node':        'en/modules/node.md'
         - 'sigma delta': 'en/modules/sigma-delta.md'
         - 'struct':      'en/modules/struct.md'