Ломается пользовательская функция при повторном использовании ее с другим аргументом

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Здравствуйте. Идея в том чтобы отрисовывать в мониторе порта (позже на текстовом экране) символьные анимации. Данные об анимациях хранятся в прогмем.  Написал функцию 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();
}
То есть как я понимаю проблема где-то в переменных аргументов функции. Надеюсь поможете разобраться в чем именно проблема, и как ее решить. Постарался максимально подробно описать проблему и закомментировать код. Заранее спасибо.
 
P.S. Это буквально первое что пробую писать, с программированием до этого никогда дела не имел, сильно больно камнями не кидайте по возможности.
rkit
Offline
Зарегистрирован: 23.11.2016

Valkyrience пишет:

P.S. Это буквально первое что пробую писать, с программированием до этого никогда дела не имел

И сходу написал 150 строк более-мене внятно организованного кода с указателями на указатели и прогмемом? Врать не надо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ошибок слишком много. Давайте исправлять их шаг за шагом.

Все номера строк по первому коду:

1. В сроках №№ 91 и 112 переменная i используется не будучи инициализированной -> результат непредсказуем.

2. В строка 38-46 полный бардак в типах. Вы постоянно преобразуете указатели в указатели на указатели. Даже разбираться не хочется, что из этого получится.

-----

Включите предупреждения компилятора, запустите, полюбуйтесь на простыню предупреждений и начинайте одно за одним исправлять. Кога предупреждений не останется, проверьте работоспособность. Если проблемы сохраняться, публикуйте новый код и описание , посмотрим.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Не вру, просто перед покупкой ардуины 2 недели курил гайды)

Valkyrience
Offline
Зарегистрирован: 21.12.2021

ЕвгенийП пишет:

1. В сроках №№ 91 и 112 переменная i используется не будучи инициализированной -> результат непредсказуем.

Разве они не инициируются  в строке выше в цикле for?

sadman41
Offline
Зарегистрирован: 19.10.2016

Объявление локальной переменной неэквивалентно её инициализации. В ячейке памяти, на которую указывает переменная, может оказаться любой мусор.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

ЕвгенийП пишет:

2. В строка 38-46 полный бардак в типах. Вы постоянно преобразуете указатели в указатели на указатели. Даже разбираться не хочется, что из этого получится.

Включите предупреждения компилятора, запустите, полюбуйтесь на простыню предупреждений и начинайте одно за одним исправлять.

Эти преобразования - это создание массивов массивов, массивов массивов массивов и т. д. Предупреждения видел, честно не знаю как исправить, буду благодарен если подскажете.

Думаете проблема где-то там? Просто оно вроде работает пока не пытаюсь оформить в виде функции.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Valkyrience пишет:

Просто оно вроде работает пока не пытаюсь оформить в виде функции.

Обычно это является признаком неинициализированных переменных. (код не смотрел)

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Хорошо, понял, буду дома попробую. Я правильно понимаю: инициализация - это предварительно задать переменной какое-то стартовое значение(например 0)?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valkyrience пишет:

Разве они не инициируются  в строке выше в цикле for?

Что значит в строке выше? Где выше? Например, строка №91 выглядит так:

for (int i; i < asiz; i++) {

И где тут инициализация переменной i ? Каким, по-Вашему значение она инициализируется?

Valkyrience пишет:

оно вроде работает пока не пытаюсь оформить в виде функции.

Я же Вам сказал, что при неинициализированных переменных

ЕвгенийП пишет:

результат непредсказуем.

может казаться, что работает вне функции, а может работать только в полнолуние. При таких ошибках не надо гадать, их надо просто исправить.

Valkyrience пишет:

Эти преобразования - это создание массивов массивов, массивов массивов массивов и т. д. Предупреждения видел, честно не знаю как исправить, буду благодарен если подскажете.

Думаете проблема где-то там? 

Такие предупреждения показывают, что Вы не понимаете, что делаете и наверняка там ошибки. Но точно сказать не могу, поскольку я вижу что Вы сделали, но не вижу, что Вы хотели сделать. Опишите подробно, что за массивы в массивах, приведите пример такого массива в массиве, может понятнее будет.

Иногда подобные предупреждения допустимы, но только при условии, что Вы точно знаете, что делаете. Пока же этого нет, избавляйтесь от них.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

ЕвгенийП пишет:

Опишите подробно, что за массивы в массивах, приведите пример такого массива в массиве, может понятнее будет.

Постараюсь. Спрайты - строки, т. е. по сути массивы; ссылки на спрайты одного итого же кадра просто с разными "персонажами" объединяются в массив разновидностей одного кадра, далее ссылки на эти массивы объединяются в массив кадров одной анимации, а ссылки на них в массив всех анимаций. Все это делаю для того, чтобы по номеру анимации можно было взять из массива ссылку на массив с кадрами этой анимации, потом номеру текущего кадра взять ссылку на массив с разновидностями этого кадра (разные "персонажи"), по номеру персонажа взять ссылку на конкретный спрайт. Так и получается преобразование указателя в указатель на указатель и т. д. Многомерный массив не делаю поскольку у анимаций различное количество кадров, если я правильно понимаю это экономит память, так как незанятые ячейки в многомерном массиве все равно занимают память. Возможно есть более простая реализация задуманного, но я к ней додуматься пока не могу.

Надеюсь получилось объяснить, что пытаюсь сделать.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

ЕвгенийП пишет:

И где тут инициализация переменной i ? Каким, по-Вашему значение она инициализируется?

Понял, дурак, перепутал объявление и инициализацию. Переменные i инициализировал, результат не изменился.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Чисто ради проверки полностью выпилил этот массив массивов массивов... Оставил 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]

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valkyrience пишет:
Видимо дело все-таки не в них.
Никто и не говорил, что обязательно в них. Ваи говорил, что ошибок много и исправлять надо по одной.

Valkyrience пишет:

Надеюсь получилось объяснить, что пытаюсь сделать.

Нет, не получилось. Приведите короткий пример.

И ещё перестаньте вставлять в код [code] - оно там не нужно, только мешает компилировать.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

ЕвгенийП пишет:

Нет, не получилось. Приведите короткий пример.


Пример массивов массивов? Я их выпилил пока что, предупреждений нет, давайте условно считать что эту ошибку я исправил.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

ЕвгенийП пишет:

И ещё перестаньте вставлять в код [code] - оно там не нужно, только мешает компилировать.

Простите, не заметил, оно автоматически вставлялось, дальше буду убирать.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valkyrience пишет:

Я их выпилил пока что

Т.е. сейчас Ваш вопрос касается кода из #12. Правильно? Просто я не хочу тратить время на разбор, а потом услышать: "ты не то смотрел".

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Да. С массивами потом буду разбираться, поскольку даже без них в виде функции не работает.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Хорошо, я сегодня посмотрю на код из двенадцатого поста, возвращайтесь позже, напишу, что нарою.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Хорошо, спасибо.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Разобрался.

Смотрите на строку №67 в посте #12

pgm_read_word(&periods[animationNumber][aniNum[slot]])

Вы пытаетесь из прогмем прочитать элемент. Но, дело в том, у Вас ведь не честный двухмерный массив. У Вас массив указателей, а каждый указатель указывает на последовательность чисел (массив).

Поэтому, Вы должны и читать в два этапа. Сначала прочитать из прогмем указатель, а потом уже, взяв нужный индекс от него, прочитать из прогмем значение.

Я сделал совсем маленький пример в котором читаю и так, как Вы, и так, как надо (т.е. в два этапа). Смотрите

// Массивы, примерно как у Вас, но поменьше
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() {}

а вот его результат (Вы получите такой же, если запустите). Сначала идёт значение, прочитанное Вашим способом, а потом - двухэтапным.

periods[0][0]= 57440 | 1001
periods[0][1]= 38152 | 1002
periods[1][0]= 57440 | 511
periods[1][1]= 38152 | 512
periods[1][2]= 61343 | 513
periods[1][3]= 60544 | 514

Как видите, при чтении так, как Вы читали, печатается чушь, а при чтении в два приёма - именно то, что реально сидело в массивах.

Без функции (или при функции, но с одинаковыми аргументами) оно у Вас работало потому, что компилятор ВСЕГДА знал по каким адресам читать и просто подставил их (адреса) во время компиляции. А как только индексы стали переменными, всё сломалось, потому что заранее вычислить адрес нельзя, а на этапе выполнения Вы его вычисляете неверно.

Разберитесь с примером и сделайте у себя правильно. Не только в строке №67, но и в остальных, где Вы неправильно читаете (№№ 71, 72, может ещё где, сами смотрите).

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Спасибо вам большое, с работы приду попробую. Я в принципе знаю, что извлекать нужно было в два этапа, просто в одном из гайдов по прогмем видел, обращение с массивом массивов в прогмем как с двухмерным массивом, вот и сделал так же. Ещё раз спасибо.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

А не получится сделать что-то вроде такого в 19 строке, чтоб не заводить промежуточный указатель subArray?

return pgm_read_word(pgm_read_word(& periods[i])+j);

Извините если вопрос нубский, я правда только учусь.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Valkyrience пишет:
А не получится сделать что-то вроде такого в 19 строке, чтоб не заводить промежуточный указатель subArray? return pgm_read_word(pgm_read_word(& periods[i])+j); Извините если вопрос нубский, я правда только учусь.

Во-первых, непонятно, чем эта константа Вам мешает. Компилятор всё равно "съёст" эту константу и никакой особой памяти выделять под неё не будет, т.е., с точки зрения кода, разницы абсолютно никакой. Зато, "короткие строки - читабельнее текст".

Но, если так уж хочется, то можно, конечно, только не так нагло - прежде, чем прибавлять j, нужно преобразовать результат чтения из прогмем к правильному типу, иначе сложение даст совсем не тот результат.

Вот так можно:

return pgm_read_word(reinterpret_cast<uint16_t *>(pgm_read_ptr(& periods[i])) + j);

Кстати, заметьте я здесь использую pgm_read_ptr, а не pgm_read_word (и там, в коде выше, я тоже поменял). Технически, в данном МК, разницы никакой, но так идеологически более правильно (кошерно) - если читаем указатель, то и используем функцию для чтения указателей.

Valkyrience
Offline
Зарегистрирован: 21.12.2021

Спасибо большое за помощь, все переделал под двухэтапное чтение, теперь все работает, как надо. Кстати с массивами массивов массивов тоже разобрался. Я просто не знал как правильно указывать тип данных при создании этих массивов: я писал вот это - const char** const, а нужно было const char* const* const. Предупреждений теперь нет. Читаю из этих массивов по тому же принципу (только чтение не 2-х этапное, а 4-х этапное), и все работает как нужно. Спасибо  

ЕвгенийП пишет:

Компилятор всё равно "съёст" эту константу и никакой особой памяти выделять под неё не будет, т.е., с точки зрения кода, разницы абсолютно никакой. 

Понял, тогда вопрос отпадает.

ЕвгенийП пишет:

Кстати, заметьте я здесь использую pgm_read_ptr, а не pgm_read_word (и там, в коде выше, я тоже поменял). Технически, в данном МК, разницы никакой, но так идеологически более правильно (кошерно) - если читаем указатель, то и используем функцию для чтения указателей.

Спасибо, учту.