Работа с EEPROM и Flash

Все МК STM8 снабжены некоторым количеством EEPROMa. Оно варьируется от жалких 128 байт в младших моделях STM8S, до 2кб в старших моделях STM8L и S. Благодаря единому адресному простанству работа с EEPROM почти ничем не отличается от работы с RAM.

Не только EERPOM, но и flash записывается легко и просто. Поэтому, хоть заметка и посвящена работе с EEPROM, тут будут некоторые комментарии касательно флеша.

В STM8L152С6 (который стоит на дискавери) EEPROM располагается начиная с адреса 0x1000. Она занимает 1 килобайт, простираясь до адреса 0x13FF. Флеш (память программ) начинается с адреса 0x8000.

Чтение из EEPROM аналогично чтению из любой другой области памяти — Flash, RAM или регистров периферии.

Запись данных тоже аналогична записи в другие области памяти. Кроме простой записи 1 байта, есть возможность записать 4 байта за один заход, что учитывая большое время записи в EEPROM (6ms) может очень пригодиться. Более того, за один заход можно записать и целый блок (128 байт), но для этого надо размещать функцию, которая будет проводить запись, в оперативке.

Стандартная библиотека предоставляет нам несколько функций для работы с EEPROM (и флеш) памятью. Все они находятся в модуле stm8l15x_flash.h. Первая реализует чтение:
uint8_t FLASH_ReadByte(uint32_t Address);

Как ясно из названия — функция читает байт по указаному адресу. Но если нам нужно прочитать не байт, а например слово или двойное слово, то проще поступить так:
#pragma location=0x1000 //Наш адрес в EEPROM/flash (в данном случае - начало еепромки)
__no_init uint32_t MyEEPROMVariable

Директива location указывает компилятору, где разместить переменную, а __no_init запрещает обнулять её при старте.

Теперь мы можем работать с MyEEPROMVariable как с обычной переменной. Копировать в другую переменную, сравнивать, записывать, и т.д. Хотя нет, записывать мы в неё пока не можем, ибо память защищена от записи.

Для того чтобы проводить запись, EEPROM надо разблокировать. Блокировка включена при старте МК и служит для защиты от неожиданных действий программы или сбоев. Защита снимается последовательной записью двух ключей в регистр FLASH_DUKR:
FLASH_DUKR = 0xAE;
FLASH_DUKR = 0x56; 

Для разблокировки флеша служит другое колдунство:
FLASH_PUKR = 0x56;
FLASH_PUKR = 0xAE;

Тоже самое можно сделать при помощи функции из библиотеки:
FLASH_Unlock(FLASH_MemType_Data); //Разблокировка EEPROM
FLASH_Unlock(FLASH_MemType_Program); //Разблокировка памяти программ


Теперь мы можем записывать в нашу MyEEPROMVariable. Надо заметить, что при записи в память программ ядро будет останавливаться на все время записи, а при записи в EEPROM — работать себе спокойненько, пока байт пишется.
Для записи по определенному адресу в библиотеке есть функции FLASH_ProgramByte(uint32_t Address, uint8_t Data) и FLASH_ProgramWord(uint32_t Address, uint32_t Data).

Первая ничего интересного из себя не представляет, а вот вторая использует механизм записи двойного слова за один заход.
FLASH->CR2 |= FLASH_CR2_WPRG;
 
 /* Write one byte - from lowest address*/
  *((PointerAttr uint8_t*)(uint16_t)Address) = *((uint8_t*)(&Data));   
 /* Write one byte*/
  *(((PointerAttr uint8_t*)(uint16_t)Address) + 1) = *((uint8_t*)(&Data) + 1);
 /* Write one byte*/
  *(((PointerAttr uint8_t*)(uint16_t)Address) + 2) = *((uint8_t*)(&Data) + 2); 
 /* Write one byte - from higher address*/
  *(((PointerAttr uint8_t*)(uint16_t)Address) + 3) = *((uint8_t*)(&Data) + 3); 


Сначала она устанавливает бит WPRG в регистре FLASH_CR2, затем записывает подряд 4 байта в память. При этом операция записи не происходит, а байты попадают в буфер. После записи последнего байта, вся пачка автоматически записывается в память. Таким образом общая скорость записи увеличивается в 4 раза.

Попробуем еще больше ускорить процесс записи, пропихивая за раз целый блок (128 байт). Для этого служит функция FLASH_ProgramBlock( uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType, FLASH_ProgramMode_TypeDef FLASH_ProgMode, uint8_t* Buffer)

BlockNum — это номер блока, который нужно записать. Например, блок 0 размещается по адресам 0x1000 — 0x107F.

FLASH_MemType — память, в которую мы хотим записать данные. FLASH_MemType_Data для записи в EEPROM или FLASH_MemType_Program для записи в память программ (флеш).

FLASH_ProgMode — режим записи. Бывает обычным (FLASH_ProgramMode_Standart), при котором запись занимает 6 миллисекунд, или быстрым (FLASH_ProgramMode_Fast) — запись будет выполнена в два раза быстрее, но необходимо вначале очистить блок.

Buffer — указатель на массив размером с один блок (например, 128 байт).

Но просто так заюзать эту функцию у тебя не получится. Чтобы она сработала, FLASH_ProgramBlock должна выполняться из RAM. К счастью, реализовать это очень просто — обо всем позаботились разработчики библиотеки и IAR. А нам остается только раскомментировать строчку
#define RAM_EXECUTION  (1)

в файле stm8l15x.h. Теперь те функции, которые должны выполняться из RAM, будут копироваться в неё при старте.

Сам механизм записи блока работает подобно механизму записи двойного слова. В начале устанавливается бит PRG (или FPRG, если используется режим быстрой записи) в регистре FLASH_CR2. Затем записываются 128 байт (не обязательно столько — зависит от размера блока), которые так-же, как и в случае с двойным словом, попадают в буфер. Как только записан последний байт, вся пачка разом записывается в EEPROM/flash.

Кстати о режиме быстрой записи. Для очистки блока перед тем, как быстро и решительно в него что-то записать, можно воспользоваться функцией
FLASH_EraseBlock(uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType);

которая тоже должна выполняться из RAM. Параметры тут имеют тот-же смысл, что и для предыдущей функции.

В общеобразовательных целях можно заглянуть в RM0031 и посмотреть, как реализуется очистка блока:
Для очистки блока надо сначала установить бит ERASE в FLASH_CR2, а затем записать нули в первые четыре байта внутри этого блока.

Надо заметить, что из-за ошибке в самом МК при совпадении таких событий как обработка запроса DMA и запись в EEPROM/flash, ядро зависнет.

На этом пожалуй все. По просьбам постоянных радиослушателей могу доставить примерчик кода, который что-то куда-то пишет. :)
  • +3
  • 10 октября 2011, 03:01
  • dcoder

Комментарии (5)

RSS свернуть / развернуть
Добавь про неприятный момент при одновременной работе с eeprom и DMA, отражен в errate.
0
  • avatar
  • ZiB
  • 10 октября 2011, 05:55
То-есть если мы заставим DMA писать в eeprom, то все нафиг повиснет? И если DMA будет работать когда идет запись в eeprom, то случится тоже самое?
0
0
Но если нам нужно прочитать не байт, а например слово или двойное слово, то проще поступить так:
можно и так:
uint32_t *MyEEPROMVariable = (uint32_t *)0x1000;
А в Cosmic для есть модификатор @eeprom: www.cosmic-software.com/faq/faq13.php
0
о просьбам постоянных радиослушателей могу доставить примерчик кода, который что-то куда-то пишет.

Было бы супер. Спасибо за статью.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.