We try to use common defines for I2C master code in our projects, so it’s easy to jump between different microcontrollers and processors. This is quite challenging with the ESP32, because its setup and then do as a single operation approach is very different to the raw operation by operation approach a microtroller will typically employ – hence the below will seem crazy for an ESP32 implementation!
This is our in-house set of defines when using using our drivers with the ESP32
Initialse I2C
int i2c_port = 0;
i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 6, //<Set GPIO number for SDA
.scl_io_num = 7, //<Set GPIO number for SCL
.sda_pullup_en = GPIO_PULLUP_DISABLE,
.scl_pullup_en = GPIO_PULLUP_DISABLE,
.master.clk_speed = 400000, //I2C clock frequency (e.g. 400000)
};
i2c_param_config(i2c_port, &i2c_config);
i2c_driver_install(i2c_port, i2c_config.mode, 0, 0, ESP_INTR_FLAG_LOWMED); //(i2c_num, mode, slv_rx_buf_len, slv_tx_buf_len, intr_alloc_flags)
I2C access defines
#define MY_DEVICE_I2C_ADDRESS (0x29 << 1) //Set as normal full byte address (including RW at bit 0)
#define MY_DEVICE_I2C_PORT_NUMBER 0
#define MY_DEVICE_I2C_START_IN_PROGRESS_BIT 0
#define MY_DEVICE_I2C_RESTART_IN_PROGRESS_BIT 0
#define MY_DEVICE_I2C_WRITE_FUNCTION(a) i2c_master_write_byte(OurI2cCmdHandle, a, 1)
#define MY_DEVICE_I2C_TX_IN_PROGRESS_BIT 0
#define MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT 0
#define MY_DEVICE_I2C_READ_BYTE(a) NextReadDataPointer = a //Use pointer to the uint8_t that will store the read byte
#define MY_DEVICE_I2C_ACK_IN_PROGRESS_BIT 0
#define MY_DEVICE_I2C_STOP_IN_PROGRESS_BIT 0
//StopI2C - Generate bus stop condition
#define MY_DEVICE_I2C_SEND_STOP i2c_master_stop(OurI2cCmdHandle); i2c_ret = i2c_master_cmd_begin(MY_DEVICE_I2C_PORT_NUMBER, OurI2cCmdHandle, (100 / portTICK_PERIOD_MS)); i2c_cmd_link_delete(OurI2cCmdHandle)
//i2c_master_cmd_begin - Send queued commands - THIS IS SPECIFIC FOR ESP32!!! COMMS DON'T ACTAULLY OCCUR UNTIL i2c_master_cmd_begin is called!!!!
//"(# / portTICK_RATE_MS)"=maximum wait time. This task will be blocked until all the commands have been sent (not thread-safe - if you want to use one I2C port in different tasks you need to take care of multi-thread issues)
//StartI2C - Generate bus start condition
#define MY_DEVICE_I2C_SEND_START OurI2cCmdHandle = i2c_cmd_link_create(); i2c_master_start(OurI2cCmdHandle)
//RestartI2C - Generate bus restart condition
#define MY_DEVICE_I2C_SEND_RESTART i2c_master_start(OurI2cCmdHandle)
//NotAckI2C - Generate bus Not ACK condition
#define MY_DEVICE_I2C_SEND_NACK i2c_master_read_byte(OurI2cCmdHandle, NextReadDataPointer, 1);
//AckI2C - Generate bus ACK condition
#define MY_DEVICE_I2C_SEND_ACK i2c_master_read_byte(OurI2cCmdHandle, NextReadDataPointer, 0);
//Idle I2C - Test if I2C1 module is idle
#define MY_DEVICE_I2C_WAIT_FOR_IDLE
static i2c_cmd_handle_t OurI2cCmdHandle;
static uint8_t *NextReadDataPointer;
static esp_err_t i2c_ret;
Using it in our code – Read
uint8_t MyDeviceRead (uint8_t DeviceAddress, uint8_t RegisterAddress, uint8_t *Data, int DataLength)
{
//----- SEND THE START CONDITION -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_START;
while(MY_DEVICE_I2C_START_IN_PROGRESS_BIT)
;
//----- SEND THE I2C DEVICE ADDRESS WITH THE WRITE BIT SET -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_WRITE_FUNCTION(DeviceAddress & 0xfe); //Bit 0 low for write
while(MY_DEVICE_I2C_TX_IN_PROGRESS_BIT)
;
if(MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT)
goto MyDeviceReadFail;
//----- SEND CONTROL REGISTER WITH ADDRESS -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_WRITE_FUNCTION(RegisterAddress);
while(MY_DEVICE_I2C_TX_IN_PROGRESS_BIT)
;
if(MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT)
goto MyDeviceReadFail;
//----- SEND RE-START -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_RESTART;
while(MY_DEVICE_I2C_RESTART_IN_PROGRESS_BIT)
;
//----- SEND THE I2C DEVICE ADDRESS WITH THE READ BIT SET -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_WRITE_FUNCTION(DeviceAddress | 0x01); //Bit 1 high for read
while(MY_DEVICE_I2C_TX_IN_PROGRESS_BIT)
;
if(MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT)
goto MyDeviceReadFail;
while (DataLength--)
{
//----- READ THE DATA BYTE -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_READ_BYTE(Data++); //Get the byte
if (DataLength) //Send Ack, or NAK if this is the last byte to read
{
//----- GETTING ANOTHER BYTE - SEND ACK -----
MY_DEVICE_I2C_SEND_ACK;
while(MY_DEVICE_I2C_ACK_IN_PROGRESS_BIT)
;
}
else
{
//----- LAST BYTE DONE - SEND NAK -----
MY_DEVICE_I2C_SEND_NACK;
while(MY_DEVICE_I2C_ACK_IN_PROGRESS_BIT)
;
}
}
//----- SEND STOP -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_STOP;
while(MY_DEVICE_I2C_STOP_IN_PROGRESS_BIT)
;
//----- ALL DONE - EXIT -----
if (i2c_ret != ESP_OK)
{
int Index;
for (Index = 0; Index < DataLength; Index++)
Data[Index] = 0;
return(0);
}
return (1);
//----- I2C COMMS FAILED -----
MyDeviceReadFail:
//SEND STOP
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_STOP;
while(MY_DEVICE_I2C_STOP_IN_PROGRESS_BIT)
;
return (0);
}
Using it in our code – Write
uint8_t MyDeviceWrite (uint8_t DeviceAddress, uint8_t RegisterAddress, uint8_t *Data, int DataLength)
{
//----- SEND THE START CONDITION -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_START;
while(MY_DEVICE_I2C_START_IN_PROGRESS_BIT)
;
//----- SEND THE I2C DEVICE ADDRESS WITH THE WRITE BIT SET -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_WRITE_FUNCTION(DeviceAddress & 0xfe); //Bit 0 low for write
while(MY_DEVICE_I2C_TX_IN_PROGRESS_BIT)
;
if(MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT)
goto MyDeviceWriteFail;
//----- SEND CONTROL REGISTER WITH ADDRESS -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_WRITE_FUNCTION(RegisterAddress);
while(MY_DEVICE_I2C_TX_IN_PROGRESS_BIT)
;
if(MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT)
goto MyDeviceWriteFail;
while (DataLength--)
{
//----- WRITE THE DATA BYTE -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_WRITE_FUNCTION(*Data++);
while(MY_DEVICE_I2C_TX_IN_PROGRESS_BIT)
;
if(MY_DEVICE_I2C_ACK_NOT_RECEIVED_BIT)
goto MyDeviceWriteFail;
}
//----- SEND STOP -----
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_STOP;
while(MY_DEVICE_I2C_STOP_IN_PROGRESS_BIT)
;
//----- ALL DONE - EXIT -----
return (1);
//----- I2C COMMS FAILED -----
MyDeviceWriteFail:
//SEND STOP
MY_DEVICE_I2C_WAIT_FOR_IDLE;
MY_DEVICE_I2C_SEND_STOP;
while(MY_DEVICE_I2C_STOP_IN_PROGRESS_BIT)
;
return (0);
}
USEFUL?
We benefit hugely from resources on the web so we decided we should try and give back some of our knowledge and resources to the community by opening up many of our company’s internal notes and libraries through mini sites like this. We hope you find the site helpful.
Please feel free to comment if you can add help to this page or point out issues and solutions you have found, but please note that we do not provide support on this site. If you need help with a problem please use one of the many online forums.