сайт для палких паяльників

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

Что такое Bootloader?

Bootloader (загрузчик) – это программа, которая находится в памяти микроконтроллера. Основная функция загрузчика – записать программу (прошивку) во флэш память микроконтроллера. То есть, прошивка микроконтроллера. Используется когда нужно дать возможность обновить прошивку конечному пользователю прибора без использования программатора.

Первичный Bootloader

В STM32 есть Bootloader который зашит в память микроконтроллера на этапе производства. Находится он в системной памяти микроконтроллера. Его невозможно стереть или подменить. Когда мы прошиваем микроконтроллер через UART, прошивкой занимается именно первичный Bootloader.

Вторичный Bootloader

Зачем нужен свой собственный Bootloader? Когда возникает вопрос об организации обновления прошивки в готовых устройствах. Вы, как разработчик, можете подключить программатор или USB-UART переходник и перепрошить микроконтролр без проблем. А что делать пользователям у которых программатора и не должно быть? Надо найти более удобный путь. В таких случаях и надо использовать свой Bootloader, который сможет обновить прошивку без применения программатора.

Например, в устройстве используется Bluetooth модуль, в этом случае можно обновить прошивку, например, с мобильного телефона. Если используется WiFi модуль, можно вообще обновить прошивку через Интернет. Если устройство использует SD-карту, можно записать на нее новую прошивку и Ваш Bootloader зашьет ее в микроконтроллер. Некоторые микроконтроллеры имеют USB, поэтому логично использовать USB для обновления прошивки. Если есть необходимость защитить коммерческий проект и защитить прошивку от распространения, но в то же время позволить свободно обновлять ее пользователям только Вашего устройства, можно шифровать прошивку. Написанный Вами Bootloader будет расшифровывать ее в момент прошивки. Защитив память микроконтроллера и выполнив другие действия, связанные с безопасностью, можно сделать невозможным копирование прошивки.

Наиболее универсальным для решения таких задач, как мне кажется, есть USB интерфейс. В этой статье мы используем именно USB для демонстрации работы Bootloader-а. Некоторые микроконтроллеры имеют встроенный USB DFU (Device Firmware Upgrade). Для прошивки через USB надо установить драйверы и использовать для прошивки специальное программное обеспечение.

У микроконтроллера STM32F103, который я использую для примеров, USB DFU нет, но есть интерфейс USB. Для примера мы напишем собственный Bootloader, который будет обновлять прошивку микроконтроллера именно через этот интерфейс. В качестве подопечного будет, уже знакомый модуль с микроконтроллером STM32F103.

Свой собственный Bootloader. Как это работает

Поскольку мы не можем перепрошить первичный Bootloader микроконтроллеров STM, наш Bootloader будет находиться в памяти программ. Фактически Bootloader – это Ваша программа, которая будет заниматься процессом прошивки. То есть, при включении микроконтроллера будет запускаться именно Bootloader, как обычная программа. Конечно, с самого начала Bootloader должен выяснить что ему делать. Заливать новую программу или передать управление уже залитой программе. Это может быть проверка наличия прошивки на носителе, или проверка какой-то кнопки или перемычки. Второй метод мне нравится больше потому что так можно избежать ненужных задержек при старте. Нажата кнопка при включении прибора – идем обновляться. Не нажата – работаем как обычно. Откуда Bootloader будет забирать прошивку и начиная с какого адреса записывать во Flash микроконтроллера, это полностью ваши проблемы, как разработчика. И мы рассмотрим решение этих вопросов на примере. В зависимости от реализации конкретного прибора и наличии у него определенной периферии, прошивку можно передавать в микроконтроллер через интерфейс UART, IIC, SPI, USB, и тому подобное.

Конечно, Bootloader будет занимать определенную часть памяти. Для основной программы останется не занятая загрузчиком память. Вы должны четко знать, сколько места занимает Ваш Bootloader и начиная с какого адреса во Flash будет находиться Ваша основная программа. Стандартно для STM32F103, программы находятся начиная с адреса 0x08000000. Начиная именно с этого адреса будет расположен Bootloader. Если наш Bootloader будет занимать 10Кб. Тогда главную прошивку надо расположить начиная с адреса 0x08002800. То есть, с отступом в 10Кб. При этом отступ должен быть кратным размеру страницы Flash. Вспомним как организована Flash память микроконтроллера. Надо четко помнить, что Flash разделена на страницы. В нашем контроллере (STM32F103) размер страницы 1Kб. Адрес, начиная с которого будет расположена основная прошивка, должен указывать на начало незанятой Bootloader-ом страницы. Например, если Bootloader будет размером 10300 байт. Он полностью займет первые 10 страниц (10240 байт) и 60 байт одиннадцатой страницы. Мы должны расположить основную прошивку начиная с 12-той страницы (адрес: 0x08003000). Почему мы не можем занять часть одиннадцатой страницы? Она почти вся свободная! Перед записью во флэш надо сначала стереть страницу. При этом стирается страница целиком. Нет возможности стереть часть страницы. Об этом говорилось в статье STM32. Программирование STM32F103. Flash. Если мы укажем адрес, где должна начинаться основная прошивка с середины одиннадцатого страницы, тогда при обновлении прошивки Bootloader-у нужно будет перед записью стереть страницу 11. Это именно та страница где находится его последние байты. Он сотрет страницу целиком. Таким образом он может “выстрелить себе в ногу”. Поэтому основная прошивка должна находиться в страницах памяти не занятых Bootloader-ом.

Bootloader в режиме USB Mass Storage

Меня привлекает Bootloader в режиме USB Mass Storage. При подключении к USB компьютера, микроконтроллер должен быть для операционной системы как USB Mass Storage Device. То есть, как небольшая флешка в несколько килобайт. Для обновления прошивки достаточно записать на нее новый файл. Это, по моему мнению, самый приемлемый метод обновления прошивки для конечного пользователя. Во-первых он не требует специализированного программного обеспечения, во-вторых должен работать на любой операционной системе где существует понятие файловой системы. Это был бы идеальный вариант. Но это в теории все красиво, на практике все несколько сложнее.

Сначала я нашел такую ​​реализацию: http://blog.myelectronics.com.ua/stm32-usb-mass-storage-bootloader/ Но для нашего микроконтроллера такое решение неприемлемо. В этом решении Flash память микроконтроллера разделена на несколько частей. А именно на части где лежит Bootloader, где находится основная прошивка, и область памяти отведенная под MASS STORAGE. Прошивка сначала записывается на MASS STORAGE диск. А потом копируется в нужную область. То есть прошивка должна быть меньше половины памяти микроконтроллера. Это очень мало, когда у нас всего 64 Кб.

Поясню. У нас всего 64 Кб, пусть 10Кб займет Bootloader, остается 54Кб. Половину отдадим под Mass Storage, это будет 27Кб. Из этого объема FAT12 откусит 20Кб, останутся 7Кб под файл прошивки. То есть на Mass Storage не влезет файл больше 7Кб. Если оптимизировать распределение памяти и отдать под Mass Storage 38Кб, тогда под программы останется 16 Кб. Это для нас тоже не приемлемо. Поэтому такое решение для нашего микроконтроллера, с небольшим объемом Flash-а, не подходит. Но поблагодарим автора! Именно фрагменты этого проекта я использовал для создания примера Bootloader-а описанного в этой статье.

Дальнейшие поиски привели меня к Bootloader-у описанного в статье http://easyelectronics.ru/proshivka-arm-cortex-m3-na-primere-stm32-i-lpc1300.html Шикарный замысел, но реализация полностью не приемлема. Контроллер действительно видно как MASS STORAGE диск, на него вроде бы можно залить новую прошивку, но корректно это происходит только когда файл копировать программой FAR. И только FAR! Удивительно, но простое копирование файла происходит по-разному если это делать разными файл-менеджерами. Операционная система Ubuntu вообще не смогла смонтировать устройство. Поправить что-то в этом Bootloader-е нельзя, поэтому эта реализация тоже была отвергнута. Пришлось писать свой Bootloader.

Почему именно USB Mass Storage? Скажем, я делаю не коммерческий проект и хочу чтобы для обновления прошивки конечным пользователям не нужно было искать специфический софт и драйверы. Для работы с Mass Storage устройствами операционные системы используют стандартные драйверы, поэтому с драйверами проблем вообще нет. Обновление прошивки может происходить на разных операционных системах поэтому желательно чтобы программы для обновления прошивки вообще не надо было, или использовались стандартные решения. В этом случае простое копирование файла – наиболее красивое решение.

Забегая вперед скажу, что не все так просто. Мне пока не удалось реализовать этот замысел в идеальном виде. Поэтому, желательно чтобы процедура заливки прошивки была более или менее стандартной и, как я сказал, можно было обойтись стандартными командами или стандартным программным обеспечением.

Продолжаем разбираться …

USB Mass Storage пример

Перед тем, как делать Bootloader в режиме USB Mass Storage сначала реализуем работу микроконтроллера в режиме USB Mass Storage, чтобы понять как это работает. Заставим контроллер работать как маленькая флешка. Возьмем пример из библиотеки STM32_USB-FS-Device_Lib_V4.0.0. Я писал о USB и использовании STM32_USB-FS-Device_Lib. Ссылка чтобы скачать пример USB Mass Storage: https://github.com/avislab/STM32F103/tree/master/Example_USB_Mass_Storage.

В этом примере под данные отведено 54Кб. Первые 10Кб отведено под программу (в дальнейшем это будет наш bootloader).
Смотри настройки в файле mass_mal.h:

#define FLASH_DISK_START_ADDRESS 0x08002800 /* Flash start address */
#define FLASH_DISK_SIZE 55296
#define FLASH_PAGE_SIZE 1024 /* 1K per page */

Запрограммируем микроконтроллер и подключим его USB шнуром к компьютеру. Компьютер должен увидеть устройство и использовать для общения с ним стандартные драйверы. При этом Windows смонтирует его как диск. При переходе на него, Windows скажет что диск не отформатирован. Это нормально. Отформатируйте его. Теперь на него можно писать файлы. Правда, небольшие. Давайте посмотрим какой объем нам доступен. 54Кб? Нет, меньше! А почему? Потому что на “диске” должна располагаться таблица FAT. То есть, мы никак не сможем записать на него файл 54Кб.

Давайте покопаемся в коде и разберемся как выполняется работа с Mass Storage. Оказывается микроконтроллер вообще ничего не знает о FAT, кластерах и так далее. Он работает только как посредник – передает данные с USB во Flash, и с Flash в USB. А куда и что писать, собственно, решает операционная система.

Отдавать 20Кб флэш памяти микроконтроллера для FAT – это многовато. А что вообще можно придумать для реализации Bootloader-а в режиме USB Mass Storage? Фактически нам нужно выполнять только одну операцию – копирование одного файла. Можно даже с фиксированным именем. Можно обманывать операционную систему и программно формировать данные FAT, но фактически не хранить ее. Для одного файла в корне с фиксированным именем это вполне реальная идея для экономии памяти. При копировании файла на наш USB Mass Storage надо будет лишь записать данные во флэш. Но я прикинул сколько придется углубляться в “муляж” FAT12 и сколько выгребать “граблей”, что решил пока отложить этот замысел. Решим задачу более простым путем.

Я не нашел в Интернете чтобы так кто-то делал, поэтому говорю что это моя собственная идея. Мне приятно так думать, но у меня есть подозрение что я не первый такой хитрый 🙂

Главная идея нашего USB Mass Storage Bootloader

Итак, поскольку операционная система видит наше устройство как диск, просто задампим (стандартным ПО) на него наш bin – файл. Файл ляжет во флеш в аккурат начиная с нужного нам адреса (в нашем случае 0x08002800). То есть, любая утилита, которая может заливать образ диска на флешку сможет залить нашу прошивку. Мы просто подсунем программе вместо образа диска нашу прошивку в формате bin. Да, система не увидит на нем файловую систему, но нам этого и не надо? Главная задача будет решена – прошивка будет обновлена.

Почему bin – файл а не, скажем HEX? Именно bin-файл – бинарный файл, который фактически является дампом памяти для микроконтроллера.

Дамп залить можно стандартной командой dd (под Ubuntu). Под Windows можно использовать очень популярную программу Win32DiskImager для записи образов дисков на Flash-носители.

Для проверки этого метода можно посмотреть как выглядит программа во флэш памяти микроконтроллера с помощью программатора и ST-LINK Utility. Залейте bin – файл обычным программатором и описанным мной способом и сравните. Результат будет идентичным!

Команда для заливки на Mass Storage bin-файла под Ubuntu:

sudo dd if=my.bin of=/dev/sdc

Где:
my.bin – файл прошивки в формате bin
/dev/sdc – USB Mass Storage устройство

Выяснить имя устройства можно с помощью команды:

sudo fdisk -l

Я знаю точный размер, поэтому можно грепнуть по размеру:

sudo fdisk -l | grep 55296

или найти по ID производителя:

lsusb -d 0483:5720

Под Windows все проще. Скачиваем и устанавливаем программу Win32DiskImager. Выбираем bin – файл для заливки, диск на который будем заливать и нажимаем “Write”.

Итак, наш USB Mass Storage, который теперь играет роль Bootloader-а, прошивку уже обновляет. Теперь осталось доделать USB Mass Storage пример таким образом, чтобы он мог передавать управление залитой программе. Я это сделал с помощью кнопки. Когда мы подключаем микроконтроллер к компьютеру USB кабелем, и при этом нога PB1 замкнута на землю, контроллер стартует как USB Mass Storage устройство и можно заливать прошивку. Если во время старта PB1 на землю не замкнута, а висит в воздухе, или подключена к +, микроконтроллер передает управление залитой прошивке.

Вот как выглядит код нашего Bootloader-а (доработанного примера USB Mass Storage):

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_rcc.h"
#include "misc.h"

#include "usb_lib.h"
#include "hw_config.h"
#include "usb_pwr.h"
#include "mass_mal.h"

#define BUTTON_PIN	GPIO_Pin_1

void GoToUserApp(void)
{
	u32 appJumpAddress;
	void (*GoToApp)(void);

	appJumpAddress = *((volatile u32*)(FLASH_DISK_START_ADDRESS + 4));
	GoToApp = (void (*)(void))appJumpAddress;
	SCB->VTOR = FLASH_DISK_START_ADDRESS;
	__set_MSP(*((volatile u32*) FLASH_DISK_START_ADDRESS)); //stack pointer (to RAM) for USER app in this address
	GoToApp();
}

int main(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	/* Initialize Button input PB */
	// Enable PORTB Clock
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	/* Configure the GPIO_BUTTON pin */
	GPIO_InitStructure.GPIO_Pin = BUTTON_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	if (GPIO_ReadInputDataBit(GPIOB, BUTTON_PIN) == Bit_SET) {
		// Go To Application
		GPIO_DeInit(GPIOB);
		GoToUserApp();
	}
	else {
		// Initialize Mass Storage
		Set_System();
		Set_USBClock();
		USB_Interrupts_Config();
		USB_Init();
		while (bDeviceState != CONFIGURED);

		while (1)
		{

		}
	}
}

Проект можно скачать здесь: https://github.com/avislab/STM32F103/tree/master/Example_Bootloader

Коррекция проектов для работы с Bootloader-ом

Поскольку наша основная программа теперь будет находиться в памяти начиная с адреса 0x08002800, а не 0x08000000, это надо указать в свойствах проекта в настройках Linker-а.

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

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x08002800);

Это все. Теперь компилируем проект и получаем готовый для заливки bin-файл.

Пример проекта можно скачать здесь:
https://github.com/avislab/STM32F103/tree/master/Example_First_Programm_for_Bootloader

Резюме

Как это выглядит для разработчика

  • Заливаем обычным программатором в микроконтроллер Bootloader как обычную программу;
  • Готовим и компилируем проект с учетом адреса начиная с которой будет находиться основная программа (зависит от размера Bootloader-а);
  • Готовый bin-файл заливаем в микроконтроллер как это будет делать конечный пользователь. Смотри ниже …

Как это выглядит для пользователя

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

  • Получает от разработчика новый bin-файл;
  • Подключает устройство USB-кабелем к компьютеру при этом удерживает нажатой кнопку. Это может быть любая кнопка управления вашим устройством.
    Если в устройстве не используются кнопки, это может быть какой-то переключатель или перемычка, которые дадут сигнал Bootloader-у что надо перейти в режим обновления прошивки. В примере Bootloader-а надо замкнуть PB1 на землю. Bootloader переходит в режим USB Mass Storage, операционная система видит диск размером 54Кб;
  • с помощью программы Win32DiskImager (Windows) или dd (для Ubuntu) пользователь записывает bin-файл на диск;
  • отключает устройство от компьютера и включает. Новая прошивка работает.

Таким образом конечному пользователю для обновления прошивки не нужен программатор, не нужно устанавливать драйверы, а для обновления прошивки достаточно использовать стандартные команды (для Windows – почти стандартное программное обеспечение). Что более чем достаточно для не коммерческого продукта. Вы можете модифицировать и оптимизировать этот пример для решения своих задач.

Успехов!

Смотри также:

22 комментария: 24. STM32. Программирование STM32F103. Bootloader

  • Katbert говорить:

    Привет!
    Случайно совершенно наткнулся на эту статью!
    Рад слышать, что моя реализация (STM32 USB Mass Storage Bootloader) была полезна =)
    Да, память она жрет. И самое печальное, что не получится прошить бинарник, чей размер > 0.5 FLASH_SIZE. Хотя я в основном использую самые старшие камни f103 серии, а это 1МБ флеша, так что можно себе позволить. К тому же, mass storage диск использую еще много для чего – разные файлы конфигурации, ключи шифрования и т.д.

    Может быть есть еще какие-то идеи, как совсем доделать до ума этот бут? У меня одним из условий было, что вообще нельзя использовать никакой софт сторонний. Заливка прошивки – drag-and-drop…

    • andre говорить:

      Должен признать, Ваш STM32 USB Mass Storage Bootloader – это лучшее, что я смог найти. И кстати, использование mass storage для фйлов – это очень крутая фишка. Это просто великолепное решение! И если я буду использовать контроллеры помощнее, то несомненно я воспользуюсь именно Вашей реализацией как наиболее удобной. Если появятся идеи, я обязательно Вам напишу. Огромное Вам спасибо.

  • Сергей К. говорить:

    (Дублирую тут комментарий с Youtube для истории.)

    Спасибо за статью! Я работаю в Eclipse Neon.3 + GNU ARM, под неё и адаптировал Ваш проект. Обнаружил проблему – если я загружаю по USB прошивку, собранную как Release, всё работает отлично, однако Debug сборка того же исходного текста не работает. К сожалению, системных отдадчиков пока не имею, поэтому отследить причину не могу. Может быть Вы сталкивались с подобным?
    Еще наверное Вам будет интересно, что применив оптимизационные флаги -Os и -flto, мне удалось уместить bin файл загрузчика в 8кб (8 040 байт).

  • Стас говорить:

    Добрый день, эта строчка в функции перехода лишняя
    SCB->VTOR = FLASH_DISK_START_ADDRESS
    Сдвигать вектора надо в начале основной программы.

    • andre говорить:

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

  • Стас говорить:

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

    • andre говорить:

      Есть. Несколько примеров рассмотрены в этой стаье: http://www.avislab.com/blog/stm32-usb_ru/
      В статье есть примеры подключения STM32 к USB в качестве виртуального последовательного порта,
      эмуляция клавиатуры и мышы, USB Mass Srorage.

  • Микита говорить:

    Обязательно ли должен присутствовать внешний на кварц на МК для прошивки через Бутлоадер?

    • andre говорить:

      Без кварца не пробовал. Крайне рекомендуется использовать кварц для тактирования МК, когда речь идет о подключении его к другому устройству. Кварц гарантирует большую стабильность частоты чем внутренний RC генератор, и меньше неожиданных проблем.

  • Виктор говорить:

    Я, по мотивам STM32 USB Mass Storage Bootloader, писал bootloader для SAM7.
    Пошел немного другим путем, а именно прошивку “записываю” в виде hex-файла, в нем, как известно есть адреса, куда шить. Файл на лету парсится и шьется нужная область памяти.

  • Сергей говорить:

    А как насчет допилить бутлоадер так, чтобы масс сторэдж диск был на внешней сд карте или сменном чипе и прошивка ложилась именно на него?

  • vadim говорить:

    можно одну ногу контроллера вместо кнопки к питанию юсб подключить, смысл в том что при питании с юсб включался режим прошивки, а при питании от других бп включался основной режим работы

  • АНДРЕЙ говорить:

    Спасибо за статьи, очень выручают. Пробовал
    Бутлоадер и ком порт, легко переносятся на другой проц, у меня f103rc 12MHz, но вот захотел скрестить твои проекты, Бутлоадер работает, а ком порт пишет не известное устройство, дай пинка в нужную сторону, где что проверить. Заранее спасибо.

  • АНДРЕЙ говорить:

    Спасибо за статьи, помогают разобраться. Пробовал Bootloder и ком порт. Получилось перенести на свой проц у меня f103rc 12MHz, работают на ура. Захотел скрестить и не получается. Bootloader работает, а на ком порт пишет неизвестное устройство. Может дашь пинка в нужную сторону. Где что посмотреть проверить?

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *

 
Translate
Архіви

© 2011-2017 Андрій Корягін, Кременчук - Київ, Україна