Чтение элемента вложенного массива из PROGMEM.

У меня есть 2D-массив массивов uint16_t (ИК-коды удаленного доступа). Из-за размера этих данных в оперативной памяти у меня возникают всевозможные проблемы, когда мой массив превышает один код. Таким образом, я пытался переместить всю эту структуру в PROGMEM. Следуя различным руководствам и этому решению, я придумал приведенный ниже код для чтения одного элемента из массива в переменную, которую я могу передать функция IRLIB2 IRSendRaw.

Переменная, содержащая код, считываемый из PROGMEM, по-прежнему огромна и вызывает некоторые проблемы. Есть ли более прямой способ, такой как понимание списка Python, для доступа к произвольному элементу и отправки его непосредственно в IRSender?

Эта библиотека PROGMEM_readAnything выглядит многообещающе, но у меня недостаточно знаний C++, чтобы сделать мысленный прыжок и реализовать ее в своем коде.

#include <IRLibSendBase.h>    //Нам нужен базовый код
#include <IRLib_HashRaw.h>    //Использовать только необработанный отправитель

#define RAW_DATA_LEN 68  
const uint16_t sources[3][RAW_DATA_LEN] PROGMEM =
  {{8550, 4306, 530, 1606, 530, 566, 502, 1610, //включение/выключение питания
  530, 566, 502, 574, 506, 1630, 506, 566,
  506, 1630, 506, 566, 502, 1610, 530, 566,
  502, 1634, 506, 1606, 530, 570, 498, 1634,
  506, 570, 510, 562, 506, 566, 502, 1634,
  506, 1606, 530, 1610, 530, 562, 538, 538,
  530, 542, 538, 1598, 538, 1570, 558, 542,
  538, 538, 530, 542, 538, 1598, 530, 1578,
  558, 1578, 562, 1000},

  {8546, 4310, 558, 1578, 562, 538, 498, 1638,  //исходный компакт-диск
  530, 542, 506, 570, 502, 1634, 502, 570,
  498, 1638, 534, 538, 498, 1638, 530, 542,
  506, 1606, 554, 1582, 558, 538, 510, 1602,
  554, 546, 506, 566, 502, 570, 510, 1602,
  554, 1582, 558, 538, 510, 566, 502, 1606,
  554, 546, 502, 1610, 558, 1574, 554, 546,
  502, 570, 510, 1602, 526, 1610, 526, 574,
  506, 1602, 526, 1000},

  {8550, 4306, 530, 1606, 534, 566, 502, 1606,  //исходный CDR
  534, 566, 502, 570, 510, 1602, 526, 570,
  510, 1602, 534, 566, 502, 1606, 534, 566,
  506, 1602, 530, 1606, 534, 566, 502, 1610,
  530, 570, 498, 574, 506, 566, 502, 570,
  510, 1602, 526, 570, 510, 566, 502, 570,
  510, 1602, 526, 1610, 526, 1606, 534, 1602,
  534, 566, 502, 1610, 530, 1602, 534, 1602,
  526, 574, 506, 1000}};

//Создаем отправителя
IRsendRaw mySender;


//Считаем один код в переменную и отправим на ИК-передатчик
uint16_t myCode[RAW_DATA_LEN];
for (int i = 0; i < RAW_DATA_LEN; i++) {
   myCode[i] = pgm_read_word_near(sources[0] + i); 
}
mySender.send(myCode,RAW_DATA_LEN,36);

, 👍0

Обсуждение

вы не можете поместить многомерный массив в PROGMEM. см. «Массив строк» в https://www.arduino.cc/reference/en/language/variables/utilities/progmem/., @Juraj

@Юрай Конечно, можешь. Однако строки — это особый случай. Нет никакой разницы в памяти между 2D-массивом и одним большим 1D-массивом., @Majenko

Не проблема заполнить остальную часть флэш-памяти Arduino Uno данными программы. Для Arduino Mega 2560 лучше держать раздел данных программы ниже границы 64 КБ. Вы копируете данные в буфер размером 68 uint16_t, то есть 136 байт. В этом проблема? У Arduino Uno 2 Кбайт оперативной памяти. Что еще использует ваш sram?, @Jot

У меня были проблемы с чтением двумерного массива из PROGMEM. теперь я знаю. для одного измерения аргумент pgm_read — arr + i. это указатель на элемент. а для двух измерений аргумент pgm_read должен быть arr[i] + j, @Juraj


1 ответ


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

4

Библиотека в существующем виде не поддерживает прямую отправку из PROGMEM. Однако это не так уж сложно обойти, если ваш C++ позволяет это сделать.

Класс IRsendRaw — это дочерний класс, который расширяет IRsendBase одной функцией. Вам просто нужно сделать то же самое, чтобы создать новый класс, реализующий функции таким образом, чтобы их можно было читать из PROGMEM.

Это оригинал:

class IRsendRaw: public virtual IRsendBase {
  public:
    void send(uint16_t *buf, uint8_t len, uint8_t khz) {
      enableIROut(khz);
      for (uint8_t i = 0; i < len; i++) {
        if (i & 1) {
          space(buf[i]);
        } 
        else {
          mark(buf[i]);
        }
      }
      space(0); // Просто чтобы быть уверенным
    }
};

Вам просто нужно создать свой собственный вариант из вашего скетча, который будет называться по-другому и будет использовать функции программной памяти вместо доступа к buf[i]:

class IRsendRawPGM: public virtual IRsendBase {
  public:
    void send(const uint16_t *buf, uint8_t len, uint8_t khz) {
      enableIROut(khz);
      for (uint8_t i = 0; i < len; i++) {
        if (i & 1) {
          space(pgm_read_word_near(buf + i));
        } 
        else {
          mark(pgm_read_word_near(buf + i));
        }
      }
      space(0); // Просто чтобы быть уверенным
    }
};

Теперь вы можете определить объект отправителя как:

IRsendRawPGM mySender;

И отправьте данные напрямую:

mySender.send(sources[0], RAW_DATA_LEN, 36);

(Примечание: этот код не тестировался).

,