PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » Программирование микроконтроллеров » STM32 - Отладка программы


STM32 - Отладка программы

Сообщений 1 страница 4 из 4

1

Более или менее сложная программа обычно требует отладки. Методы могут быть разные. В простейшем случае, для отладки можно использовать светодиод (например если не мигает, значит что-то пошло не так). В более сложных случаях, отладочные сообщения могут отображаться на индикаторе или отправляться по USART. Но наиболее продвинутый метод отладки непосредственно в МК, с возможностью пошагового выполнения программы (что позволяет визуально наблюдать как выполняется программа, например где она виснет) и с возможностью просмотра регистров МК, переменных и всей памяти и при необходимости изменения значения в них. STM32 все это предоставляет. У МК есть интерфейсы JTAG и SWD которые можно использовать как для прошивки, так и для отладки. Для этого нужен программатор-отладчик. Их есть несколько. Я использую ST-Link v2. Конечно лучше купить фирменный, но цена у него завышена. Китайцы как обычно выручают. У них можно купить подделку аналог по небольшой цене - примерно 170 рублей (2.5$ по текущему курсу).
Выглядит этот аналог таким образом.

Скриншоты

http://sf.uploads.ru/t0RXH.jpg

http://sg.uploads.ru/bg2VL.jpg

Драйверы для ST-Link http://www.st.com/web/en/catalog/tools/PF260219

У платы на боковой стороне есть 4 контакта для програматора-отладчика - выводы 3.3, DIO, DCLK и GND. Их нужно соединить с контактами отладчика 3.3, SWDIO, SWCLK и GND.

Среда EmBitz поддерживает ST-Link и по умолчанию при создании проекта использует его. Никаких дополнительных настроек производить не нужно, кроме разве что установки галочки в "Run to main()", чтобы отладка начиналась с функции main(), а не с кода инициализации. Это окно доступно в меню Отладка --> Interfaces.

http://sg.uploads.ru/t/hiGow.png

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

Код:
  /* USER CODE BEGIN 2 */
  volatile uint8_t x=0;                         // Объявляем однобайтную переменную.
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // Инвертирование состояние выхода.
        HAL_Delay(500);                         // Пауза 500 миллисекунд.
        x++;                                    // Увеличиваем число в переменной.
  }
  /* USER CODE END 3 */

На панели инструментов EmBitz есть выпадающий список конфигураций. Для отладки в нем нужно выбрать Debug. Затем скомпилировать программу выбрав в меню Сборка пункт "Rebuild all target files".
Теперь подключаем плату к ST-Link, а его к USB порту компьютера и в меню Отладка кликаем по "Start/Stop Dedug Session".

http://s8.uploads.ru/ODb3B.png

В МК будет загружена прошивка и он перейдет в режим отладки. Выполнение программы остановится на 70 строке функции main(), рядом с которой появится желтая стрелка.

http://s1.uploads.ru/d/qeSCR.png

Теперь программу можно выполнять пошагово, просматривать и изменять содержимое переменных и т. д. Все эти действия доступны в меню Отладка.

http://s2.uploads.ru/t/pYO2U.png

Есть несколько вариантов пошагового выполнения. Если кликать по пункту "Следующая строка", то программа будет пошагово выполняться без захода в функции (они выполняются, но не в пошаговом режиме). Если кликать по пункту "Войти", то программа будет выполняться с заходом в функции. Если нужно выполнить программу до конца функции, нужно кликнуть по пункту "Выйти". Если нужно выполнить программу по определенной строки, то можно поставить точку останова в этой строке и запустить выполнение или просто кликнуть по строке чтобы на нее переместился курсор и в меню Отладка кликнуть по пункту "Run to cursor line". Если нужно перезапустить отладку, то следует кликнуть по пункту Reset.

Итак, при запуске отладки, текущей строкой стала HAL_Init() как показано на скриншоте выше. Теперь давайте кликнем по пункту"Следующая строка" чтобы пропустить пошаговое выполнение функции HAL_Init(), а затем кликнем по пункту "Войти" чтобы выполнить строку кода с заходом в функцию, в данном случае в SystemClock_Config(). Выполнение программы переместится в эту функцию.

http://s2.uploads.ru/t/1aLJX.png

Несколько раз покликаем по пункту "Следующая строка", пока не доберемся до конца функции - строки 124 с функцией HAL_NVIC_SetPriority().
Теперь если навести курсор на строку 102 где создается экземпляр структуры с именем RCC_OscInitStruct, то можно увидеть что находится в полях (переменных) структуры. Так же можно поступать с переменными, массивами и т. д.

http://sa.uploads.ru/t/65IVD.png

Аналогичные данные можно получить в окне Watches (меню "Отладка" --> "Окна отладчика" --> "Наблюдать").

http://s4.uploads.ru/d/tsDkW.png

Сделаем еще несколько шагов (пункт "Следующая строка") пока не доберемся до цикла в котором мигает светодиод.
По пошаговом выполнении этого участка кода, светодиод будет вспыхивать и тухнуть при каждом выполнении функции HAL_GPIO_TogglePin(). Число в переменой x будет увеличиваться.

http://s8.uploads.ru/eujIQ.png

При создании скриншота я навел мышку на переменную x и т. к. цикл отработал 4 раза, то в ней соответственно число 4. Бывают случаи когда нужно изменить число в переменной. Для этого открываем окно Watches и в нем дважды кликаем по строке с переменной x. Появится окно редактирования и в нем вводим нужное значение переменной, например 100 и сохраняем его.

http://sg.uploads.ru/t/yRB2j.png

Теперь в переменной x число 100.

http://s7.uploads.ru/KLIkF.png

А теперь посмотрим как работает функция HAL_GPIO_TogglePin(). Когда до нее дойдет очередь, нужно вместо пункта "Следующая строка", нажать "Войти" и выполнение переместится с файла "main.c", в файл "stm32f1xx_hal_gpio.c".

http://s6.uploads.ru/t/3EVjp.png

В функции производится исключающее ИЛИ (XOR) регистра ODR из группы GPIO с данными переменной GPIO_Pin.

Проект. http://pure-basic.narod.ru/forum_files/ … _Debug.zip

0

2

Теперь пошагово выполним аналогичную программу в среде mikroBasic. ST-Link был автоматически обнаружен средой и использован в качестве аппаратного отладчика и не пришлось ничего настраивать.
Создаем проект и настраиваем тактирование МК.
Код программы такой.

Код:
program Test

dim x as byte

main:
  GPIO_Digital_Output(@GPIOC_BASE, _GPIO_PINMASK_13) 'PC.13 настраивается как выход.
  x=0
  while TRUE
    x = x + 1
    GPIOC_ODR.13 = NOT GPIOC_ODR.13                  ' Интертирование бита PC.13.
    Delay_ms(100)
  wend
end.

Далее нужно в настройках проекта (меню View пункт "Project Setting") выбрать отладочный тип компиляции и аппаратный отладчик.

http://sg.uploads.ru/t/xiM27.png

После этого компилируем программу (Меню Build пункт Build) и прошиваем ее в МК (меню Tools пункт "mE Programmer").

http://s7.uploads.ru/t/3Yk1w.png

Меню отладчика.

http://s1.uploads.ru/t/h4Er6.png

Окна отладчика.

http://sg.uploads.ru/t/At5zh.png

Затем запускаем отладку (меню Run пункт "Start Debugger"). Начнется отладка и рядом с меткой main: появится зеленая стрелка показывающая текущую выполняемую строку.

http://s0.uploads.ru/t/EuFH8.png

Если навести мышку на переменную x, то отобразится адрес переменной и число в ней.

http://sd.uploads.ru/t/AEULl.png

Может возникнуть вопрос почему в переменной не 0, ведь в нее ничего не записывается? Дело в том что память не очищается и это либо случайное значение, или результат предыдущего использования этого адреса памяти. Если несколько раз кликнуть по пункту "Step Over" чтобы была выполнена строка

Код:
x=0

тогда в переменной будет 0.
Если же в процессе отладки нам потребуется изменить число в переменной, следует открыть окно "Watch Values" (в меню оно называется "Watch Window"), затем найти в списке "Select variable from list" нужную переменную и добавить ее в список наблюдения. После чего кликнуть по строке в таблице и ввести новое значение переменной, например 100.

http://sa.uploads.ru/t/TOY1h.png

Число в переменной изменится на заданное нами.

http://sh.uploads.ru/t/6b8W0.png

Проект http://pure-basic.narod.ru/forum_files/ … _Debug.zip

0

3

Один из примеров когда без отладчика можно долго искать ошибку в программе, а с его помощью она находится на несколько минут.
Допустим, разрабатываем программу и в какой-то момент при очередной проверке, МК зависает по неизвестной причине. Что в таких случаях обычно делаете? Наверное анализируйте написанный код с момента последнего успешного варианта. Но это может быть долго, особенно если ошибка явно не видна и ее легко пропустить просматривая код. Думаю у многих хоть раз возникала подобная ситуация.
В качестве примера возьмем один из проектов, допустим этот. STM32 - 1Wire
Предположим захотели его усовершенствовать, доработали код, залили прошивку в МК и ничего не работает. :dontknow:
Можно поступить как написано выше, проанализировав код, а можно воспользоваться отладчиком. Запускаем отладку (как это сделать написано в первом сообщении). Через время останавливаем работу программы и видим что оказались в обработчике прерывания HardFault_Handler (это прерывание происходит при сбое в работе МК).

http://s7.uploads.ru/t/dG6WY.png

Его обработчик по умолчанию (если нигде в коде нет функции с именем HardFault_Handler) выглядит следующим образом.

Код:
b.

Это ассемблерная инструкция безусловного перехода на текущую строку, т. е. зацикливание.

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

Свернутый текст

http://s2.uploads.ru/t/pYO2U.png

Откроется окно, в котором показана последовательность вызовов функций от начала работы.
http://s5.uploads.ru/t/tMUV8.png
Видно что выполнение началось с функции main, находящейся в файле main.c. Потом в 75 строке этого же файла произошел переход в функцию OW_CRC8 расположенную в файле onewire.c и в 306 строке файла что-то пошло не так и произошел сбой работы МК с переходом в обработчик прерывания HardFault_Handler. Значит нужно в первую очередь посмотреть что в строке 306 файла onewire.c, поскольку это последняя выполняемая строка перед сбоем и возможно причина в ней.
http://s7.uploads.ru/t/9hHpB.png

На первый взгляд в 306 строке все нормально. В ней однобайтный массив приводится к двухбайтному виду и записывается в регистр ODR порта GPIOA. Если бы отладчик на нее не указал я бы скорее всего пропустил ее при анализе кода потому что в этой строке как уже писал, на первый взгляд все нормально. Также на скриншоте видно что в момент сбоя в переменной i задающей индекс массива, число 1. В данном случае это важно.
Чтобы точно убедится в том является ли причиной сбоя строка 306 (если в этом есть сомнения), можно пошагово (построчно) выполнить программу начиная с функции OW_CRC8. Для этого в начало функции ставится точка останова и перезапускается отладка чтобы МК начал работать сначала. В меню отладчика для этого есть пункт Reset, который продублирован на панели инструментов.
При пошаговой отладке (каждый шаг происходит по нажатию кнопки F10 или F11 которые дублируют меню отладчика) было выяснено что причина действительно в строке 306 и сбой происходит на втором витке цикла когда в переменной i число 1, а на первом витке когда в i, 0 все нормально.
Итак, место зависания нашли и строку из-за которой происходит сбой обнаружили. Теперь осталось узнать причину подобного сбоя. После непродолжительного поиска в сети было выяснено что ядро Cortex-M0 требует чтобы переменные были выровнены. Т. е. 4-ех байтовые переменные должны иметь адрес кратный 4. 2-ух байтные переменные должны иметь адрес кратный 2. Вот это условие нарушалось когда в переменной i было число 1 и это основная причина сбоя. :)
Теперь представьте сколько времени понадобилось бы чтобы найти причину если не было бы отладчика, особенно если не знаешь о необходимости выравнивать расположение переменных в памяти? Причем это особенность ядра M0. В M3, M4 и других выравнивание не обязательно и не приводит к ошибкам. Пришлось бы проанализировать код многих функций потратив на это не мало времени и не факт что причину удалось бы при этом обнаружить, потому что формально в коде ошибок нет.

Видео. http://pure-basic.narod.ru/forum_files/ … t_Debug.7z

0

4

Метод поиска ошибки рассмотренный выше имеет недостаток - при переводе МК в режим отладки, производится его перепрошивка и перезагрузка. Это не всегда допустимо ведь ошибка может происходить очень редко и при определенных условиях и чтобы понять ее причину нужно подключится к работающему МК. Это возможно, но с рядом ограничений. Потребуется тот-же проект что прошит в МК. Если в него внесены изменения, он не подойдет. Т. е. должно быть точное соответствие номеров строк исходников и адресов в отладочной информации в elf файле и программы в МК. Также должна быть отключена оптимизация во время сборки (LTO - Link Time Optimization), которая делает непригодной отладочную информацию.
Чтобы подключиться к работающему МК, нужно немного изменить настройки отладчика ST-Link. Необходимо убрать галочки в "Load program" "Vector table relocation", а так же выбрать тип сброса "Jtag-pin" (при этом не должно быть связи между выводом TRST отладчика и Reset МК). Этим будет отключена прошивка МК и его перезагрузка при запуске отладки.

Свернутый текст

https://b.radikal.ru/b21/1808/24/80b71fc66fa9.png

Запускаем отладку и на панели инструментов нажимаем кнопку остановки программы. Дальнейшие действия как написано выше, т. е. видим что программа находится в HardFault_Handler, открываем окно "Стек вызовов" и выясняем почему программа попала в этот обработчик ошибок.

Видео поясняющее написанное. http://pure-basic.narod.ru/forum_files/ … ebug_2.avi

Следующее видео показывает "горячее" подключение к работающему МК с переводом его в режим отладки и пошаговое выполнение программы с просмотром содержимого переменных. http://pure-basic.narod.ru/forum_files/ … onnect.avi

0


Вы здесь » PureBasic - форум » Программирование микроконтроллеров » STM32 - Отладка программы