PureBasic - форум

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

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


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


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

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

1

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

Скриншоты

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

5

Микроконтроллеры STM32 поддерживают выполнение кода из ОЗУ, в том числе и отладку программ. Как известно число циклов стирания флеш памяти ограничено. В МК это значение примерно равно 10 тысяч. Это не мало, но если интенсивно перепрошивать МК, то через какое-то время можно исчерпать ресурс флеш памяти. Обычно частая перепрошивка нужна при разработке программы и ее отладке в МК. При наличии свободного места в ОЗУ, можно в него записывать прошивку и тем самым сэкономить ресурс флеш памяти. Покажу на примере как это сделать. В проекте по умолчанию уже есть конфигурация Debug, которая настроена так что производится запись во флеш. Можно изменить ее настройки, но лучше сделать ее копию чтобы при необходимости иметь возможность отлаживать во флеш памяти без изменения настроек конфигурации. Для этого нужно открыть окно "Настройки проекта и целей сборки" (меню Проект -> Properties) и на вкладке "Цели сброки" выбрать "Debug" и нажать на кнопку "Дублировать". Далее появится окно в котором называем конфигурацию скажем DebugRAM. Сохраняем результат и также изменяем папку для файлов с Debug на DebugRAM. Должно получится так.

Должно получится так.

http://d.radikal.ru/d22/1812/dc/2736e1feb887.png

Теперь закрываем это окно и в списке на панели инструментов выбираем DebugRAM и дальше настраиваем ее. В меню Отладка -> Interfaces открываем окно (при этом должна быть выбрана конфигурация DebugRAM) и переходим на вкладку "GDB server" где в настройках отладчика нажимаем кнопку "Setting >>" где в окне задаем адрес "Vector table start" равным 0x20000000 (это начало ОЗУ) и ставим галочку в "Execute from RAM".

Должно получится так.

http://c.radikal.ru/c12/1812/ac/31b400e26be9.png

Теперь нужно открыть окно "Опции сборки проекта" (в меню "Проект" выбрать "Build options") и перейти на вкладку "Device", где в выпадающем списке выбрать "Use target setting". Необходимо заполнить поля как на скриншоте.

Скриншот

http://a.radikal.ru/a42/1812/c7/9b280f05e6b2.png

Значения полей могут быть другими в зависимости от типа процессора, проекта и т. д. Проще всего скопировать данные из настроек проекта (корневой, самый верхний пункт области выбора конфигурации). Весь смысл изменения этих настроек в том, что нужно задать использовать файл xxxx_sram.ld (по умолчанию добавляется в проект при его создании), являющимся скриптом линкера с настройками выполнения из ОЗУ. Теперь еще осталось перейти на вкладку "Compiler setting" и на вкладке "#defines" ввести VECT_TAB_SRAM для переназначения таблицы прерываний в начало ОЗУ (переназначение выполняется в файле system_stm32f10x.c в функции SystemInit()).

Скриншот

http://c.radikal.ru/c39/1812/93/aab531605fce.png

Теперь можно пересобрать программу и запустить отладку. Просматривая map файл видно что таблица прерываний и код находятся в ОЗУ.

Свернутый текст
Код:
.text           0x20000000      0x834
 *(.isr_vector)
 .isr_vector    0x20000000       0xec obj\debugram\src\startup_stm32f10x_md.o
                0x20000000                __isr_vector
 *(.text*)
 .text          0x200000ec       0x5c c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7-m/crtbegin.o
 .text          0x20000148       0x74 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7-m/crt0.o
                0x20000148                _start
                0x20000148                _mainCRTStartup
 .text.GPIO_Init
                0x200001bc      0x178 obj\debugram\spl\src\stm32f10x_gpio.o
                0x200001bc                GPIO_Init
 .text.GPIO_SetBits
                0x20000334       0x1c obj\debugram\spl\src\stm32f10x_gpio.o
                0x20000334                GPIO_SetBits
 .text.GPIO_ResetBits
                0x20000350       0x1c obj\debugram\spl\src\stm32f10x_gpio.o
                0x20000350                GPIO_ResetBits
 .text.RCC_APB2PeriphClockCmd
                0x2000036c       0x3c obj\debugram\spl\src\stm32f10x_rcc.o
                0x2000036c                RCC_APB2PeriphClockCmd
 .text.DWT_Init
                0x200003a8       0x48 obj\debugram\src\main.o
                0x200003a8                DWT_Init
 .text.DWT_Delay_ms
                0x200003f0       0x3c obj\debugram\src\main.o
                0x200003f0                DWT_Delay_ms
 .text.main     0x2000042c       0x58 obj\debugram\src\main.o
                0x2000042c                main
 .text          0x20000484       0xb8 obj\debugram\src\startup_stm32f10x_md.o
                0x20000484                Reset_Handler
                0x200004ca                NMI_Handler
                0x200004cc                HardFault_Handler
                0x200004ce                MemManage_Handler
                0x200004d0                BusFault_Handler
                0x200004d2                UsageFault_Handler
                0x200004d4                SVC_Handler
                0x200004d6                DebugMon_Handler
                0x200004d8                PendSV_Handler
                0x200004da                SysTick_Handler
                0x200004dc                Default_Handler
                0x200004de                WWDG_IRQHandler
                0x200004e0                PVD_IRQHandler
                0x200004e2                TAMPER_IRQHandler
                0x200004e4                RTC_IRQHandler
                0x200004e6                FLASH_IRQHandler
                0x200004e8                RCC_IRQHandler
                0x200004ea                EXTI0_IRQHandler
                0x200004ec                EXTI1_IRQHandler
                0x200004ee                EXTI2_IRQHandler
                0x200004f0                EXTI3_IRQHandler
                0x200004f2                EXTI4_IRQHandler
                0x200004f4                DMA1_Channel1_IRQHandler
                0x200004f6                DMA1_Channel2_IRQHandler
                0x200004f8                DMA1_Channel3_IRQHandler
                0x200004fa                DMA1_Channel4_IRQHandler
                0x200004fc                DMA1_Channel5_IRQHandler
                0x200004fe                DMA1_Channel6_IRQHandler
                0x20000500                DMA1_Channel7_IRQHandler
                0x20000502                ADC1_2_IRQHandler
                0x20000504                USB_HP_CAN1_TX_IRQHandler
                0x20000506                USB_LP_CAN1_RX0_IRQHandler
                0x20000508                CAN1_RX1_IRQHandler
                0x2000050a                CAN1_SCE_IRQHandler
                0x2000050c                EXTI9_5_IRQHandler
                0x2000050e                TIM1_BRK_IRQHandler
                0x20000510                TIM1_UP_IRQHandler
                0x20000512                TIM1_TRG_COM_IRQHandler
                0x20000514                TIM1_CC_IRQHandler
                0x20000516                TIM2_IRQHandler
                0x20000518                TIM3_IRQHandler
                0x2000051a                TIM4_IRQHandler
                0x2000051c                I2C1_EV_IRQHandler
                0x2000051e                I2C1_ER_IRQHandler
                0x20000520                I2C2_EV_IRQHandler
                0x20000522                I2C2_ER_IRQHandler
                0x20000524                SPI1_IRQHandler
                0x20000526                SPI2_IRQHandler
                0x20000528                USART1_IRQHandler
                0x2000052a                USART2_IRQHandler
                0x2000052c                USART3_IRQHandler
                0x2000052e                EXTI15_10_IRQHandler
                0x20000530                RTCAlarm_IRQHandler
                0x20000532                USBWakeUp_IRQHandler
 .text.SystemInit
                0x2000053c       0x68 obj\debugram\src\system_stm32f10x.o
                0x2000053c                SystemInit
 .text.SystemCoreClockUpdate
                0x200005a4       0xd8 obj\debugram\src\system_stm32f10x.o
                0x200005a4                SystemCoreClockUpdate
 .text.SetSysClock
                0x2000067c        0xc obj\debugram\src\system_stm32f10x.o
 .text.SetSysClockTo72
                0x20000688      0x100 obj\debugram\src\system_stm32f10x.o
 .text.exit     0x20000788       0x28 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7-m\libg_n.a(lib_a-exit.o)
                0x20000788                exit
 .text.__libc_init_array
                0x200007b0       0x4c c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7-m\libg_n.a(lib_a-init.o)
                0x200007b0                __libc_init_array
 .text.memset   0x200007fc       0x10 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7-m\libg_n.a(lib_a-memset.o)
                0x200007fc                memset
 .text._exit    0x2000080c        0x2 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/lib/armv7-m\libnosys_s.a(_exit.o)
                0x2000080c                _exit
 *(.init)
 *fill*         0x2000080e        0x2
 .init          0x20000810        0x4 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7-m/crti.o
                0x20000810                _init
 .init          0x20000814        0x8 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7-m/crtn.o
 *(.fini)
 .fini          0x2000081c        0x4 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7-m/crti.o
                0x2000081c                _fini
 .fini          0x20000820        0x8 c:/program files (x86)/embitz/1.11/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/5.4.1/armv7-m/crtn.o

На ассемблерном листинге также видно что код в ОЗУ.

Скриншот

http://c.radikal.ru/c08/1812/75/29ed1986a69d.png

Программа нормально выполняется и отлаживается.

Файлы. http://pure-basic.narod.ru/forum_files/ … bugRAM.zip

0


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