本文源碼地址在:http://download.csdn.net/download/noticeable/9962029
IIC 通訊應該是當代比較常用的幾種通訊方式之一,其無需特殊的IO接口,連線方式少,只有兩條串行總線(SCL,SDA),用來完成數據傳輸。
本文重點測試相關的在esp32實現IIC通訊的完成,不涉及實際的傳感器,在后面會有相應的傳感器與esp32連接實現功能的文章,這里重點關注相關的IO配置及功能實現等,以此來學習相關API接口的配置方法。
本文源碼可以分為以下幾個部分:
PART1:
定義相關參數
1 #define DATA_LENGTH 512 /*!<Data buffer length for test buffer*/ 2 #define RW_TEST_LENGTH 5 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/ 3 4 #define I2C_SLAVE_SCL_IO 26 /*!<gpio number for i2c slave clock */ 5 #define I2C_SLAVE_SDA_IO 25 /*!<gpio number for i2c slave data */ 6 #define I2C_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */ 7 #define I2C_SLAVE_TX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave tx buffer size */ 8 #define I2C_SLAVE_RX_BUF_LEN (2*DATA_LENGTH) /*!<I2C slave rx buffer size */ 9 10 #define I2C_MASTER_SCL_IO 19 /*!< gpio number for I2C master clock */ 11 #define I2C_MASTER_SDA_IO 18 /*!< gpio number for I2C master data */ 12 #define I2C_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */ 13 #define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ 14 #define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ 15 #define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */ 16 17 #define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */ 18 #define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ 19 #define READ_BIT I2C_MASTER_READ /*!< I2C master read */ 20 #define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ 21 #define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ 22 #define ACK_VAL 0x0 /*!< I2C ack value */ 23 #define NACK_VAL 0x1 /*!< I2C nack value */
這里主要定義了相關引腳分批,從設備地址,ACK返回數據及iic總線時鍾頻率、傳輸位長度等。
PART2:
i2c總線主機初始化
void i2c_master_init() { int i2c_master_port = I2C_MASTER_NUM; i2c_config_t conf; conf.mode = I2C_MODE_MASTER; conf.sda_io_num = I2C_MASTER_SDA_IO; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_io_num = I2C_MASTER_SCL_IO; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; conf.master.clk_speed = I2C_MASTER_FREQ_HZ; i2c_param_config(i2c_master_port, &conf); i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); }
i2c_master_init 函數主要是用來對i2c總線的引腳和功能進行配置,
conf.mode用來配置接口在i2c總線中的作用 可選模式為I2C_MODE_SLAVE II2C_MODE_MASTER,這里接口作為主機使用
conf.sda_io_num 、conf.scl_io_num 配置I2C總線的數據總線引腳和I2C的時鍾總線引腳
sda_pullup_en、scl_pullup_en 是否啟動內部上拉電阻,這里是數據和時鍾總線,需要使用上拉電阻。
conf.master.clk_speed 設定時鍾頻率,這里設置為標准模式100K
最后調用i2c_param_config函數對參數進行初始化,到這里,一個I2C的引腳配置就完成了。
對接口配置完后,需要調用i2c_driver_install對I2C總線的驅動進行安裝,主要是對數據發送緩存進行內存分配。
PART3:
i2c總線從機初始化
1 void i2c_slave_init() 2 { 3 int i2c_slave_port = I2C_SLAVE_NUM; 4 i2c_config_t conf_slave; 5 conf_slave.sda_io_num = I2C_SLAVE_SDA_IO; 6 conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE; 7 conf_slave.scl_io_num = I2C_SLAVE_SCL_IO; 8 conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE; 9 conf_slave.mode = I2C_MODE_SLAVE; 10 conf_slave.slave.addr_10bit_en = 0; 11 conf_slave.slave.slave_addr = ESP_SLAVE_ADDR; 12 i2c_param_config(i2c_slave_port, &conf_slave); 13 i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0); 14 }
i2c_slave_init 函數與 i2c_master_init 函數的區別不大,主要的是其多了從機地址和是否使能I2C 10bit地址模式。
PART4:
i2c總線主機向從機寫數據函數
1 esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size) 2 { 3 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 4 i2c_master_start(cmd); 5 i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN); 6 i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); 7 i2c_master_stop(cmd); 8 esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); 9 i2c_cmd_link_delete(cmd); 10 return ret; 11 }
主機向從機寫地址函數主要是完成I2C協議的寫數據操作。
i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 作用主要是創建和初始化I2C命令鏈接,這點是需要注意的(在構建I2C命令鏈接之前,我們需要調用i2c_cmd_link_create()來創建命令鏈接。發送命令后,我們需要調用i2c_cmd_link_delete()來釋放並返回資源。)
i2c_master_start(cmd); I2C主機的隊列命令產生啟動信號。
i2c_master_write_byte(cmd, (ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN); I2C主機的隊列命令將一個字節寫入I2C總線。
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); I2C主機的隊列命令將緩沖區寫入I2C總線。
i2c_master_stop(cmd); 產生I2C停止信號。
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); I2C主機發送排隊命令。此功能將觸發發送所有排隊的命令。(需要注意的是:只能在I2C主模式下調用此功能)
i2c_cmd_link_delete(cmd); 調用i2c_cmd_link_delete()來釋放並返回資源。
PART5:
主機讀取從機數據函數
1 esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t* data_rd, size_t size) 2 { 3 if (size == 0) { 4 return ESP_OK; 5 } 6 i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 7 i2c_master_start(cmd); 8 i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | READ_BIT, ACK_CHECK_EN); 9 if (size > 1) { 10 i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); 11 } 12 i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); 13 i2c_master_stop(cmd); 14 esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); 15 i2c_cmd_link_delete(cmd); 16 return ret; 17 }
此函數主要完成I2C協議所需的讀操作。
2~5行表示如果從機沒數據發送過來則為空。
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd); 與寫操作同理,調用i2c_cmd_link_create()來創建命令鏈接;並產生啟動信號。
i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | READ_BIT, ACK_CHECK_EN); I2C主機的隊列命令將一個讀指令寫入I2C總線。
9~11表示如果返回的數據大於1字節(即返回數據不止為ACK),則調用i2c_master_read對從機數據進行讀取。
i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); 調用 i2c_master_read_byte函數對i2c總線進行字節讀取。
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); I2C主機發送排隊命令。此功能將觸發接收所有排隊的命令。(需要注意的是:只能在I2C主模式下調用此功能)
i2c_cmd_link_delete(cmd); 調用i2c_cmd_link_delete()來釋放並返回資源。
從part4和part5函數可以看出主機的數據寫出和數據讀入都是依靠隊列的形式寫出來的。
PART6:
將緩存區的數據讀區顯示
1 void disp_buf(uint8_t* buf, int len) 2 { 3 int i; 4 for (i = 0; i < len; i++) { 5 printf("%02x ", buf[i]); 6 if (( i + 1 ) % 16 == 0) { 7 printf("\n"); 8 } 9 } 10 printf("\n"); 11 }
這部分即將緩存取的數據依次讀取顯示即可。
PART7:
app_main函數的編寫
1 void app_main() 2 { 3 i2c_slave_init(); 4 i2c_master_init(); 5 6 int i = 0; 7 int ret; 8 uint8_t* data = (uint8_t*) malloc(DATA_LENGTH); 9 uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH); 10 uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH); 11 //--------------------------------------------------- 12 for (i = 0; i < DATA_LENGTH; i++) { 13 data[i] = i; 14 } 15 size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); 16 if (d_size == 0) { 17 printf("i2c slave tx buffer full\n"); 18 ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH); 19 } else { 20 ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH); 21 } 22 printf("*******************\n"); 23 printf("TASK MASTER READ FROM SLAVE\n"); 24 printf("*******************\n"); 25 printf("====TASK Slave buffer data ====\n"); 26 disp_buf(data, d_size); 27 if (ret == ESP_OK) { 28 printf("====TASK Master read ====\n"); 29 disp_buf(data_rd, d_size); 30 } else { 31 printf("Master read slave error, IO not connected...\n"); 32 } 33 vTaskDelay(2000 / portTICK_RATE_MS); 34 //--------------------------------------------------- 35 int size; 36 for (i = 0; i < DATA_LENGTH; i++) { 37 data_wr[i] = i + 10; 38 } 39 40 ret = i2c_master_write_slave( I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH); 41 if (ret == ESP_OK) { 42 size = i2c_slave_read_buffer( I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); 43 } 44 printf("*******************\n"); 45 printf("TASK MASTER WRITE TO SLAVE\n"); 46 printf("*******************\n"); 47 printf("----TASK Master write ----\n"); 48 disp_buf(data_wr, RW_TEST_LENGTH); 49 if (ret == ESP_OK) { 50 printf("----TASK Slave read: [%d] bytes ----\n", size); 51 disp_buf(data, size); 52 } else { 53 printf("TASK Master write slave error, IO not connected....\n"); 54 } 55 vTaskDelay(2000 / portTICK_RATE_MS); 56 }
app_main的任務是調用初始化函數對i2c引腳的、初始化緩存器分配,並打印出數據和整個模擬I2C總線的過程信息。
實驗現象:
連接esp32的io18->io25,連接io19->io26,打開minicom,按下復位鍵,可以看到如下數據打印信息 ,說明I2C總線通訊成功。