Информационный портал технической поддержки Центра проектирования интегральных микросхем |
В статье Загрузка программы в ОЗУ и запуск через UART было рассказано о том, как загрузить программу в ОЗУ и запустить ее. Теперь рассмотрим создание программы для ОЗУ, которая умеет записывать массив данных из ОЗУ во FLASH память. Эту программу можно назвать "прошиватель". В массиве данных будет находиться программа мигания диодом. В итоге, после прошивки при запуске из Flash на плате будет мигать светодиод.
Создадим новый проект и назовем его "Flash_UartWriter". В библиотеках необходимо выбирать пункты - Startup, EEPROM, PORT, RST_CLK. Затем добавить в проект новый файл "main.c" (подробнее о создании нового проекта рассказано в статье Создаем новый проект)
Теперь установим настройки проекта для запуска в ОЗУ. Как это можно сделать описано в Запуск программы из ОЗУ в среде Keil. В данном случае, файл "setup.ini" можно не создавать и не подключать, так как не планируется запускать проект в отладчике.
Далее приведен листинг программы "прошивателя", состоящий из одного файла "main.c". Код собран из двух примеров:
Пример "Sector_Operations" при стандартной установке Keil можно найти по пути:
*Стандартный путь до пака*\1.xx\Examples\MDR32F9Q2I\EEPROM\Sector_Operations
Файл "main.c" представлен в фрагменте кода 1:
#include <MDR32FxQI_port.h>
#include <MDR32FxQI_rst_clk.h>
#include <MDR32FxQI_eeprom.h>
#define EEPROM_PAGE_SIZE (4*1024) // размер страницы flash памяти
#define EEPROM_ADDR_START 0x08000000 // адрес куда копировать данные
#define RAM_ADDR_START 0x20002000 // адрес откуда копировать данные
// Период мигания светодиодом для индикации текущего статуса
#define ST_DELAY_OK 2000000 // Успешное завершение
#define ERR_DELAY_CLEAR 500000 // Ошибка - страница памяти не стерлась
#define ERR_DELAY_VERIFY 100000 // Ошибка верификации памяти после записи
uint32_t Led_Pin = PORT_Pin_1; // Вывод индикации на второй светодиод, в HelloWorld используется PORT_Pin_0
uint32_t readAddr(uint32_t address); // Функция возвращает 32 битное слово, расположенное по указанному адресу
void Delay(int waitTicks); // Функция задержки из примера HelloWorld
void LedInit(void); // Функция инициализации PortC из примера HelloWorld, код ранее лежал в main()
void LedSetState(uint16_t ledOn); // Функция зажигает или гасит светодиод Led_Pin. Включим светодиод на время работы с Flash и выключим по окончании.
void LedShowStatus(uint32_t flashPeriod); // В Функции содержится бесконечный цикл мигания светодиодом с периодом flashPeriod из примера HelloWorld.
int main(void)
{
uint32_t Data = 0;
uint32_t i = 0;
// Тактирование EEPROM
RST_CLK_PCLKcmd(RST_CLK_PCLK_EEPROM, ENABLE);
//--------- Включаем светодиод - индикатор работы с EEPROM -------
LedInit();
LedSetState(1);
//------------ Стираем первую страницу в EEPROM ----------------
/* Erase main memory page MAIN_EEPAGE */
EEPROM_ErasePage (EEPROM_ADDR_START, EEPROM_Main_Bank_Select);
/* Check main memory page MAIN_EEPAGE */
Data = 0xFFFFFFFF;
for (i = 0; i < EEPROM_PAGE_SIZE; i += 4)
{
// При записи вся память страницы Flash заполняется единицами, т.е. значениями 0xFFFFFFFF.
// Проверяем так ли это
if (EEPROM_ReadWord (EEPROM_ADDR_START + i, EEPROM_Main_Bank_Select) != Data)
{
// Уходим в цикл мигания в случае ошибки с периодом ERR_DELAY_CLEAR
LedShowStatus(ERR_DELAY_CLEAR);
}
}
//------------ Запись программы в первую страницу EEPROM -------
/* Fill main memory page MAIN_EEPAGE */
for (i = 0; i < EEPROM_PAGE_SIZE; i += 4)
{
// Считываем по словам код программы HelloWorld, записанной по UART с адреса 0x20002000
Data = readAddr(RAM_ADDR_START + i);
// Записываем код в Flash память, начиная с адреса 0x08000000
EEPROM_ProgramWord (EEPROM_ADDR_START + i, EEPROM_Main_Bank_Select, Data);
}
/* Check main memory page MAIN_EEPAGE */
for (i = 0; i < EEPROM_PAGE_SIZE; i +=4 )
{
// Считываем по словам код программы HelloWorld, записанной по UART с адреса 0x20002000
Data = readAddr(RAM_ADDR_START + i);
// Считываем код программы из Flash памяти и сравниваем с кодом из ОЗУ
if (EEPROM_ReadWord (EEPROM_ADDR_START + i, EEPROM_Main_Bank_Select) != Data)
{
// Уходим в цикл мигания в случае ошибки с периодом ERR_DELAY_VERIFY LedShowStatus(ERR_DELAY_VERIFY);
}
}
//------------ Гасим светодиод -------
// LedSetState(0); // Выключаем светодиод по окончании работы с Flash
// Показываем длинными периодами мигания, что программа успешно закончила свою работу.
LedShowStatus(ST_DELAY_OK);
}
uint32_t readAddr(uint32_t address)
{
return (*(__IO uint32_t*) address);
}
void Delay(int waitTicks)
{
int i;
for (i = 0; i < waitTicks; i++)
{
__NOP();
}
}
void LedSetState(uint16_t ledOn)
{
if (ledOn) PORT_SetBits(MDR_PORTC, Led_Pin);
else
PORT_ResetBits(MDR_PORTC, Led_Pin);
}
void LedShowStatus(uint32_t flashPeriod)
{
// Запускаем бесконечный цикл обработки
while (1)
{
// Считываем состояние ввода PD0
// Если на выводе логический "0", то выставляем вывод в логическую "1"
if (PORT_ReadInputDataBit (MDR_PORTC, Led_Pin) == 0)
{
PORT_SetBits(MDR_PORTC, Led_Pin);
}
// Задержка
Delay(flashPeriod);
// Считываем состояние вода PD0
// Если на выводе = "1", то выставляем "0"
if (PORT_ReadInputDataBit (MDR_PORTC, Led_Pin) == 1)
{
PORT_ResetBits(MDR_PORTC, Led_Pin);
};
// Задержка
Delay(flashPeriod);
}
}
void LedInit(void)
{
// Заводим структуру конфигурации вывода(-ов) порта GPIO
PORT_InitTypeDef GPIOInitStruct;
// Включаем тактирование порта C
RST_CLK_PCLKcmd (RST_CLK_PCLK_PORTC, ENABLE);
// Инициализируем структуру конфигурации вывода(-ов) порта значениями по умолчанию
PORT_StructInit(&GPIOInitStruct);
// Изменяем значения по умолчанию на необходимые нам настройки
GPIOInitStruct.PORT_Pin = Led_Pin;
GPIOInitStruct.PORT_OE = PORT_OE_OUT;
GPIOInitStruct.PORT_SPEED = PORT_SPEED_SLOW;
GPIOInitStruct.PORT_MODE = PORT_MODE_DIGITAL;
// Применяем заполненную нами структуру для PORTC.
PORT_Init(MDR_PORTC, &GPIOInitStruct);
}
Данный код будет располагаться в ОЗУ, поскольку загружается через UART. По этой причине нет необходимости решать вопрос с расположением файла "MDR32FxQI_eeprom.c" в ОЗУ, как это было сделано в Расположение функций в ОЗУ, программирование EEPROM
Теперь необходимо получить bin-файл для программы, которую будем прошивать в Flash. Как это сделать, написано в статье Загрузка программы в ОЗУ и запуск через UART, "Получение bin файла":
открыть проект "HelloWorld";
в настройках проекта выбрать в Options - User;
найти пункт AfterBuild/Rebuild;
поставить галочку в опции Run #1;
дописать: $K\ARM\ARMCC\bin\fromelf.exe --bin --output=@L.bin !L
пересобрать проект ("F7");
в папке проекта найти файл "HelloWorld.bin".
Перед запуском "прошивателя" сотрем Flash память и убедимся, что в памяти пусто. В меню выбираем Flash - Erase. Для того чтобы данная операция отработала, к демо-плате должен быть подключен программатор в Jtag_B, переключатели Mode должны быть в режиме "000" и должно быть подано питание. Без установленного соединения, данный пункт меню не активен.
Рисунок 1 - Keil, меню "Flash"
Теперь можно нажать Reset или выключить-включить питание, чтобы убедиться, что светодиод не мигает. После работы "прошивателя", Reset и подача питания должны будут приводить к миганию светодиодом.
Для программирования микроконтроллера по UART необходимо выставить режим загрузки через UART, Mode = "110". Операции работы с UART загрузчиком были рассмотрены в статье Тестируем Bootloader в режиме UART. Далее загружаем обе программы - "HelloWorld" и "прошиватель" в ОЗУ.
Как было указано в коде, "прошиватель" будет копировать память с адреса 0x2000_2000. Сюда и необходимо загрузить файл "HelloWorld.bin". Сам же "прошиватель" запишем в адреса с 0x2000_0000, потому что так было настроено в опциях проекта. Запуск "прошивателя" также необходимо произвести с адреса 0x2000_0000. В свойствах bin файлов узнаем их размеры. Подробно действия описаны в статье Загрузка программы в ОЗУ и запуск через UART. Дополнительно загружается программа "HelloWorld".
В итоге информация, необходимая для загрузки:
Подключаем UART адаптер, подаем питание на плату, открываем программу "Terminal v1.9b". Макросы, используемые в этой программе, можно найти в статье Тестируем Bootloader в режиме UART. Необходимо добавить к ним следующие:
Код M10: загрузка HelloWorld.bin
L$00$20$00$20$CC$05$00$00 = L 0x00200020 0xCC050000 - младшими байтами вперед
Код M11: загрузка Flash_UartWriter.bin
L$00$00$00$20$DC$09$00$00 = L 0x00000020 0xDC090000
Код M12: запуск Flash_UartWriter
R$00$00$00$20 = R L 0x00000020
Важно не забыть снять галочку циклической посылки 0-ля после получения ответа от МК, иначе этот ноль продолжает посылаться, и весь последующий обмен через UART будет нарушен!
Протокол обмена в окне терминала выглядит так:
>R - т.е. получили приглашение и выполнили команду Run.
в Hex окне видим пришедшие данные:
0D 0A 3E = '>' 52 = 'R'
Для того, чтобы прошить Flash необходимо нажать Reset на плате для того, чтобы вернуться в UART-загрузчик, и начать все с начала:
В этот момент видим, что на плате зажегся светодиод. То есть запустился "прошиватель", и идет работа с Flash памятью. После короткого периода времени этот светодиод начинает медленно мигать, с периодом порядка 3 секунд. Это означает, что прошивка прошла успешно.
Если бы возникли проблемы, то светодиод мигал бы существенно быстрее. Для того чтобы различать, работает программа "прошиватель" или "HelloWorld", мигание в них реализовано разными диодами.
Рисунок 2 - Работа в программе Terminal, последовательность действий
Давайте проверим, как прошилась наша программа. Испробуем два варианта:
В обоих случаях убеждаемся, что программа прошита успешно. И после Reset и после сброса питания программа исполняется именно из Flash памяти, куда мы ее и записали.
Таким вот образом можно запросто прошить микроконтроллер через UART. В данном примере программа у нас была заведомо небольшая, поэтому мы прошивали только одну страницу Flash. Но при небольшой доработке программы "Прошиватель", можно организовать загрузку программ размера большего чем одна страница. Так же при прошивке программ больших, чем размер ОЗУ, можно побить исходный bin файл и прошить его частями.
Сайт: | https://support.milandr.ru |
E-mail: | support@milandr.ru |
Телефон: | +7 495 221-13-55 |