Ломается пользовательская функция при повторном использовании ее с другим аргументом
- Войдите на сайт для отправки комментариев
Здравствуйте. Идея в том чтобы отрисовывать в мониторе порта (позже на текстовом экране) символьные анимации. Данные об анимациях хранятся в прогмем. Написал функцию playAnimation, и она работает пока не использовать ее еще раз с другим аргументом animationNumber (даже если в этом цикле она не вызовется). В таком случае с периодом около минуты начинает выводиться пустой буфер экрана. Вот сам код:
[code]
//"Спрайты" одного кадра анимации с разными "персонажами"
const char eggSprite0[] PROGMEM = "0";
const char eggSprite00[] PROGMEM = "O";
const char* const eggSprites0[] PROGMEM = {eggSprite0, eggSprite00}; //массив "спрайтов" одного кадра но с разными "персонажами"
//"Спрайты" одного кадра анимации с разными "персонажами"
const char eggSprite1[] PROGMEM = "O";
const char eggSprite11[] PROGMEM = "o";
const char* const eggSprites1[] PROGMEM = {eggSprite1, eggSprite11}; //массив "спрайтов" одного кадра но с разными "персонажами"
//"Спрайты" одного кадра анимации с разными "персонажами"
const char walkingSpriteR0[] PROGMEM = "S";
const char walkingSpriteR00[] PROGMEM = "}";
const char* const walkingSpritesR0[] PROGMEM = {walkingSpriteR0, walkingSpriteR00}; //массив "спрайтов" одного кадра но с разными "персонажами"
//"Спрайты" одного кадра анимации с разными "персонажами"
const char walkingSpriteR1[] PROGMEM = "s";
const char walkingSpriteR11[] PROGMEM = ">";
const char* const walkingSpritesR1[] PROGMEM = {walkingSpriteR1, walkingSpriteR11}; //массив "спрайтов" одного кадра но с разными "персонажами"
//"Спрайты" одного кадра анимации с разными "персонажами"
const char walkingSpriteL0[] PROGMEM = "Z";
const char walkingSpriteL00[] PROGMEM = "{";
const char* const walkingSpritesL0[] PROGMEM = {walkingSpriteL0, walkingSpriteL00}; //массив "спрайтов" одного кадра но с разными "персонажами"
//"Спрайты" одного кадра анимации с разными "персонажами"
const char walkingSpriteL1[] PROGMEM = "z";
const char walkingSpriteL11[] PROGMEM = "<";
const char* const walkingSpritesL1[] PROGMEM = {walkingSpriteL1, walkingSpriteL11}; //массив "спрайтов" одного кадра но с разными "персонажами"
//данные об анимациях
#define EGG_ANIMATION 0
#define WALKING_ANIMATION 1
#define NUMBER_OF_ANIMATIONS 2
const char** const eggSprites[2] PROGMEM = {&eggSprites0[0], &eggSprites1[0]}; //массив массивов "спрайтов" кадров первой анимации
const char** const walkingSprites[20] PROGMEM = {walkingSpritesR0, walkingSpritesR1, //массив массивов "спрайтов" кадров второй анимации
walkingSpritesR0, walkingSpritesR0, walkingSpritesR0, walkingSpritesR0,
walkingSpritesR1, walkingSpritesR0, walkingSpritesL0, walkingSpritesL0,
walkingSpritesL0, walkingSpritesL1, walkingSpritesL0, walkingSpritesL0,
walkingSpritesL0, walkingSpritesL0, walkingSpritesL1, walkingSpritesL0,
walkingSpritesR0, walkingSpritesR0};
const char*** const sprites[NUMBER_OF_ANIMATIONS] PROGMEM = {eggSprites, walkingSprites}; //массив массивов массивов "спрайтов" кадров анимаций
const uint16_t eggPeriod[2] PROGMEM = {1000, 1000}; //массив длительностей кадров первой анимации
const uint16_t walkingPeriod[20] PROGMEM = {500, 500, 500, 500, 500, 500, 500, 500, 500,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500}; //массив длительностей кадров второй анимации
const uint16_t* const periods[NUMBER_OF_ANIMATIONS] PROGMEM = {eggPeriod, walkingPeriod};//массив массивов длительностей кадров анимаций
const byte animationSizes[NUMBER_OF_ANIMATIONS] PROGMEM = {7, 7};//массив размеров кадров(размер участка массива экрана буфера где может находиться "спрайт" во время анимации) анимаций
const byte locations[NUMBER_OF_ANIMATIONS] PROGMEM = {1, 1};//массив расположений кадров(начало участка массива буфера экрана где может находиться "спрайт" во время анимации) анимаций
const byte frameSizes[NUMBER_OF_ANIMATIONS] PROGMEM = {1,1};//размер "спрайта" анимации
const byte eggPosition[2] PROGMEM = {4, 4}; //массив мест расположения "спрайта" в массиве буфера экрана во время каждого кадра первой анимации
const byte walkingPosition[20] PROGMEM = {4, 4, 4, 5, 6, 7, 7, 7, 6, 5, 4, 4, 4, 3, 2, 1, 1, 1, 2, 3};//массив мест расположения "спрайта" в массиве буфера экрана во время каждого кадра второй анимации
const byte* const positions[NUMBER_OF_ANIMATIONS] PROGMEM = {eggPosition, walkingPosition};//массив массивов мест расположения "спрайта" в массиве буфера экрана
const byte numbersOfFrames[NUMBER_OF_ANIMATIONS] PROGMEM = {2, 20};//массив количества кадров каждой анимации
char display[] = "|_______|_____________________|";//массив буфера "экрана"
bool frameChangeFlag; // флаг обновления экрана
uint32_t aniTim[2]={0, 0}; //таймер анимации в виде массива для возможности проигрывать одновременно 2 анимации
byte aniNum[2]={0, 0}; // счетчик кадров анимации в виде массива для возможности проигрывать одновременно 2 анимации
byte a=0;
void setup() {
Serial.begin(9600);
}
void loop() {
if(a==0) {
playAnimation(1,0,0);
}
if(a==1) {
playAnimation(1,1,0);
}
displayScreen();
}
void clearFrame (byte asiz, byte loc) { //очищает экран - записывает в массив буфера экрана '_' начиная с члена [loc] до [loc+asiz]
for (int i; i < asiz; i++) {
*(display+loc+i) = '_';
}
}
void playAnimation(byte animationNumber, byte spriteType, byte slot) { //проигрывает анимацию, принимает номер анимации, номер персонажа, номер члена массива таймера и счетсика который будет использовать
if (millis() -aniTim[slot] >= pgm_read_word(&periods[animationNumber][aniNum[slot]])) { //если прошло время равное длительности конкретного кадра конкретной анимации (берется из прогмем)
aniTim[slot] =millis(); //сбрасываем таймер
clearFrame(*(&animationSizes[animationNumber]), *(&locations[animationNumber])); //очищаем участок экрана перед отрисовкой нового кадра (начало и длинна участка берется из прогмем)
char** frameAdress = pgm_read_byte(*(sprites+animationNumber)+aniNum[slot]); //получаем из прогмем адрес кадра анимации в прогмем
for (byte i =0; i < *(&frameSizes[animationNumber]); i++) { //записываем спрайт в буфер экрана
*(&display[0]+pgm_read_byte(&positions[animationNumber][aniNum[slot]])+i) =(pgm_read_byte (pgm_read_byte(frameAdress+spriteType)+i));
}
frameChangeFlag=1; //поднимаем флаг обновления экрана
if(++aniNum[slot]>=*(&numbersOfFrames[animationNumber])) aniNum[slot]=0;//прибавляем единицу к счетчику, кадров ограничиваем числом кадров
}
}
void displayScreen() { //выводит буфер экрана если поднят флаг и опускает его
if (frameChangeFlag==1) {
Serial.print('\n');
for (int i; i<31; i++) {
Serial.write(*(display+i));
}
frameChangeFlag=0;
}
}
[/code]
В общем если оставить все как есть, то все работает, но если в лупе в одной из playAnimation первую единицу поменять на ноль, то ломается.
Также если просто вставить в луп вместо playAnimation просто тело данной функции, задав аргументы в виде локальных переменных, то работает, например вот так:
void loop() {
if(a==0) {
byte animationNumber=1;
byte spriteType=0;
byte slot=0;
if (millis() -aniTim[slot] >= pgm_read_word(&periods[animationNumber][aniNum[slot]])) { //если прошло время равное длительности конкретного кадра конкретной анимации (берется из прогмем)
aniTim[slot] =millis(); //сбрасываем таймер
clearFrame(*(&animationSizes[animationNumber]), *(&locations[animationNumber])); //очищаем участок экрана перед отрисовкой нового кадра (начало и длинна участка берется из прогмем)
char** frameAdress = pgm_read_byte(*(sprites+animationNumber)+aniNum[slot]); //получаем из прогмем адрес кадра анимации в прогмем
for (byte i =0; i < *(&frameSizes[animationNumber]); i++) { //записываем спрайт в буфер экрана
*(&display[0]+pgm_read_byte(&positions[animationNumber][aniNum[slot]])+i) =(pgm_read_byte (pgm_read_byte(frameAdress+spriteType)+i));
}
frameChangeFlag=1; //поднимаем флаг обновления экрана
if(++aniNum[slot]>=*(&numbersOfFrames[animationNumber])) aniNum[slot]=0;//прибавляем единицу к счетчику, кадров ограничиваем числом кадров
}
}
if(a==1) {
byte animationNumber=0;
byte spriteType=0;
byte slot=1;
if (millis() -aniTim[slot] >= pgm_read_word(&periods[animationNumber][aniNum[slot]])) { //если прошло время равное длительности конкретного кадра конкретной анимации (берется из прогмем)
aniTim[slot] =millis(); //сбрасываем таймер
clearFrame(*(&animationSizes[animationNumber]), *(&locations[animationNumber])); //очищаем участок экрана перед отрисовкой нового кадра (начало и длинна участка берется из прогмем)
char** frameAdress = pgm_read_byte(*(sprites+animationNumber)+aniNum[slot]); //получаем из прогмем адрес кадра анимации в прогмем
for (byte i =0; i < *(&frameSizes[animationNumber]); i++) { //записываем спрайт в буфер экрана
*(&display[0]+pgm_read_byte(&positions[animationNumber][aniNum[slot]])+i) =(pgm_read_byte (pgm_read_byte(frameAdress+spriteType)+i));
}
frameChangeFlag=1; //поднимаем флаг обновления экрана
if(++aniNum[slot]>=*(&numbersOfFrames[animationNumber])) aniNum[slot]=0;//прибавляем единицу к счетчику, кадров ограничиваем числом кадров
}
}
displayScreen();
}
И сходу написал 150 строк более-мене внятно организованного кода с указателями на указатели и прогмемом? Врать не надо.
Ошибок слишком много. Давайте исправлять их шаг за шагом.
Все номера строк по первому коду:
1. В сроках №№ 91 и 112 переменная i используется не будучи инициализированной -> результат непредсказуем.
2. В строка 38-46 полный бардак в типах. Вы постоянно преобразуете указатели в указатели на указатели. Даже разбираться не хочется, что из этого получится.
-----
Включите предупреждения компилятора, запустите, полюбуйтесь на простыню предупреждений и начинайте одно за одним исправлять. Кога предупреждений не останется, проверьте работоспособность. Если проблемы сохраняться, публикуйте новый код и описание , посмотрим.
Не вру, просто перед покупкой ардуины 2 недели курил гайды)
1. В сроках №№ 91 и 112 переменная i используется не будучи инициализированной -> результат непредсказуем.
Разве они не инициируются в строке выше в цикле for?
Объявление локальной переменной неэквивалентно её инициализации. В ячейке памяти, на которую указывает переменная, может оказаться любой мусор.
2. В строка 38-46 полный бардак в типах. Вы постоянно преобразуете указатели в указатели на указатели. Даже разбираться не хочется, что из этого получится.
Включите предупреждения компилятора, запустите, полюбуйтесь на простыню предупреждений и начинайте одно за одним исправлять.
Эти преобразования - это создание массивов массивов, массивов массивов массивов и т. д. Предупреждения видел, честно не знаю как исправить, буду благодарен если подскажете.
Думаете проблема где-то там? Просто оно вроде работает пока не пытаюсь оформить в виде функции.
Просто оно вроде работает пока не пытаюсь оформить в виде функции.
Обычно это является признаком неинициализированных переменных. (код не смотрел)
Хорошо, понял, буду дома попробую. Я правильно понимаю: инициализация - это предварительно задать переменной какое-то стартовое значение(например 0)?
Разве они не инициируются в строке выше в цикле for?
Что значит в строке выше? Где выше? Например, строка №91 выглядит так:
for (int i; i < asiz; i++) {И где тут инициализация переменной i ? Каким, по-Вашему значение она инициализируется?
оно вроде работает пока не пытаюсь оформить в виде функции.
Я же Вам сказал, что при неинициализированных переменных
результат непредсказуем.
может казаться, что работает вне функции, а может работать только в полнолуние. При таких ошибках не надо гадать, их надо просто исправить.
Эти преобразования - это создание массивов массивов, массивов массивов массивов и т. д. Предупреждения видел, честно не знаю как исправить, буду благодарен если подскажете.
Думаете проблема где-то там?
Такие предупреждения показывают, что Вы не понимаете, что делаете и наверняка там ошибки. Но точно сказать не могу, поскольку я вижу что Вы сделали, но не вижу, что Вы хотели сделать. Опишите подробно, что за массивы в массивах, приведите пример такого массива в массиве, может понятнее будет.
Иногда подобные предупреждения допустимы, но только при условии, что Вы точно знаете, что делаете. Пока же этого нет, избавляйтесь от них.
Опишите подробно, что за массивы в массивах, приведите пример такого массива в массиве, может понятнее будет.
Постараюсь. Спрайты - строки, т. е. по сути массивы; ссылки на спрайты одного итого же кадра просто с разными "персонажами" объединяются в массив разновидностей одного кадра, далее ссылки на эти массивы объединяются в массив кадров одной анимации, а ссылки на них в массив всех анимаций. Все это делаю для того, чтобы по номеру анимации можно было взять из массива ссылку на массив с кадрами этой анимации, потом номеру текущего кадра взять ссылку на массив с разновидностями этого кадра (разные "персонажи"), по номеру персонажа взять ссылку на конкретный спрайт. Так и получается преобразование указателя в указатель на указатель и т. д. Многомерный массив не делаю поскольку у анимаций различное количество кадров, если я правильно понимаю это экономит память, так как незанятые ячейки в многомерном массиве все равно занимают память. Возможно есть более простая реализация задуманного, но я к ней додуматься пока не могу.
Надеюсь получилось объяснить, что пытаюсь сделать.
И где тут инициализация переменной i ? Каким, по-Вашему значение она инициализируется?
Понял, дурак, перепутал объявление и инициализацию. Переменные i инициализировал, результат не изменился.
Чисто ради проверки полностью выпилил этот массив массивов массивов... Оставил 2 простые таблицы ссылок со спрайтами и переделал немного playAnimation под это дело. Предупреждения пропали, но результат точно такой же. Видимо дело все-таки не в них.
Вот на всякий случай код без этих массивов:
[code] //данные об анимациях #define EGG_ANIMATION 0 #define WALKING_ANIMATION 1 #define NUMBER_OF_ANIMATIONS 2 const uint16_t eggPeriod[2] PROGMEM = {1000, 1000}; //массив длительностей кадров первой анимации const uint16_t walkingPeriod[20] PROGMEM = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500}; //массив длительностей кадров второй анимации const uint16_t* const periods[NUMBER_OF_ANIMATIONS] PROGMEM = {eggPeriod, walkingPeriod};//массив массивов длительностей кадров анимаций const byte animationSizes[NUMBER_OF_ANIMATIONS] PROGMEM = {7, 7};//массив размеров кадров(размер участка массива экрана буфера где может находиться "спрайт" во время анимации) анимаций const byte locations[NUMBER_OF_ANIMATIONS] PROGMEM = {1, 1};//массив расположений кадров(начало участка массива буфера экрана где может находиться "спрайт" во время анимации) анимаций const byte frameSizes[NUMBER_OF_ANIMATIONS] PROGMEM = {1,1};//размер "спрайта" анимации const byte eggPosition[2] PROGMEM = {4, 4}; //массив мест расположения "спрайта" в массиве буфера экрана во время каждого кадра первой анимации const byte walkingPosition[20] PROGMEM = {4, 4, 4, 5, 6, 7, 7, 7, 6, 5, 4, 4, 4, 3, 2, 1, 1, 1, 2, 3};//массив мест расположения "спрайта" в массиве буфера экрана во время каждого кадра второй анимации const byte* const positions[NUMBER_OF_ANIMATIONS] PROGMEM = {eggPosition, walkingPosition};//массив массивов мест расположения "спрайта" в массиве буфера экрана const byte numbersOfFrames[NUMBER_OF_ANIMATIONS] PROGMEM = {2, 20};//массив колличества кадров каждой анимации char display[] = "|_______|_____________________|";//массив буфера "экрана" bool frameChangeFlag=0; // флаг обновления экрана uint32_t aniTim[2]={0, 0}; //таймер анимации в виде массива для возможности проигрывать одновременно 2 анимации byte aniNum[2]={0, 0}; // счетчик кадров анимации в виде массива для возможности проигрывать одновременно 2 анимации char const sprtR0[]="S"; char const sprtR1[]="s"; char const sprtL0[]="Z"; char const sprtL1[]="z"; const char* const sprts[20]={sprtR0, sprtR1, sprtR0, sprtR0, sprtR0, sprtR0, sprtR1, sprtR0, sprtL0, sprtL0, sprtL0,sprtL1,sprtL0,sprtL0,sprtL0,sprtL0,sprtL1,sprtL0,sprtR0,sprtR0}; const char sprtE0[] = "0"; const char sprtE1[] = "O"; const char* const sprtsE[2] = {sprtE0, sprtE1}; byte a=0; void setup() { Serial.begin(9600); } void loop() { if(a==0) { playAnimation(1,0,0); } if(a==1) { playAnimation(1,0,0); } displayScreen(); } void clearFrame (byte asiz, byte loc) { //очищает экран - записывает в массив буфера экрана '_' начиная с члена [loc] до [loc+asiz] for (int i=0; i < asiz; i++) { *(display+loc+i) = '_'; } } void playAnimation(byte animationNumber, byte spriteType, byte slot) { //проигрывает анимацию, принимает номер анимации, номер персонажа, номер члена массива таймера и счетсика который будет использовать if (millis() -aniTim[slot] >= pgm_read_word(&periods[animationNumber][aniNum[slot]])) { //если прошло время равное длительности конкретного кадра конкретной анимации (берется из прогмем) aniTim[slot] =millis(); //сбрасываем таймер clearFrame(pgm_read_byte(&animationSizes[animationNumber]), pgm_read_byte(&locations[animationNumber])); //очищаем участок экрана перед отрисовкой нового кадра (начало и длинна участка берется из прогмем for (byte i =0; i < *(&frameSizes[animationNumber]); i++) { //записываем спрайт в буфер экрана if (animationNumber==1) *(&display[0]+pgm_read_byte(&positions[animationNumber][aniNum[slot]])+i) =*(*(&sprts[0]+aniNum[slot])+i); if (animationNumber==0) *(&display[0]+pgm_read_byte(&positions[animationNumber][aniNum[slot]])+i) =*(*(&sprtsE[0]+aniNum[slot])+i); } frameChangeFlag=1; //поднимаем флаг обновления экрана if(++aniNum[slot]>=*(&numbersOfFrames[animationNumber])) aniNum[slot]=0;//прибавляем единицу к счетчику, кадров ограничиваем числом кадров } } void displayScreen() { //выводит буфер экрана если поднят флаг и опускает его if (frameChangeFlag==1) { Serial.print('\n'); for (int i=0; i<31; i++) { Serial.write(*(display+i)); } frameChangeFlag=0; } } [/code]Надеюсь получилось объяснить, что пытаюсь сделать.
Нет, не получилось. Приведите короткий пример.
И ещё перестаньте вставлять в код [code] - оно там не нужно, только мешает компилировать.
Нет, не получилось. Приведите короткий пример.
Пример массивов массивов? Я их выпилил пока что, предупреждений нет, давайте условно считать что эту ошибку я исправил.
И ещё перестаньте вставлять в код [code] - оно там не нужно, только мешает компилировать.
Простите, не заметил, оно автоматически вставлялось, дальше буду убирать.
Я их выпилил пока что
Т.е. сейчас Ваш вопрос касается кода из #12. Правильно? Просто я не хочу тратить время на разбор, а потом услышать: "ты не то смотрел".
Да. С массивами потом буду разбираться, поскольку даже без них в виде функции не работает.
Хорошо, я сегодня посмотрю на код из двенадцатого поста, возвращайтесь позже, напишу, что нарою.
Хорошо, спасибо.
Разобрался.
Смотрите на строку №67 в посте #12
Вы пытаетесь из прогмем прочитать элемент. Но, дело в том, у Вас ведь не честный двухмерный массив. У Вас массив указателей, а каждый указатель указывает на последовательность чисел (массив).
Поэтому, Вы должны и читать в два этапа. Сначала прочитать из прогмем указатель, а потом уже, взяв нужный индекс от него, прочитать из прогмем значение.
Я сделал совсем маленький пример в котором читаю и так, как Вы, и так, как надо (т.е. в два этапа). Смотрите
// Массивы, примерно как у Вас, но поменьше const uint16_t egg[] PROGMEM = { 1001, 1002 }; const uint16_t walk[] PROGMEM = { 511, 512, 513, 514 }; const uint16_t * const periods[2] PROGMEM = {egg, walk}; // Количество элементов в массиве для всех трех массивов constexpr int totalEggs = sizeof(egg) / sizeof(egg[0]); constexpr int totalWalks = sizeof(walk) / sizeof(walk[0]); constexpr int totalPeriods = sizeof(periods) / sizeof(periods[0]); // Читаем элемент массива так, как Вы его читалои uint16_t readAsYouDid(const int i, const int j) { return pgm_read_word(&periods[i][j]); } // Читаем так, как надо читать - в два этапа uint16_t readAsYouMust(const int i, const int j) { const uint16_t * subArray = pgm_read_ptr(& periods[i]); return pgm_read_word(& subArray[j]); } // Здесь просто печатаем все элементы массива // Каждый элемент сначала как Вы его читали, // а потом как надо его читать void setup() { Serial.begin(115200); for (int i = 0; i < totalPeriods; i++) { for (int j = 0; j < ( i ? totalWalks : totalEggs); j++) { Serial.print("periods["); Serial.print(i); Serial.print("]["); Serial.print(j); Serial.print("]= "); Serial.print(readAsYouDid(i, j)); Serial.print(" | "); Serial.println(readAsYouMust(i, j)); } } } void loop() {}а вот его результат (Вы получите такой же, если запустите). Сначала идёт значение, прочитанное Вашим способом, а потом - двухэтапным.
Как видите, при чтении так, как Вы читали, печатается чушь, а при чтении в два приёма - именно то, что реально сидело в массивах.
Без функции (или при функции, но с одинаковыми аргументами) оно у Вас работало потому, что компилятор ВСЕГДА знал по каким адресам читать и просто подставил их (адреса) во время компиляции. А как только индексы стали переменными, всё сломалось, потому что заранее вычислить адрес нельзя, а на этапе выполнения Вы его вычисляете неверно.
Разберитесь с примером и сделайте у себя правильно. Не только в строке №67, но и в остальных, где Вы неправильно читаете (№№ 71, 72, может ещё где, сами смотрите).
Спасибо вам большое, с работы приду попробую. Я в принципе знаю, что извлекать нужно было в два этапа, просто в одном из гайдов по прогмем видел, обращение с массивом массивов в прогмем как с двухмерным массивом, вот и сделал так же. Ещё раз спасибо.
А не получится сделать что-то вроде такого в 19 строке, чтоб не заводить промежуточный указатель subArray?
return pgm_read_word(pgm_read_word(& periods[i])+j);
Извините если вопрос нубский, я правда только учусь.
Во-первых, непонятно, чем эта константа Вам мешает. Компилятор всё равно "съёст" эту константу и никакой особой памяти выделять под неё не будет, т.е., с точки зрения кода, разницы абсолютно никакой. Зато, "короткие строки - читабельнее текст".
Но, если так уж хочется, то можно, конечно, только не так нагло - прежде, чем прибавлять j, нужно преобразовать результат чтения из прогмем к правильному типу, иначе сложение даст совсем не тот результат.
Вот так можно:
Кстати, заметьте я здесь использую pgm_read_ptr, а не pgm_read_word (и там, в коде выше, я тоже поменял). Технически, в данном МК, разницы никакой, но так идеологически более правильно (кошерно) - если читаем указатель, то и используем функцию для чтения указателей.
Спасибо большое за помощь, все переделал под двухэтапное чтение, теперь все работает, как надо. Кстати с массивами массивов массивов тоже разобрался. Я просто не знал как правильно указывать тип данных при создании этих массивов: я писал вот это - const char** const, а нужно было const char* const* const. Предупреждений теперь нет. Читаю из этих массивов по тому же принципу (только чтение не 2-х этапное, а 4-х этапное), и все работает как нужно. Спасибо
Компилятор всё равно "съёст" эту константу и никакой особой памяти выделять под неё не будет, т.е., с точки зрения кода, разницы абсолютно никакой.
Понял, тогда вопрос отпадает.
Кстати, заметьте я здесь использую pgm_read_ptr, а не pgm_read_word (и там, в коде выше, я тоже поменял). Технически, в данном МК, разницы никакой, но так идеологически более правильно (кошерно) - если читаем указатель, то и используем функцию для чтения указателей.
Спасибо, учту.