ARDUINO Вопрос PROGMEM строкам и указателям

ChudoWindowsOFFICE

✩✩✩✩✩✩✩
24 Мар 2023
11
0
Всем привет! Не давно захотел сделать простую основу, можно сказать ядро, для всех будущих проектов.
Компоненты:
LCD 1602 без I2C, Arduino Nano, и энкодер

Простите за текстовый вид схемы, под рукой не оказалось прог для черчения схем :(

[Arduino] D12 - RS [Display]
[Arduino] D11 - E [Display]
[Arduino] D5 - D4 [Display]
[Arduino] D4 - D5 [Display]
[Arduino] D2 - D6 [Display]
[Arduino] D3 - D7 [Display]
[Arduino] GND - RW, VSS, B- [Display]
[Arduino] V5 - VDD, B+ [Display]
[Arduino] GND - GND [Энкодер]
[Arduino] 5V - VDD [Энкодер]
[Arduino] D6 - S1 [Энкодер]
[Arduino] D7 - S2 [Энкодер]
[Arduino] D8 - Key [Энкодер]
Главный файл:
#include <LiquidCrystal.h>
#include <GyverEncoder.h>
#include "pins.h"
#include "Timer.h"
#include "Func.h"
#include "Strings.h"

LiquidCrystal lcd (D_P_RS, D_P_Enable, D_P_D4, D_P_D5, D_P_D6, D_P_D7);
Encoder enc (E_P_S1, E_P_S2, E_P_Key, E_T);

void setup() {
  // Инициализация компонентов
  lcd.begin(D_W, D_H);

  // Стартовая заставка
  lcd.setCursor(1, 0);
  print(lcd, &load, 1);
}

void loop() {
  // put your main code here, to run repeatedly:

}
Func.h:
void printArr(LiquidCrystal lcd, char* buf, uint8_t size) {
  for (uint8_t i = 0; i < size; i++)
  {
    lcd.print(buf[i]);
  }
}
Strings.h:
// Строки из заставки
const char s1[] PROGMEM = "Simple device";
const char s2[] PROGMEM = "for programming";
const char s3[] PROGMEM = "ver 1.0";
const char* const load[] PROGMEM = {
  s1, s2, s3
};

// Функции для работы со строками
void print (LiquidCrystal lcd, const char* const arr[], uint8_t item) {
  char buffer[D_W]; // Буфер для вывода на дисплей
  uint16_t ptr = pgm_read_word(&(arr[item])); // Указатель на строку
  uint8_t i = 0; // Счетчик

  do {
    buffer[i] = (char)(pgm_read_byte(ptr++)); // Прочитать в буфер один символ из PGM и подвинуть указатель на 1
  } while(buffer[i++] != NULL);

  printArr(lcd, buffer, strlen(buffer));
}
Timer.h:
enum class TypeWork : uint8_t {
  TimerReset,
  OnFlag
};

class Timer {
  public:
    Timer(uint32_t periodMS = 0) {
      _period = periodMS;
    }

    void attach(void (*callback)()) {
      _callback = callback;
    }

    void tick() {
      if (millis() - _tmr >= _period)
      {
        resetTmr();
        if (_callback) _callback();
        else _flag = true;
      }
    }

    void call() {
      if (_callback) _callback();
      else _flag = true;
    }

    bool ready() {
      if (_flag) {
        _flag = false;
        return true;
      }
      else return false;
    }

    void resetTmr() {
      _tmr = millis();
    }

    uint32_t getPeriod() {
      return _period;
    }
  private:
    void (*_callback)() = nullptr;
    uint32_t _period = 0;
    uint32_t _tmr = millis();
    bool _flag = false;
};

class Timer2 {
  public:
    Timer2 (uint32_t periodMS, TypeWork type) {
      _period = periodMS;
      _type = type;
    }

    void attach(void (*callback)()) {
      _callback = callback;
    }

    void tick() {
      if (millis() - _tmr >= _period)
      {
        if (_type == TypeWork::TimerReset and _work == false)
        {
          _tmr = millis();
        }

        if (_work == true)
        {
          if (_callback) _callback();
          else _flag = true;
        }
      }
    }

    void enable() {
      _work = true;
    }

    void call() {
      if (_callback) _callback();
      else _flag = true;
    }

    void resetTmr() {
      _tmr = millis();
    }

    uint32_t getPeriod() {
      return _period;
    }
    
    bool ready() {
      if (_flag)
      {
        _flag = false;
        return true;
      }
      return false;
    }
  private:
    uint32_t _period = 0;
    uint32_t _tmr = millis();
    bool _flag = false;
    bool _work = false;
    TypeWork _type = TypeWork::OnFlag;
    void (*_callback)() = nullptr;
};
pins.h:
#define D_W 16
#define D_H 2
#define D_P_RS 12
#define D_P_Enable 11
#define D_P_D4 4
#define D_P_D5 5
#define D_P_D6 2
#define D_P_D7 3

#define E_T TYPE2
#define E_P_S1 6
#define E_P_S2 7
#define E_P_Key 8

#define L_P 13

#define B_P 9
#define B_T_F_1 1000
#define B_T_F_2 100
#define B_T_F_3 500

Сама проблема с передачей ссылки на массив сыллок из PROGMEM памяти для работы со строками:
cannot convert 'const char* const ()[3]' to 'const char const*' for argument '2' to 'void print(LiquidCrystal, const char* const*, uint8_t)'

Сам пробовал разобрать, но без успешно
 

ChudoWindowsOFFICE

✩✩✩✩✩✩✩
24 Мар 2023
11
0
Нет, читал в уроке про меню по OLED и там понравилось возможность сохранить строки во Flash или же PROGMEM памяти. Решил реализовать на подобие этого свою реализацию. По скольку строк в проекте будет много, я решил разделить массив с указателями на отдельные блоки, например:
C++:
// Строки из заставки
const char s1[] PROGMEM = "Simple device";
const char s2[] PROGMEM = "for programming";
const char s3[] PROGMEM = "ver 1.0";
const char* const load[] PROGMEM = {
  s1, s2, s3
};

// Строки из меню
const char s4[] PROGMEM = "Timer";
const char s5[] PROGMEM = "Stopwatch";
const char s6[] PROGMEM = "Distance";
const char* const menu[] PROGMEM = {
  s4, s5, s6
};
Но для этого надо как-то понимать где и какой массив с ссылками надо обрабатывать сейчас. Я решил сделать так:
Есть функция Print которая принимает: дисплей, указатель на массив с указателями, номер строки в массиве указателей.
Проблема в том, что я не понимаю как передать ссылку на массив с указателями.

UPD:
Видео от Nich1con'а с Заметки Ардуинщика, там вариант с реализацией такого меню
 

ChudoWindowsOFFICE

✩✩✩✩✩✩✩
24 Мар 2023
11
0
Там работа 1 массив с ссылками, а мне надо чтобы массив можно было передать в функцию + в коде явные ошибки с работай PROGMEM
 

ChudoWindowsOFFICE

✩✩✩✩✩✩✩
24 Мар 2023
11
0
Проблема во время компиляции исчезла если в главном файле на строке 17 заменить:
print(lcd, &load, 1);
на
print(lcd, (const char* const*) &load, 1);
Пока в железе не проверял