Commit ab61e9c0 authored by Natalia's avatar Natalia Committed by Marcel Stör
Browse files

I2C sw driver with support of multiple buses, Slow, Fast, FastPlus, and...

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
parent 3f5ae99e
This diff is collapsed.
#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__
......@@ -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
......
......@@ -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 );
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 ) },
......
......@@ -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
......
......@@ -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);
}
// *****************************************************************************
......
......@@ -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 );
......
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment