/* I²C 的 ESP-IDF v5.4 实现(新版 i2c_master 驱动,不是已废弃的 driver/i2c.h)。 */ #include #include "driver/i2c_master.h" #include "hal_i2c.h" #include "hal_i2c_acct.h" #define I2C_XFER_TIMEOUT_MS 50 #define I2C_DEFAULT_FREQ_HZ 400000 static i2c_master_bus_handle_t s_bus; static i2c_master_dev_handle_t s_devs[128]; /* 按 7 位地址索引,用到才建 */ static uint32_t s_freq_hz; static hal_err_t map_err(esp_err_t e) { switch (e) { case ESP_OK: return HAL_OK; case ESP_ERR_INVALID_ARG: return HAL_ERR_ARG; case ESP_ERR_INVALID_STATE: return HAL_ERR_STATE; case ESP_ERR_TIMEOUT: return HAL_ERR_TIMEOUT; case ESP_ERR_NOT_FOUND: return HAL_ERR_NAK; case ESP_FAIL: return HAL_ERR_NAK; /* 从机没 ACK 走这条 */ default: return HAL_ERR_BUS; } } hal_err_t hal_i2c_init(const hal_i2c_cfg_t *cfg) { if (cfg == NULL || cfg->sda_gpio < 0 || cfg->scl_gpio < 0) { return HAL_ERR_ARG; } if (s_bus != NULL) { return HAL_OK; /* 幂等 */ } i2c_master_bus_config_t bc = { .i2c_port = -1, /* 自动选口 */ .sda_io_num = (gpio_num_t)cfg->sda_gpio, .scl_io_num = (gpio_num_t)cfg->scl_gpio, .clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7, /* 板上已有 4.7 kΩ 外部上拉(设计文档 §8.2),内部上拉 45 kΩ 太弱, 开着只会掩盖「上拉电阻漏焊」这类问题。 */ .flags.enable_internal_pullup = false, }; esp_err_t e = i2c_new_master_bus(&bc, &s_bus); if (e != ESP_OK) { return map_err(e); } s_freq_hz = cfg->freq_hz != 0 ? cfg->freq_hz : I2C_DEFAULT_FREQ_HZ; hal_i2c_acct_set_freq(s_freq_hz); /* 占用核算按真实速率折算,见 hal_i2c_acct.h */ return HAL_OK; } static hal_err_t dev_of(uint8_t addr, i2c_master_dev_handle_t *out) { if (s_bus == NULL) { return HAL_ERR_STATE; } if (addr > 0x7f) { return HAL_ERR_ARG; } if (s_devs[addr] == NULL) { i2c_device_config_t dc = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, .scl_speed_hz = s_freq_hz, }; esp_err_t e = i2c_master_bus_add_device(s_bus, &dc, &s_devs[addr]); if (e != ESP_OK) { return map_err(e); } } *out = s_devs[addr]; return HAL_OK; } hal_err_t hal_i2c_write(uint8_t addr, const uint8_t *data, size_t len) { i2c_master_dev_handle_t dev; hal_err_t err = dev_of(addr, &dev); if (err != HAL_OK) { return err; } hal_i2c_acct_add(len, 0); return map_err(i2c_master_transmit(dev, data, len, I2C_XFER_TIMEOUT_MS)); } hal_err_t hal_i2c_read(uint8_t addr, uint8_t *data, size_t len) { i2c_master_dev_handle_t dev; hal_err_t err = dev_of(addr, &dev); if (err != HAL_OK) { return err; } hal_i2c_acct_add(0, len); return map_err(i2c_master_receive(dev, data, len, I2C_XFER_TIMEOUT_MS)); } hal_err_t hal_i2c_write_read(uint8_t addr, const uint8_t *wr, size_t wr_len, uint8_t *rd, size_t rd_len) { i2c_master_dev_handle_t dev; hal_err_t err = dev_of(addr, &dev); if (err != HAL_OK) { return err; } hal_i2c_acct_add(wr_len, rd_len); return map_err(i2c_master_transmit_receive(dev, wr, wr_len, rd, rd_len, I2C_XFER_TIMEOUT_MS)); } hal_err_t hal_i2c_probe(uint8_t addr) { if (s_bus == NULL) { return HAL_ERR_STATE; } hal_i2c_acct_add(0, 0); return map_err(i2c_master_probe(s_bus, addr, I2C_XFER_TIMEOUT_MS)); }