50251

[i] Создание FLM.

Дата последнего изменения: 30.11.2023 09:24:15
Материал из настоящей статьи, относящийся к микросхемам К1986ВЕ92QI и К1986ВЕ1QI, распространяется в том числе на микроконтроллеры К1986ВЕ92FI, К1986ВЕ92F1I, К1986ВЕ94GI и К1986ВЕ1FI, К1986ВЕ1GI

Введение

Классическим периферийным блоком современного микроконтроллера является 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).

Каждый из тестовых примеров нужно запускать из ОЗУ-памяти микроконтроллера, контролируя в режиме отладки корректность выполнения реализованных алгоритмов.
Исходные файлы алгоритмов FLM и файлы их тестирования для рассмотренных в статье микросхем могут быть предоставлены по запросу на адрес технической поддержки support@milandr.ru

Сохранить статью в PDF

Теги

Была ли статья полезной?