pgm_read_word, чтение данных из Flash.

О-го-го

✩✩✩✩✩✩✩
14 Дек 2023
7
0
Hi! Простой пример кода ниже (такое определение описано на страничке AlexGyver "РАБОТА С PROGMEM ПАМЯТЬЮ").
При любом единичном цикле for (uint16_t n=0; n<1; n++) или for (uint16_t n=1; n<2; n++) данные массива читаются правильно.
При любом не единичном цикле for (uint16_t n=0; n<2; n++) или for (uint16_t n=1; n<3; n++) данные массива читаются не правильно.

Это на обычной Ардуине(328P).
Кто подскажет? Может компилятор чего оптимизирует?

Форматирование (BB-код):
const uint16_t _A[ ]PROGMEM = {0x11, 0x12, 0x13, 0x14};
const uint16_t _B[ ] PROGMEM = {0x21, 0x22, 0x23, 0x24};
const uint16_t _C[ ] PROGMEM = {0x31, 0x32, 0x33, 0x34};
const uint16_t* const Ch_arr[ ] PROGMEM = {_A, _B, _C};

void setup() {
  Serial.begin(9600);
  uint16_t ni_arr;
  for (uint16_t n=0; n<1; n++){
    for (uint16_t i=0; i<4; i++){
      ni_arr=pgm_read_word(&Ch_arr[n]);
      Serial.println(ni_arr, HEX);
    }
  } 
}

void loop() {
}
 
Изменено:

Сотнег

★★★★★★★
15 Янв 2020
4,373
1,498
@О-го-го,
почему неправильно?
Вы 4 раза читаете одну и ту же ячейку, поэтому получаете 4 одинаковых результата.
Всё ведь правильно?
 
  • Лойс +1
Реакции: Vaqtincha

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
573
код неправильный, т.к. указатели на массивы ты ТОЖЕ положил в прогмем. В таком случае сначала нужно достать из прогмема указатель на массив, а потом уже читать из него данные. Вот так:
C++:
const uint16_t _A[ ]PROGMEM = {0x11, 0x12, 0x13, 0x14};
const uint16_t _B[ ] PROGMEM = {0x21, 0x22, 0x23, 0x24};
const uint16_t _C[ ] PROGMEM = {0x31, 0x32, 0x33, 0x34};
const uint16_t* const Ch_arr[ ] PROGMEM = {_A, _B, _C};

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

  for (int i = 0; i < 3; i++) {
    uint16_t* arr = pgm_read_ptr(&Ch_arr[i]);
    for (int j = 0; j < 4; j++) {
      Serial.print(pgm_read_word(&arr[j]), HEX);
      Serial.print(',');
    }
    Serial.println();
  }
}

void loop() {
}
 

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
573
Если не убирать массивы в прогмем-список, то можно делать вот так
C++:
const uint16_t _A[ ]PROGMEM = {0x11, 0x12, 0x13, 0x14};
const uint16_t _B[ ] PROGMEM = {0x21, 0x22, 0x23, 0x24};
const uint16_t _C[ ] PROGMEM = {0x31, 0x32, 0x33, 0x34};
uint16_t* Ch_arr[ ] = {_A, _B, _C};

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

  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
      Serial.print(pgm_read_word(&Ch_arr[i][j]), HEX);
      Serial.print(',');
    }
    Serial.println();
  }
}

void loop() {
}
 

О-го-го

✩✩✩✩✩✩✩
14 Дек 2023
7
0
@О-го-го,
... Вы 4 раза читаете одну и ту же ячейку, ...
Прошу пардону:). Не представляю почему скопировалась часть текста из: ni_arr=pgm_read_word(&Ch_arr[n]); , но строка у меня такая ni_arr=pgm_read_word(&Ch_arr[n] квадратная скобка i квадратная скобка);
В первом сообщении при исправлении и сохранении не дает исправить (все равно висит неправильная запись)

код неправильный, т.к. указатели на массивы ты ТОЖЕ положил в прогмем.
AlexGyver спасибо, сейчас поразбираюсь.
 
Изменено:

О-го-го

✩✩✩✩✩✩✩
14 Дек 2023
7
0
В таком случае сначала нужно достать из прогмема указатель на массив, а потом уже читать из него данные. Вот так:
Если не убирать массивы в прогмем-список, то можно делать вот так
Испытал оба варианта:
-- Указателина_массивы_во_Flash;
-- Указателина_массивы_в_SRAM.
Оба варианта работают, но есть непонятная подозрительная ситуация - вот тестовый код:
C++:
const uint16_t _A[] PROGMEM = {0x11, 0x12, 0x13, 0x14};
const uint16_t _B[] PROGMEM = {0x21, 0x22, 0x23, 0x24};
const uint16_t _C[] PROGMEM = {0x31, 0x32, 0x33, 0x34};
const uint16_t* const Arr_Flash[] PROGMEM = {_A, _B, _C};
uint16_t* Arr_SRAM[] = {_A, _B, _C};

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

//Указатели_на_массивы_во_Flash>-------------------------
  uint16_t D_Flash; 
  for (int i = 0; i < 3; i++) {
    uint16_t* arr = pgm_read_ptr(&Arr_Flash[i]);
    for (int j = 0; j < 4; j++) {
      D_Flash=pgm_read_word(&arr[j]);
      Serial.print(D_Flash, HEX);
      Serial.print(',');
    }
    Serial.println();
  }
//<Указатели_на_массивы_во_Flash-------------------------
  
//Указатели_на_массивы_в_SRAM>-------------------
  uint16_t D_SRAM;
  for (uint16_t n=0; n<3; n++){
    for (uint16_t k=0; k<4; k++){
      D_SRAM=pgm_read_word(&Arr_SRAM[n][k]);
      Serial.print(D_SRAM, HEX);
      Serial.print(':');
    }
    Serial.println();
  }
//<Указатели_на_массивы_в_SRAM-------------------

}

void loop() {
}
получаю такой вывод:
10:02:47.905 -> 11,12,13,14,
10:02:47.905 -> 21,22,23,24,
10:02:47.905 -> 31,32,33,34,
10:02:47.905 -> BF92:F92:1F93:CF93:
10:02:47.905 -> 940C:44:940C:6C:
10:02:47.944 -> 33:33B:23:31A6:
Блок кода "Указателина_массивы_в_SRAM" дает неожидаемые данные.
Если закомментировать верхний блок кода "Указателина_массивыво_Flash", тогда нижний блок кода "Указателинамассивыв_SRAM" выдает верные данные. И все повторяется наоборот если поменять очередность выполнения блоков. И, кмк, никакие общие данные для записи не используются, что бы блоки влияли друг на друга. Может кто объяснит?
 

Сотнег

★★★★★★★
15 Янв 2020
4,373
1,498
@О-го-го,
а какой смысл собирать три одномерных массива в массив указателей?
Почему не использовать один двумерный массив?
Ну или, в крайнем случае, один длинный одномерный...
 

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
573
@О-го-го, судя по всему компилятор не справляется с адресацией PROGMEM массивов в глобальной области определения, скорее всего по этой же причине макросы F/PSTR не работают в глобале. В массив с адресами записывается вообще какой то мусор. Нужно перенести строчку uint16_t* Arr_SRAM[] = {_A, _B, _C}; ниже в setup() и всё будет работать корректно
 
  • Лойс +1
Реакции: О-го-го

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
573
@Сотнег, смысл очень даже значимый - атрибут PROGMEM применяется только к правой сущности. Укладывание массива из указателей на массивы позволяет засунуть в прогмем как сами массивы, так и их адреса. При двумерном массиве в прогмем поместятся только сами массивы. А вот большой одномерный массив при условии равной длины "подмассивов" - выбор настоящих мужиков