Reported from different sources from github or bbs: https://github.com/espressif/esp-idf/issues/680 https://github.com/espressif/esp-idf/issues/922 We tested reading several sensor or other I2C slave devices, if the power and SDA/SCL wires are in proper condition, everything works find with reading the slave. If we remove the power supply for the slave during I2C is reading, or directly connect SDA or SCL to ground, this would cause the I2C FSM get stuck in wrong state, all we can do is the reset the I2C hardware in this case. After this commit, no matter whether the power supply of I2C slave is removed or SDA / SCL are shorted to ground, the driver can recover from wrong state. We are not sure whether this the save issue with the reported one yet, but to make the driver more robust. Further information: 1. For I2C master mode, we have tested different situations, e.g., to short the SDA/SCL directly to GND/VCC, to short the SDA to SCL, to un-plug the slave device, to power off the slave device. Under all of those situations, this version of driver can recover and keep working. 2. Some slave device will die by accident and keep the SDA in low level, in this case, master should send several clock to make the slave release the bus. 3. Slave mode of ESP32 might also get in wrong state that held the SDA low, in this case, master device could send a stop signal to make esp32 slave release the bus. Modifications: 1. Disable I2C_MASTER_TRAN_COMP interrupt to void extra interrupt. 2. Disable un-used timeout interrupt for slave. 3. Add bus reset if error detected for master mode. 4. Add bus clear if SDA level is low when error detected. 5. Modify the argument type of i2c_set_pin. 6. add API to set timeout value 7. add parameter check for timing APIs
312 lines
13 KiB
C
312 lines
13 KiB
C
/* i2c - Example
|
|
|
|
For other examples please check:
|
|
https://github.com/espressif/esp-idf/tree/master/examples
|
|
|
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
|
|
|
Unless required by applicable law or agreed to in writing, this
|
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
CONDITIONS OF ANY KIND, either express or implied.
|
|
*/
|
|
#include <stdio.h>
|
|
#include "driver/i2c.h"
|
|
|
|
/**
|
|
* TEST CODE BRIEF
|
|
*
|
|
* This example will show you how to use I2C module by running two tasks on i2c bus:
|
|
*
|
|
* - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance.
|
|
* - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip.
|
|
*
|
|
* Pin assignment:
|
|
*
|
|
* - slave :
|
|
* GPIO25 is assigned as the data signal of i2c slave port
|
|
* GPIO26 is assigned as the clock signal of i2c slave port
|
|
* - master:
|
|
* GPIO18 is assigned as the data signal of i2c master port
|
|
* GPIO19 is assigned as the clock signal of i2c master port
|
|
*
|
|
* Connection:
|
|
*
|
|
* - connect GPIO18 with GPIO25
|
|
* - connect GPIO19 with GPIO26
|
|
* - connect sda/scl of sensor with GPIO18/GPIO19
|
|
* - no need to add external pull-up resistors, driver will enable internal pull-up resistors.
|
|
*
|
|
* Test items:
|
|
*
|
|
* - read the sensor data, if connected.
|
|
* - i2c master(ESP32) will write data to i2c slave(ESP32).
|
|
* - i2c master(ESP32) will read data from i2c slave(ESP32).
|
|
*/
|
|
|
|
#define DATA_LENGTH 512 /*!<Data buffer length for test buffer*/
|
|
#define RW_TEST_LENGTH 129 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/
|
|
#define DELAY_TIME_BETWEEN_ITEMS_MS 1234 /*!< delay time between different test items */
|
|
|
|
#define I2C_EXAMPLE_SLAVE_SCL_IO 26 /*!<gpio number for i2c slave clock */
|
|
#define I2C_EXAMPLE_SLAVE_SDA_IO 25 /*!<gpio number for i2c slave data */
|
|
#define I2C_EXAMPLE_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */
|
|
#define I2C_EXAMPLE_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */
|
|
#define I2C_EXAMPLE_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */
|
|
|
|
#define I2C_EXAMPLE_MASTER_SCL_IO 19 /*!< gpio number for I2C master clock */
|
|
#define I2C_EXAMPLE_MASTER_SDA_IO 18 /*!< gpio number for I2C master data */
|
|
#define I2C_EXAMPLE_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */
|
|
#define I2C_EXAMPLE_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
|
|
#define I2C_EXAMPLE_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */
|
|
#define I2C_EXAMPLE_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
|
|
|
|
#define BH1750_SENSOR_ADDR 0x23 /*!< slave address for BH1750 sensor */
|
|
#define BH1750_CMD_START 0x23 /*!< Command to set measure mode */
|
|
#define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */
|
|
#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */
|
|
#define READ_BIT I2C_MASTER_READ /*!< I2C master read */
|
|
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
|
|
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
|
|
#define ACK_VAL 0x0 /*!< I2C ack value */
|
|
#define NACK_VAL 0x1 /*!< I2C nack value */
|
|
|
|
SemaphoreHandle_t print_mux = NULL;
|
|
|
|
/**
|
|
* @brief test code to read esp-i2c-slave
|
|
* We need to fill the buffer of esp slave device, then master can read them out.
|
|
*
|
|
* _______________________________________________________________________________________
|
|
* | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop |
|
|
* --------|--------------------------|----------------------|--------------------|------|
|
|
*
|
|
*/
|
|
static esp_err_t i2c_example_master_read_slave(i2c_port_t i2c_num, uint8_t* data_rd, size_t size)
|
|
{
|
|
if (size == 0) {
|
|
return ESP_OK;
|
|
}
|
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
|
i2c_master_start(cmd);
|
|
i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | READ_BIT, ACK_CHECK_EN);
|
|
if (size > 1) {
|
|
i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
|
|
}
|
|
i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
|
|
i2c_master_stop(cmd);
|
|
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
|
i2c_cmd_link_delete(cmd);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Test code to write esp-i2c-slave
|
|
* Master device write data to slave(both esp32),
|
|
* the data will be stored in slave buffer.
|
|
* We can read them out from slave buffer.
|
|
*
|
|
* ___________________________________________________________________
|
|
* | start | slave_addr + wr_bit + ack | write n bytes + ack | stop |
|
|
* --------|---------------------------|----------------------|------|
|
|
*
|
|
*/
|
|
static esp_err_t i2c_example_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size)
|
|
{
|
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
|
i2c_master_start(cmd);
|
|
i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN);
|
|
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
|
|
i2c_master_stop(cmd);
|
|
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
|
i2c_cmd_link_delete(cmd);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief test code to write esp-i2c-slave
|
|
*
|
|
* 1. set mode
|
|
* _________________________________________________________________
|
|
* | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop |
|
|
* --------|---------------------------|---------------------|------|
|
|
* 2. wait more than 24 ms
|
|
* 3. read data
|
|
* ______________________________________________________________________________________
|
|
* | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop |
|
|
* --------|---------------------------|--------------------|--------------------|------|
|
|
*/
|
|
static esp_err_t i2c_example_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l)
|
|
{
|
|
int ret;
|
|
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
|
i2c_master_start(cmd);
|
|
i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
|
|
i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN);
|
|
i2c_master_stop(cmd);
|
|
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
|
i2c_cmd_link_delete(cmd);
|
|
if (ret != ESP_OK) {
|
|
return ret;
|
|
}
|
|
vTaskDelay(30 / portTICK_RATE_MS);
|
|
cmd = i2c_cmd_link_create();
|
|
i2c_master_start(cmd);
|
|
i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
|
|
i2c_master_read_byte(cmd, data_h, ACK_VAL);
|
|
i2c_master_read_byte(cmd, data_l, NACK_VAL);
|
|
i2c_master_stop(cmd);
|
|
ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
|
|
i2c_cmd_link_delete(cmd);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief i2c master initialization
|
|
*/
|
|
static void i2c_example_master_init()
|
|
{
|
|
int i2c_master_port = I2C_EXAMPLE_MASTER_NUM;
|
|
i2c_config_t conf;
|
|
conf.mode = I2C_MODE_MASTER;
|
|
conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO;
|
|
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
|
conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO;
|
|
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
|
conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ;
|
|
i2c_param_config(i2c_master_port, &conf);
|
|
i2c_driver_install(i2c_master_port, conf.mode,
|
|
I2C_EXAMPLE_MASTER_RX_BUF_DISABLE,
|
|
I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief i2c slave initialization
|
|
*/
|
|
static void i2c_example_slave_init()
|
|
{
|
|
int i2c_slave_port = I2C_EXAMPLE_SLAVE_NUM;
|
|
i2c_config_t conf_slave;
|
|
conf_slave.sda_io_num = I2C_EXAMPLE_SLAVE_SDA_IO;
|
|
conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE;
|
|
conf_slave.scl_io_num = I2C_EXAMPLE_SLAVE_SCL_IO;
|
|
conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE;
|
|
conf_slave.mode = I2C_MODE_SLAVE;
|
|
conf_slave.slave.addr_10bit_en = 0;
|
|
conf_slave.slave.slave_addr = ESP_SLAVE_ADDR;
|
|
i2c_param_config(i2c_slave_port, &conf_slave);
|
|
i2c_driver_install(i2c_slave_port, conf_slave.mode,
|
|
I2C_EXAMPLE_SLAVE_RX_BUF_LEN,
|
|
I2C_EXAMPLE_SLAVE_TX_BUF_LEN, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief test function to show buffer
|
|
*/
|
|
static void disp_buf(uint8_t* buf, int len)
|
|
{
|
|
int i;
|
|
for (i = 0; i < len; i++) {
|
|
printf("%02x ", buf[i]);
|
|
if (( i + 1 ) % 16 == 0) {
|
|
printf("\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void i2c_test_task(void* arg)
|
|
{
|
|
int i = 0;
|
|
int ret;
|
|
uint32_t task_idx = (uint32_t) arg;
|
|
uint8_t* data = (uint8_t*) malloc(DATA_LENGTH);
|
|
uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH);
|
|
uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH);
|
|
uint8_t sensor_data_h, sensor_data_l;
|
|
int cnt = 0;
|
|
while (1) {
|
|
printf("test cnt: %d\n", cnt++);
|
|
ret = i2c_example_master_sensor_test( I2C_EXAMPLE_MASTER_NUM, &sensor_data_h, &sensor_data_l);
|
|
xSemaphoreTake(print_mux, portMAX_DELAY);
|
|
if(ret == ESP_ERR_TIMEOUT) {
|
|
printf("I2C timeout\n");
|
|
} else if(ret == ESP_OK) {
|
|
printf("*******************\n");
|
|
printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx);
|
|
printf("*******************\n");
|
|
printf("data_h: %02x\n", sensor_data_h);
|
|
printf("data_l: %02x\n", sensor_data_l);
|
|
printf("sensor val: %f\n", (sensor_data_h << 8 | sensor_data_l) / 1.2);
|
|
} else {
|
|
printf("No ack, sensor not connected...skip...\n");
|
|
}
|
|
xSemaphoreGive(print_mux);
|
|
vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);
|
|
//---------------------------------------------------
|
|
for (i = 0; i < DATA_LENGTH; i++) {
|
|
data[i] = i;
|
|
}
|
|
xSemaphoreTake(print_mux, portMAX_DELAY);
|
|
size_t d_size = i2c_slave_write_buffer(I2C_EXAMPLE_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
|
|
if (d_size == 0) {
|
|
printf("i2c slave tx buffer full\n");
|
|
ret = i2c_example_master_read_slave(I2C_EXAMPLE_MASTER_NUM, data_rd, DATA_LENGTH);
|
|
} else {
|
|
ret = i2c_example_master_read_slave(I2C_EXAMPLE_MASTER_NUM, data_rd, RW_TEST_LENGTH);
|
|
}
|
|
|
|
if (ret == ESP_ERR_TIMEOUT) {
|
|
printf("I2C timeout\n");
|
|
printf("*********\n");
|
|
} else if (ret == ESP_OK) {
|
|
printf("*******************\n");
|
|
printf("TASK[%d] MASTER READ FROM SLAVE\n", task_idx);
|
|
printf("*******************\n");
|
|
printf("====TASK[%d] Slave buffer data ====\n", task_idx);
|
|
disp_buf(data, d_size);
|
|
printf("====TASK[%d] Master read ====\n", task_idx);
|
|
disp_buf(data_rd, d_size);
|
|
} else {
|
|
printf("Master read slave error, IO not connected...\n");
|
|
}
|
|
xSemaphoreGive(print_mux);
|
|
vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);
|
|
//---------------------------------------------------
|
|
int size;
|
|
for (i = 0; i < DATA_LENGTH; i++) {
|
|
data_wr[i] = i + 10;
|
|
}
|
|
xSemaphoreTake(print_mux, portMAX_DELAY);
|
|
//we need to fill the slave buffer so that master can read later
|
|
ret = i2c_example_master_write_slave( I2C_EXAMPLE_MASTER_NUM, data_wr, RW_TEST_LENGTH);
|
|
if (ret == ESP_OK) {
|
|
size = i2c_slave_read_buffer( I2C_EXAMPLE_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS);
|
|
}
|
|
if (ret == ESP_ERR_TIMEOUT) {
|
|
printf("I2C timeout\n");
|
|
} else if (ret == ESP_OK) {
|
|
printf("*******************\n");
|
|
printf("TASK[%d] MASTER WRITE TO SLAVE\n", task_idx);
|
|
printf("*******************\n");
|
|
printf("----TASK[%d] Master write ----\n", task_idx);
|
|
disp_buf(data_wr, RW_TEST_LENGTH);
|
|
printf("----TASK[%d] Slave read: [%d] bytes ----\n", task_idx, size);
|
|
disp_buf(data, size);
|
|
} else {
|
|
printf("TASK[%d] Master write slave error, IO not connected....\n", task_idx);
|
|
}
|
|
xSemaphoreGive(print_mux);
|
|
vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS);
|
|
}
|
|
}
|
|
|
|
void app_main()
|
|
{
|
|
print_mux = xSemaphoreCreateMutex();
|
|
i2c_example_slave_init();
|
|
i2c_example_master_init();
|
|
xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL);
|
|
xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL);
|
|
|
|
}
|
|
|