|
Энергонезависимая Память AVR |
|
|
|
 |
Ответов
(1 - 45)
|
Feb 27 2011, 12:38
|

unexpected token
   
Группа: Свой
Сообщений: 899
Регистрация: 31-08-06
Из: Мехелен, Брюссель
Пользователь №: 19 987

|
Цитата(Br.Misha @ Feb 27 2011, 15:27)  4 байта для хранения счетчика записей. После каждой записи я буду увеличивать счетчик на 1 и когда дойдет до 100000, то буду записывать даные и счетчик в следующие 20 байт, получается, циклов записи будет около 2 милионов (для атмега8). Как вам идея? Так наверное будет работать, только зачем для счетчика до 100000 использовать 4 байта, когда достаточно 3-х: 2^24 = 16777216? Ну это так, для экономии памяти  100000 раз применительно к каждому биту, я считаю.
--------------------
А у тебя SQUID, и значит, мы умрем.
|
|
|
|
|
Feb 27 2011, 14:15
|

Местный
  
Группа: Участник
Сообщений: 318
Регистрация: 21-07-06
Из: Минск
Пользователь №: 18 986

|
Делал так: CODE //----------------------------------------------------------------------------
//Eeprom support module
//----------------------------------------------------------------------------
#include <Main.h> #include <Eeprom.h>
//------------------------------ Constants: ----------------------------------
#define RBUFF 100 //EEPROM ring buffer size #define RSIG 0xDA //record header signature
//------------------------------ Variables: ----------------------------------
typedef struct { char Sig; //record header signature bool On; //On flag int Val; //stored value } EVal;
__no_init __eeprom int EData[EE_N]; //EEPROM data array __no_init __eeprom EVal EVals[RBUFF]; //EEPROM ring buffer
static char EePnt = 0; //pointer to currently used record
//-------------------------- Read EEPROM data: -------------------------------
int Eeprom_Read(char a) { return(EData[a]); //read parameter }
//-------------------------- Write EEPROM data: -------------------------------
void Eeprom_Write(char a, int d) { if(EData[a] != d) //if parameter changed { EData[a] = d; //write parameter __watchdog_reset(); //watchdog reset } }
//-------------------------- Read V from EEPROM: -----------------------------
int Eeprom_ReadV(bool *on) { EePnt = RBUFF; //set incorrect pointer value for(char i = 0; i < RBUFF; i++) //search signature in ring buffer if(EVals[i].Sig == RSIG) //if signature found { EePnt = i; //initialize pointer break; } if(EePnt == RBUFF) //if pointer incorrect (signature not found) { EePnt = 0; //set pointer to first array record *on = 0; //supply off return(0); //return V = 0 } *on = EVals[EePnt].On; //read on flag return(EVals[EePnt].Val); //read V }
//--------------------------- Write V to EEPROM: -----------------------------
void Eeprom_WriteV(int v, bool on) { char NewPnt = EePnt; //save previous pointer value if(NewPnt++ == RBUFF) //advance pointer NewPnt = 0; //roll-up pointer EVals[NewPnt].On = on; //save on flag EVals[NewPnt].Val = v; //save V EVals[NewPnt].Sig = RSIG; //save signature at new location EVals[EePnt].Sig = 0xFF; //clear signature at old location __watchdog_reset(); //watchdog reset EePnt = NewPnt; //save new pointer value }
//----------------------------------------------------------------------------
Сообщение отредактировал IgorKossak - Feb 27 2011, 15:21
Причина редактирования: Бездумное оформление кода
--------------------
|
|
|
|
|
Mar 1 2011, 14:29
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(GDI @ Mar 1 2011, 17:10)  Только надо еще как то узнать о том что произошел отказ... Узнавать-то вроде и не надо. Номер последней записи обновляется _после_ записи данных. Всё само-собой получается. Производитель гарантирует 100тыс циклов записи, так что наворачивать еще что-либо сверх рекомендованного в аппноте ИМХО не резонно.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Mar 1 2011, 15:01
|

Гуру
     
Группа: Свой
Сообщений: 2 076
Регистрация: 10-09-08
Пользователь №: 40 106

|
Цитата(defunct @ Mar 1 2011, 16:54)  Лучше не ждать когда накрутит 100000, а просто каждую запись писать в следующие n-байт. 100% Цитата(defunct @ Mar 1 2011, 16:54)  Счетчик в этом случаем может быть 1 байтным и хранить только sequence (порядковый номер) записи. Не согласен! Если просто хранить номер записи то младший бит будет меняться с каждым увеличением. Думаю что в качестве указателя желательно применить : a: "бегущий" бит ноль(единица) в байте (увеличение ресурса в 8 раз); б: "бегущий" байт 00H(FFH) в массиве X[n] (увеличение ресурса в n раз).
|
|
|
|
|
Mar 1 2011, 15:30
|
Частый гость
 
Группа: Свой
Сообщений: 120
Регистрация: 21-08-06
Из: СПб
Пользователь №: 19 701

|
Цитата(defunct @ Mar 1 2011, 15:54)  сделать массив из 20-ти записей по 16 байт, писать вначале 0-ю запись, потом 1-ю ..... 19-ю, опять 0-ю и так покругу. Счетчик в этом случаем может быть 1 байтным и хранить только sequence (порядковый номер) записи. И по возможности не располагать его с 0 адреса в еепром. Вроде даже где-то в апликухах об этом говорилось: в AVR при неблагоприятном стечении обстоятельств значение в нулевой ячейке может быть утеряно.
|
|
|
|
|
Mar 2 2011, 07:34
|
Участник

Группа: Участник
Сообщений: 54
Регистрация: 25-01-06
Из: Самара
Пользователь №: 13 578

|
Цитата(ukpyr @ Mar 1 2011, 21:53)  старая байка, уже давно не актуальная Для рабочего режима - может быть. Однако при отладке с помощью JTAGICE II довольно часто портится именно 0 ячейка EEPROM. С чем это связано - неизвестно. Возможно, с процессом заливки новой программы? Естественно, программирование EEPROM при этом запрещено. Портится именно 0 ячейка, далее - все в порядке...
|
|
|
|
|
Mar 2 2011, 10:03
|
Местный
  
Группа: Свой
Сообщений: 426
Регистрация: 5-04-07
Из: Санкт-Петербург
Пользователь №: 26 782

|
Цитата(ukpyr @ Mar 1 2011, 20:53)  старая байка, уже давно не актуальная тем не менее IAR при стандартных настройках 0-ую ячейку ЕЕПРОМ не пользует.
|
|
|
|
|
Mar 3 2011, 10:06
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(Petka @ Mar 2 2011, 20:44)  avr-gcc вообще не распределяет eeprom память. Как понимать "avr-gcc использует"? Хорошо. Под avr-gcc я имел ввиду весь тулчейн, точно так же как и CV, ICC и IAR. Линкер помещает в отдельную секцию данные с атрибутом EEMEM, а потом утилита objcopy выкусывает её в отдельный файл - образ прошивки eeprom памяти. И в результате, по умолчанию, данные в eeprom располагаются начиная с нулевого адреса. Вся эта канитель конфигурится через Makefile и программист может изменить массу параметров, в том числе и стартовый адрес eeprom данных. Поправьте или дополните меня если считаете нужным.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Mar 3 2011, 16:20
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(zombi @ Mar 1 2011, 17:01)  Если просто хранить номер записи то младший бит будет меняться с каждым увеличением. Во-первых вы немного ошибаетесь - можно подобрать количество записей так, что изменения в младшем бите будут очень редкими. Например, если записи всего две, то младший бит в sequence ячейках практически никогда меняться не будет, т.к. в 0-ю запись попадут все четные sequence, в 1-ю - все нечетные. Во-вторых - даже если младший бит будет меняться постоянно, ну и что? Мы же пишем всегда в новые n-байт, т.е. ячеек с sequence'ом будет столько же сколько и записей, наработка на отказ - все те же n * 100000, где n - размерность массива записей. Ниже пример реализации алгоритма, правда здесь в качестве хранилища используется флеш (проц SAM7): Код void js_LoadConfig(void) { U8 *p = (U8 *)(CONFIG_START_FLASH_ADDRESS); U8 last_seq = *p; U8 *pLastConfig = p; U8 buf[sizeof(jsContext.Cfg)]; PJS_CONFIG pCfg = (PJS_CONFIG)buf; int i;
for (i = 0; i < CONFIG_MIRROR_PAGE_COUNT; i++) { p += FLASH_PAGE_SIZE_BYTES; if (((U8)(last_seq + 1)) == *p) { last_seq = *p; pLastConfig = p; } else { p = pLastConfig; break; } } // at this point p - points to the address with the most fresh config record memcpy( buf, p, sizeof(buf)); jsContext.LastLocation = (U32)p; jsContext.Cfg.Sequence = *p;
// check CRC of the record if ( CRC16( buf, sizeof(buf) != 0 ) { printf("CRC error exp=%2x, cur=%2x, flash_addr=%x, seq=%d\n", hCRC, pCfg->CRC, (U32)p, *p); printf("The configuration has NOT been applied!\n" ); printf("or there is no previously saved configuration.\n"); // store defaults js_StoreConfig(); } else { // CRC is ok, applying new settings memcpy( &jsContext.Cfg, pCfg, sizeof(jsContext.Cfg)); printf("Configuration applied, seq_%x\n", *p); } }
void js_StoreConfig(void) { jsContext.Cfg.Sequence += 1; jsContext.LastLocation += FLASH_PAGE_SIZE_BYTES; if (jsContext.LastLocation > CONFIG_LAST_FLASH_CELL_ADDRESS) jsContext.LastLocation = CONFIG_START_FLASH_ADDRESS;
jsContext.Cfg.CRC = CRC16( (U8 *)&jsContext.Cfg, sizeof(jsContext.Cfg) - 2); iap_PageWrite( jsContext.LastLocation, (U32 *)&jsContext.Cfg, sizeof(jsContext.Cfg) );
printf("configuration stored, L(%x), Sq(%x), (%d)bytes\n", jsContext.LastLocation, jsContext.Cfg.Sequence, sizeof(jsContext.Cfg)); } CONFIG_MIRROR_PAGE_COUNT --> это число записей в массиве. Sequence постоянно увеличивается, и новый sequence пишется уже в новую запись. При загрузке конфигурации - пробегаемся по массиву, находим самую страшую запись и берем ее. 2 moderator: просьба тэг [ code ] на [ codebox ] не менять, пасиба.
|
|
|
|
|
Mar 3 2011, 16:43
|

Чайник, 1 литр
   
Группа: Свой
Сообщений: 655
Регистрация: 17-05-06
Из: Moscow
Пользователь №: 17 168

|
А всё-таки, EEPROM в AVR страничной организации, или же нет? В документации, правда, в разделе записи в EEPROM с программатора, указан страничный обмен: к примеру, страница - 8 байт, и минимально адресуемый элемент - 1 байт. Ежели оно минимально адресуемо байтом, то какой смысл считать в них биты, ибо стирание\запись всё равно для всех восьми? А если из МК при записи в EEPROM одного байта переписывается вся страница, про принципу read-modify-write, то всё и того хуже...
|
|
|
|
|
Mar 4 2011, 15:02
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(zombi @ Mar 3 2011, 18:58)  что такое это sequence, где оное хранится и какова его размерность? Sequence это первый байт структуры Cfg (она вся пишется во флеш, последние ее два байта - CRC16). Структура Cfg - пишется покругу во флеш, при каждой записи sequence увеличивается на 1. При старте девайса - последовательно вычитываются записи начиная с нулевой, там где обнаруживается разница Код sn[i+1] - sn[ i ] != 1 (т.е. следующий Sequence отличается от текущего не на единицу), и будет i - номер искомой самой свежей записи. Цитата(SysRq @ Mar 3 2011, 19:00)  Тогда нет смысла мудрить с отдельными битами, ибо для всего байта erase\write выполняется. Нет write-only команды. Я и не предлагал
|
|
|
|
|
Mar 5 2011, 11:34
|

Гуру
     
Группа: Свой
Сообщений: 2 076
Регистрация: 10-09-08
Пользователь №: 40 106

|
Цитата(aaarrr @ Mar 4 2011, 01:21)  Хранится в каждой записи. Размерность должна быть такой, чтобы максимальное значение sequence было больше числа возможных записей, тогда всегда можно достоверно определить наиболее свежую. Цитата(defunct @ Mar 4 2011, 18:02)  Sequence это первый байт структуры Cfg (она вся пишется во флеш, последние ее два байта - CRC16). Спасибо! Разобрался. Действительно очень красиво все получается.  Цитата(ukpyr @ Mar 4 2011, 01:06)  Цитата(zombi @ Mar 3 2011, 20:13)  Осталось только выяснить : приводит ли к исчепанию ресурса стирание(запись значения FFH) в ячейку которая и до того была FFH
Полезно перед записью каждого байта проверять на равенство записанному. Уменьшается износ и часто сильно ускоряется обновление параметров. В одном проекте не хватало заряда конденсатора для сохранения в EEPROM после пропадания питания (больше 0.5сек), добавил проверку - успевало записать за десятки миллисекунд. Т.е. получается что время на запись "тогоже самого" тратится по любому, а вот приводит ли это к износу еепром?
|
|
|
|
|
Mar 5 2011, 12:29
|

Чайник, 1 литр
   
Группа: Свой
Сообщений: 655
Регистрация: 17-05-06
Из: Moscow
Пользователь №: 17 168

|
Цитата(zombi @ Mar 5 2011, 14:34)  Т.е. получается что время на запись "тогоже самого" тратится по любому, а вот приводит ли это к износу еепром? Убивается, ждём-с. Сейчас 1' 248' 000 циклов выполнено, пока жива... (Хм, в программке вроде бы не наврал нигде.. в один и тот же адрес пишу 0xFF в количестве 992 штук, затем 8 значений с единичкой в разных разрядах, и в обоих случаях читаю и проверяю совпало ли, и циклы считаю.) Код (ATMega128; здесь USB - мост на UART), на проверку, может ошибка где? CODE #include <stdio.h>
static char str[100];
#define EEPROM_ADDRESS_TO_DESTROY 4094U
uint32_t cycles_; uint16_t cycles_1000_;
void show_cycles(uint8_t is_failed) { if(is_failed) { sprintf(str, "\r\nFAILED ON: %ld", cycles_); } else { sprintf(str, "\r\nCURRENT: %ld", cycles_); } for(uint8_t i = 0; i < 100; i++) { if(str[i]) USB_TransmitByte(str[i]); else break; } }
void eeprom_write_no_check(uint16_t address, uint8_t data) { while(EECR & _BV(EEWE));
EEAR = address; EEDR = data;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { EECR |= _BV(EEMWE); EECR |= _BV(EEWE); } }
uint8_t eeprom_read_no_check(uint16_t address) { while(EECR & _BV(EEWE));
EEAR = address; EECR |= _BV(EERE);
return EEDR; }
__attribute__((OS_main)) int main(void) { USB_Initialize(); sei(); for(;;) { eeprom_write_no_check(EEPROM_ADDRESS_TO_DESTROY, 0xFF); ++cycles_; ++cycles_1000_; if(eeprom_read_no_check(EEPROM_ADDRESS_TO_DESTROY) != 0xFF) { show_cycles(TRUE); for(;;); } if(cycles_1000_ == 1000) { cycles_1000_ = 0; show_cycles(FALSE); for(uint8_t data = 1; data; data <<= 1) { eeprom_write_no_check(EEPROM_ADDRESS_TO_DESTROY, data); ++cycles_; ++cycles_1000_; if(eeprom_read_no_check(EEPROM_ADDRESS_TO_DESTROY) != data) { show_cycles(TRUE); for(;;); } } } if(USB_IsDataReceived()) { if(USB_ReceiveByte() == '?') { show_cycles(FALSE); } } }
return 0; } Если в коде ошибок нет, то поставлю писать что-нить отличное от 0xFF, проверим далее...
Сообщение отредактировал SysRq - Mar 5 2011, 15:03
|
|
|
|
|
Mar 5 2011, 20:23
|

Гуру
     
Группа: Свой
Сообщений: 2 076
Регистрация: 10-09-08
Пользователь №: 40 106

|
Цитата(SysRq @ Mar 5 2011, 16:54)  Примерно так и делаю, посмотрите код выше. Если одинаковые значения не пишутся, то сейчас всего тысяч 6 циклов...  Ага, вроде правильно все. Цитата(SysRq @ Mar 5 2011, 19:23)  Выключил на 1'800'000. Изменил в исходнике 0xFF на 0x00, адрес на 1 вниз, перешил, запустил. Ждём, убьётся ли записью нулей... Наверное можно предположить что ограничение 100.000 циклов это максимальное количество изменеий каждого БИТА еепром.
|
|
|
|
|
Mar 6 2011, 15:02
|

Чайник, 1 литр
   
Группа: Свой
Сообщений: 655
Регистрация: 17-05-06
Из: Moscow
Пользователь №: 17 168

|
Результат записи постоянно меняющегося значения: убилось на 3'327'762 цикле. CODE // ... CURRENT: 3326000 CURRENT: 3327000 FAILED ON: 3327762
// перезапуск
// ... CURRENT: 99000 CURRENT: 100000 FAILED ON: 100889
// перезапуск
// ... CURRENT: 101000 CURRENT: 102000 FAILED ON: 102918 -- Собстно, выводы: а) страничной организации нет, ибо я убивал соседние адреса в рамках возможной страницы; б) убивается, похоже, именно записью нуля: см. поведение после перезапуска в логах. PS: ATMega128-16AU @ 14.7456 @ комнатная температура.
|
|
|
|
|
Mar 17 2011, 07:56
|
Группа: Новичок
Сообщений: 7
Регистрация: 25-04-06
Пользователь №: 16 452

|
Попробуйте провести эксперимент на температуре 125 или 100С, например. Дойдет ли хотя бы до 200тыс?
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|