Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Запись в EEPROM
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
GoodNews
Подскажите пожалуйста, что я делаю неправильно? Имеется структура, определенная как массив из 51 элемента для хранения в eeprom. Пытаюсь записать данные в один из элементов массива, однако в итоге получаю кусок flash'a в eeprom. Почему?
CODE

typedef struct _chInMem {
uint32_t returnValue;
} chInMem;
chInMem channelMem[] EEMEM = {
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{0}, {0}, {0}, {0}, {0},
{107900000}
};
...
chInMem channelInMem;
eeprom_busy_wait();
channelInMem.returnValue = channel_freq;
eeprom_write_block(&channelInMem, &channelMem, sizeof(channelMem));
...
Сергей Борщ
Цитата(GoodNews @ Mar 1 2010, 22:19) *
Почему?
Код
eeprom_write_block(&channelInMem, &channelMem, sizeof(channelMem));
...
Потому что channelMem у вас - массив структур. И вы в этот массив записываете кусок памяти размером с весь массив. Т.е. кроме одной структуры channelInMem вы записываете еще и идущий следом мусор в остальные ячейки. А что вы хотели сделать на самом деле? Выглядит несколько странно - имеете массив, а при записи никак не упоминаете номер желаемого элемента массива.
GoodNews
Цитата(Сергей Борщ @ Mar 2 2010, 10:33) *
Потому что channelMem у вас - массив структур. И вы в этот массив записываете кусок памяти размером с весь массив. Т.е. кроме одной структуры channelInMem вы записываете еще и идущий следом мусор в остальные ячейки. А что вы хотели сделать на самом деле? Выглядит несколько странно - имеете массив, а при записи никак не упоминаете номер желаемого элемента массива.

Я со структурами ещё пока знакомлюсь и не умею тольком ими оперировать, хотя основную идею понимаю. Собственно и с памятью тоже только пару раз работал и то, чисто в исследовательских целях. Я хотел сделать так, чтобы можно было записать в элемент массива с индексом, который я передаю функции в качестве параметра, числовое значение. Просто не могу понять как правильно это реализовать.
Сергей Борщ
Цитата(GoodNews @ Mar 2 2010, 10:53) *
Просто не могу понять как правильно это реализовать.
Код
eeprom_write_block(&channelInMem, &channelMem[index], sizeof(chInMem));
Yaumen
Задача несколько туманная, тем более что начинаете разбираться с массивом вложенным в другой массив.
Если вы хотели в EEPROM переписывать содержимое Вашего массива, то создайте одномерный массив и пробуйте разбираться сначала с ним.

А еще, при передаче в функцию указателя на начало массива, указывают либо:

channelInMem

либо

&channelInMem[0]


но никак не:
&channelInMem
Сергей Борщ
Цитата(Yaumen @ Mar 2 2010, 14:22) *
Задача несколько туманная, тем более что начинаете разбираться с массивом вложенным в другой массив.
Если вы хотели в EEPROM переписывать содержимое Вашего массива, то создайте одномерный массив и пробуйте разбираться сначала с ним.
Вы что-то путаете - у автора массив структур.
Цитата(Yaumen @ Mar 2 2010, 14:22) *
А еще, при передаче в функцию указателя на начало массива, указывают либо:

channelInMem
либо
&channelInMem[0]

но никак не:
&channelInMem
При передаче указателя на элемент массива - да, но в данном случае передается указатель на структуру.
GoodNews
Цитата(Сергей Борщ @ Mar 2 2010, 13:12) *
Код
eeprom_write_block(&channelInMem, &channelMem[index], sizeof(chInMem));

Спасибо большое! В таком виде получилось. Сделал дамп eeprom'а через avrdude - вижу, что данные попали в память. Единственное что странно, так это их расположение (это я думаю к коду не относится) - если в памяти была размещена только одна структура, то она занимала 200 байт, данные распологались подряд. Когда я разместил ещё отдельную от структуры переменную в eeprom - она попал в середину (как бы это правильно выразится - идут часть ячеек структуры, потом в середине эта переменная, потом - продолжение структуры). На функционале скорее всего никак не отразится, просто интересно почему именно так.
Сергей Борщ
Цитата(GoodNews @ Mar 2 2010, 23:37) *
Когда я разместил ещё отдельную от структуры переменную в eeprom - она попал в середину (как бы это правильно выразится - идут часть ячеек структуры, потом в середине эта переменная, потом - продолжение структуры). На функционале скорее всего никак не отразится, просто интересно почему именно так.
Это абсолютно ненормально и на функционале отразится самым непосредственным образом. Массив - непрерывная последовательность элементов. Показывайте минимальный пример, позволяющий воспроизвести такое странное поведение компилятора, выкладывайте .map-файл.
GoodNews
Я посмотрел вывод. Там как ни странно всё нормально. Т.е. в .eep вначале идёт большая структура, а потом моя переменная. То, что попало в eeprom таким образом очень странно. Тем не менее - во вложении всё необходимое. Компилятор - AVR-GCC.
Сергей Борщ
Полагаю, ошибка здесь:
Код
                    NEXT_FREE_CHANNEL = channel_number;
                    eeprom_write_block(&NEXT_FREE_CHANNEL, &channel_number, sizeof(uint8_t));
В первой строке вы пишете channel number в ОЗУ по тому же адресу, по которому в eeprom расположен NEXT_FREE_CHANNEL. Куда он попадает и что при этом рушит - не берусь судить. Больше криминала на первый взгляд не видно.
Несколько замечайнний:
Код
static uint8_t NEXT_FREE_CHANNEL EEMEM = 1; //Тут будет храниться номер следующего свободного канала
Принято заглавными буквами обозначать макроопределения и константные литералы. У вас же NEXT_FREE_CHANNEL - переменная.
Код
const uint8_t USER_CHANNELS_AVAILABLE = 50; //Всего доступно ячеек
Эта константа в общем случае будет занимать память - компилятор предполагает, что к этой константе могут быть обращения из других единиц компиляции. Поскольку она не нужна вам в других единицах компиляции - объявите ее как static const uint8_t. Это позволит компилятору подставить в код сразу ее значение. Ваша запись была бы корректной в C++. Поведение C++ в этом моменте отличается, там константы по умолчанию имеют локальное связывание. В C++ для получения доступа к константе из других файлов ее надо объявлять extern const uint8_t USER_CHANNELS_AVAILABLE = 50. Но у вас файлы имеют расширение .c, а значит, компилируются в режиме C. Добавьте static, код может стать оптимальнее.
Код
eeprom_busy_wait();
можно не вызывать - такая проверка выполняется внутри функций чтения-записи.
Код
                   eeprom_write_block(&channelInMem, &channelMem[channel_number - 1], sizeof(chInMem));
                    write_channel_eeprom(channel_number, 0, 1);
                    break;
            case 1: //Пишем номер следующего доступного канала
Здесь можно убрать
Код
                    write_channel_eeprom(channel_number, 0, 1);
                    break;
Выполнится то же самое, но без рекурсии.

Для совсем красивости можно массив ваших каналов и номер свободного канала объединить в структуру.
GoodNews
Большое спасибо за помощь! Я поправки внёс согласно вашим замечаниям. По поводу значения следующей свободной ячейки. Я так понимаю, что я попутал src с dst. Т.е. вначале указываем откуда извлекаем данные, а затем - куда их пишем. Получается, что вначале данные находятся в RAM по адресу channel_number, а помещены должны быть в адрес, где находится next_free_channel, я так понимаю?

Собственно по поводу С++ я думал что он не применим относительно 8-битных контроллеров (хотя бы в данном случае с AVR-ками), поэтому и начал изучение платформы с применением именно C. По поводу области видимости - я в принципе стараюсь все переменные использовать локально. extern использую только когда действительно необходимо вызвать функцию (использовать переменную) из другой подпрограммы (ну или объявить её вобще в хедере).
Сергей Борщ
Цитата(GoodNews @ Mar 4 2010, 09:32) *
По поводу значения следующей свободной ячейки. Я так понимаю, что я попутал src с dst. Т.е. вначале указываем откуда извлекаем данные, а затем - куда их пишем. Получается, что вначале данные находятся в RAM по адресу channel_number, а помещены должны быть в адрес, где находится next_free_channel, я так понимаю?
Нет. Тут дело в разных адресных пространствах. У AVR 4 адресных пространства: I/O, ОЗУ, флеш и EEPROM. В каждом из них адреса начинаются с 0. И для доступа в каждое используются разные приемы(команды). gcc, в отличие от других компиляторов для AVR, не имеет поддержки нескольких адресных пространств. Поэтому авторы avr-gcc поместили все адресные пространства avr в разные места одного линейного адресного пространства gcc. Например, нулевой физический адрес ОЗУ попадает в логический адрес 0x00800000, нулевой физический адрес EEPROM попадает в логический адрес 0x00810000. В процессе линковки 2 старших байта логического адреса отбрасываются. Но сам компилятор не имеет возможности различать эти адресные пространства, он считает все переменные размещенными в ОЗУ. Поэтому для доступа к другим адресным пространствам пришлось создать макросы eeprom_read_???(), eeprom_write_????(), pgm_read_???().
Вот и смотрите - ваша переменная NEXT_FREE_CHANNEL объявлена с атрибутом EEMEM. значит попадает в адрес, скажем 0x008100C8. Вы обратились к ней без макроса, т.е. NEXT_FREE_CHANNEL = channel_number эквивалентно *(uint32_t *)0x008100C8 = channel_number. Это будет скомпилировано в команду ST (STD) - компилятор считает все переменные принадлежащими ОЗУ. После линковки старшие байты отбросятся и получится, что ваша строка эквивалентна *(uint32_t *)0x00C8 = channel_number. Итого, вместо желаемой записи в eeprom произошла запись в ОЗУ. И отследить эту ошибку средствами компилятора, увы, невозможно. Ждем появления 5.x версии gcc, в котрой заявлена поддержка разных адресных пространств.
Цитата(GoodNews @ Mar 4 2010, 09:32) *
Собственно по поводу С++ я думал что он не применим относительно 8-битных контроллеров (хотя бы в данном случае с AVR-ками)
Если без фанатизма - то вполне применим. Многие пользуются, не жалуются. Жалуются те, кто не пользовался или не умеет.
Цитата(GoodNews @ Mar 4 2010, 09:32) *
По поводу области видимости - я в принципе стараюсь все переменные использовать локально. extern использую только когда действительно необходимо вызвать функцию (использовать переменную) из другой подпрограммы (ну или объявить её вобще в хедере).
Да, но сдесь не об области видимости, а о связывании. Попробуйте в другом файле объявить еще одну константу uint8_t USER_CHANNELS_AVAILABLE = 50 - в С получите по копии переменной (пусть даже константной) в каждом файле и ошибку линковки. В С++ будет все нормально.
GoodNews
Я так понимаю, что next_free_channel = channel_number; вобще лишняя операция, так как присвоение произойдёт при выполнении макроса eeprom_write_block?
Я изначально хотел включить данную переменную в структуру, но немного не понял что в итоге получится, так как структура фактически представляет собой массив полей.
Сергей Борщ
Цитата(GoodNews @ Mar 4 2010, 11:43) *
Я так понимаю, что next_free_channel = channel_number; вобще лишняя операция, так как присвоение произойдёт при выполнении макроса eeprom_write_block?
Да. она не только лишняя, она вредная, поскольку портит ОЗУ.
Цитата(GoodNews @ Mar 4 2010, 11:43) *
Я изначально хотел включить данную переменную в структуру, но немного не понял что в итоге получится, так как структура фактически представляет собой массив полей.
Массив тоже может быть членом структуры.
Код
typedef struct
{
    uint32_t Channel[50];
    uint8_t  Next_free_channel;
} settings_t;
GoodNews
Цитата(Сергей Борщ @ Mar 4 2010, 12:52) *
Да. она не только лишняя, она вредная, поскольку портит ОЗУ.
Массив тоже может быть членом структуры.
Код
typedef struct
{
     uint32_t Channel[50];
     uint8_t  Next_free_channel;
} settings_t;

Теперь понятно. Там в итоге ещё одна строка лишняя оказалась. А вот по поводу массива в структуре - потом просто взять определить массив отдельно как и в моём случае, только работать уже с полем? Т.е settings_t chan.Channel[] EEMEM = {...};
и соответственно settings_t nchan.Next_free_channel = {...};
Кстати, *_t разве не зарезервировано под формат данных?
Сергей Борщ
Цитата(GoodNews @ Mar 4 2010, 13:20) *
А вот по поводу массива в структуре - потом просто взять определить массив отдельно как и в моём случае, только работать уже с полем? Т.е settings_t chan.Channel[] EEMEM = {...};
и соответственно settings_t nchan.Next_free_channel = {...};
Нет, определяете сразу всю структуру:
Код
settings_t EEMEM chan =
{   // структура
    {  // массив
        0,  // Channel[0]
        0,  // Channel[1]
        0,  // Channel[2]
        ...
        0,  // Channel[49]
    },
    
    12345;  // Free channel
};

Кстати, если можно не указывать явно последние нулевые элементы массива:
Код
settings_t EEMEM chan =
{   // структура
    {  // массив
        123,  // Channel[0]
        // все остальные элементы - нули
    },
    12345;  // Free channel
};

Цитата(GoodNews @ Mar 4 2010, 13:20) *
Кстати, *_t разве не зарезервировано под формат данных?
Нет. Просто один из многих стилей кодирования предполагает, что на _t оканчиваются названия новых (невстроенных) типов.
GoodNews
В общем что-то получилось (всмысле компилятор пока не ругался на ошибки). Попробую вечером залить прошивку - посмотрю что творится в памяти. Спасибо за терпение!

P.S. - а где посмотреть по поводу новой версии avr-gcc?
Сергей Борщ
Цитата(GoodNews @ Mar 4 2010, 15:09) *
P.S. - а где посмотреть по поводу новой версии avr-gcc?
Про avr-gcc - нигде. Про 5 версию самого gcc где-то то ли на avrfreaks, то ли в списке рассылки avr-libc пробегала ссылка на обрывки переписки разработчиков, если память не изменяет. Сейчас и не найду уже.
Сергей Борщ
Цитата(Сергей Борщ @ Mar 4 2010, 16:37) *
Про 5 версию самого gcc где-то то ли на avrfreaks, то ли в списке рассылки avr-libc пробегала ссылка на обрывки переписки разработчиков, если память не изменяет. Сейчас и не найду уже.
Случайно наткнулся. Оказывается, уже можно добавлять поддержку разных адресных пространств в gcc: 17.30 Adding support for named address spaces
Остается надеяться, что скоро pgm_read_byte и подобное останется только для совместимости со старыми исходниками.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.