[i] Создание FLM.
Введение
Классическим периферийным блоком современного микроконтроллера является FLASH-память, которая позволяет хранить данные при отсутствии питания. Механизм её записи более сложный, чем ОЗУ-памяти, где можно просто по необходимому адресу выставить значения на шину данных, а по сигналу записи они защелкнутся памятью на хранение. Для записи данных во FLASH нужен определенный алгоритм программирования. Обычно такие программы называют загрузчиками. Эти файлы как раз и являются готовыми программами-прошивальщиками, которые позволяют скомпилированный образ программы загрузить во FLASH-память. Причем, стоит отметить, что они могут реализовывать абсолютно любой механизм для программирования любого типа памяти. Например, можно создать загрузчик для записи данных во внешнюю FLASH-память, подключенную по SPI или параллельному интерфейсу, или даже для записи данных во внешнюю OTP. Но самый распространенный вариант – это загрузчики для программирования внутренней-FLASH памяти. Посмотрим на две самые популярные среды разработки для микроконтроллеров с ядром Cortex-M – Keil uVision и IAR Workbench. Каждая из них имеет свой формат загрузчиков и примеры их реализации. У Keil uVision – FLM файлы, у IAR – файлы формата *.out (по сути ELF). Благодаря этим файлам, работая в среде, имеется возможность нажать «Download» и программа будет «прошита» в микросхему. Рассмотрим, как сделать реализацию такого файла загрузчика FLM для среды Keil, а именно сделаем такие файлы для прошивки внутренней FLASH-памяти для всей актуальной линейки микроконтроллеров Миландра.Структура FLM
FLM – он же Flash Programming Algorithm (именно так именуется официальной документацией CMSIS) по сути представляет из себя программу, которая загружается в ОЗУ контроллера, а затем получает данные для записи в память, то есть взаимодействует с тем или иным периферийным блоком. Получается, что, когда «прошивается» память контроллера, на самом деле загружается специальный алгоритм в ОЗУ-память микросхемы, который, взаимодействуя с контроллером FLASH-памяти, фактически организует процесс программирования микросхемы самой себя.Рисунок 1 - Окно выбора алгоритма программирования FLM в среде Keil
В среде Keil есть шаблонный проект для реализации FLM. Его можно найти в директории, где установлена среда, по пути: Keil_v5\ARM\Flash\_Template. Сама же директория Flash хранит в себе папки с исходниками FLM от популярных производителей микроконтроллеров и их производные – готовые файлы FLM. Шаблонный проект содержит следующие основные файлы:
-FlashDev.c
-FlashPrg.c
-директория Test,
а также файлы проекта среды. Для того, чтобы происходила генерация FLM-файла, в настройках проекта в окне USER, после сборки/компиляции (After Build/Rebuild) указывается следующая команда:
cmd.exe /C copy "Objects\%L" ".\@L.FLM"
В папке Test приведён пример тестирования функций, составляющих алгоритм программирования. К нему обязательно вернёмся, но позже. Сначала реализуем непосредственно алгоритмы.
Приведём список микроконтроллеров Миландр, в которых присутствует внутренняя FLASH-память, поскольку именно для них будем подготавливать новые FLM.
K1901ВЦ1QI;
К1986ВЕ1QI;
К1986ВЕ92QI;
К1986ВК214;
К1986ВК234.
FlashDev.c
В файле FlashDev.c содержатся структуры, описывающие информацию о памяти и самом микроконтроллере. Структуры проинициализированы для каждого контроллера и выбираются с помощью макросов условной компиляции. Объявление структуры содержится в FlashOS.H
Имеет следующий вид:
struct FlashDevice {
unsigned short Vers; // Version Number and Architecture
char DevName[128]; // Device Name and Description
unsigned short DevType; // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ...
unsigned long DevAdr; // Default Device Start Address
unsigned long szDev; // Total Size of Device
unsigned long szPage; // Programming Page Size
unsigned long Res; // Reserved for future Extension
unsigned char valEmpty; // Content of Erased Memory
unsigned long toProg; // Time Out of Program Page Function
unsigned long toErase; // Time Out of Erase Sector Function
struct FlashSectors sectors[SECTOR_NUM];
};
Все поля уже прокомментированы и сразу, в качестве примера, приведём инициализированную структуру для микроконтроллеров К1986ВЕ1QI:
struct FlashDevice const FlashDevice = {
FLASH_DRV_VERS, // Driver Version, do not modify!
"1986VE1/VE3 IAP 128kB Flash Rev_3", // Device Name
ONCHIP, // Device Type
0x00000000, // Device Start Address
0x00020000, // Device Size in Bytes (128kB)
0x1000, // Programming Page Size
0, // Reserved, must be 0
0xFF, // Initial Content of Erased Memory
5000, // Program Page Timeout 5 Sec
5000, // Erase Sector Timeout 5 Sec
// Specify Size and Address of Sectors
0x00001000, 0x00000000, // Sector Size 4kB (32 Pages)
SECTOR_END
};
FlashPrg.c
FlashPrg.c – это основной файл, где описаны функции для программирования. В шаблоне их представлено 7:
extern int Init (unsigned long adr, // Initialize Flash
unsigned long clk,
unsigned long fnc);
extern int UnInit (unsigned long fnc); // De-initialize Flash
extern int BlankCheck (unsigned long adr, // Blank Check
unsigned long sz,
unsigned char pat);
extern int EraseChip (void); // Erase complete Device
extern int EraseSector (unsigned long adr); // Erase Sector Function
extern int ProgramPage (unsigned long adr, // Program Page Function
unsigned long sz,
unsigned char *buf);
extern unsigned long Verify (unsigned long adr, // Verify Function
unsigned long sz,
unsigned char *buf);
По рекомендациям CMSIS обязательны к реализации 4, а именно:
int EraseSector (unsigned long adr);
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf);
int Init (unsigned long adr, unsigned long clk, unsigned long fnc);
int UnInit (unsigned long fnc);
А опциональны (не обязательны):
int EraseChip (void);
int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat);
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf);
Стоит обратить внимание на обязательность EraseSector (…) и необязательность EraseChip (…). К сожалению, в текущих официальных FLM для микроконтроллеров «Миландр» всегда был реализован EraseChip, а при выборе Erase Sectors сыпались ошибки, потому что данная функция просто не содержала какого-либо описания, хотя и обязательна (ранее функция просто сразу возвращала нужное значение).
Алгоритмы операций
Erase
Рассмотрим алгоритм использования FLM-файла средой Keil при операции стирания, которая может быть стиранием по секторам или стиранием всей микросхемы.Когда в опциях отладчика (Рисунок 1) выбрано стирание по секторам, то выполняется определенный алгоритм, в котором, очевидно, из 7 функций задействованы не только функции, относящиеся к стиранию. Согласно документации, CMSIS:
Рисунок 2 - Алгоритм операции стирания
FLM загружается в RAM-память. Далее исполняется функция Init() – процедура инициализации. Она необходима для отключения прерываний и настройки тактирования. Важно отметить, что программирование внутренней памяти выполняется при тактировании ядра от внутреннего HSI-генератора. Это позволяет всегда программировать микросхему независимо от того, установлен ли внешний источник частоты на плате или нет. Для микросхем К1986ВК214/234 у данной функции есть дополнительный функционал. Его рассмотрим отдельно. В зависимости от того, что выбрано в окне отладчика - Рисунок 1, поле Download Function (выбрать можно одну из 3-х опций: Erase Chip, Erase Sectors, Do not Erase) происходит стирание полное или по секторам.
При выполнении полного стирания вызывается функция EraseChip (), внутри которой происходит вызов библиотечной EEPROM_EraseAllPages() (драйвер MDR32F9Qx_eeprom.c официальной библиотеки), которая сотрет весь чип. Аппаратно микросхемы К1901ВЦ1QI, К1986ВЕ1QI, К1986ВЕ92QI поддерживают полное стирание всей памяти или стирание страницы. (К1986ВК214/234 вновь отличается – вернёмся к нему отдельно)
При процессе стирания по секторам, как видно из диаграммы рисунка 2, для начала вызывается функция BlankCheck (), которая выполняет роль проверки стертой страницы, сравнивая данные памяти по каждому адресу сектора со значением стертой ячейки. Если ячейка не пуста, то тогда уже вызывается функционал EraseSector(), внутри которого реализована библиотечная функция EEPROM_ErasePage(), потом проверяется следующая страница. Условимся, что в терминах документации на микроконтроллеры, стереть можно страницу, а в терминах функций FLM стирается сектор, поэтому пускай это будет одно и тоже. Если BlankCheck () сразу дает информацию о том, что страница пуста, то понятно, что EraseSector() не вызывается, что позволяет лишний раз не стирать микросхему, тем самым продлевая ресурс FLASH-памяти, который ограничен определенным количеством циклов перезаписи.
После следует вызов UnInit (), в которой происходит процедура деинициализации: возвращение настроек частоты, разрешение прерываний.
Programm/Verify
В алгоритме программирования данных, как и в случае стирания, также запускаются функции Init() и UnInit ().
Рисунок 3 - Алгоритм операции программирования
По сути, они являются сопутствующими для всех операций с памятью. В зависимости от того, в связке с какой процедурой - стиранием или программированием – вызываются, они могу давать разный функционал. Для этого на их вход и подается третий аргумент:
/*
* Initialize Flash Programming Functions
* Parameter: adr: Device Base Address
* clk: Clock Frequency (Hz)
* fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify)
* Return Value: 0 - OK, 1 - Failed
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {…}
Далее на рисунке 4 приведён алгоритм верификации, как он реализован и какие функции вызываются.
Рисунок 4 - Алгоритм операции верификации
Особенности работы с К1986ВК214/234
Как было подмечено ранее, у микросхем для электросчетчиков К1986ВК214/234 есть свои особенности. Уделим им особое внимание.
1. Контроллер FLASH-памяти, в отличие от микроконтроллеров К1901ВЦ1QI, К1986ВЕ1QI, К1986ВЕ92QI, не умеет стирать всю память, а только блок размером 32 Кбайта или страницу (которую принимаем за сектор) размером 512 байт. Поэтому для стирания всей памяти организован цикл последовательного стирания всех блоков:
for (i = 0; i < BLOCK_SIZE * CNT_BLOCK; i += BLOCK_SIZE)
EEPROM_EraseBlock(i, EEPROM_Main_Bank_Select);
Каждый блок имеет размер по 32 КБ. У контроллера К1986ВК234 4 блока, у К1986ВК214 всего 2.
2. Второе важное примечание заключается в том, что загрузчик всех 2-х номиналов микросхем расположен в информационной FLASH-памяти и его можно «затереть», работая с микросхемой. В связи с тем, что таблица векторов непереносима и всегда расположена с адреса 0x0, то с данного адреса может отображаться две области внутренней FLASH-памяти:
- EEPROM_Info_Bank (0x0000_0000 - 0x0000_1FFF) – область информационной FLASH-памяти с загрузочной программой объёмом 8 Кбайт;
- EEPROM_Main_Bank (0x0000_0000 - 0x0001_FFFF) – область основной FLASH-памяти программ объёмом 128 Кбайт. (64 Кбайта в случае, если это микросхема К1986ВК214)
Область памяти, которая отображается с адреса 0х0, определяется значением бита FPOR регистра BKP_REG_0E. А именно:
FPOR = 0 - EEPROM_Info_Bank
FPOR = 1 - EEPROM_Main_Bank.
Таким образом, при старте по питанию микросхемы всегда начинают выполнять программу с информационной памяти, где располагается загрузчик. Загрузчик считывает комбинацию на выводах MODE и, если она равна b’00, то устанавливается бит FPOR=1, происходит сброс и переключение памяти на основную. Подробнее об этом можно прочитать в статье про старт микроконтроллеров.
В связи с этими особенностями функционал FLM функции Init (…) при процедуре стирания памяти для микроконтроллеров К1986ВК234/214 расширен:
if (fnc == 1)
{
if ((MDR_BKP->REG_0E & BKP_REG_0E_FPOR) == 0x00000000)
{
for (i = 0; i < PAGE_SIZE * PAGES_TO_BOOT; i += PAGE_SIZE)
EEPROM_ErasePage(i, EEPROM_Info_Bank_Select);
for (i = 0; i < BOOT_SIZE; i++)
EEPROM_ProgramWord (i*4, EEPROM_Info_Bank_Select, BOOT_LOADER[i]);
}
}
Тут происходит проверка, установлен ли бит FPOR, и если нет, то стирается информационная память и в неё записывается стандартный бутлоадер, режимы которого указаны спецификации.
3. Последнее важное, на чем стоит застроить внимание — это процедура верификации. В виду того, что память отображается в адресном пространстве в зависимости от значения бита FPOR, выполняя процедуру верификации (то есть чтение и сравнение), можно получить ошибки. Например, если FPOR сброшен и загрузчик стартовый удален, то будет выполнено программирование основной памяти, а чтение данных для верификации будет происходить из информационной памяти, поскольку, как уже отмечено ранее, FPOR=0. Поэтому для микросхем К1986ВК234/214 реализован механизм верификации с помощью чтения регистровым доступом:
#define READ_BY_EEPROM
uint32_t ReadWord(uint32_t Address)
{
#ifndef READ_BY_EEPROM
return *((volatile uint32_t *)(Address));
#else
return EEPROM_ReadWord(Address, BANK_SELECTOR);
#endif
Тестирование
Для каждого микроконтроллера реализованы проекты тестирования разработанных FLM, которые проверяют функционал реализованных функций. Сам алгоритм использован тот, который предложен в шаблонном примере - FlashTest.c (шаблон от Keil).Каждый из тестовых примеров нужно запускать из ОЗУ-памяти микроконтроллера, контролируя в режиме отладки корректность выполнения реализованных алгоритмов.
Сохранить статью в PDF