extend i2c master with hardware interfaces for synchronous and asynchronous operation

This commit is contained in:
devsaurus 2017-02-01 00:14:49 +01:00
parent 381726f103
commit f5632f41f9
5 changed files with 550 additions and 59 deletions

View File

@ -2,23 +2,32 @@
#include "module.h"
#include "lauxlib.h"
#include "lextra.h"
#include "platform.h"
#include "i2c_common.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 );
uint32_t speed = (uint32_t)luaL_checkinteger( L, 4 );
MOD_CHECK_ID( i2c, id );
luaL_argcheck( L, id < I2C_ID_MAX, 1, "invalid 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 (id == I2C_ID_SW) {
luaL_argcheck( L, speed <= PLATFORM_I2C_SPEED_SLOW, 4, "wrong arg range" );
if (!platform_i2c_setup( id, sda, scl, (uint32_t)speed ))
luaL_error( L, "setup failed" );
} else {
luaL_argcheck( L, speed <= PLATFORM_I2C_SPEED_FASTPLUS, 4, "wrong arg range" );
li2c_hw_master_setup( L, id, sda, scl, speed );
}
return 0;
}
@ -28,9 +37,14 @@ static int i2c_start( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( i2c, id );
luaL_argcheck( L, id < I2C_ID_MAX, 1, "invalid id" );
if (id == I2C_ID_SW) {
if (!platform_i2c_send_start( id ))
luaL_error( L, "command failed");
} else {
li2c_hw_master_start( L, id );
}
return 0;
}
@ -40,9 +54,14 @@ static int i2c_stop( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( i2c, id );
luaL_argcheck( L, id < I2C_ID_MAX, 1, "invalid id" );
if (id == I2C_ID_SW) {
if (!platform_i2c_send_stop( id ))
luaL_error( L, "command failed" );
} else {
li2c_hw_master_stop( L, id );
}
return 0;
}
@ -50,14 +69,28 @@ static int i2c_stop( lua_State *L )
// 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 );
int stack = 0;
MOD_CHECK_ID( i2c, id );
luaL_argcheck( L, address >= 0 && address <= 127, 2, "wrong arg range" );
unsigned id = luaL_checkinteger( L, ++stack );
luaL_argcheck( L, id < I2C_ID_MAX, stack, "invalid id" );
int ret = platform_i2c_send_address( id, (uint16_t)address, direction, 1 );
int address = luaL_checkinteger( L, ++stack );
luaL_argcheck( L, address >= 0 && address <= 127, stack, "wrong arg range" );
int direction = luaL_checkinteger( L, ++stack );
luaL_argcheck( L,
direction == PLATFORM_I2C_DIRECTION_TRANSMITTER ||
direction == PLATFORM_I2C_DIRECTION_RECEIVER,
stack, "wrong arg range" );
bool ack_check_en = luaL_optbool( L, ++stack, true );
int ret;
if (id == I2C_ID_SW) {
ret = platform_i2c_send_address( id, (uint16_t)address, direction, ack_check_en ? 1 : 0 );
} else {
ret = li2c_hw_master_address( L, id, (uint16_t)address, direction, ack_check_en );
}
lua_pushboolean( L, ret > 0 );
return 1;
@ -68,13 +101,20 @@ static int i2c_address( lua_State *L )
static int i2c_write( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
luaL_argcheck( L, id < I2C_ID_MAX, 1, "invalid id" );
const char *pdata;
size_t datalen, i;
int numdata;
uint32_t wrote = 0;
unsigned argn;
MOD_CHECK_ID( i2c, id );
bool ack_check_en = true;
if (lua_isboolean( L, -1 )) {
ack_check_en = lua_toboolean( L, -1 );
lua_pop( L, 1 );
}
if( lua_gettop( L ) < 2 )
return luaL_error( L, "wrong arg type" );
for( argn = 2; argn <= lua_gettop( L ); argn ++ )
@ -86,8 +126,12 @@ static int i2c_write( lua_State *L )
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 )
if (id == I2C_ID_SW) {
if( platform_i2c_send_byte( id, numdata, 1) != 1 && ack_check_en )
break;
} else {
li2c_hw_master_write( L, id, numdata, ack_check_en );
}
wrote ++;
}
else if( lua_istable( L, argn ) )
@ -100,8 +144,12 @@ static int i2c_write( lua_State *L )
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 )
if (id == I2C_ID_SW) {
if( platform_i2c_send_byte( id, numdata, 1 ) == 0 && ack_check_en)
break;
} else {
li2c_hw_master_write( L, id, numdata, ack_check_en );
}
}
wrote += i;
if( i < datalen )
@ -111,8 +159,12 @@ static int i2c_write( lua_State *L )
{
pdata = luaL_checklstring( L, argn, &datalen );
for( i = 0; i < datalen; i ++ )
if( platform_i2c_send_byte( id, pdata[ i ], 1 ) == 0 )
if (id == I2C_ID_SW) {
if( platform_i2c_send_byte( id, pdata[ i ], 1 ) == 0 && ack_check_en)
break;
} else {
li2c_hw_master_write( L, id, pdata[ i ], ack_check_en );
}
wrote += i;
if( i < datalen )
break;
@ -126,11 +178,14 @@ static int i2c_write( lua_State *L )
static int i2c_read( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
luaL_argcheck( L, id < I2C_ID_MAX, 1, "invalid id" );
uint32_t size = ( uint32_t )luaL_checkinteger( L, 2 ), i;
if (id == I2C_ID_SW) {
luaL_Buffer b;
int data;
MOD_CHECK_ID( i2c, id );
if( size == 0 )
return 0;
luaL_buffinit( L, &b );
@ -141,6 +196,10 @@ static int i2c_read( lua_State *L )
luaL_addchar( &b, ( char )data );
luaL_pushresult( &b );
return 1;
} else {
li2c_hw_master_read( L, id, size );
return 0;
}
}
static const LUA_REG_TYPE i2c_map[] = {
@ -150,12 +209,23 @@ static const LUA_REG_TYPE i2c_map[] = {
{ LSTRKEY( "address" ), LFUNCVAL( i2c_address ) },
{ LSTRKEY( "write" ), LFUNCVAL( i2c_write ) },
{ LSTRKEY( "read" ), LFUNCVAL( i2c_read ) },
{ LSTRKEY( "transfer" ), LFUNCVAL( li2c_hw_master_transfer ) },
{ 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 ) },
{ LSTRKEY( "SW" ), LNUMVAL( I2C_ID_SW ) },
{ LSTRKEY( "HW0" ), LNUMVAL( I2C_ID_HW0 ) },
{ LSTRKEY( "HW1" ), LNUMVAL( I2C_ID_HW1 ) },
{LNILKEY, LNILVAL}
};
NODEMCU_MODULE(I2C, "i2c", i2c_map, NULL);
int luaopen_i2c( lua_State *L ) {
li2c_hw_master_init( L );
return 0;
}
NODEMCU_MODULE(I2C, "i2c", i2c_map, luaopen_i2c);

View File

@ -0,0 +1,25 @@
#ifndef _NODEMCU_I2C_COMMON_H_
#define _NODEMCU_I2C_COMMON_H_
#include "lauxlib.h"
typedef enum {
I2C_ID_SW = 0,
I2C_ID_HW0,
I2C_ID_HW1,
I2C_ID_MAX
} i2c_id_type;
void li2c_hw_master_init( lua_State *L );
void li2c_hw_master_setup( lua_State *L, unsigned id, unsigned sda, unsigned scl, uint32_t speed );
void li2c_hw_master_start( lua_State *L, unsigned id );
void li2c_hw_master_stop( lua_State *L, unsigned id );
int li2c_hw_master_address( lua_State *L, unsigned id, uint16_t address, uint8_t direction, bool ack_check_en );
void li2c_hw_master_write( lua_State *L, unsigned id, uint8_t data, bool ack_check_en );
void li2c_hw_master_read( lua_State *L, unsigned id, uint32_t len );
int li2c_hw_master_transfer( lua_State *L );
#endif /*_NODEMCU_I2C_COMMON_H_*/

View File

@ -0,0 +1,361 @@
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "driver/i2c.h"
#include "i2c_common.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_task.h"
#include "task/task.h"
#include <string.h>
// result data descriptor
// it's used as variable length data block, filled by the i2c driver during read
typedef struct {
size_t len;
uint8_t data[1];
} i2c_result_type;
// job descriptor
// contains all information for the transfer task and subsequent result task
// to process the transfer
typedef struct {
unsigned port;
i2c_cmd_handle_t cmd;
unsigned to_ms;
i2c_result_type *result;
esp_err_t err;
int cb_ref;
} i2c_job_desc_type;
// user data descriptor for a port
// provides buffer for the job setup and holds port-specific task & queue handles
typedef struct {
i2c_job_desc_type job;
TaskHandle_t xTransferTaskHandle;
QueueHandle_t xTransferJobQueue;
} i2c_hw_master_ud_type;
// the global variables for storing userdata for each I2C port
static i2c_hw_master_ud_type i2c_hw_master_ud[I2C_NUM_MAX];
// Transfer task handle and job queue
static task_handle_t i2c_result_task_id;
// Transfer Task
// This is a fully-fledged FreeRTOS task which runs concurrently and pulls
// jobs off the queue. Jobs are forwarded to i2c_master_cmd_begin() which blocks
// this task throughout the I2C transfer.
// Posts a task to the nodemcu task system to resume.
//
void vTransferTask( void *pvParameters )
{
QueueHandle_t xQueue = (QueueHandle_t)pvParameters;
i2c_job_desc_type *job;
for (;;) {
job = (i2c_job_desc_type *)malloc( sizeof( i2c_job_desc_type ) );
// get a job descriptor
xQueueReceive( xQueue, job, portMAX_DELAY );
job->err = i2c_master_cmd_begin( job->port, job->cmd,
job->to_ms > 0 ? job->to_ms / portTICK_RATE_MS : portMAX_DELAY );
task_post_low( i2c_result_task_id, (task_param_t)job );
}
}
// Free memory of a job descriptor
//
void free_job_memory( lua_State *L, i2c_job_desc_type *job )
{
if (job->result)
luaM_free( L, job->result );
luaL_unref( L, LUA_REGISTRYINDEX, job->cb_ref);
if (job->cmd)
i2c_cmd_link_delete( job->cmd );
}
// Result Task
// Is posted by the Transfer Task and triggers the Lua callback with optional
// read result data.
//
void i2c_result_task( task_param_t param, task_prio_t prio )
{
i2c_job_desc_type *job = (i2c_job_desc_type *)param;
lua_State *L = lua_getstate();
if (job->cb_ref != LUA_NOREF) {
lua_rawgeti( L, LUA_REGISTRYINDEX, job->cb_ref );
lua_pushinteger( L, job->err );
if (job->result) {
// all fine, deliver read data
lua_pushlstring( L, (char *)job->result->data, job->result->len );
} else {
lua_pushnil( L );
}
lua_call(L, 2, 0);
}
// free all memory
free_job_memory( L, job );
free( job );
}
// Set up FreeRTOS task and queue
// Prepares the gory tasking stuff.
//
void setup_task_and_queue( lua_State *L, i2c_hw_master_ud_type *ud )
{
// create queue
// depth 1 allows to skip "wait for empty queue" in synchronous mode
// consider this when increasing depth
ud->xTransferJobQueue = xQueueCreate( 1, sizeof( i2c_job_desc_type ) );
if (!ud->xTransferJobQueue)
luaL_error( L, "failed to create queue" );
char pcName[configMAX_TASK_NAME_LEN+1];
snprintf( pcName, configMAX_TASK_NAME_LEN+1, "I2C_Task_%d", ud->job.port );
pcName[configMAX_TASK_NAME_LEN] = '\0';
// create task with higher priority
BaseType_t xReturned = xTaskCreate( vTransferTask,
pcName,
1024,
(void *)ud->xTransferJobQueue,
ESP_TASK_MAIN_PRIO + 1,
&(ud->xTransferTaskHandle) );
if (xReturned != pdPASS) {
vQueueDelete( ud->xTransferJobQueue );
ud->xTransferJobQueue = NULL;
luaL_error( L, "failed to create task" );
}
}
#define get_udata(L, id) \
unsigned port = id - I2C_ID_HW0; \
luaL_argcheck( L, port < I2C_NUM_MAX, 1, "invalid hardware id" ); \
i2c_hw_master_ud_type *ud = &(i2c_hw_master_ud[port]); \
i2c_job_desc_type *job = &(ud->job);
static int i2c_lua_checkerr( lua_State *L, esp_err_t err ) {
const char *msg;
switch (err) {
case ESP_OK: return 0;
case ESP_FAIL: msg = "command failed or NACK from slave"; break;
case ESP_ERR_INVALID_ARG: msg = "parameter error"; break;
case ESP_ERR_INVALID_STATE: msg = "driver state error"; break;
case ESP_ERR_TIMEOUT: msg = "timeout"; break;
default: msg = "unknown error"; break;
}
return luaL_error( L, msg );
}
// Set up userdata for a transfer
//
static void i2c_setup_ud_transfer( lua_State *L, i2c_hw_master_ud_type *ud )
{
free_job_memory( L, &(ud->job) );
ud->job.result = NULL;
ud->job.cb_ref = LUA_NOREF;
// set up an empty command link
ud->job.cmd = i2c_cmd_link_create();
}
// Set up the HW as master interface
// Cares for I2C driver creation and initialization.
// Prepares an empty job descriptor and triggers setup of FreeRTOS stuff.
//
void li2c_hw_master_setup( lua_State *L, unsigned id, unsigned sda, unsigned scl, uint32_t speed )
{
get_udata(L, id);
i2c_config_t cfg;
memset( &cfg, 0, sizeof( cfg ) );
cfg.mode = I2C_MODE_MASTER;
luaL_argcheck( L, GPIO_IS_VALID_OUTPUT_GPIO(sda), 1, "invalid sda pin" );
cfg.sda_io_num = sda;
cfg.sda_pullup_en = GPIO_PULLUP_ENABLE;
luaL_argcheck( L, GPIO_IS_VALID_OUTPUT_GPIO(scl), 1, "invalid scl pin" );
cfg.scl_io_num = scl;
cfg.scl_pullup_en = GPIO_PULLUP_ENABLE;
luaL_argcheck( L, speed > 0 && speed <= 1000000, 1, "invalid speed" );
cfg.master.clk_speed = speed;
// init driver level
i2c_lua_checkerr( L, i2c_param_config( port, &cfg ) );
i2c_lua_checkerr( L, i2c_driver_install( port, cfg.mode, 0, 0, 0 ));
job->port = port;
job->cmd = NULL;
job->result = NULL;
job->cb_ref = LUA_NOREF;
i2c_setup_ud_transfer( L, ud );
// kick-start transfer task
setup_task_and_queue( L, ud );
}
void li2c_hw_master_start( lua_State *L, unsigned id )
{
get_udata(L, id);
if (!job->cmd)
luaL_error( L, "no commands scheduled" );
i2c_lua_checkerr( L, i2c_master_start( job->cmd ) );
}
void li2c_hw_master_stop( lua_State *L, unsigned id )
{
get_udata(L, id);
if (!job->cmd)
luaL_error( L, "no commands scheduled" );
i2c_lua_checkerr( L, i2c_master_stop( job->cmd ) );
}
int li2c_hw_master_address( lua_State *L, unsigned id, uint16_t address, uint8_t direction, bool ack_check_en )
{
get_udata(L, id);
if (!job->cmd)
luaL_error( L, "no commands scheduled" );
i2c_lua_checkerr( L, i2c_master_write_byte( job->cmd, address << 1 | direction, ack_check_en ) );
return 1;
}
void li2c_hw_master_write( lua_State *L, unsigned id, uint8_t data, bool ack_check_en )
{
get_udata(L, id);
if (!job->cmd)
luaL_error( L, "no commands scheduled" );
i2c_lua_checkerr( L, i2c_master_write_byte( job->cmd, data, ack_check_en ) );
}
void li2c_hw_master_read( lua_State *L, unsigned id, uint32_t len )
{
get_udata(L, id);
if (!job->cmd)
luaL_error( L, "no commands scheduled" );
if (job->result)
luaL_error( L, "only one read per transfer supported" );
// allocate read buffer as user data
i2c_result_type *res = (i2c_result_type *)luaM_malloc( L, sizeof( i2c_result_type ) + len-1 );
res->len = len;
job->result = res;
if (len > 1)
i2c_lua_checkerr( L, i2c_master_read( job->cmd, res->data, len-1, 0 ) );
i2c_lua_checkerr( L, i2c_master_read( job->cmd, res->data + len-1, 1, 1 ) );
}
// Initiate the I2C transfer
// Depending on the presence of a callback parameter, it will stay in synchronous mode
// or posts the job to the FreeRTOS queue for asynchronous processing.
//
int li2c_hw_master_transfer( lua_State *L )
{
int stack = 0;
unsigned id = luaL_checkinteger( L, ++stack );
get_udata(L, id);
if (!job->cmd)
luaL_error( L, "no commands scheduled" );
stack++;
if (lua_isfunction( L, stack ) || lua_islightfunction( L, stack )) {
lua_pushvalue( L, stack ); // copy argument (func) to the top of stack
luaL_unref( L, LUA_REGISTRYINDEX, job->cb_ref );
job->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else
stack--;
int to_ms = luaL_optint( L, ++stack, 0 );
if (to_ms < 0)
to_ms = 0;
job->to_ms = to_ms;
if (job->cb_ref != LUA_NOREF) {
// asynchronous mode
xQueueSend( ud->xTransferJobQueue, job, portMAX_DELAY );
// the transfer task should be unblocked now
// (i.e. in eReady state since it can receive from the queue)
// block this task briefly to allow switch over to the transfer task (it has higher prio)
vTaskDelay( 1 );
// invalidate last job, it's queued now
job->cmd = NULL; // don't delete link! it's used by the transfer task
job->result = NULL; // don't free result memory! it's used by the transfer task
job->cb_ref = LUA_NOREF; // don't unref! it's used by the transfer task
// prepare the next transfer
i2c_setup_ud_transfer( L, ud );
return 0;
} else {
// synchronous mode
// no need to wait for queue to become empty when queue depth is 1
// note that i2c_master_cmd_begin() implements mutual exclusive access
// if it is currently in progress from the transfer task, it will block here until
esp_err_t err = i2c_master_cmd_begin( job->port, job->cmd,
job->to_ms > 0 ? job->to_ms / portTICK_RATE_MS : portMAX_DELAY );
if (err == ESP_OK) {
if (job->result) {
// all fine, deliver read data
lua_pushlstring( L, (char *)job->result->data, job->result->len );
} else {
lua_pushnil( L );
}
// prepare the next transfer
i2c_setup_ud_transfer( L, ud );
return 1;
}
i2c_setup_ud_transfer( L, ud );
return i2c_lua_checkerr( L, err );
}
}
void li2c_hw_master_init( lua_State *L )
{
// prepare task id
i2c_result_task_id = task_get_id( i2c_result_task );
}

View File

@ -94,7 +94,8 @@ uint8_t platform_sigma_delta_set_duty( uint8_t channel, int8_t duty );
enum
{
PLATFORM_I2C_SPEED_SLOW = 100000,
PLATFORM_I2C_SPEED_FAST = 400000
PLATFORM_I2C_SPEED_FAST = 400000,
PLATFORM_I2C_SPEED_FASTPLUS = 1000000
};
// I2C direction

View File

@ -3,39 +3,50 @@
| :----- | :-------------------- | :---------- | :------ |
| 2014-12-22 | [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [i2c.c](../../../app/modules/i2c.c)|
This module supports different interfaces for talking to I²C slaves. All interfaces can be assigned to arbitrary GPIOs for SCL and SDA and can be operated concurrently.
- `i2c.SW` software based bitbanging, synchronous operation
- `i2c.HW0` ESP32 hardware port 0, synchronous or asynchronous operation
- `i2c.HW1` ESP32 hardware port 1, synchronous or asynchronous operation
The hardware interfaces differ from the SW interface as the commands (start, stop, read, write) are queued up to an internal command list. Actual I²C communication is initiated afterwards using the `i2c.transfer()` function. Commands for the `i2c.SW` interface are immediately effective on the I²C bus and read data is also instantly available.
## i2c.address()
Setup I²C address and read/write mode for the next transfer.
Send (`i2c.SW`) or queue (`i2c.HWx`) I²C address and read/write mode for the next transfer.
Communication stops when the slave answers with NACK to the address byte. This can be avoided with parameter `ack_check_en` on `false`.
#### Syntax
`i2c.address(id, device_addr, direction)`
`i2c.address(id, device_addr, direction[, ack_check_en])`
#### Parameters
- `id` always 0
- `id` interface id
- `device_addr` device address
- `direction` `i2c.TRANSMITTER` for writing mode , `i2c. RECEIVER` for reading mode
- `direction` `i2c.TRANSMITTER` for writing mode , `i2c.RECEIVER` for reading mode
- `ack_check_en` enable check for slave ACK with `true` (default), disable with `false`
#### Returns
`true` if ack received, `false` if no ack received.
`true` if ack received (always for ids `i2c.HW0` and `i2c.HW1`), `false` if no ack received (only possible for `i2c.SW`).
#### See also
[i2c.read()](#i2cread)
## i2c.read()
Read data for variable number of bytes.
Read (`i2c.SW`) or queue (`i2c.HWx`) data for variable number of bytes.
#### Syntax
`i2c.read(id, len)`
#### Parameters
- `id` always 0
- `id` interface id
- `len` number of data bytes
#### Returns
`string` of received data
- `string` of received data for interface `i2c.SW`
- `nil` for ids `i2c.HW0` and `i2c.HW1`
#### Example
```lua
id = 0
id = i2c.SW
sda = 1
scl = 2
@ -70,10 +81,13 @@ Initialize the I²C module.
`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
- `id` interface id
- `pinSDA` 0~33, IO index
- `pinSCL` 0-33, IO index
- `speed` bit rate in Hz, positive integer
- `i2c.SLOW` for 100000 Hz, max for `i2c.SW`
- `i2c.FAST` for 400000 Hz
- `i2c.FASTPLUS` for 1000000 Hz
#### Returns
`speed` the selected speed
@ -82,13 +96,13 @@ Initialize the I²C module.
[i2c.read()](#i2cread)
## i2c.start()
Send an I²C start condition.
Send (`i2c.SW`) or queue (`i2c.HWx`) an I²C start condition.
#### Syntax
`i2c.start(id)`
#### Parameters
`id` always 0
`id` interface id
#### Returns
`nil`
@ -97,13 +111,13 @@ Send an I²C start condition.
[i2c.read()](#i2cread)
## i2c.stop()
Send an I²C stop condition.
Send (`i2c.SW`) or queue (`i2c.HWx`) an I²C stop condition.
#### Syntax
`i2c.stop(id)`
####Parameters
`id` always 0
`id` interface id
#### Returns
`nil`
@ -111,15 +125,35 @@ Send an I²C stop condition.
####See also
[i2c.read()](#i2cread)
## i2c.transfer()
Starts a transfer for the specified hardware module. Providing a callback function allows the transfer to be started asynchronously in the background and `i2c.transfer()` finishes immediately. Without a callback function, the transfer is executed synchronously and `i2c.transfer()` comes back when the transfer completed. Data from a read operation is returned from `i2c.transfer()` in this case.
First argument to the callback is the error code (0 = no error), followed by a string with data obtained from a read operation during the transfer or `nil`.
#### Syntax
`i2c.transfer(id[, cb_fn][, to_ms])`
#### Parameters
- `id` interface id, `i2c.SW` not allowed
- `cb_fn(err, data)` function to be called when transfer finished
- `to_ms` timeout for the transfer in ms, defaults to 0=infinite
#### Returns
- `string` of received data (or `nil` if no read) for synchronous operation
- `nil` for asynchronous operation
## i2c.write()
Write data to I²C bus. Data items can be multiple numbers, strings or lua tables.
Write (`i2c.SW`) or queue (`i2c.HWx`) data to I²C bus. Data items can be multiple numbers, strings or lua tables.
Communication stops when the slave answers with NACK to a written byte. This can be avoided with parameter `ack_check_en` on `false`.
####Syntax
`i2c.write(id, data1[, data2[, ..., datan]])`
`i2c.write(id, data1[, data2[, ..., datan]][, ack_check_en])`
####Parameters
- `id` always 0
- `id` interface id
- `data` data can be numbers, string or lua table.
- `ack_check_en` enable check for slave ACK with `true` (default), disable with `false`
#### Returns
`number` number of bytes written