Окт 252013
 

Итак, продолжим знакомство со встроенной flash памятью. Начало было положено здесь. Необходимость записи пользовательских данных во flash память может возникнуть тогда, когда надо сохранить эти данные при отключении питания. В отличие от многих других микроконтроллеров, тех же 8-разрядных AVR, к примеру, большинство микроконтроллеров STM32 начисто лишены собственной EEPROM памяти. Энергонезависимая память данных EEPROM есть только у серии STM32L1 (поправьте, если ошибаюсь, STMicroelectronics периодически обновляет свою линейку Cortex-ов), а такие популярные серии, как STM32F1, почему-то обделены данной памятью. Поэтому, чтобы сохранить настройки, результаты измерений и т.п. при возможном пропадании питания придется либо использовать внешнюю микросхему EEPROM, либо данные записывать в собственную flash память. С первым случаем все понятно, а далее рассмотрим как выполняются процедуры записи данных и стирания, какие регистры для этого служат, и некоторые другие подробности.

Возможны различные варианты программирования flash памяти. Это может быть программирование через встроенный bootloader или интерфейсы JTAG и SWD. Этот вариант подразумевает полное перепрограммирование flash памяти. Другой вариант предполагает возможность программирования отдельных ячеек flash памяти прямо в процессе работы приложения, не затрагивая при этом кода основной программы. Именно этот вариант нас и интересует в данном случае.

Запись данных во flash память имеет некоторые особенности.

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

Во-вторых, данные можно записать только в чистые ячейки памяти, то есть в те, которые содержать сплошные «1» (высокий логический уровень) и не содержат «0». Перед процедурой записи контроллер FPEC обязательно проверит данные, находящиеся по адресу, в который предстоит произвести запись. И, обнаружив данные, отличные от 0xFFFF, запретит запись, выставив соответствующий флаг ошибки. Поэтому перед записью каких-либо данных нелишне будет произвести очистку данной области памяти. Выполнить стирание данных в памяти можно двумя способами. Можно стереть сразу всю память, уничтожив при этом и программу. А можно стереть только одну страницу. Меньше страницы очистит нельзя, только целиком. Поэтому и надо следить за тем, чтобы программа не размещалась в одной странице с пользовательскими данными. Размеры страниц для некоторых типов STM32 приведены в предыдущей статье. Эти данные были взяты из документации, например, для серии Value Line STM32F100 документ называется RM0063 Programming manualSTM32F100xx value line Flash programming”.

В-третьих, контроллер FPEC, который управляет процедурами записи и стирания flash памяти, изначально имеет блокировку, его управляющий регистр FLASH_CR недоступен для записи. Перед началом записи данных необходимо выполнить разблокировку. В данном случае речь идет о бите LOCK регистра FLACH_CR, который изначально установлен. Кроме этого, доступ к данным можно перекрыть через специальные регистры Option Bytes, запретив чтение flash памяти, а также запись конкретных страниц.

Ниже приведена структура регистров FPEC (Flash Program and Erase Controller), используемых при операциях записи/стирания flash памяти с описанием их полей.

FLASH_KEYR

Данный регистр используется для разблокировки контроллера FPEC, он задействуется при сбросе бита LOCK в регистре FLASH_CR. Разблокировка производится последовательной записью в этот регистр двух ключей:

KEY1 = 0x45670123

KEY2 = 0xCDEF89AB

После записи сначала KEY1, а затем KEY2 в данный регистр, бит LOCK будет сброшен, FPEC разблокирован, регистр FLASH_CR станет доступным для записи. После записи данных можно снова установить бит LOCK программно, запретив работу FPEC. Затем, когда снова понадобится записать данные, придется опять выполнить процедуру разблокировки с помощью ключей KEY1, KEY2. Но это в том случае, когда последовательность ключей была записана верно. В случае ошибки при записи, FPEC заблокируется, и повторная запись верных ключей его уже не сможет разблокировать. В данном случае поможет только RESET, после сброса снова будет возможна процедура разблокировки FPEC.

FLASH_OPTKEYR

Данный регистр используется для получения доступа к установке/сбросу разрядов в блоке Option Bytes. Эти регистры изначально доступны только для чтения. Необходимо записать в FLASH_OPTKEYR ту же последовательность ключей (см. выше) для получения возможности изменения Option bytes. После записи правильных ключей в регистре FLASH_CR будет установлен бит OPTWRE, который указывает, что разрешен доступ записи Option bytes.

FLASH_SR

EOP: End of operation. Устанавливается аппаратно при успешном завершении операции записи/стирания flash памяти. Для сброса бита необходимо записать в него «1».

WRPRTERR: Write protection error. Устанавливается при попытке программирования защищенной от записи области памяти. Сбрасывается записью «1».

PGERR: Programming error. Устанавливается при программировании, когда данные по заданному адресу отличны от значения 0xFFFF. То есть не стерты перед программированием. Сбрасывается записью «1».

BSY: Busy. Установленный бит показывает, что в текущий момент идет операция записи или стирания. Бит аппаратно сбрасывается по окончании операций записи/стирания или возникновении ошибки при выполнении этих операций.

FLASH_CR

EOPIE: End of operation interrupt enable. Если вы хотите разрешить генерацию прерывания по завершению операции записи/стирания данных во flash (то есть при установке бита EOP в регистре FLASH_SR), то необходимо установить этот разряд.

ERRIE: Error interrupt enable. Установка этого разряда разрешит генерацию прерывания при возникновении ошибок, то есть при установке бит PGERR или WRPRTERR в регистре FLASH_SR.

OPTWRE: Option bytes write enable. Установленный бит разрешает программирование Option bytes. Устанавливается записью последовательности ключей в регистр FLASH_OPTKEYR, а сбросить можно программно.

LOCK: Блокировка контроллера записи/стирания flash памяти – FPEC. При установленном бите запрещено программирование регистра FLASH_CR. Программно этот бит можно только установить, т.е. включить блокировку. Сбросить бит возможно вводом последовательности из двух ключей KEY1 и KEY2 (см. выше) в регистр FLASH_KEYR.

STRT: Start. Установка разряда запускает процедуру стирания flash памяти.

OPTER: Выбор процедуры стирания Option byte.

OPTPG: Выбор процедуры программирования Option byte.

MER: Mass erase. Выбор процедуры полного стирания flash памяти.

PER: Page erase. Выбор процедуры стирания одной страницы flash памяти.

PG: Programming. Выбор процедуры программирования flash памяти.

FLASH_AR

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

Остальные регистры в данной статье рассматриваться не будут, поскольку напрямую в операциях записи/стирания они не задействованы. Они предназначены для установки защиты от записи/чтения, а также некоторых других настроек. А в данный момент рассмотрим процедуры стирания и программирования flash памяти непосредственно из работающего приложения (in-application programming — IAP).

Для всех операций записи/стирания необходимо сначала разблокировать FPEC – контроллер программирования/стирания flash памяти. Это делается последовательной записью 2 ключей в соответствующий регистр. Фрагмент кода:

#define FLASH_KEY1      ((uint32_t)0x45670123)
#define FLASH_KEY2      ((uint32_t)0xCDEF89AB)

int main()
{
  //FLASH_Unlock
  FLASH->KEYR = FLASH_KEY1;
  FLASH->KEYR = FLASH_KEY2;
..............................

Теперь разрешено программирование управляющего регистра FLASH_CR.

Для стирания страницы необходимо выполнить следующую последовательность команд:

//FLASH_Erase Page
  while((FLASH->SR&FLASH_SR_BSY));
  FLASH->CR |= FLASH_CR_PER; //Page Erase Set
  FLASH->AR = 0x0801FC00//Page Address
  FLASH->CR |= FLASH_CR_STRT; //Start Page Erase
  while((FLASH->SR&FLASH_SR_BSY)); 
  FLASH->CR &= ~FLASH_CR_PER; //Page Erase Clear

Данный код применялся для стирания последней 127 страницы flash памяти микроконтроллера STM32F100RB, установленного на плате STM32VL-DISCOVERY. Это последняя страница его памяти программ, имеющая размер в 1кБайт.

Для полного стирания всей памяти программ код будет выглядеть следующим образом:

//FLASH_Mass Erase
  while((FLASH->SR&FLASH_SR_BSY));
  FLASH->CR |= FLASH_CR_MER; //Mass Erase Set
  FLASH->CR |= FLASH_CR_STRT; //Start Page Erase
  while((FLASH->SR&FLASH_SR_BSY)); 
  FLASH->CR &= ~FLASH_CR_MER; //Mass Erase Clear

При выполнении этого кода реально стирается вся программа в микроконтроллере, при пошаговом выполнении отладки в IAR после выполнения команды

  FLASH->CR |= FLASH_CR_STRT; //Start Page Erase

последующие команды уже не выполняются. Их просто нет, вся программа уже стерта!

Напомню, что перед программированием flash памяти необходимо быть уверенным, что по заданным адресам все данные стерты, т.е. содержат значения 0xFFFF. Либо необходимо выполнить стирание данной страницы.

При программировании размерность записываемых данных равна 16 битам (half-word). Сама процедура программирования представляет собой запись данных по конкретному адресу. Вот так, например:

FLASH->CR |= FLASH_CR_PG;
while((FLASH->SR&FLASH_SR_BSY));
*(__IO uint16_t*)0x0801FC00 = data;
FLASH->CR &= ~FLASH_CR_PG;

В следующем примере приведен полностью код, в котором сначала производится очистка содержимого последней 127 страницы микроконтроллера STM32F100, а затем заполнение страницы данными.

#include "stm32f10x.h"

#define FLASH_KEY1      ((uint32_t)0x45670123)
#define FLASH_KEY2      ((uint32_t)0xCDEF89AB)
#define Page_127        0x0801FC00

uint16_t i;

int main()
{
  //FLASH_Unlock
  FLASH->KEYR = FLASH_KEY1;
  FLASH->KEYR = FLASH_KEY2;
  
  //FLASH_Erase Page
  while((FLASH->SR&FLASH_SR_BSY));
  FLASH->CR |= FLASH_CR_PER; //Page Erase Set
  FLASH->AR = Page_127; //Page Address
  FLASH->CR |= FLASH_CR_STRT; //Start Page Erase
  while((FLASH->SR&FLASH_SR_BSY)); 
  FLASH->CR &= ~FLASH_CR_PER; //Page Erase Clear
  
  //FLASH_Program HalfWord
  FLASH->CR |= FLASH_CR_PG;
  for(i=0; i<1024; i+=2)
  {
    while((FLASH->SR&FLASH_SR_BSY));
    *(__IO uint16_t*)(Page_127 + i) = i;
  }
  FLASH->CR &= ~FLASH_CR_PG;
  
  FLASH->CR |= FLASH_CR_LOCK;
  
  while(1);
}

На скриншотах результат работы программы.

Стерли страницу:

Стирание страницы

Заполнили страницу новыми данными:

Запись страницы

Как видим в данном примере производится запись данных размерностью 16 бит. Если необходимо записывать данные размерностью 32 бита, то можно разделить слово по 16 бит и дважды выполнить запись данных.

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