Запрограммируйте AVR EEPROM непосредственно из источника C

11

Когда вы включаете следующий код в источник AVR C, вы, очевидно, можете напрямую программировать предохранители, без необходимости в дополнительной команде или файле .hex:

#include <avr/io.h>

FUSES = {
        .low =          LFUSE_DEFAULT ,
        .high =         HFUSE_DEFAULT ,
        .extended =     EFUSE_DEFAULT ,
};

Есть ли подобная хитрость для программных значений в EEPROM?

Я проверил, /usr/lib/avr/include/avr/fuse.hгде я могу найти некоторые комментарии о макросе, но я не могу найти подобный комментарий в нем, /usr/lib/avr/include/avr/eeprom.hи интерпретация материала препроцессора немного выходит за рамки моей лиги.

Было бы очень удобно, если бы я мог включить значения EEPROM по умолчанию в исходный код C. Кто-нибудь знает, как этого добиться?

edit1:

Этот трюк FUSES выполняется только во время ISP, а не во время RUN. Таким образом, в результирующем коде сборки в контроллере нет программируемых предохранителей. Вместо этого программист автоматически проходит дополнительный цикл программирования FUSES.

edit2:

Я использую набор инструментов avr-gcc и avrdude в Linux.

jippie
источник
Это только при программировании провайдера? Большинство загрузчиков не позволяют программировать предохранители, верно?
Angelatlarge
2
У меня нет времени, чтобы написать полный ответ на данный момент, но в качестве подсказки попробуйте поиск по директиве EEMEM. Кроме того, вам может понадобиться изменить настройку компоновщика, чтобы создать отдельный файл .EPP, который будет использовать программист.
PeterJ
@angelatlarge Только программирование провайдера. В этой настройке нет загрузчика.
Джиппи
2
Обратите внимание, что ответы на это полностью зависят от того, что инструментальная цепочка хочет записать в своем выводе, и что программист желает проанализировать. Большинство цепочек инструментов могут быть сконфигурированы для создания специального раздела (или размещения данных по фиктивному адресу), так что в конечном итоге все сводится к тому, что программист может либо извлечь его, либо получить возможность управлять собственным скриптом, который будет это делать.
Крис Страттон

Ответы:

7

С avr-gcc EEMEMмакрос может быть использован для определения переменной, смотрите libcдокументацию и пример здесь :

#include <avr/eeprom.h>
char myEepromString[] EEMEM = "Hello World!";

объявляет массив символов в разделе с именем «.eeprom», который после компиляции сообщает программисту, что эти данные должны быть запрограммированы в EEPROM. В зависимости от вашего программного обеспечения, вам может понадобиться явно указать имя файла «.eep», созданного в процессе сборки, или он может неявно найти его сам.

JimmyB
источник
Я использовал имя файла, немного отличающееся от «предполагаемого», но это команды, которые я включил для программирования EEPROM из командной строки (и makefile):
jippie
1
Создайте файл, содержащий данные Intel Hex, используемые программистом:avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 ihex $(src).elf $(src).eeprom.hex
jippie
1
Фактическое программирование выполняется:avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex
Джиппи
7

Да, вы можете вручную записать данные по умолчанию в EEPROM в исходном коде. Во-первых, ознакомьтесь с этим удивительным руководством по EEPROM с AVR: учебное пособие по AVR EEPROM Дина. Кроме того, я должен добавить, что лучше создать файл .eep, содержащий данные EEPROM, используя make-файл, который будет запрограммирован на устройстве вместе с исходным кодом. Однако, если вы не знакомы с различными операциями make-файла и компоновщика, это все равно можно сделать из файла исходного кода - это произойдет, как только цепь запитается, что остановит начальную операцию программы.

В начале программы (перед любым видом основного цикла) вы можете сделать что-то вроде этого:

#include <avr/eeprom.h>

#define ADDRESS_1 46  // This could be anything from 0 to the highest EEPROM address
#define ADDRESS_2 52  // This could be anything from 0 to the highest EEPROM address
#define ADDRESS_3 68  // This could be anything from 0 to the highest EEPROM address

uint8_t dataByte1 = 0x7F;  // Data for address 1
uint8_t dataByte2 = 0x33;  // Data for address 2
uint8_t dataByte3 = 0xCE;  // Data for address 3

eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
eeprom_update_byte((uint8_t*)ADDRESS_2, dataByte2);
eeprom_update_byte((uint8_t*)ADDRESS_3, dataByte3);

Функция «update» сначала проверяет, существует ли это значение, чтобы сэкономить на ненужных записях, сохраняя срок службы EEPROM. Тем не менее, выполнение этого для очень многих мест может занять довольно много времени. Может быть, лучше проверить одно место. Если это желаемое значение, то остальные обновления можно полностью пропустить. Например:

if(eeprom_read_byte((uint8_t*)SOME_LOCATION) != DESIRED_VALUE){
  eeprom_write_byte((uint8_t*)SOME_LOCATION, DESIRED_VALUE);
  eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
  eeprom_update_byte((uint8_t*)ADDRESS_2, dataByte2);
  eeprom_update_byte((uint8_t*)ADDRESS_3, dataByte3);
}

Если вы хотите обновить большие объемы данных, попробуйте использовать другие функции, такие как eeprom_update_block(...). И обязательно прочитайте этот урок; это хорошо написано.

Вы можете поместить все операторы обновления EEPROM в один условный оператор препроцессора. Это очень просто сделать:

#if defined _UPDATE_EEPROM_
  #define ADDRESS_1 46  // This could be anything from 0 to the highest EEPROM address
  uint8_t dataByte = 0x7F;  // Data for address 1
  eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
#endif // _UPDATE_EEPROM_

Этот фрагмент кода даже не будет скомпилирован, если вы не выполните следующее:

#define _UPDATE_EEPROM_

Вы можете оставить это как комментарий, а затем раскомментировать, если вам нужно изменить значения EEPROM по умолчанию. Для получения дополнительной информации о препроцессоре C, ознакомьтесь с этим онлайн-руководством . Я думаю, что вы можете быть наиболее заинтересованы в разделах о макросах и условных выражениях.

Курт Э. Клотье
источник
Похоже, правильный ответ есть в последнем абзаце связанного PDF. Ch. 7 Setting Initial Values,
Джиппи
Да вы правы. Я упоминал об этом в моем первом абзаце, но продолжил, если вы не были знакомы с файлами .eep и компоновщиком в make-файле!
Курт Э. Клотье
1
Использование статических адресов EEPROM - плохая практика. Вместо этого лучше использовать атрибут EEMEM и позволить компилятору управлять распределением адресов. Кроме того, я бы порекомендовал внедрить / выполнить проверку CRC для каждого раздела. В случае сбоя CRC соответствующий раздел содержит неинициализированные или поврежденные данные. Таким образом, вы даже можете реализовать механизм возврата к предыдущей конфигурации в случае повреждения данных.
Rev 1.0
«Использование статических адресов EEPROM - плохая практика». Почему?
Angelatlarge
1
При использовании EEMEMпеременных компилятор заботится о том, какая переменная находится в EEPROM. Таким образом, вы обращаетесь только к (постоянным, сгенерированным компилятором) указателям на переменные при доступе к данным. Если, с другой стороны, вы явно определяете адрес, где находится каждая переменная, вам придется самостоятельно позаботиться об этих адресах, в том числе убедиться, что никакие переменные не занимают случайно один и тот же адрес, перезаписывая друг друга; или перерасчет всех адресов в случае изменения размера хранилища переменной в будущем и т. д.
JimmyB