Pull to refresh

Полноценная GDB отладка через USB на плате BluePill (STM32F103С8T)

Reading time5 min
Views12K

В данной статье речь пойдет о программировании и полноценной отладке микроконтроллера STM32F103C8T6 через USB.

Однажды, от коллег поступило предложение о участии в IoT проекте. Система предусматривала однопоточный запуск скриптов. Отладка производилась с помощью логов. И тут мне в голову пришла мысль о полноценной удаленной отладке проектов под микроконтроллеры.

Для начала, нужно было опробовать все на прототипе. В качестве отладочной платы была выбрана почти всем знакомая BluePill на микроконтроллере STM32F103. Поскольку на данной отладке имеется интерфейс MicroUSB, было принято решение в прототипе использовать именно этот интерфейс. В будущем предполагался переход на UART подключенный к GSM модулю.

Требовалось реализовать загрузчик, имеющий несколько функциональных блоков. Задачу можно разбить на следующие подзадачи:

  1. Реализация драйвера интерфейса USB со стороны микроконтроллера.

  2. Разработка кода обновления прошивки микроконтроллера с помощью GDB.

  3. Разработка GDB сервера.

  4. Вывод отладочных логов.

Обо всем по порядку. Для прототипирования был реализован загрузчик (bootloader).

1. Первым был реализован протокол отладочного интерфейса. Т.е. USB. В качестве класса USB-устройств был выбран WinUSB. Для его реализации я воспользовался исходным кодом библиотеки libopencm3. Для этого необходимо описать дескриптор устройства, дескрипторы конфигурации, интерфейса, конечных точек, а так же дескрипторы содержащие строки "MSFT100" и "WINUSB". Последние два дескриптора требуются для определения устройства как WinUSB. Конфигурация конечных точек (USB-Endpoint) выбрана следующим образом control endpoint 0, bulk out endpoint 1, bulk in endpoint 81, bulk in endpoint 82. Конечная точка с номером ноль присутствует во всех устройствах USB, endpoint 1- применяется для передачи команд в загрузчик микроконтроллера, endpoint 81 - для передачи ответов на команды на компьютер, а 82 - для передачи текстовой информации (логов). Подробнее о USB можно прочитать в публикациях из разряда "USB in a NutShell".

2. Требовался код работы с флеш памятью. Вы можете подумать что тут все просто. Это так и не так одновременно. Первая проблема, которая возникла,- невозможность стереть флеш память в обработчике прерывания. Дело в том, что архитектура Cortex M предусматривает два режима работы процессора. Thread и Handler. В первом режиме процессор находится после старта, а так же когда нет активных прерываний. В Handler mode исполняются все обработчики исключений и прерываний. К сожалению, стирание flash-памяти на STM32F103C8T6 в Handler режиме приводит к корректному статусу стирания памяти, но сама память не стирается.

Эта проблема решается посредством запуска кода стирания Flash в Thread режиме. Сделать это можно, так, как обычно происходит в операционных системах. Для этого нужно понимать что такое контекст потока. Контекст потока, - это состояние набора регистров процессора, стека, описывающее конкретный момент работы системы. При входе в обработчик прерывания контекст текущего работающего потока сохраняется, а при выходе из обработчика, он восстанавливается и выполнение программы продолжается. При переключении задач в ОС, по определенному алгоритму, в определенный момент, восстанавливается контекст текущей активной задачи. Нам, при выходе из обработчика, нужно лишь восстановить "свой" контекст, для вызова функции стирания Flash памяти.

Другая проблема является более сложной. Заключается она в том, что, при работе с флеш памятью может происходить выполнение обработчика прерывания, той прошивки, которая находится под отладкой. Эта проблема решается несколькими действиями перед стиранием памяти. Первое что требуется сделать,- заблокировать вызов любых обработчиков прерываний, используемых в отлаживаемой прошивке. Или проще говоря тех, которые не используются в Bootloader-e. Но даже в этом случае команда на стирание памяти может поступить в то время, когда один из обработчиков уже выполняется. Для решения этого вопроса я решил воспользоваться пошаговым режимом работы процессора и "по шагам" вывести процессор из всех обработчиков прерываний. После этого флеш-память можно стирать.

3. Требовалось реализовать GDB-сервер. Я воспользовался исходным кодом проекта BlackMagic, для обработки команд приходящих из среды разработки. На самом деле приходящих от приложения arm-none-eabi-gdb. Далее команды транслировались в команды бинарного протокола, который используется в процессе взаимодействия с микроконтроллером. Нижний уровень GDB-сервера выполнен с использованием библиотеки WinUSB.

4. После того как прототип заработал, я пришел к решению добавить вывод отладочной информации с использованием printf. Для передачи отладочных сообщений использовал endpoint 82. На самом деле 8 - это единица в старшем разряде, указывающая направление передачи данных по шине USB в сторону компьютера (Host-а).

Но таким образом функцией printf можно было пользоваться только в bootloader-е. А как же быть с отлаживаемым приложением? Обычно, для взаимодействия с операционной системой, используются прерывания/системные вызовы. Так, BIOS использует int13, ms-dos int21. Мы же на микроконтроллере воспользуемся системным вызовом, т.е. командой "svc". При выполнении этой ассемблерной инструкции в прошивке, будет вызван обработчик прерывания SVC, находящийся в bootloader-е. Что нам и требовалось сделать.

Bootloader использует 10Kb flash памяти, но зарезервировано 16Kb с целью расширения функционала. Так же используется 4K оперативной памяти. Оперативная память применяется для хранения буферов USB, контекста прерванного процесса, а так же как память стека обработчиков прерываний. Итого. Остается 16Kb из 20Kb оперативной памяти и 48Kb flash памяти. Хотя на самом деле Flash-память в контроллере STM32F103C8T6 не 64Kb а 128Kb,- соответственно остается 112Kb.

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

И наконец, - что поддерживается:

  1. Загрузка прошивки на плату с использованием GDB. Т.е. непосредственно из среды программирования/отладки. В моем случае это STM32CubeIDE. Адрес вектора прерываний должен находится по адресу 0x8004000.

  2. Просмотр и изменение регистров процессора в контексте отлаживаемой прошивки.

  3. Просмотр и изменение памяти.

  4. Просмотр и изменение регистров периферии.

  5. Восемь точек останова.

  6. Режим пошаговой отладки.

  7. Принудительная остановка.

  8. Отладочная печать в консоль GDB-сервера.

В отлаживаемой прошивке нельзя изменять адрес вектора обработчика прерываний. Хотя можно добавить системный вызов setVectorBase, что решит вопрос. Нельзя изменять приоритеты прерываний на произвольные значения. Приоритет должен находиться в диапазоне 0x40 - 0xF0. Нельзя запрещать прерывания systick, прерывание usb, и прерывания DebugMon, SvcHandler, а так же всех FaultHandler-s.

Касательно исходных кодов. Это был быстро-прототип. Что то в нем сделано на скорую руку, что то возможно не совсем корректно. Но принцип работы проверен.

Код прототипа проекта доступен по ссылке

Больше информации скоро будет доступно по ссылке https://vrnnet.ru

Tags:
Hubs:
Total votes 32: ↑31 and ↓1+30
Comments21

Articles