Не удалось выделить SSD1306 при добавлении константы

Я пытаюсь заставить этот код работать:

#include <Button.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>

#define SCREEN_WIDTH 128 // Ширина OLED-дисплея в пикселях
#define SCREEN_HEIGHT 32 // Высота OLED-дисплея в пикселях

// Объявление для дисплея SSD1306, подключенного к I2C (контакты SDA, SCL)
#define OLED_RESET     4 // Номер контакта сброса (или -1, если используется общий контакт сброса Arduino)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RTC_DS1307 RTC;

// когда я удаляю эту часть, все работает...
int mode = 1;
const String periodicSystem[60] = {
    "H", "He", "Li", "Be", "B", "C", "N", "O", 
    "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", 
    "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", 
    "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", 
    "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", 
    "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn",
    "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd"
};
/////////////////////////////////////////////////// ////////////////////

Button button1(7);

void setup() {
  Serial.begin(9600);

  // SSD1306_SWITCHCAPVCC = генерировать напряжение дисплея от 3,3 В внутри
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Адрес 0x3C для 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Не продолжать, цикл бесконечен
  }

  RTC.begin();
  if (! RTC.isrunning()) {
    Serial.println("RTC is NOT running!");
    // Это будет отражать время компиляции вашего скетча
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }

  display.display();
}

void loop() {
  DateTime now = RTC.now();
  if(button1.pressed()) {
      display.clearDisplay();
      display.setTextSize(1); // Рисуем текст в масштабе 2X
      display.setTextColor(WHITE);
      display.setCursor(10, 0);
      display.print(now.second());
      display.display();
  }
}

Всякий раз, когда я пытаюсь добавить константу periodicSystem, я получаю сообщение об ошибке Ошибка выделения SSD1306 через последовательный порт. Ошибка также исчезает, когда я удаляю часть с помощью button1.pressed(). По сути, я не могу добавить константу periodicSystem и одновременно считывать цифровой вывод. Мне кажется, это какая-то проблема с памятью (я только добавляю константу и больше ничего с ней не делаю), но моя IDE сообщает мне, что я использую только 52% доступного хранилища. Что я делаю не так?

Вот моя проводка (на самом деле я использую дисплей 128x32, у меня не было подходящей детали для Fritzing; мой дисплей помечен SDA, SCK, VCC, GND вместо SDA, SCK, VDD, GND; Я использую модуль RTC DS1307 AT24C32):

Моя проводка

, 👍1

Обсуждение

Подсказка: подумайте о более эффективном использовании памяти для таблицы строк. Использовать для этого Arduino String — очень-очень плохая идея. Используйте строки символов и поместите всю таблицу в память программы. Ссылка. https://www.arduino.cc/reference/en/language/variables/utilities/progmem/, @Mikael Patel


1 ответ


Лучший ответ:

2

Вашему дисплею требуется 624 байта ОЗУ для внутреннего буфера (128 * ((32 + 7) / 8)). Массиву periodicSystem требуется 360 байт оперативной памяти только для объектов. Это не включает память, используемую для хранения фактических строковых данных, которая, по моим подсчетам, составляет 168 байт.

На данный момент мы достигли 624+360+168 = 1152 байт из максимума в 2048. Пройдена половина пути, и мы не затронули последовательные буферы или другие объекты, которые вы используете.

Массив (const или другой) объектов String — это, безусловно, наименее эффективный способ хранения константных строковых данных на Arduino. Вы используете 528 байт ОЗУ для хранения того, что может храниться в 121 байт флэш-памяти.

Лично я бы хранил данные так:

const char periodicSystem[] PROGMEM = "H HeLiBeB C N O F NeNaMgAlSiP S ClArK CaScTiV CrMnFeCoNiCuZnGaGeAsSeBrKrRbSrY ZrNbMoTcRuRhPdAgCdInSnSbTeI XeCsBaLaCePrNd";

Каждый символ состоит ровно из двух байтов, и PROGMEM заставляет его оставаться во флэш-памяти и никогда не копироваться в ОЗУ.

Вы можете получить два байта каждого символа с помощью:

char b1 = pgm_read_byte(periodicSystem + (i * 2));
char b2 = pgm_read_byte(periodicSystem + (i * 2) + 1);

Затем либо распечатайте их отдельно, либо объедините в строку C:

char symbol[3] = { b1, b2, 0 };

Разумеется, это будет включать в себя пробел. Так что вы можете удалить это, если хотите:

if (symbol[1] == ' ') symbol[1] = 0;

Теоретически вы также можете изменить пробелы в исходных данных на символ 0, если хотите — в зависимости от того, что вы с ним делаете, это не должно вызвать никаких проблем:

const char periodicSystem[] PROGMEM = "H\0HeLiBeB\0C\0N\0O\0F\0NeNaMgAlSiP\0S\0ClArK\0CaScTiV\0CrMnFeCoNiCuZnGaGeAsSeBrKrRbSrY\0ZrNbMoTcRuRhPdAgCdInSnSbTeI\0XeCsBaLaCePrNd";
,