Always execute i2c bus recovery on setup (#2379)

This commit is contained in:
Christian Taedcke 2021-09-23 20:11:40 +02:00 committed by GitHub
parent 210a9a4162
commit 963b28181f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 0 deletions

View file

@ -2,6 +2,7 @@
#include "i2c_bus_arduino.h" #include "i2c_bus_arduino.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <Arduino.h>
#include <cstring> #include <cstring>
namespace esphome { namespace esphome {
@ -10,6 +11,7 @@ namespace i2c {
static const char *const TAG = "i2c.arduino"; static const char *const TAG = "i2c.arduino";
void ArduinoI2CBus::setup() { void ArduinoI2CBus::setup() {
recover();
#ifdef USE_ESP32 #ifdef USE_ESP32
static uint8_t next_bus_num = 0; static uint8_t next_bus_num = 0;
if (next_bus_num == 0) if (next_bus_num == 0)
@ -90,6 +92,32 @@ ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cn
return ERROR_UNKNOWN; return ERROR_UNKNOWN;
} }
void ArduinoI2CBus::recover() {
// Perform I2C bus recovery, see
// https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf
// or see the linux kernel implementation, e.g.
// https://elixir.bootlin.com/linux/v5.14.6/source/drivers/i2c/i2c-core-base.c#L200
// try to get about 100kHz toggle frequency
const auto half_period_usec = 1000000 / 100000 / 2;
const auto recover_scl_periods = 9;
// configure scl as output
pinMode(scl_pin_, OUTPUT); // NOLINT
// set scl high
digitalWrite(scl_pin_, 1); // NOLINT
// in total generate 9 falling-rising edges
for (auto i = 0; i < recover_scl_periods; i++) {
delayMicroseconds(half_period_usec);
digitalWrite(scl_pin_, 0); // NOLINT
delayMicroseconds(half_period_usec);
digitalWrite(scl_pin_, 1); // NOLINT
}
delayMicroseconds(half_period_usec);
}
} // namespace i2c } // namespace i2c
} // namespace esphome } // namespace esphome

View file

@ -22,6 +22,9 @@ class ArduinoI2CBus : public I2CBus, public Component {
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
void set_frequency(uint32_t frequency) { frequency_ = frequency; } void set_frequency(uint32_t frequency) { frequency_ = frequency; }
private:
void recover();
protected: protected:
TwoWire *wire_; TwoWire *wire_;
bool scan_; bool scan_;

View file

@ -1,6 +1,7 @@
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#include "i2c_bus_esp_idf.h" #include "i2c_bus_esp_idf.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <cstring> #include <cstring>
@ -13,6 +14,8 @@ void IDFI2CBus::setup() {
static i2c_port_t next_port = 0; static i2c_port_t next_port = 0;
port_ = next_port++; port_ = next_port++;
recover();
i2c_config_t conf{}; i2c_config_t conf{};
memset(&conf, 0, sizeof(conf)); memset(&conf, 0, sizeof(conf));
conf.mode = I2C_MODE_MASTER; conf.mode = I2C_MODE_MASTER;
@ -141,6 +144,41 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt) {
return ERROR_OK; return ERROR_OK;
} }
void IDFI2CBus::recover() {
// Perform I2C bus recovery, see
// https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414AN686_0.pdf
// or see the linux kernel implementation, e.g.
// https://elixir.bootlin.com/linux/v5.14.6/source/drivers/i2c/i2c-core-base.c#L200
// try to get about 100kHz toggle frequency
const auto half_period_usec = 1000000 / 100000 / 2;
const auto recover_scl_periods = 9;
const gpio_num_t scl_pin = static_cast<gpio_num_t>(scl_pin_);
// configure scl as output
gpio_config_t conf{};
conf.pin_bit_mask = 1ULL << static_cast<uint32_t>(scl_pin_);
conf.mode = GPIO_MODE_OUTPUT;
conf.pull_up_en = GPIO_PULLUP_DISABLE;
conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
conf.intr_type = GPIO_INTR_DISABLE;
gpio_config(&conf);
// set scl high
gpio_set_level(scl_pin, 1);
// in total generate 9 falling-rising edges
for (auto i = 0; i < recover_scl_periods; i++) {
delayMicroseconds(half_period_usec);
gpio_set_level(scl_pin, 0);
delayMicroseconds(half_period_usec);
gpio_set_level(scl_pin, 1);
}
delayMicroseconds(half_period_usec);
}
} // namespace i2c } // namespace i2c
} // namespace esphome } // namespace esphome

View file

@ -24,6 +24,9 @@ class IDFI2CBus : public I2CBus, public Component {
void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; }
void set_frequency(uint32_t frequency) { frequency_ = frequency; } void set_frequency(uint32_t frequency) { frequency_ = frequency; }
private:
void recover();
protected: protected:
i2c_port_t port_; i2c_port_t port_;
bool scan_; bool scan_;