mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 08:28:12 +01:00
Always execute i2c bus recovery on setup (#2379)
This commit is contained in:
parent
210a9a4162
commit
963b28181f
4 changed files with 72 additions and 0 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
Loading…
Reference in a new issue