Multiple tasks using the same I2C bus
Posted: Thu Oct 14, 2021 6:23 am
Hello. Due to lack of GPIOs in ESP32 device, I am using i2c pin expander to write and read states of some pins.
I have 2 processes that use this I2C bus.
process1 - GPIO_control_task(). This task is being executed every 2 seconds and it reads all the states of GPIO pins and writes them if some external conditions are met..
process2 - I have BLE running and whenever I write some data via LightBlue app, ESP_GATTS_WRITE_EVT event is executed. In this event, I have my data_prase function which parses the hex command and performs some action accordingly. Some of the actions include reading/writing GPIO pins through the i2c expander.
So I have 2 processes that may attempt to use the i2c bus simultaneously. Is that an issue I should worry about? While writing the I2C driver for the expander, I have notice this description:
Which says that the i2c API is not thread safe. Does that mean that I must handle this myself and ensure that both processes does not try to read/write from the i2c at the same time? Any help is appreciated. Im new to FreeRTOS and just learning.
Since there is no task for BLE, I am not sure how to properly handle this. For example I create global variable i2c_mutex and in GPIO_Control_task I have the following:
This is my BLE_GATTS_WRITE_EVENT
Notice the parse_ble_param function at the bottom:
As you can see 2 processes above will attempt to write to i2c expander and set the pin 5. I need clarification whether I can use mutex in the BLE callback as above.
I have 2 processes that use this I2C bus.
process1 - GPIO_control_task(). This task is being executed every 2 seconds and it reads all the states of GPIO pins and writes them if some external conditions are met..
process2 - I have BLE running and whenever I write some data via LightBlue app, ESP_GATTS_WRITE_EVT event is executed. In this event, I have my data_prase function which parses the hex command and performs some action accordingly. Some of the actions include reading/writing GPIO pins through the i2c expander.
So I have 2 processes that may attempt to use the i2c bus simultaneously. Is that an issue I should worry about? While writing the I2C driver for the expander, I have notice this description:
Code: Select all
/**
* @brief I2C master send queued commands.
* This function will trigger sending all queued commands.
* The task will be blocked until all the commands have been sent out.
* The I2C APIs are not thread-safe, if you want to use one I2C port in different tasks,
* you need to take care of the multi-thread issue.
* @note
* Only call this function in I2C master mode
*
* @param i2c_num I2C port number
* @param cmd_handle I2C command handler
* @param ticks_to_wait maximum wait ticks.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL Sending command error, slave doesn't ACK the transfer.
* - ESP_ERR_INVALID_STATE I2C driver not installed or not in master mode.
* - ESP_ERR_TIMEOUT Operation timeout because the bus is busy.
*/
esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, TickType_t ticks_to_wait);
Since there is no task for BLE, I am not sure how to properly handle this. For example I create global variable i2c_mutex and in GPIO_Control_task I have the following:
Code: Select all
void GPIO_Control_task (void* param){
while(1)
{
// enter critical i2c section
if(xSemaphoreTake(i2c_mutex,0) == pdTRUE){
static int value = 0;
value = !value;
printf("Hello from main\r\n");
PCA_9539_digital_write(5,value); // this function writes to i2c register
xSemaphoreGive(i2c_mutex); // release the semaphore after done writing
vTaskDelay(2000/portTICK_PERIOD_MS);
}
else{
printf("cannot do anything because this task is blocked by i2c mutex\n");
}
}
Code: Select all
case ESP_GATTS_WRITE_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_WRITE_EVT, write value:");
esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);
if (heart_rate_handle_table[HRS_IDX_HR_CTNL_PT_DESCR] == param->write.handle && param->write.len == 2){
printf("inside if statement\n");
uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0];
if (descr_value == 0x0001){
ESP_LOGI(GATTS_TABLE_TAG, "notify enable");
uint8_t notify_data[15];
for (int i = 0; i < sizeof(notify_data); ++i)
{
notify_data[i] = i % 0xff;
}
//the size of notify_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[HRS_IDX_HR_CTNL_PT_DESCR],
sizeof(notify_data), notify_data, false);
}else if (descr_value == 0x0002){
ESP_LOGI(GATTS_TABLE_TAG, "indicate enable");
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); ++i)
{
indicate_data[i] = i % 0xff;
}
//the size of indicate_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[HRS_IDX_HR_CTNL_PT_DESCR],
sizeof(indicate_data), indicate_data, true);
}
else if (descr_value == 0x0000){
ESP_LOGI(GATTS_TABLE_TAG, "notify/indicate disable ");
}else{
ESP_LOGE(GATTS_TABLE_TAG, "unknown descr value");
esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);
}
}
if (param->write.need_rsp){
printf("if param->write.need_rsp \n");
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
}
PARSE_BLE_PARAM(gatts_if,param->write.value,param->write.len, param->write.conn_id, param->write.trans_id);
Code: Select all
bool PARSE_BLE_PARAM(esp_gatt_if_t gatts_if, uint8_t* data, uint16_t length, uint16_t conn_id, uint32_t trans_id){
printf("length of data received = %u \n",length);
uint8_t op_code = data[0];
if(op_coode == 0x01){ // turn on pin 5 of expander
if(xSemaphoreTake(i2c_mutex,1000)==pdTRUE)
{
PCA_9539_digital_write(5,1); // this function writes to i2c register
xSemaphoreGive(i2c_mutex); // realease semaphore
}
else{
printf("semaphore is busy \n");
}
}
return 0;
}
As you can see 2 processes above will attempt to write to i2c expander and set the pin 5. I need clarification whether I can use mutex in the BLE callback as above.