5#include <driver/gpio.h>
16static const char *
const TAG =
"i2c.idf";
19 static i2c_port_t next_port = I2C_NUM_0;
20 this->
port_ = next_port;
21 if (this->
port_ == I2C_NUM_MAX) {
22 ESP_LOGE(TAG,
"No more than %u buses supported", I2C_NUM_MAX);
28 ESP_LOGW(TAG,
"Using max allowed timeout: 13 ms");
34 next_port = (i2c_port_t) (next_port + 1);
36 i2c_master_bus_config_t bus_conf{};
37 memset(&bus_conf, 0,
sizeof(bus_conf));
38 bus_conf.sda_io_num = gpio_num_t(
sda_pin_);
39 bus_conf.scl_io_num = gpio_num_t(
scl_pin_);
40 bus_conf.i2c_port = this->
port_;
41 bus_conf.glitch_ignore_cnt = 7;
42#if SOC_LP_I2C_SUPPORTED
43 if (this->
port_ < SOC_HP_I2C_NUM) {
44 bus_conf.clk_source = I2C_CLK_SRC_DEFAULT;
46 bus_conf.lp_source_clk = LP_I2C_SCLK_DEFAULT;
49 bus_conf.clk_source = I2C_CLK_SRC_DEFAULT;
52 esp_err_t
err = i2c_new_master_bus(&bus_conf, &this->
bus_);
54 ESP_LOGW(TAG,
"i2c_new_master_bus failed: %s", esp_err_to_name(
err));
59 i2c_device_config_t dev_conf{};
60 memset(&dev_conf, 0,
sizeof(dev_conf));
61 dev_conf.dev_addr_length = I2C_ADDR_BIT_LEN_7;
62 dev_conf.device_address = I2C_DEVICE_ADDRESS_NOT_USED;
64 dev_conf.scl_wait_us = this->
timeout_;
65 err = i2c_master_bus_add_device(this->
bus_, &dev_conf, &this->
dev_);
67 ESP_LOGW(TAG,
"i2c_master_bus_add_device failed: %s", esp_err_to_name(
err));
75 ESP_LOGV(TAG,
"Scanning for devices");
81 ESP_LOGCONFIG(TAG,
"I2C Bus:");
85 " Frequency: %" PRIu32
" Hz",
88 ESP_LOGCONFIG(TAG,
" Timeout: %" PRIu32
"us", this->
timeout_);
90 switch (this->recovery_result_) {
92 ESP_LOGCONFIG(TAG,
" Recovery: bus successfully recovered");
95 ESP_LOGCONFIG(TAG,
" Recovery: failed, SCL is held low on the bus");
98 ESP_LOGCONFIG(TAG,
" Recovery: failed, SDA is held low on the bus");
102 ESP_LOGCONFIG(TAG,
"Results from bus scan:");
104 ESP_LOGCONFIG(TAG,
"Found no devices");
108 ESP_LOGCONFIG(TAG,
"Found device at address 0x%02X", s.first);
110 ESP_LOGE(TAG,
"Unknown error at address 0x%02X", s.first);
122 ESP_LOGW(TAG,
"i2c bus not initialized!");
126 i2c_operation_job_t jobs[8]{};
128 uint8_t write_addr = (
address << 1) | I2C_MASTER_WRITE;
129 uint8_t read_addr = (
address << 1) | I2C_MASTER_READ;
130 ESP_LOGV(TAG,
"Writing %zu bytes, reading %zu bytes", write_count, read_count);
131 if (read_count == 0 && write_count == 0) {
133 ESP_LOGV(TAG,
"0x%02X BUS PROBE",
address);
134 jobs[num_jobs++].command = I2C_MASTER_CMD_START;
135 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
136 jobs[num_jobs].write.ack_check =
true;
137 jobs[num_jobs].write.data = &write_addr;
138 jobs[num_jobs++].write.total_bytes = 1;
140 if (write_count != 0) {
142 jobs[num_jobs++].command = I2C_MASTER_CMD_START;
143 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
144 jobs[num_jobs].write.ack_check =
true;
145 jobs[num_jobs].write.data = &write_addr;
146 jobs[num_jobs++].write.total_bytes = 1;
147 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
148 jobs[num_jobs].write.ack_check =
true;
149 jobs[num_jobs].write.data = (uint8_t *) write_buffer;
150 jobs[num_jobs++].write.total_bytes = write_count;
152 if (read_count != 0) {
153 ESP_LOGV(TAG,
"0x%02X RX bytes %zu",
address, read_count);
154 jobs[num_jobs++].command = I2C_MASTER_CMD_START;
155 jobs[num_jobs].command = I2C_MASTER_CMD_WRITE;
156 jobs[num_jobs].write.ack_check =
true;
157 jobs[num_jobs].write.data = &read_addr;
158 jobs[num_jobs++].write.total_bytes = 1;
159 if (read_count > 1) {
160 jobs[num_jobs].command = I2C_MASTER_CMD_READ;
161 jobs[num_jobs].read.ack_value = I2C_ACK_VAL;
162 jobs[num_jobs].read.data = read_buffer;
163 jobs[num_jobs++].read.total_bytes = read_count - 1;
165 jobs[num_jobs].command = I2C_MASTER_CMD_READ;
166 jobs[num_jobs].read.ack_value = I2C_NACK_VAL;
167 jobs[num_jobs].read.data = read_buffer + read_count - 1;
168 jobs[num_jobs++].read.total_bytes = 1;
171 jobs[num_jobs++].command = I2C_MASTER_CMD_STOP;
172 ESP_LOGV(TAG,
"Sending %zu jobs", num_jobs);
173 esp_err_t
err = i2c_master_execute_defined_operations(this->
dev_, jobs, num_jobs, 20);
174 if (
err == ESP_ERR_INVALID_STATE) {
175 ESP_LOGV(TAG,
"TX to %02X failed: not acked",
address);
177 }
else if (
err == ESP_ERR_TIMEOUT) {
178 ESP_LOGV(TAG,
"TX to %02X failed: timeout",
address);
180 }
else if (
err != ESP_OK) {
181 ESP_LOGV(TAG,
"TX to %02X failed: %s",
address, esp_err_to_name(
err));
190void IDFI2CBus::recover_() {
191 ESP_LOGI(TAG,
"Performing bus recovery");
193 const auto scl_pin =
static_cast<gpio_num_t
>(
scl_pin_);
194 const auto sda_pin =
static_cast<gpio_num_t
>(
sda_pin_);
202 const auto half_period_usec = 7;
205 gpio_set_level(scl_pin, 1);
206 gpio_config_t scl_config{};
207 scl_config.pin_bit_mask = 1ULL <<
scl_pin_;
208 scl_config.mode = GPIO_MODE_INPUT_OUTPUT_OD;
209 scl_config.pull_up_en = GPIO_PULLUP_ENABLE;
210 scl_config.pull_down_en = GPIO_PULLDOWN_DISABLE;
211 scl_config.intr_type = GPIO_INTR_DISABLE;
212 gpio_config(&scl_config);
215 gpio_set_level(sda_pin, 1);
216 gpio_config_t sda_conf{};
217 sda_conf.pin_bit_mask = 1ULL <<
sda_pin_;
218 sda_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD;
219 sda_conf.pull_up_en = GPIO_PULLUP_ENABLE;
220 sda_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
221 sda_conf.intr_type = GPIO_INTR_DISABLE;
222 gpio_config(&sda_conf);
227 if (gpio_get_level(scl_pin) == 0) {
228 ESP_LOGE(TAG,
"Recovery failed: SCL is held LOW on the bus");
240 for (
auto i = 0; i < 9; i++) {
241 gpio_set_level(scl_pin, 0);
243 gpio_set_level(scl_pin, 1);
254 while (wait-- && gpio_get_level(scl_pin) == 0) {
258 if (gpio_get_level(scl_pin) == 0) {
259 ESP_LOGE(TAG,
"Recovery failed: SCL is held LOW during clock pulse cycle");
268 if (gpio_get_level(sda_pin) == 0) {
269 ESP_LOGE(TAG,
"Recovery failed: SDA is held LOW after clock pulse cycle");
286 gpio_set_level(sda_pin, 0);
295 gpio_set_level(sda_pin, 1);
void feed_wdt(uint32_t time=0)
virtual void mark_failed()
Mark this component as failed.
bool scan_
Should we scan ? Can be set in the yaml.
std::vector< std::pair< uint8_t, bool > > scan_results_
array containing scan results
i2c_master_bus_handle_t bus_
void dump_config() override
i2c_master_dev_handle_t dev_
ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, size_t read_count) override
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
@ ERROR_TIMEOUT
timeout while waiting to receive bytes
@ ERROR_NOT_ACKNOWLEDGED
I2C bus acknowledgment not received.
@ ERROR_NOT_INITIALIZED
call method to a not initialized bus
@ ERROR_UNKNOWN
miscellaneous I2C error during execution
@ RECOVERY_FAILED_SDA_LOW
@ RECOVERY_FAILED_SCL_LOW
Providing packet encoding functions for exchanging data with a remote host.
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Application App
Global storage of Application pointer - only one Application can exist.