Jump to content

    

Прошивка Flash и EEPROM одним файлом hex или elf

Здравствуйте!

У меня такая вот ситуация. Подключаю библиотеку eeprom.h. Инициализирую переменную в eeprom памяти строчкой uint8_t var EEMEM = 15; . После сборки создается файл с расширением eep который заливается в МК отдельно. Интересен такой вопрос можно ли прошить flash и eeprom память avr используя только один файл hex или elf? Как это сделать: что добавить, что убрать? От библиотеки eeprom.h не хотелось бы отказываться (хорошая библиотека, удобная)

Edited by Twin_by

Share this post


Link to post
Share on other sites

Неправильно вы вопрос ставите.

Всё зависит от того, какой утилитой вы пользуетесь для программирования (прошивки) микроконтроллера.

Смотрите доку на утилиту в части поддерживаемых ей форматов входных файлов и делайте выводы...

Одно могу сказать точно, что форматы с одним линейным адресным пространством

вряд ли могут быть использованы для программирования сразу двух независимых типов памяти AVR ввиду их наложения.

Share this post


Link to post
Share on other sites
Неправильно вы вопрос ставите.

Всё зависит от того, какой утилитой вы пользуетесь для программирования (прошивки) микроконтроллера.

Смотрите доку на утилиту в части поддерживаемых ей форматов входных файлов и делайте выводы...

Одно могу сказать точно, что форматы с одним линейным адресным пространством

вряд ли могут быть использованы для программирования сразу двух независимых типов памяти AVR ввиду их наложения.

 

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

Share this post


Link to post
Share on other sites

1. На производстве используют программатор ASISP и ПО к нему. В нем можно создать проект в котором указывается файл для FLASH, файл для EEPROM, настройки FUSE/LOCK бит, проверку всего после прошивки и кое что еще по мелочи (типа запись счетчика в EEPROM - серийный номер платы). Этот проект настраивается один раз и сохраняется. Далее наладчику/настройщику остается нажать кнопку "Автопрограммирование" один раз для каждой платы.

 

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

 

P.S. ASISP можно подключить в ввиде тулза к Atmel Studio и для программирования надо будет нажимать одну кнопку из Atmel Studio.

Edited by alexeyv

Share this post


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

 

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

 

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

Edited by Twin_by

Share this post


Link to post
Share on other sites

Тут наверное имеется в виду что-то типа

 

//_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 и т.п. но при этом иметь структурированные данные для нормальной читаемости программы.

 

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мс

Edited by IgorKossak
[codebox] для длинного кода, [code] - для короткого!

Share this post


Link to post
Share on other sites

Выбираешь один из неиспользуемых адресов в 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);

Edited by alexeyv

Share this post


Link to post
Share on other sites
Например:

#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 (для автоматического присвоения адреса) не получится

Share this post


Link to post
Share on other sites
Если я правильно понял ваш код, то вы в нем сами присваиваете адрес ячейки в 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 МК входил в ступор (его победили, но привычка осталась)

Share this post


Link to post
Share on other sites
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.

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

Share this post


Link to post
Share on other sites
Задаёте значения своим переменным:

EEMEM eeprom_data_t eeprom =
{
    .var1 = 33U,
    .var2 = 333.0f
}

 

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

 

А в какой момент и как это происходит?

Share this post


Link to post
Share on other sites
А в какой момент и как это происходит?
После сборки проекта вы получаете секцию с ЕЕПРОМ переменными в elf файле, после чего имеется возможность экспортировать эту секцию в eeprom-hex файл, а данные секции .text и иже с ним в flash-hex файл.

Потом программатором зашиваете эти оба файла в микроконтроллер.

 

Share this post


Link to post
Share on other sites
После сборки проекта вы получаете секцию с ЕЕПРОМ переменными в elf файле, после чего имеется возможность экспортировать эту секцию в eeprom-hex файл, а данные секции .text и иже с ним в flash-hex файл.

Потом программатором зашиваете эти оба файла в микроконтроллер.

 

Т.е все равно нужно использовать два файла для прошивки eeprom и flash?

Edited by Twin_by

Share this post


Link to post
Share on other sites

1) Все данные ЕЕPROМ в структуру, это святое и не обсуждаемо.

2) Такм-же в структуре сигнатура размером в несколько байт.

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

 

Все. И никаких инициализаций EEPROM при программировании по причине нахренненужности.

Share this post


Link to post
Share on other sites
Все. И никаких инициализаций EEPROM при программировании по причине нахренненужности.

Аналогично всегда делаю. Можно, конечно, придумать случай когда нет места для кода инициализации EEPROM, но мне такого не встречалось. И если мало переменных в EEPROM, то предпочитаю вместо сигнатуры сами данные на корректность проверять.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this