Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Прошивка Flash и EEPROM одним файлом hex или elf
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Twin_by
Здравствуйте!
У меня такая вот ситуация. Подключаю библиотеку eeprom.h. Инициализирую переменную в eeprom памяти строчкой uint8_t var EEMEM = 15; . После сборки создается файл с расширением eep который заливается в МК отдельно. Интересен такой вопрос можно ли прошить flash и eeprom память avr используя только один файл hex или elf? Как это сделать: что добавить, что убрать? От библиотеки eeprom.h не хотелось бы отказываться (хорошая библиотека, удобная)
demiurg_spb
Цитата(Twin_by @ May 25 2015, 08:26) *

Неправильно вы вопрос ставите.
Всё зависит от того, какой утилитой вы пользуетесь для программирования (прошивки) микроконтроллера.
Смотрите доку на утилиту в части поддерживаемых ей форматов входных файлов и делайте выводы...
Одно могу сказать точно, что форматы с одним линейным адресным пространством
вряд ли могут быть использованы для программирования сразу двух независимых типов памяти AVR ввиду их наложения.
Twin_by
Цитата(demiurg_spb @ May 25 2015, 16:54) *
Неправильно вы вопрос ставите.
Всё зависит от того, какой утилитой вы пользуетесь для программирования (прошивки) микроконтроллера.
Смотрите доку на утилиту в части поддерживаемых ей форматов входных файлов и делайте выводы...
Одно могу сказать точно, что форматы с одним линейным адресным пространством
вряд ли могут быть использованы для программирования сразу двух независимых типов памяти AVR ввиду их наложения.


прошиваю я через Atmel Studio 6.2. В закладке Memories (Menu -> Tools -> Device Programming) указываю путь для elf файла и отдельно путь для eep файла. Встала задача для прошивки МК использовать только один файл (hex или elf). На форуме я видел что то похожее http://electronix.ru/forum/index.php?showt...73&hl=EEMEM . Там нужно что делать через линкеры, объектные файлы и тд. Но т.к у меня опыта еще маловато я не совсем понимаю о чем конкретно идет речь
alexeyv
1. На производстве используют программатор ASISP и ПО к нему. В нем можно создать проект в котором указывается файл для FLASH, файл для EEPROM, настройки FUSE/LOCK бит, проверку всего после прошивки и кое что еще по мелочи (типа запись счетчика в EEPROM - серийный номер платы). Этот проект настраивается один раз и сохраняется. Далее наладчику/настройщику остается нажать кнопку "Автопрограммирование" один раз для каждой платы.

2. По поводу работы с EEPROM. В начале программы определяется чиста ли EEPROM. Если чиста - то вызывается функция начальной инициализации. Далее - обычная работа программы. Таким образом не надо создавать отдельного файла для прошивки EEPROM - код программы сам ее инициализирует при первом запуске программы.

P.S. ASISP можно подключить в ввиде тулза к Atmel Studio и для программирования надо будет нажимать одну кнопку из Atmel Studio.
Twin_by
Цитата(alexeyv @ May 26 2015, 06:51) *
2. По поводу работы с EEPROM. В начале программы определяется чиста ли EEPROM. Если чиста - то вызывается функция начальной инициализации. Далее - обычная работа программы. Таким образом не надо создавать отдельного файла для прошивки EEPROM - код программы сам ее инициализирует при первом запуске программы.


Т.е вы предлагаете после начала функции main проверять пуста ли EEPROM. Если Да, то инициализировать переменную uint8_t var EEMEM = 15; Правильно я понимаю???

Вот сейчас попробовал инициализировать переменную после начала функции main. И как то безрезультатно, не дает мне этого сделать. Позволяет инициализировать только перед функцией main где инициализируются глобальные переменные.
qwerty1023
Тут наверное имеется в виду что-то типа

Код
//_e_data_ok магическая константа
#define DATA_OK 0x5A

volatile __no_init __eeprom uchar EEMEM;
volatile __no_init __eeprom uchar _e_data_ok;


void main(void)
{


if (_e_data_ok != DATA_OK)
{
  EEMEM = 15;
  _e_data_ok = DATA_OK;
}


while(1)
{
....
}

}


А вообще, если это ответсвенная переменная, не помешало бы "накрыть" это все какойнить crc и уже по целостности crc судить о валидности данных в EEPROM.

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

CODE
struct sEEPROMSet
{
...
uchar msgAllEnd2DurationSec; // длительность "пииик" x100мс
uint devSetFlags;
//uint crc16; - не участвует в стркуктуре для экономии ОЗУ но есть во флеш
};


union
{
uchar byte[sizeof(sEEPROMSet)];
sEEPROMSet;
} block;


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++

uint crc16update(uchar inData, uint inCrc)
{
uchar i;

// считаем crc
inCrc ^= inData;

for (i = 0; i < 8; i++)
{
if (inCrc & 1)
{
inCrc >>= 1;
inCrc ^= 0x1021;
}
else
{
inCrc >>= 1;
}
}
return inCrc;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++

bool GetMemBlock(ulong start, uint dataSize, uchar * data)
{
uchar tmpData;
uint s, memCrc, crc = 0xFFFF;

//Led2On();

// записываем комманду непрерывного чтения
memStart();
SPIRW(0x03); // непрерывное чтение
memSetAdr(start);

for(s = 0; s < dataSize; s++)
{
// читаем байт из флешки
tmpData = SPIRW(0);
// записываем по указателю и увеличиваем указатель
data[s] = tmpData;
// считаем crc
crc = crc16update(tmpData, crc);
}

// читаем значение CRC16
memCrc = SPIRW(0);
memCrc |= (SPIRW(0) << 8);

memStop();

//Led2Off();

// проверяем
if (crc == memCrc)
{
return true;
}
else
{
return false;
}
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++

bool LoadSettings(uchar modeNum)
{
union
{
uchar byte[sizeof(sWorkModeSetBlock)];
sWorkModeSetBlock;
} block;

// читаем таблицу размещения настроек режимов
if (!GetMemBlock((ulong)WORK_MODE_TABLE_START_ADR, (uint)sizeof(sWorkModeSetBlock), block.byte))
{
// ошибка при чтении блока, аварийный выход
LCD_send(0x0E,'r',0xFF,0x00,0xFF,0xFF,0xFF,0xFF,0,0);
return false;
}

// блок считался без ошибок, выбираем настройки режима

// заполняем рабочую структуру настроек

//копируем тип режима
workData.soundMode = block.modeSet[modeNum].soundMode;

// копируем настройки обязательного звука 1
msgBeep1Adr = block.msgAllEnd1Adr; // адресс начала звука "пик-пик" во флешке
msgBeep1DurationSempl = block.msgAllEnd1DurationSempl; // длительность "пик-пик" в семплах
msgBeep1DurationSec = block.msgAllEnd1DurationSec; // длительность "пик-пик" x100мс
alexeyv
Выбираешь один из неиспользуемых адресов в EEPROM и сравниваешь его с 0xFF - если совпало, пишешь туда что-то не равное 0xFF и инициализируешь все остальные переменные, хранящиеся в EEPROM.
Например:
Код
#define EE_ADR_FLAG       ((void*)0x10)
unsigned char    val = eeprom_read_byte(EE_ADR_FLAG);
if(val == 0xFF)
{    // инициализируем другие переменные
       // ................
       eeprom_write_byte(EE_ADR1,DEF_ADR1);
       eeprom_write_byte(EE_ADR2,DEF_ADR2);
       // ................
       //  изменяем флажок - все  записано
       eeprom_write_byte(EE_ADR_FLAG,0xAA);
}

// дальше считываем из EEPROM в рабочие переменные в оперативной памяти
val1 = eeprom_read_byte(EE_ADR1);
val2 = eeprom_read_byte(EE_ADR2);
Twin_by
Цитата(alexeyv @ May 26 2015, 11:05) *
Например:
Код
#define EE_ADR_FLAG       ((void*)0x10)
unsigned char    val = eeprom_read_byte(EE_ADR_FLAG);
if(val == 0xFF)
{    // инициализируем другие переменные
       // ................
       eeprom_write_byte(EE_ADR1,DEF_ADR1);
       eeprom_write_byte(EE_ADR2,DEF_ADR2);
       // ................
       //  изменяем флажок - все  записано
       eeprom_write_byte(EE_ADR_FLAG,0xAA);
}

// дальше считываем из EEPROM в рабочие переменные в оперативной памяти
val1 = eeprom_read_byte(EE_ADR1);
val2 = eeprom_read_byte(EE_ADR2);


Если я правильно понял ваш код, то вы в нем сами присваиваете адрес ячейки в eeprom где будет хранится значение переменной? Т.е использовать директиву EEMEM (для автоматического присвоения адреса) не получится
alexeyv
Цитата
Если я правильно понял ваш код, то вы в нем сами присваиваете адрес ячейки в eeprom где будет хранится значение переменной? Т.е использовать директиву EEMEM (для автоматического присвоения адреса) не получится


Почему же, получится! можете использовать и EEMEM
Код
unsigned char eeprom_var1            EEMEM;
......
eeprom_write_byte(eeprom_var1,DEFAUT_VALUE);
.....
val1 = eeprom_read_byte(eeprom_var1);


Просто я уже привык определять адреса таким образом. При отладке знаешь где посмотреть переменную, не надо лазить по map-файлам. Да и раньше у Atmel'a был один косячок с EEPROM - при расположении переменных по каким-то адресам в EEPROM МК входил в ступор (его победили, но привычка осталась)
demiurg_spb
Цитата(alexeyv @ May 29 2015, 05:19) *
Код
val1 = eeprom_read_byte(eeprom_var1);
1. Во всех ваших примерах надо добавить &eeprom_var1...
2. Лет 10 как никаких проблем с EEPROM нет, да и те, что были, вами лично надуманы. Единственное, что было это:
http://www.nongnu.org/avr-libc/user-manual...prom_corruption

А привычка у вас плохая осталась - компилятор не проверяет типизацию при обращении к данным.
Возникает дополнительный шанс совершить ошибку.
3. Вообще с eeprom правильно работать следующим образом:
Через typedef определяете структуру, содержащую все данные из EEPROM:
Код
typedef struct
{
    uint8_t   __dummy; // don't use zero address
    uint8_t   var1;
    float       var2;
    ...
} eeprom_data_t;
Задаёте значения своим переменным:
Код
EEMEM eeprom_data_t eeprom =
{
    .var1 = 33U,
    .var2 = 333.0f
};
Работаете с ними:
Код
extern EEMEM eeprom_data_t eeprom;
uint8_t x = eeprom_read_byte(&eeprom.var1);
...
eeprom_update_byte(&eeprom.var1, x);

float f = eeprom_read_float(&eeprom.var2);
...
eeprom_update_float(&eeprom.var2, f);
В таком случае вы дополнительно обеспечиваете гарантию неизменности задуманной вами очерёдности данных в EEPROM,
независимо от версий и причуд компилятора (хорошо бы, конечно, использовать ещё и атрибут __packed, но для вводного курса этого и так достаточно).
Также, благодаря update вместо write вы упрощаете пользовательское приложение и подливаете срок службы EEPROM.
Урок окончен)))
qwerty1023
Цитата(demiurg_spb @ May 29 2015, 11:07) *
Задаёте значения своим переменным:
Код
EEMEM eeprom_data_t eeprom =
{
    .var1 = 33U,
    .var2 = 333.0f
}


Урок окончен)))


А в какой момент и как это происходит?
demiurg_spb
Цитата(qwerty1023 @ May 29 2015, 11:47) *
А в какой момент и как это происходит?
После сборки проекта вы получаете секцию с ЕЕПРОМ переменными в elf файле, после чего имеется возможность экспортировать эту секцию в eeprom-hex файл, а данные секции .text и иже с ним в flash-hex файл.
Потом программатором зашиваете эти оба файла в микроконтроллер.
Twin_by
Цитата(demiurg_spb @ May 29 2015, 11:52) *
После сборки проекта вы получаете секцию с ЕЕПРОМ переменными в elf файле, после чего имеется возможность экспортировать эту секцию в eeprom-hex файл, а данные секции .text и иже с ним в flash-hex файл.
Потом программатором зашиваете эти оба файла в микроконтроллер.


Т.е все равно нужно использовать два файла для прошивки eeprom и flash?
zltigo
1) Все данные ЕЕPROМ в структуру, это святое и не обсуждаемо.
2) Такм-же в структуре сигнатура размером в несколько байт.
3) При запуске проверка сигнатуры, если ее нет, то инициализация данных значениями по умолчанию и пофиг, это первый запуск или содержимое слетело. Сигнатуру можно менять и при изменениии версии софта требующей других начальных установок или расширения данных. Такой поход к делу позволяет так-же не заморчиваться содержимым EEPROМ и при внутрисхемном программировании.

Все. И никаких инициализаций EEPROM при программировании по причине нахренненужности.
VladislavS
Цитата(zltigo @ May 29 2015, 12:10) *
Все. И никаких инициализаций EEPROM при программировании по причине нахренненужности.

Аналогично всегда делаю. Можно, конечно, придумать случай когда нет места для кода инициализации EEPROM, но мне такого не встречалось. И если мало переменных в EEPROM, то предпочитаю вместо сигнатуры сами данные на корректность проверять.
alexeyv
to demiurg_spb
Цитата
1. Во всех ваших примерах надо добавить &eeprom_var1...

Не во всех, а только в последнем - там действительно да, очепятка, сори.

Цитата
2. Лет 10 как никаких проблем с EEPROM нет, да и те, что были, вами лично надуманы. Единственное, что было это:
http://www.nongnu.org/avr-libc/user-manual...prom_corruption

Я и не уточнял возраст этой проблемы. Да, это было давно. Но в свое время я много времени думал почему CPU иногда косячит.
у меня было такое: адрес в EEPROM какой-то из диапазона 0x06...0x0А (не помню), и если по этому адресу записать то ли 0x27, то ли 0x2А (не помню), CPU вставал в ступор. причем зависал только на одном значении.

За исключением создания структуры, моя логика работы с EEPROM не отличается от логики других форумчан. Я не понимаю зачем создавать структуру?

Кстати, у ТС был вопрос как избавиться от второго файла, а не как работать с EEPROM.
demiurg_spb
Цитата(alexeyv @ Jun 1 2015, 06:53) *
Но в свое время я много времени думал почему CPU иногда косячит.
у меня было такое: адрес в EEPROM какой-то из диапазона 0x06...0x0А (не помню), и если по этому адресу записать то ли 0x27, то ли 0x2А (не помню), CPU вставал в ступор. причем зависал только на одном значении.
Приведите пожалуйста ссылку на ERRATA, где говорится о вашей ситуации.
Удивительно, но я начинал использование AVR с at90s2233 и с тех пор ничего подобного не встречал.

Цитата
За исключением создания структуры, моя логика работы с EEPROM не отличается от логики других форумчан. Я не понимаю зачем создавать структуру?
Я уже об этом писал и объяснил зачем это нужно делать ВСЕГДА. Хотите я вам скину ссылку на avg-gcc 4.X.X, который вдруг стал располагать данные в EEPROM в обратном порядке (формально это не бага, т.к. стандарт ничего против такого поведения не имеет)?
Если же данные поместить в упакованную структуру, то уже ничто не в праве её изменить.
Конечно, если вы не предоставляете доступа к EEPROM данным наружу и не считаете КС содержимого EEPROM можно обойтись и без структуры, только зачем?
Ведь вы и так делаете префикс для имени переменной из EEPROM, так почему бы не использовать специально созданный для таких случаев механизм?
Ещё один большой плюс структуры - это очень лаконичное её объявление как EXTERN - всего одной строчкой.

Цитата
Кстати, у ТС был вопрос как избавиться от второго файла, а не как работать с EEPROM.
Не зная алфавита сложно начать составлять слова...
Twin_by
Цитата(demiurg_spb @ May 29 2015, 11:07) *
Задаёте значения своим переменным:
Код
EEMEM eeprom_data_t eeprom =
{
    .var1 = 33U,
    .var2 = 333.0f
};
Работаете с ними:

В этом случае будет создан файл *.eep. Я так подозреваю

Цитата(demiurg_spb @ Jun 1 2015, 10:06) *
Я уже об этом писал и объяснил зачем это нужно делать ВСЕГДА. Хотите я вам скину ссылку на avg-gcc 4.X.X, который вдруг стал располагать данные в EEPROM в обратном порядке (формально это не бага, т.к. стандарт ничего против такого поведения не имеет)?

Да есть, такое. Когда первый раз с этим столкнулся долго не мог понять что у меня где в eeprom

alexeyv
Цитата
Я уже об этом писал и объяснил зачем это нужно делать ВСЕГДА.

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


Цитата
В этом случае будет создан файл *.eep. Я так подозреваю

Так и есть

Цитата
Да есть, такое. Когда первый раз с этим столкнулся долго не мог понять что у меня где в eeprom

поэтому я объявляю адреса EEPROM так:
Код
#define EE_ADR_VAR1       ((void*)0x11)

Всегда знаешь где что лежит, независимо от предпочтений компилятора
Сергей Борщ
Цитата(alexeyv @ Jun 3 2015, 05:39) *
У меня в проекте шесть различных независимых модулей, у каждого от одного до десятка байт конфигурации. Зачем мне их объединять в одну структуру?
Чтобы после очередной смены версии компилятора или положения звезд на небе их расположение в памяти осталось неизменным. И обновление прошивки не приводило к необходимости повторной конфигурации.
Цитата(alexeyv @ Jun 3 2015, 05:39) *
поэтому я объявляю адреса EEPROM так:
Код
#define EE_ADR_VAR1       ((void*)0x11)

Всегда знаешь где что лежит, независимо от предпочтений компилятора
Взвалить на себя работу линкера? С ненулевой вероятностью положить одни данные поверх других? Нет уж, увольте.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.