I am using the IDF 5.2.2 toolchain.
The only examples that are out there use the old API, and I made some code that when it connects a master and a slave it works. The master has this code:
Code: Select all
const i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
// .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
};
static esp_err_t i2c_master_init(void) {
esp_err_t err = i2c_param_config(i2c_master_port, &conf);
if (err!=ESP_OK) {
return err;
}
return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
static esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t slave_addr, uint8_t* data_rd, size_t size, uint16_t timeout_ms) {
if (size==0) {
return ESP_OK;
}
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (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, pdMS_TO_TICKS(timeout_ms));
i2c_cmd_link_delete(cmd);
return ret;
}
static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t slave_addr, uint8_t* data_wr, size_t size, uint16_t timeout_ms) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (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, pdMS_TO_TICKS(timeout_ms));
i2c_cmd_link_delete(cmd);
return ret;
}
void app_main(void) {
i2c_master_init();
int ret;
uint8_t slaveAddr = ESP_SLAVE_ADDR2;
while (1) {
vTaskDelay(pdMS_TO_TICKS(RIT1));
memset(master_rx_data, 0, I2C_SLAVE_RX_BUF_LEN);
ret = i2c_master_read_slave(i2c_master_port, slaveAddr, master_rx_data, RW_TEST_LENGTH, TIMEOUT);
if (ret==ESP_ERR_TIMEOUT) {
ESP_LOGE(TAG, "I2C timeout");
}
else if (ret==ESP_OK) {
ESP_LOGI(TAG, "Read correct slave 0x%X", slaveAddr);
}
else {
ESP_LOGE(TAG, "Master read slave error, IO not connected... slave 0x%X", slaveAddr);
}
uint16_t crc = crc16(0xFFFF, master_rx_data, RW_TEST_LENGTH-2);
uint16_t crc_rx_buf = *((uint16_t *)&master_rx_data[RW_TEST_LENGTH-2]);
if (crc_rx_buf!=crc) {
ESP_LOGE(TAG, "CRC error!!!! slave 0x%X", slaveAddr);
ret = i2c_master_read_slave(i2c_master_port, slaveAddr, master_rx_data, RW_TEST_LENGTH, TIMEOUT);
}
else {
ESP_LOGI(TAG, "CRC correct slave 0x%X", slaveAddr);
}
vTaskDelay(pdMS_TO_TICKS(RIT2));
for (int i = 0; i<RW_TEST_LENGTH; i++) {
master_wr_data[i] = i+10;
}
crc = crc16(0xFFFF, master_wr_data, RW_TEST_LENGTH-2);
uint16_t *crc_in_buf = (uint16_t *)&master_wr_data[RW_TEST_LENGTH-2];
*crc_in_buf = crc;
ESP_LOGI(TAG, "Write on slave slave 0x%X", slaveAddr);
ret = i2c_master_write_slave(i2c_master_port, slaveAddr, master_wr_data, RW_TEST_LENGTH, TIMEOUT);
}
}
The slave has this code:
Code: Select all
const i2c_config_t conf_slave = {
.sda_io_num = I2C_SLAVE_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_SLAVE_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.mode = I2C_MODE_SLAVE,
.slave.addr_10bit_en = 0,
.slave.slave_addr = ESP_SLAVE_ADDR,
.clk_flags = 0,
};
static esp_err_t i2c_slave_init(void) {
esp_err_t err = i2c_param_config(i2c_slave_port, &conf_slave);
if (err!=ESP_OK) {
return err;
}
return i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
}
void app_main(void) {
i2c_slave_init();
ESP_LOGI(TAG, "I2C Slave initialized successfully");
while (1) {
// TX: prepara i dati da inviare
for (int i = 0; i<RW_TEST_LENGTH; i++) {
slave_wr_data[i] = i;
}
uint16_t crc = crc16(0xFFFF, slave_wr_data, RW_TEST_LENGTH-2);
*(uint16_t *)&slave_wr_data[RW_TEST_LENGTH-2] = crc;
size_t d_size = i2c_slave_write_buffer(i2c_slave_port, slave_wr_data, RW_TEST_LENGTH, pdMS_TO_TICKS(TIMEOUT));
if (d_size==0) {
ESP_LOGW(TAG, "TX buffer full (scrittura ignorata) slave 0x%X", ESP_SLAVE_ADDR);
}
int size = i2c_slave_read_buffer(i2c_slave_port, slave_rx_data, I2C_SLAVE_RX_BUF_LEN, pdMS_TO_TICKS(TIMEOUT));
if (size==RW_TEST_LENGTH) {
uint16_t crc_rx_buf = *((uint16_t *)&slave_rx_data[RW_TEST_LENGTH-2]);
crc = crc16(0xFFFF, slave_rx_data, RW_TEST_LENGTH-2);
if (crc_rx_buf!=crc) {
ESP_LOGE(TAG, "CRC Error!!!! slave 0x%X", ESP_SLAVE_ADDR);
while (i2c_slave_read_buffer(i2c_slave_port, dump_buf, sizeof(dump_buf), TIMEOUT_ERR)>0) {
ESP_LOGW(TAG, "Empty buffer slave 0x%X", ESP_SLAVE_ADDR);
}
}
else {
ESP_LOGI(TAG, "CRC correct, dati ok slave 0x%X", ESP_SLAVE_ADDR);
}
}
else if (size>0) {
ESP_LOGW(TAG, "Unhexpected length: %d slave 0x%X", size, ESP_SLAVE_ADDR);
while (i2c_slave_read_buffer(i2c_slave_port, dump_buf, sizeof(dump_buf), TIMEOUT_ERR)>0) {
ESP_LOGW(TAG, "Empty buffer (unhexpected length) slave 0x%X", ESP_SLAVE_ADDR);
}
}
else {
ESP_LOGW(TAG, "No received data or timeout slave 0x%X", ESP_SLAVE_ADDR);
}
memset(slave_rx_data, 0, sizeof(slave_rx_data));
vTaskDelay(pdMS_TO_TICKS(RIT));
}
}
Note that in the master I need to do an additional read to empty the fifo buffer, i2c_reset_rx_fifo and i2c_reset_tx_fifo do not work.
Also in the slave to empty the fifo buffer I do not use i2c_reset_rx_fifo and i2c_reset_tx_fifo, I have to use i2c_slave_read_buffer.
Anyway, with the correct timeout and wait times, the communication between master and slave works.
However, if I add a new device, and make the master do a write and a read per device, both slaves fill up with data, even if the master correctly sets the target address.
I finally tried to modify the slave with the new API, hoping to be able to use the callback events to handle the buffer clearing: in the routine call
Code: Select all
i2c_slave_config_t i2c_slv_config = {
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_NUM_0,
.send_buf_depth = 256,
.scl_io_num = I2C_SLAVE_SCL_IO,
.sda_io_num = I2C_SLAVE_SDA_IO,
.slave_addr = ESP_SLAVE_ADDR,
.intr_priority = 1
};
i2c_new_slave_device(&i2c_slv_config, &slave_handle);Has anyone already connected three ESP32S3s to I2C communication, and can provide me with some sample code or tell me what I'm doing wrong? Do the new I2C APIs work?