Tổng quan

I2C là giao thức truyền thông nối tiếp đồng bộ phổ biến hiện nay, được sử dụng rộng rãi trong việc kết nối nhiều IC với nhau, hay kết nối giữa IC và các ngoại vi với tốc độ thấp. Ví dụ về kết nối oled bằng I2C có thể xem ở đây.

Giao tiếp I2C sử dụng 2 dây để kết nối là SCL (Serial Clock) và SDA (Serial Data). Trong đó dây SCL có tác dụng để đồng bộ hóa giữa các thiết bị khi truyền dữ liệu, còn SDA là dây dữ liệu truyền qua.

Board ESP32-IoT-Uno có 2 bộ điều khiển I2C có thể thiết lập ở chế độ chủ (master) hoặc tớ (slave).

Chuẩn bị

Board ESP32 IoT Uno https://github.com/esp32vn/esp32-iot-uno

✅ Mọi người cũng xem : uống coconut không tiktok là gì

Ví dụ minh họa:

Ví dụ sau sẽ mô tả cách sử dụng giao tiếp I2C.

Mô tả code:
Sử dụng 1 I2C port ở chế độ master để điều khiển 1 I2C port khác ở chế độ slave trên cùng 1 board ESP32.
Gán chân:
  • Slave:
    • Gán chân GPIO26 là chân dữ liệu SDA của i2c slave port
    • Gán chân GPIO27 là chân tín hiệu clock SCL của i2c slave port
  • Master
    • Gán chân GPIO16 là chân dữ liệu SDA của i2c master port
    • Gán chân GPIO17 là chân tín hiệu clock SCL của i2c master port
Kết nối chân:

Tiến hành kết nối các chân SDA và SCL của slave lần lượt với SDA và SCL của master:

  • GPIO26 –> GPIO16
  • GPIO27 –> GPIO17
Các bước cơ bản:
  1. Khởi tạo các port I2C bao gồm cài driver và set các thông số cần thiết.

  2. Đọc dữ liệu từ slave về master như sau:

    • Đẩy dữ liệu từ slave ra I2C buffer
    • Master gửi lệnh để đọc dữ liệu từ I2C buffer
  3. Gửi dữ liệu từ master đến slave:

    • Đẩy dữ liệu từ master đến I2C buffer
    • Slave đọc dữ liệu từ buffer lưu vào bộ nhớ.
/* i2c - Example For other examples please check: https://github.com/espressif/esp-idf/tree/master/examples This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include <stdio.h> #include "driver/i2c.h" /** * TEST CODE BRIEF * * This example will show you how to use I2C module by running a task on i2c bus: * * - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. * * Pin assignment: * * - slave : * GPIO26 is assigned as the data signal of i2c slave port * GPIO27 is assigned as the clock signal of i2c slave port * - master: * GPIO16 is assigned as the data signal of i2c master port * GPIO17 is assigned as the clock signal of i2c master port * * Connection: * * - connect GPIO16 with GPIO26 * - connect GPIO17 with GPIO27 * * Test items: * * - i2c master(ESP32) will write data to i2c slave(ESP32). * - i2c master(ESP32) will read data from i2c slave(ESP32). */ #define DATA_LENGTH 512 /*!<Data buffer length for test buffer*/ #define RW_TEST_LENGTH 129 /*!<Data length for r/w test, any value from 0-DATA_LENGTH*/ #define DELAY_TIME_BETWEEN_ITEMS_MS 1234 /*!< delay time between different test items */ #define I2C_EXAMPLE_SLAVE_SCL_IO 26 /*!<gpio number for i2c slave clock */ #define I2C_EXAMPLE_SLAVE_SDA_IO 27 /*!<gpio number for i2c slave data */ #define I2C_EXAMPLE_SLAVE_NUM I2C_NUM_0 /*!<I2C port number for slave dev */ #define I2C_EXAMPLE_SLAVE_TX_BUF_LEN (10*DATA_LENGTH) /*!<I2C slave tx buffer size */ #define I2C_EXAMPLE_SLAVE_RX_BUF_LEN (10*DATA_LENGTH) /*!<I2C slave rx buffer size */ #define I2C_EXAMPLE_MASTER_SCL_IO 16 /*!< gpio number for I2C master clock */ #define I2C_EXAMPLE_MASTER_SDA_IO 17 /*!< gpio number for I2C master data */ #define I2C_EXAMPLE_MASTER_NUM I2C_NUM_1 /*!< I2C port number for master dev */ #define I2C_EXAMPLE_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ #define I2C_EXAMPLE_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ #define I2C_EXAMPLE_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */ #define ESP_SLAVE_ADDR 0x28 /*!< ESP32 slave address, you can set any 7bit value */ #define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ #define READ_BIT I2C_MASTER_READ /*!< I2C master read */ #define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ #define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ #define ACK_VAL 0x0 /*!< I2C ack value */ #define NACK_VAL 0x1 /*!< I2C nack value */ /** * @brief test code to read esp-i2c-slave * We need to fill the buffer of esp slave device, then master can read them out. * * _______________________________________________________________________________________ * | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop | * --------|--------------------------|----------------------|--------------------|------| * */ static esp_err_t i2c_example_master_read_slave(i2c_port_t i2c_num, uint8_t* data_rd, size_t size) READ_BIT, ACK_CHECK_EN); if (size > 1) i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); i2c_cmd_link_delete(cmd); return ret; /** * @brief Test code to write esp-i2c-slave * Master device write data to slave(both esp32), * the data will be stored in slave buffer. * We can read them out from slave buffer. * * ___________________________________________________________________ * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | * --------|---------------------------|----------------------|------| * */ static esp_err_t i2c_example_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size) i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) /** * @brief test code to write esp-i2c-slave * * 1. set mode * _________________________________________________________________ * | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop | * --------|---------------------------|---------------------|------| * 2. wait more than 24 ms * 3. read data * ______________________________________________________________________________________ * | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop | * --------|---------------------------|--------------------|--------------------|------| */ /** * @brief i2c master initialization */ static void i2c_example_master_init() int i2c_master_port = I2C_EXAMPLE_MASTER_NUM; i2c_config_t conf; conf.mode = I2C_MODE_MASTER; conf.sda_io_num = I2C_EXAMPLE_MASTER_SDA_IO; conf.sda_pullup_en = GPIO_PULLUP_ENABLE; conf.scl_io_num = I2C_EXAMPLE_MASTER_SCL_IO; conf.scl_pullup_en = GPIO_PULLUP_ENABLE; conf.master.clk_speed = I2C_EXAMPLE_MASTER_FREQ_HZ; i2c_param_config(i2c_master_port, &conf); i2c_driver_install(i2c_master_port, conf.mode, I2C_EXAMPLE_MASTER_RX_BUF_DISABLE, I2C_EXAMPLE_MASTER_TX_BUF_DISABLE, 0); /** * @brief i2c slave initialization */ static void i2c_example_slave_init() int i2c_slave_port = I2C_EXAMPLE_SLAVE_NUM; i2c_config_t conf_slave; conf_slave.sda_io_num = I2C_EXAMPLE_SLAVE_SDA_IO; conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE; conf_slave.scl_io_num = I2C_EXAMPLE_SLAVE_SCL_IO; conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE; conf_slave.mode = I2C_MODE_SLAVE; conf_slave.slave.addr_10bit_en = 0; conf_slave.slave.slave_addr = ESP_SLAVE_ADDR; i2c_param_config(i2c_slave_port, &conf_slave); i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_EXAMPLE_SLAVE_RX_BUF_LEN, I2C_EXAMPLE_SLAVE_TX_BUF_LEN, 0); /** * @brief test function to show buffer */ static void disp_buf(uint8_t* buf, int len) int i; for (i = 0; i < len; i++) printf("%02x ", buf[i]); if (( i + 1 ) % 16 == 0) printf("n"); printf("n"); static void i2c_test_task(void* arg) int i = 0; int ret; uint32_t task_idx = (uint32_t) arg; uint8_t* data = (uint8_t*) malloc(DATA_LENGTH); uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH); uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH); while (1) for (i = 0; i < DATA_LENGTH; i++) data[i] = i; size_t d_size = i2c_slave_write_buffer(I2C_EXAMPLE_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); if (d_size == 0) printf("i2c slave tx buffer fulln"); ret = i2c_example_master_read_slave(I2C_EXAMPLE_MASTER_NUM, data_rd, DATA_LENGTH); else ret = i2c_example_master_read_slave(I2C_EXAMPLE_MASTER_NUM, data_rd, RW_TEST_LENGTH); printf("*******************n"); printf("TASK[%d] MASTER READ FROM SLAVEn", task_idx); printf("*******************n"); printf("====TASK[%d] Slave buffer data ====n", task_idx); disp_buf(data, d_size); if (ret == ESP_OK) printf("====TASK[%d] Master read ====n", task_idx); disp_buf(data_rd, d_size); else printf("Master read slave error, IO not connected...n"); vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); //--------------------------------------------------- int size; for (i = 0; i < DATA_LENGTH; i++) data_wr[i] = i + 10; //we need to fill the slave buffer so that master can read later ret = i2c_example_master_write_slave( I2C_EXAMPLE_MASTER_NUM, data_wr, RW_TEST_LENGTH); if (ret == ESP_OK) size = i2c_slave_read_buffer( I2C_EXAMPLE_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); printf("*******************n"); printf("TASK[%d] MASTER WRITE TO SLAVEn", task_idx); printf("*******************n"); printf("----TASK[%d] Master write ----n", task_idx); disp_buf(data_wr, RW_TEST_LENGTH); if (ret == ESP_OK) printf("----TASK[%d] Slave read: [%d] bytes ----n", task_idx, size); disp_buf(data, size); else printf("TASK[%d] Master write slave error, IO not connected....n", task_idx); vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); void app_main() i2c_example_slave_init(); i2c_example_master_init(); xTaskCreate(i2c_test_task, "i2c test", 1024 * 2, NULL, 10, NULL); 

API Reference

Một số hàm tham khảo

✅ Mọi người cũng xem : có nên mua máy chạy bộ trên không

Structures

struct i2c_config_t

Khởi tạo các thông số cho I2C.

Public Members

i2c_mode_t mode

I2C mode

gpio_num_t sda_io_num

Chân GPIO cho tín hiệu SDA

gpio_pullup_t sda_pullup_en

Chế độ pull-up cho tín hiệu SDA

gpio_num_t scl_io_num

Chân GPIO cho tín hiệu SCL

gpio_pullup_t scl_pullup_en

Chế độ pull-up cho tín hiệu SCL

uint32_t clk_speed

Tần số clock cho I2C master mode, (không lớn hơn 1MHz)

uint8_t addr_10bit_en

Chế độ 10 bit địa chỉ cho slave

uint16_t slave_addr

Địa chỉ của Slave

✅ Mọi người cũng xem : nên mua micro karaoke bluetooth loại nào

Macro

I2C_APB_CLK_FREQ APB_CLK_FREQ

Nguồn clock cấp cho I2C là APB clock (Advanced Peripheral Bus) 80MHz

I2C_FIFO_LEN(32)

Độ dài của FIFO hardware

Type Definitions

typedef void *i2c_cmd_handle_t

I2C command handle

✅ Mọi người cũng xem : lòng cao thượng là gì

Enumerations

enum i2c_mode_t

Các giá trị:

I2C_MODE_SLAVE = 0

I2C slave mode

I2C_MODE_MASTER

I2C master mode

I2C_MODE_MAX
enum i2c_rw_t

Các giá trị:

I2C_MASTER_WRITE = 0

I2C ghi dữ liệu từ master đến slave

I2C_MASTER_READ

I2C đọc dữ liệu từ slave về master

enum i2c_trans_mode_t

Các gía trị:

I2C_DATA_MODE_MSB_FIRST = 0

Bit có trọng số cao của dữ liệu được truyền trước

I2C_DATA_MODE_LSB_FIRST = 1

Bit có trọng số thấp của dữ liệu được truyền trước

I2C_DATA_MODE_MAX
enum i2c_opmode_t

Các giá trị:

I2C_CMD_RESTART = 0

Lệnh restart I2C

I2C_CMD_WRITE

I2C write command

I2C_CMD_READ

I2C read command

I2C_CMD_STOP

I2C stop command

I2C_CMD_END

Lệnh kết thúc I2C

enum i2c_port_t

Các giá trị:

I2C_NUM_0 = 0

I2C port 0

I2C_NUM_1

I2C port 1

I2C_NUM_MAX
enum i2c_addr_mode_t

Các giá trị:

I2C_ADDR_BIT_7 = 0

7 bit địa chỉ cho I2C slave

I2C_ADDR_BIT_10

10 bit địa chỉ cho I2C slave

I2C_ADDR_BIT_MAX

Lưu ý

  • Hướng dẫn cài đặt ESP-IDF
  • Nạp và Debug chương trình xem tại đây
  • Tài nguyên hệ thống xem tại đây