Ломается пользовательская функция при повторном использовании ее с другим аргументом
- Войдите на сайт для отправки комментариев
Здравствуйте. Идея в том чтобы отрисовывать в мониторе порта (позже на текстовом экране) символьные анимации. Данные об анимациях хранятся в прогмем. Написал функцию 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 выглядит так:
И где тут инициализация переменной i ? Каким, по-Вашему значение она инициализируется?
оно вроде работает пока не пытаюсь оформить в виде функции.
Я же Вам сказал, что при неинициализированных переменных
результат непредсказуем.
может казаться, что работает вне функции, а может работать только в полнолуние. При таких ошибках не надо гадать, их надо просто исправить.
Эти преобразования - это создание массивов массивов, массивов массивов массивов и т. д. Предупреждения видел, честно не знаю как исправить, буду благодарен если подскажете.
Думаете проблема где-то там?
Такие предупреждения показывают, что Вы не понимаете, что делаете и наверняка там ошибки. Но точно сказать не могу, поскольку я вижу что Вы сделали, но не вижу, что Вы хотели сделать. Опишите подробно, что за массивы в массивах, приведите пример такого массива в массиве, может понятнее будет.
Иногда подобные предупреждения допустимы, но только при условии, что Вы точно знаете, что делаете. Пока же этого нет, избавляйтесь от них.
Опишите подробно, что за массивы в массивах, приведите пример такого массива в массиве, может понятнее будет.
Постараюсь. Спрайты - строки, т. е. по сути массивы; ссылки на спрайты одного итого же кадра просто с разными "персонажами" объединяются в массив разновидностей одного кадра, далее ссылки на эти массивы объединяются в массив кадров одной анимации, а ссылки на них в массив всех анимаций. Все это делаю для того, чтобы по номеру анимации можно было взять из массива ссылку на массив с кадрами этой анимации, потом номеру текущего кадра взять ссылку на массив с разновидностями этого кадра (разные "персонажи"), по номеру персонажа взять ссылку на конкретный спрайт. Так и получается преобразование указателя в указатель на указатель и т. д. Многомерный массив не делаю поскольку у анимаций различное количество кадров, если я правильно понимаю это экономит память, так как незанятые ячейки в многомерном массиве все равно занимают память. Возможно есть более простая реализация задуманного, но я к ней додуматься пока не могу.
Надеюсь получилось объяснить, что пытаюсь сделать.
И где тут инициализация переменной i ? Каким, по-Вашему значение она инициализируется?
Понял, дурак, перепутал объявление и инициализацию. Переменные i инициализировал, результат не изменился.
Чисто ради проверки полностью выпилил этот массив массивов массивов... Оставил 2 простые таблицы ссылок со спрайтами и переделал немного playAnimation под это дело. Предупреждения пропали, но результат точно такой же. Видимо дело все-таки не в них.
Вот на всякий случай код без этих массивов:
Надеюсь получилось объяснить, что пытаюсь сделать.
Нет, не получилось. Приведите короткий пример.
И ещё перестаньте вставлять в код [code] - оно там не нужно, только мешает компилировать.
Нет, не получилось. Приведите короткий пример.
Пример массивов массивов? Я их выпилил пока что, предупреждений нет, давайте условно считать что эту ошибку я исправил.
И ещё перестаньте вставлять в код [code] - оно там не нужно, только мешает компилировать.
Простите, не заметил, оно автоматически вставлялось, дальше буду убирать.
Я их выпилил пока что
Т.е. сейчас Ваш вопрос касается кода из #12. Правильно? Просто я не хочу тратить время на разбор, а потом услышать: "ты не то смотрел".
Да. С массивами потом буду разбираться, поскольку даже без них в виде функции не работает.
Хорошо, я сегодня посмотрю на код из двенадцатого поста, возвращайтесь позже, напишу, что нарою.
Хорошо, спасибо.
Разобрался.
Смотрите на строку №67 в посте #12
Вы пытаетесь из прогмем прочитать элемент. Но, дело в том, у Вас ведь не честный двухмерный массив. У Вас массив указателей, а каждый указатель указывает на последовательность чисел (массив).
Поэтому, Вы должны и читать в два этапа. Сначала прочитать из прогмем указатель, а потом уже, взяв нужный индекс от него, прочитать из прогмем значение.
Я сделал совсем маленький пример в котором читаю и так, как Вы, и так, как надо (т.е. в два этапа). Смотрите
а вот его результат (Вы получите такой же, если запустите). Сначала идёт значение, прочитанное Вашим способом, а потом - двухэтапным.
Как видите, при чтении так, как Вы читали, печатается чушь, а при чтении в два приёма - именно то, что реально сидело в массивах.
Без функции (или при функции, но с одинаковыми аргументами) оно у Вас работало потому, что компилятор ВСЕГДА знал по каким адресам читать и просто подставил их (адреса) во время компиляции. А как только индексы стали переменными, всё сломалось, потому что заранее вычислить адрес нельзя, а на этапе выполнения Вы его вычисляете неверно.
Разберитесь с примером и сделайте у себя правильно. Не только в строке №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 (и там, в коде выше, я тоже поменял). Технически, в данном МК, разницы никакой, но так идеологически более правильно (кошерно) - если читаем указатель, то и используем функцию для чтения указателей.
Спасибо, учту.