Multiple tasks using the same I2C bus

zazas321
Posts: 231
Joined: Mon Feb 01, 2021 9:41 am

Multiple tasks using the same I2C bus

Postby zazas321 » 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:

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);
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:

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");
        }
    }
   
This is my BLE_GATTS_WRITE_EVENT

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);

Notice the parse_ble_param function at the bottom:

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.

Who is online

Users browsing this forum: Dennie, Google [Bot], weoiss and 79 guests