Этот клон Ардуино предлагает специальный бутлоадер, который позволяет заливать прошивку через USB, без использования внешних компонентов типа ST-Link или USB-UART переходника.
Сегодня мне понадобилось поработать с голым контроллером из-под CooCox и без stm32duino. Но вот в чем проблема. Даже простая моргалка лампочкой влитая через этот бутлоадер не работает.
Давайте разбираться. Возможно, мои выкладки покажутся кому-то банальностью. Но я только начинаю изучать контроллеры STM32 и на поиск проблемы убил как минимум полдня. Вдруг эта статья сократит кому-то время разработки.
Я ничего не имею против ST-Link и других отладчиков. Но в моем готовом устройстве его не будет, но точно будет USB. Почему бы сразу не заложить возможность обновлять прошивку через USB? Лично я нахожу этот способ удобным. тем более что все равно у меня уже подключен шнурок по которому идет питание и USB Serial.
Давайте посмотрим как работает бутлоадер. Для начала на примере контроллеров AVR. Почему я о нем вспомнил? Я переходил с Arduino и подсознательно ожидал такого же поведения. Но в STM32 оказалось все по другому. Потому хочу рассказать о разнице этих двух микроконтроллеров.
Итак. В микроконтроллерах AVR ATMega под бутлоадер можно зарезервировать некоторое количество памяти ближе к концу флеша. С помощью fuse битов можно регулировать с какого адреса будет стартовать программа. Если бутлоадера нет - программа стартует с адреса 0x0000. Если бутлоадер есть - он запускается с некоторого другого адреса (скажем, в ATMega32 с 0x3C00, если размер бутлоадера выбран 2к).
Когда бутлоадер сделал свои дела он передает управление основной программе с адреса 0x0000. Т.е. программа всегда стартует с адреса 0x0000. Компилятор и линковщик работают с учетом того, что код будет находится в начале адресного пространства.
В микроконтроллерах STM32 все не так. Все программы стартуют с адреса 0x0800000. Бутлоадер не является чем-то таким особенным. Это такая же программа, которая стартует с того же самого начального адреса. В процессе работы бутлоадер может принять прошивку (через USB или UART, считать с флешки, принять со спутника, достать из подпространства, whatever...) и записать ее по адресам выше чем находится сам загрузчик. Ну и, конечно же, в конце своей работы передать управление основной программе.
Так вот при компиляции прошивки нужно знать куда же бутлоадер запишет прошивку и соответствующим образом скорректировать адреса.
На этом с теорией все. Переходим к практике. Ниже пошаговая инструкция как прикрутить USB загрузчик к микроконтроллерам серии STM32F1xx, а может быть и к некоторым другим тоже.
Есть, правда, некоторые ограничения по схемотехнике. Тут я, к сожалению, не силен. ЯТП нужен подтягивающий резистор 1.5к для порта PA12 (он же USB D+). Это позволяет загрузчику в нужные моменты времени подключаться и отключаться от USB.
- Указать линкеру стартовый адрес. В CooCox это делается в настройках проекта, вкладка Link, раздел Memory Areas, Адрес IROM1 Start Address. Бутлоадер занимает первые 8 килобайт, значит стартовый адрес прошивки будет 0x0800000 + 0x2000 = 0x08002000. Поле Size, наверное, тоже стоит уменьшить на 8к.
- Где нибудь вначале программы перед инициализацией периферии сделать вызов
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
UPDATE 17.05.2018: В современной версии STM32Cube функции NVIC_SetVectorTable() нет. Вместо этого можно в файле system_stm32f1xx.c (или аналогичного для другого микроконтроллера) исправить дефайн VECT_TAB_OFFSET
"maple_upload.bat" COM20 2 1EAF:0003 "Path\To\Firmware.bin"
Вместо COM20 нужно подставить свой порт куда прицепился микроконтроллер.
Заливатор штука очень нежная, относительных путей не любит. так что путь к прошивке нужно указывать полностью.
1EAF:0003 - это VID и PID
2 - это параметр AltID, который указывает что прошивку нужно заливать по адресу 0x08002000 (читать ).
Из-за этого может возникнуть неудобство. Если микроконтроллер заглючил и повис, то он уже не слушает порт. Следовательно он не может услышать ключевую последовательность и перегрузиться в бутлоадер. Тогда только ресет в помощь.
На этом все. Надеюсь моя статья прольет свет на то, как работает загрузчик в STM32 и как можно загружать прошивку через USB порт. К сожалению порог вхождения по прежнему высок, но вдруг кому-то моя статья поможет его преодолеть.
Любой микроконтроллер stm32 можно прошивать через USART_1 и другие интерфейсы, подробно смотрите в . Для этого в МК есть специальный системный загрузчик, который зашивается в System memory (спец. область памяти) на этапе производства, его нельзя удалить или изменить. Это загрузчик инициализируется путём «подтягивания» пина BOOT_0 к «плюсу», после чего он ожидает поступления прошивки.
Через USART можно загружать любые.bin или.hex файлы.
Описание сделано на примере платы Blue Pill , однако всё сказанное справедливо для любого stm32 .
Фирменные платы типа Discovery и Nucleo тоже можно прошивать через USART.
Для работы потребуется USB to UART конвертер…
Если такой штуковины нет, то есть два варианта превращения ардуины в конвертер…
превращение
1
. Замкнуть RESET
на GND
(может не прокатить)
.
2
. Загрузить в неё вот такой незамысловатый скетч:
Void setup() { pinMode(0, INPUT); pinMode(1, INPUT); } void loop() {}
Чтобы проверить работает ли ардуина в качестве конвертера, замкните RX и TX, откройте и отправьте какие-нибудь символы. Если они вернулись, значит всё работает.
Перед прошивкой необходимо подтянуть пин BOOT0 к «плюсу», это переведёт МК в режим «системного бутлоадера». На описываемой плате это осуществляется перестановкой джампера…
Если плата фирменная
На фирменных платах (Discovery, Nucleo) тоже нужно подтянуть пин BOOT0 к плюсу, но просто так это сделать не получится. Возьмите схему вашей платы и найдите там соответствующие перемычки…
Искать надо что-то вроде этого:
Не помню от какой платы эта схема, но суть такая: в данном случае пин BOOT_0 подтянут к «минусу» через резистор R16 и перемычку SB16. Если убрать перемычку SB16, то пин подтянется к «плюсу» через резистор R17 и можно прошивать. После прошивки надо вернуть всё на место.
Перемычка может выглядеть как резистор.
Соединяем конвертер и STM следующим образом…
картинка
Конвертер RX
<-> PA9
STM
Конвертер TX
<-> PA10
STM
Конвертер GND
<-> GND
STM
… и подключаем конвертер и STM к компьютеру.
Инструкция по прошивке для
Если хотите, можете взять с сайта ST (в самом низу страницы).
Нажмите Reset на плате.
Перейдите в папку Flash Loader Demonstrator и запустите Flash Loader Demonstrator.exe
Выбираем СОМ-порт конвертера и жмем Next ...
WARNING Remove Protection (защита от записи)
Такое сообщение…
… означает, что плата, залочена. Ничего страшного нет, просто снимите защиту кнопкой Remove protection и следом нажмите ОК .
Защита снята.
Если выдаёт ещё какие-то ошибки, то либо не нажали Reset, либо что-то с конвертером.
Если светофор даёт зеленый свет, то смело жмите Next ...
Жмем Next ...
В пункте Download to device указываем путь к нужному.bin или.hex файлу и жмем Next ...
Всё готово, верните джампер в исходное положение и нажмите Reset.
Инструкция по прошивке для
Скачайте и распакуйте куда-нибудь.
Из папки соответствующей вашей ОС скопируйте файл stm32flash в домашнюю папку.
Эта утилита есть в репах ubuntu, в других дистрах не знаю, поэтому выкладываю.
Перед прошивкой всегда нажимайте Reset на плате.
В терминале дайте команду:
./stm32flash -w /home/dima/myfile.bin -v -g 0x0 /dev/ttyUSB0
Название и путь к файлу у вас будет свой. HEX-файлы тоже можно прошивать этой утилитой.
Если будет ругаться на права, тогда дайте команду…
Sudo chmod 777 /dev/ttyUSB0
… или запускайте утилиту через sudo .
Защита от записи
Если появилось вот такое сообщение…
… тогда дайте по очереди две команды:
./stm32flash -u /dev/ttyUSB0 ./stm32flash -k /dev/ttyUSB0
Первая снимает защиту от записи, вторая от чтения.
Если у Вас стоял ардуиновский загрузчик, то он будет затёрт, чтоб его востановить просто залейте описанным выше способом этот
Допустим вы выпустили на рынок своё устройство и опасаетесь того, что кто-то начнёт его копировать… Действительно - достаточно подключиться программатором к нашему устройству и считать прошивку, например, через утилиту ST-Link Utility (Target ⇒ Connect ).
Конечно можно просто оторвать ножки микроконтроллера… но разработчики ST Microelectronics предлагают альтернативу получше. У всех МК есть система защиты (read out protection). Суть её крайне проста - если в специальном регистре (Option bytes) установлено определённое значение - то возможность отладки и считывания прошивки отключается. В таком режиме у вас также пропадёт возможность перепрошивки МК. Разумеется, эту защиту можно отключить, поменяв значение в регистре Option bytes, однако в таком случае память программы будет затёрта, а значит её никто не сможет скопировать.
Поменять значение в регистре можно при помощи всё той же ST-Link Utility, Target ⇒ Option Bytes… ⇒ Read out protection ⇒ ENABLE .
![](https://i0.wp.com/66.media.tumblr.com/7e42f89de3d6bbf7277fcf5b296caaf2/tumblr_inline_ohie8nf8n61t55lnu_500.png)
Отключить, соответственно, можно аналогичным способом. Это не совсем удобно, если устройств много. Можно включить защиту программно.
Для реализации защиты удобно воспользоваться стандартной библиотекой периферии, а конкретно stm32f10x_flash.c содержит необходимую нам функцию. Сам регистр, а также работа с флеш-памятью описана в документе . Однако перед этим вспомним как реализована защита многократного включения заголовочных файлов. Мы использовали такие директивы препроцессора как:
- #define - директива указывает препроцессору заменять строку в файле, однако если аргумент (то что стоит в конце) не указывается сам идентификатор (то что по середине) остётся в системе и может быть проверен другими директивами (т.е. можно написать условие, при котором определённый код будет выполняться или наоборот);
- #ifndef - директиву можно прочитать как «если не определенно», однако нам больше подойдёт другая #ifdef - «если определенно»;
- #endif - директива указывающая конец условия.
Создадим идентификатор, который будет говорить, что данная сборка финальная. Пока данная строка закомментирована - код помещённый между #ifdef и #endif выполняться не будет.
//#define RELEASE
При конечной сборке достаточно просто раскомментировать строчку и прошить устройство.
#ifdef RELEASE // code here #endif
А теперь к самой сути. Мы уже разбирались как работать с внутренней флеш-памятью микроконтроллера. Перед записью битов защиты необходимо разблокировать доступ к памяти, затем необходимо произвести нужные операции и снова заблокировать доступ. Получается следующее:
#ifdef RELEASE #warning "Protection is ON. Debug is OFF" if (FLASH_GetReadOutProtectionStatus() == RESET) { FLASH_Unlock(); FLASH_ReadOutProtection(ENABLE); FLASH_Lock(); } #endif
Сегодня мы будем защищать прошивку на stm32 от считывания кул-хакерцами. Дабы не тянуть резину, вот кусок кода:
#ifdef NDEBUG
if (FLASH_GetReadOutProtectionStatus() == RESET)
{
FLASH_Unlock();
FLASH_ReadOutProtection(ENABLE);
FLASH_Lock();
}
#endif
Как не сложно догадаться, кусок использует библиотеку от stm. Такой кусок кода использовать очень удобно - контроллер сам устанавливает свою защиту при первом запуске, а первый запуск легко организовать после прошивки. Вот она, свобода от тирании фьюзов!
Да, я знаю, насчет холивора насчет «библиотека vs непосредственное обращение к регистрам». Моя позиция тут такова - если нужна большая скорость, или осталось совсем мало памяти, но нужно непосредственное обращение к регистрам. Если и скорость не важна и памяти много, лучше использовать библиотеку - так программа пишется быстрее и получается более читаемая.
Снять защиту
Контроллер то мы защитили. Но вот беда - нужно поправить нашу программу, а при установленной защите ничего с контроллером сделать нельзя - ни прочитать ни записать. Как снять защиту? Сразу предупреждаю - ребята из ST все сделали правильно и вместе с защитой уничтожается вся прошивка.Для снятия защиты нам понадобится программа st-link utility. Скачать ее можно .
Запускаем программу и выбираем вот этот пункт, либо просто жмем ctrl-b:
В поле «состояние защиты от чтения выбираем выключено». И нажимаем кнопку Apply:
Вместе с битом защиты стирается и вся память:
Защищайтесь, мы за безопасный эмбед.
В своем проекте я использую микроконтроллер STM32F103C8 и фреймворк stm32duino . Этот клон Ардуино предлагает специальный бутлоадер, который позволяет заливать прошивку через USB, без использования внешних компонентов типа ST-Link или USB-UART переходника.
Сегодня мне понадобилось поработать с голым контроллером из-под CooCox и без stm32duino. Но вот в чем проблема. Даже простая моргалка лампочкой влитая через этот бутлоадер не работает.
Давайте разбираться. Возможно, мои выкладки покажутся кому-то банальностью. Но я только начинаю изучать контроллеры STM32 и на поиск проблемы убил как минимум полдня. Вдруг эта статья сократит кому-то время разработки.
Я ничего не имею против ST-Link и других отладчиков. Но в моем готовом устройстве его не будет, но точно будет USB. Почему бы сразу не заложить возможность обновлять прошивку через USB? Лично я нахожу этот способ удобным. тем более что все равно у меня уже подключен шнурок по которому идет питание и USB Serial.
Давайте посмотрим как работает бутлоадер. Для начала на примере контроллеров AVR. Почему я о нем вспомнил? Я переходил с Arduino и подсознательно ожидал такого же поведения. Но в STM32 оказалось все по другому. Потому хочу рассказать о разнице этих двух микроконтроллеров.
Итак. В микроконтроллерах AVR ATMega под бутлоадер можно зарезервировать некоторое количество памяти ближе к концу флеша. С помощью fuse битов можно регулировать с какого адреса будет стартовать программа. Если бутлоадера нет - программа стартует с адреса 0x0000. Если бутлоадер есть - он запускается с некоторого другого адреса (скажем, в ATMega32 с 0x3C00, если размер бутлоадера выбран 2к).
Когда бутлоадер сделал свои дела он передает управление основной программе с адреса 0x0000. Т.е. программа всегда стартует с адреса 0x0000. Компилятор и линковщик работают с учетом того, что код будет находится в начале адресного пространства.
В микроконтроллерах STM32 все не так. Все программы стартуют с адреса 0x0800000. Бутлоадер не является чем-то таким особенным. Это такая же программа, которая стартует с того же самого начального адреса. В процессе работы бутлоадер может принять прошивку (через USB или UART, считать с флешки, принять со спутника, достать из подпространства, whatever...) и записать ее по адресам выше чем находится сам загрузчик. Ну и, конечно же, в конце своей работы передать управление основной программе.
Так вот при компиляции прошивки нужно знать куда же бутлоадер запишет прошивку и соответствующим образом скорректировать адреса.
На этом с теорией все. Переходим к практике. Ниже пошаговая инструкция как прикрутить USB загрузчик к микроконтроллерам серии STM32F1xx, а может быть и к некоторым другим тоже.
Есть, правда, некоторые ограничения по схемотехнике. Тут я, к сожалению, не силен. ЯТП нужен подтягивающий резистор 1.5к для порта PA12 (он же USB D+). Это позволяет загрузчику в нужные моменты времени подключаться и отключаться от USB.
- Указать линкеру стартовый адрес. В CooCox это делается в настройках проекта, вкладка Link, раздел Memory Areas, Адрес IROM1 Start Address. Бутлоадер занимает первые 8 килобайт, значит стартовый адрес прошивки будет 0x0800000 + 0x2000 = 0x08002000. Поле Size, наверное, тоже стоит уменьшить на 8к.
- Где нибудь вначале программы перед инициализацией периферии сделать вызов
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
"maple_upload.bat" COM20 2 1EAF:0003 "Path\To\Firmware.bin"
Вместо COM20 нужно подставить свой порт куда прицепился микроконтроллер.
Заливатор штука очень нежная, относительных путей не любит. так что путь к прошивке нужно указывать полностью.
1EAF:0003 - это VID и PID
2 - это параметр AltID, который указывает что прошивку нужно заливать по адресу 0x08002000 (читать ).
Из-за этого может возникнуть неудобство. Если микроконтроллер заглючил и повис, то он уже не слушает порт. Следовательно он не может услышать ключевую последовательность и перегрузиться в бутлоадер. Тогда только ресет в помощь.
На этом все. Надеюсь моя статья прольет свет на то, как работает загрузчик в STM32 и как можно загружать прошивку через USB порт. К сожалению порог вхождения по прежнему высок, но вдруг кому-то моя статья поможет его преодолеть.