Проблема с выводом изображения на экран через контроллер st7567s при подключении через I2C(SDA SLC) шину arduino

Всем привет! У меня есть проект на Arduino Mega: экран с контроллером ST7567S и два датчика — для света (BH1750) и для температуры, влажности и давления (BMP280 + AHT20). Все устройства подключены к шине I2C, а меню на экране управляется через энкодер. Я написала свою библиотеку для отображения меню и программу для Mega, но столкнулась с проблемой при подключении всех устройств к одной шине.

Описание проблемы

  1. Работа по отдельности:
  • Экран и меню отображаются корректно, если подключен только он или он с одним из датчиков.
  1. Проблема на аппаратном I2C:
  • При подключении всех трех устройств (экран, BH1750, BMP280+AHT20) к аппаратной шине I2C (пины 20 и 21 на Mega) экран теряет контрастность. Меню видно, но очень тускло и только под углом.

  • Если отключить любой из датчиков, экран возвращается к нормальной яркости.

  1. Программный I2C:
  • При использовании программной реализации I2C (U8G2_ST7567_ENH_DG128064I_F_SW_I2C из U8g2) контрастность сохраняется даже со всеми датчиками, но меню обновляется медленно и иногда зависает при прокрутке энкодера.

Что я пробовала

  1. Аппаратный I2C:
  • Использовала U8G2_ST7567_ENH_DG128064I_F_HW_I2C из U8g2 для работы с аппаратной шиной.

  • Добавляла подтягивающие резисторы 4.7 кОм и 2.2 кОм параллельно к SDA и SCL — без изменений.

  • Предположила перегрузку шины, но I2C теоретически поддерживает более 100 устройств, а у меня всего три.

Схема подключения, тут I2C шина выведена через макетку: Схема подключения 1

Схема подключения 2 (лучше видны выводы)

  1. Программный I2C:
  • Перешла на U8G2_ST7567_ENH_DG128064I_F_SW_I2C с использованием аналоговых пинов. Контрастность вернулась, но появились задержки и зависания меню.
  1. Сторонние библиотеки:
  • Нашла библиотеку для ST7567S, написанную через Wire.h (как для датчиков). Она работала лучше некоторых альтернатив, но качество рендеринга уступает U8g2. Другие библиотеки либо не подходят для моего контроллера, либо имеют кривую реализацию.

  • Подозреваю, что проблема может быть в несогласованности: датчики используют Wire.h для общения по I2C, а U8g2, похоже, имеет собственную реализацию аппаратного I2C (не уверена, нужно проверить).

  1. Почему это не проблема питания:
  • Во-первых, I2C поддерживает более 100 устройств, и на датчиках/Arduino обычно есть встроенные подтягивающие резисторы. Все устройства (экран и датчики) работают в диапазоне 3.3–5.5 В, а Mega выдает стабильные 5 В через USB — этого достаточно.

  • Во-вторых, подключение дополнительных резисторов (4.7 кОм и 2.2 кОм) к SDA и SCL не дало эффекта.

  • В-третьих, при переходе на программный I2C напряжение на шине не меняется (все питается от той же Mega), но контрастность восстанавливается. Если бы дело было в питании, смена реализации I2C не повлияла бы на результат. Однако с программным I2C появляются фризы.

Код программы для Mega:

#include <SimpleMenu.h>
#include <U8g2lib.h>
#include <GParser.h>
#include "GyverEncoder.h"
#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_BMP280.h>
#include <AHT20.h>

#define DEBUG  // Включить отладку
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

#define PIN_LED 13
#define S2 A2
#define S1 A1
#define KEY A0
#define MENU_POINTS_NUM 20

String inString;
Encoder encoder(S1, S2, KEY);
U8G2_ST7567_ENH_DG128064I_F_HW_I2C lcd(U8G2_R2, U8X8_PIN_NONE);  // Аппаратный I2C (20/21)
SimpleMenu* menu = nullptr;
BH1750 lightMeter;
Adafruit_BMP280 bmp280;
AHT20 aht20;

const MenuItem menu_items[MENU_POINTS_NUM] PROGMEM = {
  {"Главное меню", 0}, {"Включить свет", 1}, {"Выключить свет", 2}, {"Заряд батареи", 3},
  {"Температура", 4}, {"Влажность", 5}, {"Датчик движения", 6}, {"Настройки Wi-Fi", 7},
  {"Обновить прошивку", 8}, {"Режим сна", 9}, {"Таймер", 10}, {"Яркость экрана", 11},
  {"Звук вкл/выкл", 12}, {"Сброс настроек", 13}, {"Тест дисплея", 14}, {"Версия ПО", 15},
  {"Лог событий", 16}, {"Калибровка", 17}, {"Режим отладки", 18}, {"Выход", 19},
};

void setup() {
  Serial.begin(115200);
  Serial3.begin(115200);
  Serial.setTimeout(5);
  Serial3.setTimeout(5);

  pinMode(PIN_LED, OUTPUT);
  digitalWrite(PIN_LED, LOW);
  encoder.setType(TYPE2);

  Wire.begin();  // Аппаратный I2C
  DEBUG_PRINTLN("Starting setup");

  menu = new SimpleMenu(menu_items, lcd, MENU_POINTS_NUM);
  DEBUG_PRINTLN("Menu created");
  menu->DrawMenu();
  DEBUG_PRINTLN("Menu drawn");

  if (lightMeter.begin()) DEBUG_PRINTLN("BH1750 initialized");
  else DEBUG_PRINTLN("BH1750 failed!");
  if (bmp280.begin(0x76)) DEBUG_PRINTLN("BMP280 initialized");
  else DEBUG_PRINTLN("BMP280 failed!");
  if (aht20.begin()) DEBUG_PRINTLN("AHT20 initialized");
  else DEBUG_PRINTLN("AHT20 failed!");
}

void loop() {
  encoder.tick();
  bool isPressed = encoder.isPress();
  bool right = encoder.isRight();
  bool left = encoder.isLeft();

  if (right || left) {
    DEBUG_PRINTLN("двигаем энкодер влево или вправо");
    menu->UpdateEncoder(right, left);
    lcd.clearBuffer();
    menu->DrawMenu();
    lcd.sendBuffer();
  } else if (isPressed) {
    DEBUG_PRINTLN("энкодер нажат");
    int selected = menu->GetSelectedIndex();
    if (selected == 5) {  // Влажность (AHT20)
      float hum = aht20.getHumidity();
      DEBUG_PRINTLN("Humidity: " + String(hum) + " %");
    } else if (selected == 4) {  // Температура (BMP280)
      float temp = bmp280.readTemperature();
      String msg = "#0005," + String(temp) + ";";
      Serial3.print(msg);
      DEBUG_PRINTLN("Sent to ESP: " + msg);
    } else if (selected == 11) {  // Яркость (BH1750)
      float lux = lightMeter.readLightLevel();
      DEBUG_PRINTLN("Light: " + String(lux) + " lx");
    }
  }
}

Моя библиотека SimpleMenu:

#pragma once
#include <U8g2lib.h>
#include <Arduino.h>

#define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

struct MenuItem {
    const char name[128];
    const int8_t value;
};

class SimpleMenu {
private:
    static constexpr int8_t kScreenWidth = 128;
    static constexpr int8_t kScreenHeight = 64;
    static constexpr int8_t kItemsPerPage = 4;
    static constexpr int8_t kFirstPageItems = 3;
    static constexpr int8_t kEncoderStepThreshold = 2;
    static constexpr int8_t kTextXOffset = 5;
    static constexpr int8_t kFrameWidth = kScreenWidth;
    static constexpr int8_t kFrameHeight = 15;
    static constexpr int8_t kLineSpacing = 16;
    static constexpr int8_t kTextYOffset = 10;
    static constexpr uint8_t kDefaultContrast = 200;
    static constexpr uint8_t kCharWidth = 6;

    const MenuItem* const items_;
    const int8_t item_count_;
    U8G2_ST7567_ENH_DG128064I_F_HW_I2C& lcd_;
    int8_t pointer_;
    int8_t encoder_pos_;
    int8_t current_page_;
    int8_t total_pages_;
    char buffer_[128];

    void InitDisplay();
    int8_t CalculateTotalPages() const;
    void DrawFirstPage();
    void DrawOtherPage(int8_t start_index);
    void DrawItem(int8_t index, int8_t y_frame, bool is_selected);
    void HandleRightTurn();
    void HandleLeftTurn();
    int8_t GetMaxItemsOnPage() const;

public:
    SimpleMenu(const MenuItem* items, U8G2_ST7567_ENH_DG128064I_F_HW_I2C& lcd, int8_t item_count)
        : items_(items), item_count_(item_count), lcd_(lcd), pointer_(0), encoder_pos_(0), current_page_(0) {
        total_pages_ = CalculateTotalPages();
        InitDisplay();
    }

    void UpdateEncoder(bool is_right, bool is_left);
    bool DrawMenu();
    int8_t GetSelectedIndex() const;
};

// Реализация методов опущена для краткости, полный код доступен выше

Вопросы

  1. Может ли потеря контрастности на аппаратном I2C быть связана с различиями в реализации I2C между U8g2 и Wire.h? Как это проверить?

  2. Почему программный I2C решает проблему с контрастностью, но вызывает фризы? Как устранить задержки?

  3. Возможен ли физический конфликт данных переданных по I2C между устройствами? Как это диагностировать?

  4. Есть ли способ настроить работу всех устройств на одной шине без написания собственной библиотеки для ST7567S?

Буду рада любым советам! U8g2 — как по мне лучшая библиотека для моего экрана, но я не могу разобраться, как согласовать ее с датчиками на Wire.h.

Докину фотки с внешним источником питания, чтобы окончательно исключить этот момент, а так же продемонстрировать деффект:

  1. Подключение стороннего БП:

Подключение стороннего БП

  1. Дефект при подключении аппаратно (под углом текст виден, но едва):

Дефект

  1. Тот же сторонний БП, но уже программная реализация I2C + те фризы выводе о которых я говорила видно на строке "Таймер":

Подключение программно

Кривой таймер


Ответы (0 шт):