]> granicus.if.org Git - esp-idf/commitdiff
driver(i2c): Fixed ack_err interrupt can't exit bug.
authorkooho <2229179028@qq.com>
Tue, 13 Mar 2018 12:14:51 +0000 (20:14 +0800)
committerkooho <2229179028@qq.com>
Mon, 26 Mar 2018 08:31:17 +0000 (16:31 +0800)
components/driver/i2c.c

index e87623bcc790fdae11299f415dda06e77faa25f8..26ef67370bb86a65422d070dcb79c3c9881f6d2c 100644 (file)
@@ -40,7 +40,6 @@ static portMUX_TYPE i2c_spinlock[I2C_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, p
 /* DRAM_ATTR is required to avoid I2C array placed in flash, due to accessed from ISR */\r
 static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 };\r
 \r
-\r
 #define I2C_ENTER_CRITICAL_ISR(mux)    portENTER_CRITICAL_ISR(mux)\r
 #define I2C_EXIT_CRITICAL_ISR(mux)     portEXIT_CRITICAL_ISR(mux)\r
 #define I2C_ENTER_CRITICAL(mux)        portENTER_CRITICAL(mux)\r
@@ -78,6 +77,7 @@ static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 };
 #define I2C_SLAVE_SDA_SAMPLE_DEFAULT   (10)        /* I2C slave sample time after scl positive edge default value */\r
 #define I2C_SLAVE_SDA_HOLD_DEFAULT     (10)        /* I2C slave hold time after scl negative edge default value */\r
 #define I2C_MASTER_TOUT_CNUM_DEFAULT   (8)         /* I2C master timeout cycle number of I2C clock, after which the timeout interrupt will be triggered */\r
+#define I2C_ACKERR_CNT_MAX             (10)\r
 \r
 typedef struct {\r
     uint8_t byte_num;  /*!< cmd byte number */\r
@@ -367,6 +367,7 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
         } else if (status & I2C_RX_REC_FULL_INT_ST_M) {\r
             I2C[i2c_num]->int_clr.rx_rec_full = 1;\r
         } else if (status & I2C_ACK_ERR_INT_ST_M) {\r
+            I2C[i2c_num]->int_ena.ack_err = 0;\r
             I2C[i2c_num]->int_clr.ack_err = 1;\r
             if (p_i2c->mode == I2C_MODE_MASTER) {\r
                 p_i2c_obj[i2c_num]->status = I2C_STATUS_ACK_ERROR;\r
@@ -377,6 +378,7 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
         } else if (status & I2C_TRANS_START_INT_ST_M) {\r
             I2C[i2c_num]->int_clr.trans_start = 1;\r
         } else if (status & I2C_TIME_OUT_INT_ST_M) {\r
+            I2C[i2c_num]->int_ena.time_out = 0;\r
             I2C[i2c_num]->int_clr.time_out = 1;\r
             p_i2c_obj[i2c_num]->status = I2C_STATUS_TIMEOUT;\r
             i2c_master_cmd_begin_static(i2c_num);\r
@@ -388,12 +390,10 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
                     p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data;\r
                 }\r
                 xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken);\r
-                if (HPTaskAwoken == pdTRUE) {\r
-                    portYIELD_FROM_ISR();\r
-                }\r
                 I2C[i2c_num]->int_clr.rx_fifo_full = 1;\r
             } else {\r
-                if (p_i2c->status != I2C_STATUS_ACK_ERROR) {\r
+                // add check for unexcepted situations caused by noise.\r
+                if (p_i2c->status != I2C_STATUS_ACK_ERROR && p_i2c->status != I2C_STATUS_IDLE) {\r
                     i2c_master_cmd_begin_static(i2c_num);\r
                 }\r
             }\r
@@ -420,9 +420,6 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
                     WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), data[idx]);\r
                 }\r
                 vRingbufferReturnItemFromISR(p_i2c->tx_ring_buf, data, &HPTaskAwoken);\r
-                if (HPTaskAwoken == pdTRUE) {\r
-                    portYIELD_FROM_ISR();\r
-                }\r
                 I2C[i2c_num]->int_ena.tx_fifo_empty = 1;\r
                 I2C[i2c_num]->int_clr.tx_fifo_empty = 1;\r
             } else {\r
@@ -435,9 +432,6 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
                 p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data;\r
             }\r
             xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken);\r
-            if (HPTaskAwoken == pdTRUE) {\r
-                portYIELD_FROM_ISR();\r
-            }\r
             I2C[i2c_num]->int_clr.rx_fifo_full = 1;\r
         } else {\r
             I2C[i2c_num]->int_clr.val = status;\r
@@ -447,9 +441,10 @@ static void IRAM_ATTR i2c_isr_handler_default(void* arg)
         i2c_cmd_evt_t evt;\r
         evt.type = I2C_CMD_EVT_ALIVE;\r
         xQueueSendFromISR(p_i2c->cmd_evt_queue, &evt, &HPTaskAwoken);\r
-        if (HPTaskAwoken == pdTRUE) {\r
-            portYIELD_FROM_ISR();\r
-        }\r
+    }\r
+    //We only need to check here if there is a high-priority task needs to be switched.\r
+    if(HPTaskAwoken == pdTRUE) {\r
+        portYIELD_FROM_ISR();\r
     }\r
 }\r
 \r
@@ -497,19 +492,17 @@ static esp_err_t i2c_master_clear_bus(i2c_port_t i2c_num)
     int sda_io = GPIO.func_in_sel_cfg[sda_in_sig].func_sel;\r
     I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io)), I2C_SCL_IO_ERR_STR, ESP_ERR_INVALID_ARG);\r
     I2C_CHECK((GPIO_IS_VALID_GPIO(sda_io)), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG);\r
-\r
-    if (gpio_get_level(sda_io) == 1) {\r
-        return ESP_OK;\r
-    }\r
+    // We do not check whether the SDA line is low\r
+    // because after some serious interference, the bus may keep high all the time and the i2c bus is out of service.\r
     gpio_set_direction(scl_io, GPIO_MODE_OUTPUT_OD);\r
     gpio_set_direction(sda_io, GPIO_MODE_OUTPUT_OD);\r
-    gpio_set_level(scl_io, 0);\r
+    gpio_set_level(scl_io, 1);\r
+    gpio_set_level(sda_io, 1);\r
     gpio_set_level(sda_io, 0);\r
     for (int i = 0; i < 9; i++) {\r
-        gpio_set_level(scl_io, 1);\r
         gpio_set_level(scl_io, 0);\r
+        gpio_set_level(scl_io, 1);\r
     }\r
-    gpio_set_level(scl_io, 1);\r
     gpio_set_level(sda_io, 1);\r
     i2c_set_pin(i2c_num, sda_io, scl_io, 1, 1, I2C_MODE_MASTER);\r
     return ESP_OK;\r
@@ -583,6 +576,8 @@ esp_err_t i2c_param_config(i2c_port_t i2c_num, const i2c_config_t* i2c_conf)
     if (ret != ESP_OK) {\r
         return ret;\r
     }\r
+    // Reset the I2C hardware in case there is a soft reboot.\r
+    i2c_hw_disable(i2c_num);\r
     i2c_hw_enable(i2c_num);\r
     I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]);\r
     I2C[i2c_num]->ctr.rx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set rx data msb first\r
@@ -1047,7 +1042,7 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
             portYIELD_FROM_ISR();\r
         }\r
         return;\r
-    } else if (p_i2c->status == I2C_STATUS_READ) {\r
+    } else if (p_i2c->cmd_link.head != NULL && p_i2c->status == I2C_STATUS_READ) {\r
         i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd;\r
         while (p_i2c->rx_cnt-- > 0) {\r
             *cmd->data++ = READ_PERI_REG(I2C_DATA_APB_REG(i2c_num));\r
@@ -1066,6 +1061,8 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
         if (HPTaskAwoken == pdTRUE) {\r
             portYIELD_FROM_ISR();\r
         }\r
+        // Return to the IDLE status after cmd_eve_done signal were send out.\r
+        p_i2c->status = I2C_STATUS_IDLE;\r
         return;\r
     }\r
     while (p_i2c->cmd_link.head) {\r
@@ -1121,7 +1118,6 @@ static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num)
         if (p_i2c->cmd_link.head == NULL || p_i2c->cmd_idx >= 15) {\r
             p_i2c->tx_fifo_remain = I2C_FIFO_LEN;\r
             p_i2c->cmd_idx = 0;\r
-            p_i2c->status = I2C_STATUS_IDLE;\r
             break;\r
         }\r
     }\r
@@ -1139,6 +1135,8 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
     I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_MASTER, I2C_MASTER_MODE_ERR_STR, ESP_ERR_INVALID_STATE);\r
     I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG);\r
 \r
+    // Sometimes when the FSM get stuck, the ACK_ERR interrupt will occur endlessly until we reset the FSM and clear bus.\r
+    static uint8_t clear_bus_cnt = 0;\r
     esp_err_t ret = ESP_FAIL;\r
     i2c_obj_t* p_i2c = p_i2c_obj[i2c_num];\r
     portTickType ticks_start = xTaskGetTickCount();\r
@@ -1150,6 +1148,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
     if (p_i2c->status == I2C_STATUS_TIMEOUT\r
         || I2C[i2c_num]->status_reg.bus_busy == 1) {\r
         i2c_hw_fsm_reset(i2c_num);\r
+        clear_bus_cnt = 0;\r
     }\r
     i2c_reset_tx_fifo(i2c_num);\r
     i2c_reset_rx_fifo(i2c_num);\r
@@ -1164,7 +1163,10 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
     p_i2c->rx_fifo_remain = I2C_FIFO_LEN;\r
     i2c_reset_tx_fifo(i2c_num);\r
     i2c_reset_rx_fifo(i2c_num);\r
-\r
+    // These two interrupts some times can not be cleared when the FSM gets stuck.\r
+    // so we disable them when these two interrupt occurs and re-enable them here.\r
+    I2C[i2c_num]->int_ena.ack_err = 1;\r
+    I2C[i2c_num]->int_ena.time_out = 1;\r
     //start send commands, at most 32 bytes one time, isr handler will process the remaining commands.\r
     i2c_master_cmd_begin_static(i2c_num);\r
 \r
@@ -1190,8 +1192,14 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
                     // If the I2C slave are powered off or the SDA/SCL are connected to ground, for example,\r
                     // I2C hw FSM would get stuck in wrong state, we have to reset the I2C module in this case.\r
                     i2c_hw_fsm_reset(i2c_num);\r
+                    clear_bus_cnt = 0;\r
                     ret = ESP_ERR_TIMEOUT;\r
                 } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) {\r
+                    clear_bus_cnt++;\r
+                    if(clear_bus_cnt >= I2C_ACKERR_CNT_MAX) {\r
+                        i2c_master_clear_bus(i2c_num);\r
+                        clear_bus_cnt = 0;   \r
+                    }\r
                     ret = ESP_FAIL;\r
                 } else {\r
                     ret = ESP_OK;\r
@@ -1205,6 +1213,7 @@ esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle,
             // If the I2C slave are powered off or the SDA/SCL are connected to ground, for example,\r
             // I2C hw FSM would get stuck in wrong state, we have to reset the I2C module in this case.\r
             i2c_hw_fsm_reset(i2c_num);\r
+            clear_bus_cnt = 0;\r
             break;\r
         }\r
     }\r