Запись во внутренюю EEPROM

Обсуждаем контроллеры компании Atmel.
Ответить
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

Ребята помогите реализовать запись во внутренюю еепром, щас у меня стоит внешняя память но решил откинуть дабы жрало меньше.
На внешней запись в еепром была так реализована

Код: Выделить всё

void writeData(unsigned char c1){ 
i2c_start(); 
i2c_write(0xa0);
i2c_write(addr>>8); 
i2c_write(addr & 0x00FF); 
page_stop_status=0;     

i2c_write(c1); 
    
} 
из даташита запись, чтение

Код: Выделить всё

void EEPROM_write(unsigned int uiAddress, unsigned char ucData)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEPE))
;
/* Set up address and Data Registers */
EEAR = uiAddress;
EEDR = ucData;
/* Write logical one to EEMPE */
EECR |= (1<<EEMPE);
/* Start eeprom write by setting EEPE */
EECR |= (1<<EEPE);
}

unsigned char EEPROM_read(unsigned int uiAddress)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEPE))
;
/* Set up address register */
EEAR = uiAddress;
/* Start eeprom read by writing EERE */
EECR |= (1<<EERE);
/* Return data from Data Register */
return EEDR;
}
как реализовать запись с продолжением, тоесть не стирая старых данных дописывая их. ну и чтение всез данных из еепром по юарт.
Реклама
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Не понятно в чём проблема.
Доступ к байту внутренней EEPROM происходит независимо от содержимого остальных байт.

Код: Выделить всё

EEPROM_write(Address+0, Data1);
EEPROM_write(Address+1, Data2);
EEPROM_write(Address+2, Data3);

EEPROM_write(Address2+99, Data1);
EEPROM_write(Address2+0, Data2);
EEPROM_write(Address2+2, Data3);
Реклама
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Разработчики компиляторов часто пишут свои функции для работы с EEPROM, так что по регистрам лазать не обязательно. Например, в gcc это заголовочный файл <avr/eeprom.h> и функции eeprom_read_byte(), eeprom_read_word() и т.д.
Что значит "запись с продолжением"?
Можно использовать переменную в качестве адреса последнего записанного байта, тогда при каждом включении запись будет начинаться с начала.
Можно пожертвовать частью диапазона входных данных для флага конца записанной области. Например, записываете значения 10-битного АЦП, то есть 16-битные числа в диапазоне 0x0000 - 0x3FF, тогда флагом конца записи может быть чистая EEPROM - 0xFFFF. Если для того же АЦП используете 8-битную точность, можно объединить значения 0xFE и 0xFF (считать что напряжения, соответствующим значениям, равны и вместо 0xFF записывать 0xFE), тогда флагом конца станет число 0xFF.
Можно отвести переменную в EEPROM, которая бы считала число записанных байт. Правда, у EEPROM ограничен ресурс перезаписи, но и тут можно извращаться. Например, завести указатель на две переменные - на количество записанных байт и на количество перезаписей первой переменной. Меняться они будут синхронно. Когда дойдете до определенного предела - указатель перескакивает с изношенных ячеек на новые и можно пользоваться дальше. Уж 10 миллионов перезаписей вам наверняка хватит.
В общем, зависит от задачи.
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

COKPOWEHEU писал(а):Разработчики компиляторов часто пишут свои функции для работы с EEPROM, так что по регистрам лазать не обязательно. Например, в gcc это заголовочный файл <avr/eeprom.h> и функции eeprom_read_byte(), eeprom_read_word() и т.д.
Что значит "запись с продолжением"?
Можно использовать переменную в качестве адреса последнего записанного байта, тогда при каждом включении запись будет начинаться с начала.
Можно пожертвовать частью диапазона входных данных для флага конца записанной области. Например, записываете значения 10-битного АЦП, то есть 16-битные числа в диапазоне 0x0000 - 0x3FF, тогда флагом конца записи может быть чистая EEPROM - 0xFFFF. Если для того же АЦП используете 8-битную точность, можно объединить значения 0xFE и 0xFF (считать что напряжения, соответствующим значениям, равны и вместо 0xFF записывать 0xFE), тогда флагом конца станет число 0xFF.
Можно отвести переменную в EEPROM, которая бы считала число записанных байт. Правда, у EEPROM ограничен ресурс перезаписи, но и тут можно извращаться. Например, завести указатель на две переменные - на количество записанных байт и на количество перезаписей первой переменной. Меняться они будут синхронно. Когда дойдете до определенного предела - указатель перескакивает с изношенных ячеек на новые и можно пользоваться дальше. Уж 10 миллионов перезаписей вам наверняка хватит.
В общем, зависит от задачи.
с продолжением значит не затирая старые данные записал в 1 яйчейку выключил девайс включил записал ещёё он дальше в след яйчеку записал.
Реклама
Эиком - электронные компоненты и радиодетали
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

с записью я разобрался,

Код: Выделить всё

volatile unsigned int addrr = 0;

void write(unsigned char c1){ 

EEPROM_write(addrr, c1); 
addrr = addrr + 1;
if (addrr == 512)
    addrr = 0;
}
а вот как читать с еепром с выводлом в терминал не могу понять :(
Реклама
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

читает всю память

Код: Выделить всё

for (i = 0; i < 512; i++)
    putchar(EEPROM_read(i));
не знаю можно на внутренней реализовать запись с того месте где закончилась, запись после перезагрузки процессора, а то он постоянно с начала пишет.
Реклама
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

kotriks писал(а): с продолжением значит не затирая старые данные записал в 1 яйчейку выключил девайс включил записал ещёё он дальше в след яйчеку записал.
Зачем спрашивать если не читаете ответов? Я написал несколько способов как это делается. Точно можно сказать только зная задачу.
а то он постоянно с начала пишет.
Он пишет не "с начала" а в ту и только ту ячейку которую ему укажут.
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

но как сделать указатель чтобы запись шла в пустую следующюю яйчейку, а не уже записанную?? :(
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Ну вам что, код написать?
Ладно, вот вариант с хранением количества байт в ОЗУ

Код: Выделить всё

void eeprom_append_byte(char data){
  static uint8_t *pos = NULL; //чтобы не было предупреждения о переводе uint8_t --> char
  eeprom_write_byte(pos, data);
  pos++;
}
и вариант с хранением в EEPROM

Код: Выделить всё

#define COUTN_POS 0x00F0
void eeprom_append_byte(char data){
  uint8_t *pos;
  eeprom_read_block(&pos, (void*)COUNT_POS, sizeof(uint8_t*)); //немного магии с преобразованием типов переменных
  eeprom_write_byte(pos,data);
  pos++;
  eeprom_write_block((void*)COUNT_POS, &pos, sizeof(uint8_t));
}
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Нужно определиться что произойдёт по достижении конца памяти. Если запись на этом останавливается, то достаточно проверить свободны ли оставшиеся ячейки памяти. Если же память представляет собой что-то вроде кольцевого буфера, то подход зависит от записываемой информации. Например одометр на приборной панели одного автомобиля считает пройденные километры и записывает с определённым интервалом значения в EEPROM. При включении достаточно просканировать память, максимальное значение и будет самой последней записью. Другой пример. Устройство хранит журнал событий за несколько лет. С каждой записью сохраняется 32-разрядный идентификатор(по сути счётчик). Это позволяет генерировать уникальные идентификаторы в течение десятков лет, т.е. значительно больше чем предполагаемый срок службы оборудования.
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

COKPOWEHEU писал(а):Ну вам что, код написать?
Ладно, вот вариант с хранением количества байт в ОЗУ

Код: Выделить всё

void eeprom_append_byte(char data){
  static uint8_t *pos = NULL; //чтобы не было предупреждения о переводе uint8_t --> char
  eeprom_write_byte(pos, data);
  pos++;
}
и вариант с хранением в EEPROM

Код: Выделить всё

#define COUTN_POS 0x00F0
void eeprom_append_byte(char data){
  uint8_t *pos;
  eeprom_read_block(&pos, (void*)COUNT_POS, sizeof(uint8_t*)); //немного магии с преобразованием типов переменных
  eeprom_write_byte(pos,data);
  pos++;
  eeprom_write_block((void*)COUNT_POS, &pos, sizeof(uint8_t));
}
Спасибо канешно, но откуда вы такие переменные берёте в код вижене иж нет :)
Нужно определиться что произойдёт по достижении конца памяти. Если запись на этом останавливается, то достаточно проверить свободны ли оставшиеся ячейки памяти. Если же память представляет собой что-то вроде кольцевого буфера, то подход зависит от записываемой информации. Например одометр на приборной панели одного автомобиля считает пройденные километры и записывает с определённым интервалом значения в EEPROM. При включении достаточно просканировать память, максимальное значение и будет самой последней записью. Другой пример. Устройство хранит журнал событий за несколько лет. С каждой записью сохраняется 32-разрядный идентификатор(по сути счётчик). Это позволяет генерировать уникальные идентификаторы в течение десятков лет, т.е. значительно больше чем предполагаемый срок службы оборудования.
по окончанию памяти я вставил while(1); чтобы проц в ступор вставал :)
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Что значит откуда я беру такие переменные? Я их в коде объявляю, это обычный способ для Си. Единственное что специфично именно для avr-gcc это eeprom_read_byte(), eeprom_read_block(), eeprom_write_block(), их вам придется менять на свои аналоги.
Модератор
Аватара пользователя
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

kotriks писал(а): Спасибо канешно, но откуда вы такие переменные берёте в код вижене иж нет :)
А чё, в кодевижене разве нет функций записи и чтения EEPROM ?
Совершенно не понятно, в чём проблема :dont_know:
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

С cvavr не работал, так что могу ошибаться. Но вроде там функций работы с EEPROM и flash нет. Вместо этого каким-то образом (сегменты памяти что ли?) помечается каждая переменная и работа с ними идет как с обычными, без спецфункций, то есть особенности работы с EEPROM и flash возложены на компилятор.
Друг Кота
Аватара пользователя
Сообщения: 3459
Зарегистрирован: Ср сен 27, 2006 16:18:57

Сообщение Барсик »

COKPOWEHEU писал(а):то есть особенности работы с EEPROM и flash возложены на компилятор.
Так оно и есть. Объявил переменную как eeprom и дело в шляпе. Пиши в неё просто операцией присвоения и читай так же. Причём этот CodeVision хитрый. Перед тем как писать в EEPROM, он, сволочь, проверяет что уже там записано. Если то же самое, что требуется записать, то ничего туда не пишет. Я смотрел осциллографом реальное время записи в EEPROM, и чуть мозг себе не сломал. Не мог понять, почему в одном случае это время, как и полагается, миллисекунды, а в другом - микросекунды.
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

Простите, я может чего не понимаю, но в даташите на AVR-контроллеры есть примеры записи и чтения EEPROM как на асме, так и на Си.
В чем проблема самому написать функцию, используя Си-шный код из даташита?
При чем тут функции кодевижна? Есть язык Си, в котором программист может создать совершенно любую функцию.
Или я не прав?
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Правы, но зачем изобретать велосипед? Тем более что разработчики компилятора должны, по идее, лучше разбираться в особенностях контроллеров, чем любители. Вон, гуру cvavr утверждает, что при тамошнем способе ячейка сначала проверяется, могут запрещаться прерывания, могут быть ассемблерные вставки и так далее. Кроме того, это более стандартизованный способ, а значит, лучше в плане читаемости.
Но, разумеется, никто вам не запретит пользоваться своими функциями.
Держит паяльник хвостом
Сообщения: 933
Зарегистрирован: Ср апр 13, 2011 11:09:20
Откуда: Екатеринбург

Сообщение Alkul »

COKPOWEHEU писал(а):Правы, но зачем изобретать велосипед? Тем более что разработчики компилятора должны, по идее, лучше разбираться в особенностях контроллеров, чем любители.
Да какой там, помилуй бог, велосипед? Там все просто, как три копейки. Два служебных регистра и регистровая пара адреса. Любитель, если он хочет в этой сфере как-то расти, должен изучать матчасть. Я понимаю, что программирование на Си, особенно у новичков, вообще не способствует изучению особенностей применяемого МК - зачем? есть компилятор, он все знает. Прямо как фонвизинский Митрофанушка: "зачем знать географию, есть извозчик, он довезет".
COKPOWEHEU писал(а):Вон, гуру cvavr утверждает, что при тамошнем способе ячейка сначала проверяется, могут запрещаться прерывания, могут быть ассемблерные вставки и так далее. Кроме того, это более стандартизованный способ, а значит, лучше в плане читаемости.
Ну, наворотить можно много чего. Тем не менее, под спойлером я еще раз привожу Си-шные функции для чтения и записи байта из даташита на МК. Какая религия может запретить их применение? Не зная, какой МК использует тредстартер, привожу функции из даташита на "народную" ATmeg'у 8. Но не думаю, что для других МК эти функции будут отличаться. Естественно, что для использования этих функций надо файл-описатель МК подключать. Либо самому объяснить компилятору, что такое эти EEAR, EEDR и т.д.
Очевидно, что если автор использует прерывания, то в этих функциях перед началом циклов записи-чтения прерывания надо запрещать, а в конце функции - разрешать. Еще красивее - сначала проверить бит I регистра SREG, и если он установлен, то прерывания запрещать, в конце снова разрешать, а если бит сброшен, то ничего не делать с прерываниями.
Спойлер

Код: Выделить всё

void EEPROM_write(unsigned int uiAddress, unsigned char ucData)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEWE))
;
/* Set up address and data registers */
EEAR = uiAddress;
EEDR = ucData;
/* Write logical one to EEMWE */
EECR |= (1<<EEMWE);
/* Start eeprom write by setting EEWE */
EECR |= (1<<EEWE);
}

unsigned char EEPROM_read(unsigned int uiAddress)
{
/* Wait for completion of previous write */
while(EECR & (1<<EEWE))
;
/* Set up address register */
EEAR = uiAddress;
/* Start eeprom read by writing EERE */
EECR |= (1<<EERE);
/* Return data from data register */
return EEDR;
}
По-моему, у авторе проблемы не с доступом к EEPROM, а с алгоритмизированием вообще.
Вот, к примеру, делать вот так
kotriks писал(а):по окончанию памяти я вставил while(1); чтобы проц в ступор вставал :)
в общем случае неправильно. Не должен МК уходить в бесконечный цикл при невозможности записи в EEPROM. И вообще в любую память. А еще шире - при невозможности любого действия МК не должен "зависать".

Если запись дошла до конца EEPROM, очевидно, что надо начинать запись сначала. А если там настолько важные данные, которые нельзя потерять, значит, надо принимать меры к тому, чтобы такая ситуация в принципе не могла произойти.
kotriks писал(а):но как сделать указатель чтобы запись шла в пустую следующюю яйчейку, а не уже записанную?? :(
Вам уже пример программы привели, но я просто продублирую саму идею.
По фиксированному адресу (например, 0х00) в EEPROM выделяете необходимое кол-во байт, в зависимости от объема EEPROM, в которых храните адрес первой свободной ячейки в EEPROM. После старта МК читаете содержимое ячеек, хранящих адрес свободной ячейки и вуаля, используете этот адрес в функциях чтения или записи. После каждой записи в EEPROM инкрементировать адрес и переписывать ячейки, хранящие адрес. Все. Если дошли до конца EEPROM, то следующий адрес вычисляется, как 0х00+количество ячеек, хранящих адрес.
Напишите свою функцию, производящую все эти действия, при этом для физических актов чтения-записи ваша функция должна пользоваться теми функциями, которые я показал выше.
Для корректной работы в исходном состоянии нужно создать и файл *.eep, в котором в адресных ячейках разместить первый свободный адрес и вместе с заливкой программы прошить и EEPROM.
А можно использовать на адрес, а смещение, это как удобней, для EEPROM особой разницы нет.
Задача-то элементарная.
Грызет канифоль
Сообщения: 274
Зарегистрирован: Чт апр 28, 2011 15:08:58

Сообщение kotriks »

COKPOWEHEU писал(а):Ну вам что, код написать?
Ладно, вот вариант с хранением количества байт в ОЗУ

Код: Выделить всё

void eeprom_append_byte(char data){
  static uint8_t *pos = NULL; //чтобы не было предупреждения о переводе uint8_t --> char
  eeprom_write_byte(pos, data);
  pos++;
}
и вариант с хранением в EEPROM

Код: Выделить всё

#define COUTN_POS 0x00F0
void eeprom_append_byte(char data){
  uint8_t *pos;
  eeprom_read_block(&pos, (void*)COUNT_POS, sizeof(uint8_t*)); //немного магии с преобразованием типов переменных
  eeprom_write_byte(pos,data);
  pos++;
  eeprom_write_block((void*)COUNT_POS, &pos, sizeof(uint8_t));
}
Оказывается все эти переменные есть в 3 квавре. спасибо!
Друг Кота
Сообщения: 4753
Зарегистрирован: Сб апр 21, 2012 19:21:43

Сообщение Orion33 »

Удалено.
Ответить

Вернуться в «AVR»