Запись и чтение из eeprom массивов символов
- Войдите на сайт для отправки комментариев
Пт, 01/05/2020 - 22:43
Имеется следующая задача:
Есть шилд
Необходимо записать в память EEPROM несколько динамических массивов символов, затем их считать из EEPROM в массивы.
Длина массивов первоначально неизвестна, поэтому они динамические с максимальным количеством элементов (в моём случае 20).
Делается это для экономии оперативной памяти, т.к. работа идёт с большим количеством массивов.
Запись и чтение происходит следующим образом:
#include <EEPROM.h> char* str = "text"; char* str1; char* str2; ... // Выделяем память для str1 str1 = new char[20]; // Вставляем какие-то данные str1[0] = 'a'; str1[1] = 'b'; str1[2] = 'c'; // Запись в EEPROM str1 EEPROM.put(address1, str1); // Освобождаем память от str1 delete [] str1; // Запись в EEPROM str = "text" - статический массив символов EEPROM.put(address, str); ***************************************** Перезагружаем контроллер ***************************************** // Читаем из EEPROM в str1 EEPROM.get(address1, str1); // Читает ерунду // Читаем из EEPROM в str2 EEPROM.get(address2, str2); // Читает нормально - str = "text"!..
Что я делаю не так??
Поправки:
Шилд - MEGA2560
// Читаем из EEPROM в str2
36
EEPROM.
get
(address, str2);
// Читает нормально - str = "text"!..
Какую ерунду читает - "abc" + всякую херь?
Но, конечно, я бы тоже выводил всякую хрень и даже зависал, если бы меня принуждали писать в незарезервированную под строку область памяти.
maks.dav - вряд ли вы так хоть что-то сэкономите, так как для чтения из ЕЕПРОМ память все равно придется выделять, причем ровно столько же. сколько знаимает массив.
Если строчки не менются. куда полезнее было бы сохранять их PROGMEM
sadman41, нет, только всякую херь
Тогда каким образом строка str = "text" читается в неразмеченную область памяти?
maks.dav - вряд ли вы так хоть что-то сэкономите, так как для чтения из ЕЕПРОМ память все равно придется выделять, причем ровно столько же. сколько знаимает массив.
b707, подскажите, тогда каким образом из eeprom получается читать в такой же неразмеченный массив (указатель) массив символов str = "text"?
Если строчки не менются. куда полезнее было бы сохранять их PROGMEM
строчки меняются
Тогда каким образом строка str = "text" читается в неразмеченную область памяти?
Вы язык Си, видно, второй день как начали изучать?
В строке str = "text" вы инициализируете строку константой в момент создания, поэтому под нее выделяется память. А str1 и str2 у вас просто пустые указатели, память под них не выделена и прочитать в них из ЕЕПРОМ ничего нельзя, сначала нужно выделить память оператором new. поэтому я и пишу, что этим методом никакой экономии памяти не будет
Тогда каким образом строка str = "text" читается в неразмеченную область памяти?
Совпадение множества факторов.
Если есть интерес разобраться в деталях, берите объектный код, дизассемблируйте.
В строке str = "text" вы инициализируете строку константой в момент создания, поэтому под нее выделяется память. А str1 и str2 у вас просто пустые указатели, память под них не выделена и прочитать в них из ЕЕПРОМ ничего нельзя, сначала нужно выделить память оператором new
Читаю я не в строку str, которая была до этого инициализирована массивом "text" (естественно размер массива будет равен 4 в этом случае), а в указатель str2 - и читается прекрасно, без всякой ереси в начале или конце. Вот этот момент для меня пока не понятен вообще никак, почему так происходит?
удачное совпадение
Хорошо, есть ли какой-то механизм, или ещё что, чтобы контролировать момент, когда ОЗУ близка к заполнению?
Просто в конкретном проекте, если на каждый указатель цеплять размеченную память, боюсь, можно не подрасчитать и зависнуть..
P.S. Абсолютно все массивы с данными используются в работе, никакой массив освобождать нельзя
Вот интересно: почему ответ автомеханика "дороги херовые, ёпть" на вопрос "почему лопнула правая стойка?" сразу понятен клиенту и он не стремиться узнать отчего левая-то целая.
Но ответ "произведена запись в незарезервированную область памяти" отчего-то, де-факто, является неудовлетворительным и неясным.
Выделяйте память только в необходимые моменты (выделил-считал-вывел-освободил) или используйте один и тот же массив-буфер и для ввода и для вывода.
А средства контроля есть, конечно. Ищите "AVR Freememory".
хорошо, если использую буфер, считываю одну строку, я могу её прицепить к одному указателю, затем считать в буфер новую строку и прицепить к другому, не получится, что мои 2 указателя будут привязаны к одной и той же области памяти?
или для буфера всегда применять оператор new? чтобы он всегда размечал память по новым адресам
Селяви. Одновременно вы не сможете уместить в RAM больше данных, чем её объем. По-очерёдно - пожалуйста. А когда работаешь с МК, то приходится раскорячиваться как та пресловутая корова из фильма "ДМБ".
хорошо, если использую буфер, считываю одну строку, я могу её прицепить к одному указателю, затем считать в буфер новую строку и прицепить к другому, не получится, что мои 2 указателя будут привязаны к одной и той же области памяти?
Блин, вот же непонятливый :) "прицпив строку к указателю", вы потратите ровно столько памяти, сколько она занимала до записи в ЕЕПРОМ :)
Иначе никак, строки должны где-то лежать. Если у вас все строки "нужны и используются" - они будут занимать ровно столько памяти. сколько их размер в сумме. И не байтом меньше.
повторяю, чудес не бывает. вашим способом расход памяти не уменьшить ни на байт, поймите
Если я правильно понимаю, динамические типы данных хранятся в ОЗУ, а статические и объявленные в глобальных переменных во флэш?
Нет, они будут перегружены в RAM, так как подразумевается, что значение переменной может быть изменено.
"прицпив строку к указателю", вы потратите ровно столько памяти, сколько она занимала до записи в ЕЕПРОМ :)
т.е. вы хотите сказать, что str = {'t', 'e', 'x', 't'} и str = "text" - в памяти располагаются как-то по-разному?
Если я правильно понимаю, динамические типы данных хранятся в ОЗУ, а статические и объявленные в глобальных переменных во флэш?
Нет в МК все данные хранятся в ОЗУ, в том числе и статические и глобальные. Во флеш хранятся только константы - и то только в том случае, если программист явно обьявил. что эти данные должны помещаться там, например ключевым словом PROGMEM.
И надо понимать, что данные во флеш не могут быть перезаписаны из программы), так что если ваши данные в ходе программы должны менятся - их можно поместить только в ОЗУ
т.е. вы хотите сказать, что str = {'t', 'e', 'x', 't'} и str = "text" - в памяти располагаются как-то по-разному?
это почему?
Уточню: PROGMEM препятствует перегрузке значения в RAM. Изначально-то все задекларированное находится в Programm space, т.е. флеше.
А оптимизатор тривиальные типы констант вообще может в RAM не переносить, а заюзать как дефайн. Во всяком случае - замена дефайна на const переменную оверхеда мне не даёт, а вот типы путать не позволяет.
Нет в МК все данные хранятся в ОЗУ, в том числе и статические и глобальные. Во флеш хранятся только константы - и то только в том случае, если программист явно обьявил. что эти данные должны помещаться там, например ключевым словом PROGMEM.
И надо понимать, что данные во флеш не могут быть перезаписаны из программы), так что если ваши данные в ходе программы должны менятся - их можно поместить только в ОЗУ
Я прошу прощения, за свою дотошность, пишу в основном прикладные программы на C#, там никогда не приходилось применять указатели и тому подобные вещи, поэтому некоторые моменты мне не очень ясны. Да и с контроллерами и их архитектурами, периферией я, таки всё же, на вы.
т.е. вы хотите сказать, что str = {'t', 'e', 'x', 't'} и str = "text" - в памяти располагаются как-то по-разному?
это почему?
Ну так для динамического массива str1 = new char[4] выделяется память 4 байта, так же как и для str = "text" тоже 4 байта, только в первом случае мы каждому элементу присваиваем значение, а во втором они присвоены при объявлении - но по факту это внешне одинаковые массивы. По логике и записываются в eeprom одинаково по 4 байта на каждый массив, но почему-то в первом случае при считывании прога уже не знает, сколько байт записали, а во втором знает, т.к. считывает конкретно эти 4 байта слова "text" без проблем. Или я опять не догоняю?:)
По логике в первом случае вы передаёте указатель на область памяти, а во втором - массив известной длины (хотя я уж засомневался).
С телефона неудобно смотреть что там генерирует темплейт eeprom.put(), но у меня есть подозрения, что при передаче указателя очень сложно определить сколько именно байт необходимо запулить в eeprom. ASCIIZ строка не хранит свою длину атрибутом.
Этот простой вопрос требует лазанья по кишкам Wiring, а нам лень ))
Но вот, тем не менее. Если передать туда указатель (т.е. переменную, хранящую адрес), то запись начнется с адреса указателя. Т.е. с "адреса адреса". Думаю, что там, на длину 2 байта, хранятся нечеловекочитаемые данные.
Ну так для динамического массива str1 = new char[4] выделяется память 4 байта, так же как и для str = "text" тоже 4 байта
сразу ошибка. Для строчки str = "text" выделяется 5 байт. потому что такой способ инициализации записывает в память строку с нуль-терминатором, в то время как при создании массива str1 = new char[4] о терминаторе вы должны позаботится сами.
Если вы этого не делаете - не записываете в конце строки нулевой байт - это может обьяснять. почему у вас программа работает с этими строками по разному.
сразу ошибка. Для строчки str = "text" выделяется 5 байт. потому что такой способ инициализации записывает в память строку с нуль-терминатором, в то время как при создании массива str1 = new char[4] о терминаторе вы должны позаботится сами.
Если вы этого не делаете - не записываете в конце строки нулевой байт - это может обьяснять. почему у вас программа работает с этими строками по разному.
А вот это как раз-таки, видимо, решение моей загадки!! Я об этом даже не слышал...
Спасибо, что быстро не сдались и не ушли с обсуждения темы!:)
Это не является непосредственным решением данной проблемы, это ремарка.
maks.dav Вам же sadman41 написал, что
EEPROM.put(address1, str1);
Запишет в EEPROM не строку, а ее адрес !!!
b707, если я правильно понял, если у меня информативность строки 4 байта, я должен объявить массив на 5 байт и в 5-й записать тупо \0?
Видимо надо на индусском ???
maks.dav Вам же sadman41 написал, что
EEPROM.put(address1, str1);
Запишет в EEPROM не строку, а ее адрес !!!
а чтобы записать строку, что нужно сделать??
На МК поднимать "кучу" с целью экономить память - КАЛАМБУР !
Видимо надо на индусском ???
??? не понял, к чему это?
На МК поднимать "кучу" с целью экономить память - КАЛАМБУР !
да, я как бы понял этот момент ещё в начале темы, поэтому больше его не поднимал
меня заинтересовали другие нюансы: с особенностью записи массивов строк в eeprom
Это к тоvу что "0" в конце выдал бы a b c и всякую хрень, если бы это писалось в EEPROM.
Что бы записать значение надо разименовать указатель на это значение. В C# разве не так же ???
b707, если я правильно понял, если у меня информативность строки 4 байта, я должен объявить массив на 5 байт и в 5-й записать тупо \0?
это зависит от того, как вы с этой строкой собираетесь работать. Но если строчки у вас переменной длины - то варианта только два, либо хранить длину строки в отдельной переменной, либо ставить терминатор в конце.
Нужно отметить два момента:
1. Садман прав, EEPROM.put() в данном случае. похоже, не подходит. Лучше напишите процедуру сохранения строки сами, там и кода-то будет строчки три.
2. И еще раз повторю - даже если вы научитесь правильно записывать строки в ЕЕПРОМ и потом читать оттуда - вашу основную задачу это не решит, память в так не съэкономите.
Это к тоvу что "0" в конце выдал бы a b c и всякую хрень, если бы это писалось в EEPROM.
Ну так этого момента я не знал. Поэтому сижу тут и уточняю эти все нюансы..
Что вы предлагаете? конкретной длины массив писать и читать? ну это вроде и так понятно, что
sandman41, предположил, что непонятный момент связан с отсуствием "\0" в качестве последнего байта строки. и то, что в готовой строке "text" он присутствует - я склонен ему верить, - это всё к вопросу: почему в первом случае мы отлично можем читать массив таким способом, а в другом у нас возникает проблема. И уже завтра проверю, сегодня уже поздновато, да мозгу нужно отдохнуть и переварить информацию..
Нюанс тут в том, что put() - темплейт, формирующий функцию записи. И без мозголомства он может записать тип данных известного на этапе компиляции размера.
Например, put() для long сработает, потому что размер long известен и sizeof() вернёт размерность 4.
put() для char name[]="john" тоже сработает, так как размер известен и sizeof() вернёт 5.
С указателями же put() будет работать предсказуемо херово, если только не вмешается оптимизатор и не сделает предрасчет длины строки (или ещё не отмочит чтонить).
Кстати, get() с параметром типа "указатель" тоже будет работать как бог на душу положит.
b707, если я правильно понял, если у меня информативность строки 4 байта, я должен объявить массив на 5 байт и в 5-й записать тупо \0?
это зависит от того, как вы с этой строкой собираетесь работать. Но если строчки у вас переменной длины - то варианта только два, либо хранить длину строки в отдельной переменной, либо ставить терминатор в конце.
Нужно отметить два момента:
1. Садман прав, EEPROM.put() в данном случае. похоже, не подходит. Лучше напишите процедуру сохранения строки сами, там и кода-то будет строчки три.
2. И еще раз повторю - даже если вы научитесь правильно записывать строки в ЕЕПРОМ и потом читать оттуда - вашу основную задачу это не решит, память в так не съэкономите.
Спасибо за помощь, по поводу 2го варианта я ещё выше понял, что нужно выбрать другой подход к этой проблеме
В #26 даже исходник функции Put вам нашли !!!
Нюанс тут в том, что put() - темплейт, формирующий функцию записи. И без мозголомства он может записать тип данных известного на этапе компиляции размера.
Например, put() для long сработает, потому что размер long известен и sizeof() вернёт размерность 4.
put() для char name[]="john" тоже сработает, так как размер известен и sizeof() вернёт 5.
С указателями же put() будет работать непредсказуемо, если только не вмешается оптимизатор и не сделает предрасчет длины строки (или ещё не отмочит чтонить).
Кстати, get() с параметром типа "указатель" тоже будет работать как бог на душу положит.
Большое спасибо за пояснение, по крайней мере нарисовалась уже общая картина, с чем мне нужно работать!:)
Надо глянуть в листинг - что там компилятор накропал ...
В #26 даже исходник функции Put вам нашли !!!
за что ему отдельная благодарность:))
Чутка поправил функцию put:
И запустил код ТС ...
В обоих случаях (str и str1) в EEPROM записался бы адрес переменной, а не ее значение (что в принципе и не удивительно, раз передаём указатель - указатель и запишется ... если разименовать, то записывается первый символ)!!!
А ты сомневалса?
Меня смутило что str якобы читался верно...
Меня смутило что str якобы читался верно...
Komandir, он не якобы читался, он нормально читался, но спасибо за проделанный эксперимент и результат, видимо действительно это какие-то неясные совпадения..
Komandir, у меня возник вопрос: если str инициализировать как константу (const), она будет храниться в флэше? или тоже в озу?
И ещё: при использовании оператора delete [] str1 (например), после этого момента, когда выводишь переменную - затирается только первая половина символов, вторая половина остаётся - он так и должен работать?