Как известно, совместимость с инструментарием GNU и поддержка GDB делают практически любую популярную среду разработки пригодной для отладки широкого спектра встраиваемых платформ, чаще всего бесплатно и легально. В теории.
Что получается на практике при попытке подружить STM32 и NetBeans, и возможно ли в принципе получить работоспособную систему с поддержкой новейших камней — под катом.
Немного лирики
Съезжать с Микрочипа очень не хотелось. Однако после покупки компанией Атмела, сначала прикрыли, возможно, одно из самых перспективных семейств в портфолио компании — PIC32MM, а затем и всю линейку MIPS. Стало очевидно, что в обозримом будущем переход на ARM неизбежен, а т. к. Микрочип за два года так и не интегрировал поддержку атмеловских контроллеров в свою экосистему, никаких преимуществ «Оставайтесь с нами» не давало. Скорее наоборот — скорректированная вверх ценовая политика и традиционные организационные трудности слияния компаний сделали Атмеловские АРМы менее привлекательными. Одновременно подвернулся проект, об который PIC32MZ просто споткнулся. Критическая масса была набрана.
Почему STM: широкий охват рынка, бюджетная отладка, бесплатная полнофункциональная среда SW4STM32 на базе опенсорса, ну и политический аспект — ST Microelectronics поддерживается правительством Франции как стратегический ресурс, поэтому внезапный уход с рынка или поглощение, вроде бы, не грозят.
Отладка — первые впечатления
SW4STM32 установился традиционным способом — многократным нажатием кнопочки Next > (* здесь и далее все эксперименты происходят на Win7 x64). Подходящий для тестирования нужной функции демо-проект был вынут из пакета STM32Cube Firmware, все сработало более-менее сразу из коробки. Первый запуск JTAG эмулятора оставил впечатление: весь цикл входа в режим отладки, начиная от подключения и заканчивая остановкой в начале main () с обновлением контекста, оказывается, может укладываться в 1-2 с. По сравнению с микрочиповскими отладчиками (даже REAL ICE за полтыщи) разница в скорости — кратная!
И все же, кое-что неприятно удивило.
Что не так в Eclipse / SW4STM32
Мириады настроек и нелогичная организация, скрытые пункты меню, перспективы, баги в интерфейсе и визуальные артефакты, отсутствующие часто используемые горячие кнопки и функции на тулбаре, мелкие корявые нечитаемые пиктограммы и маркеры, отсутствие «Find Usages» — отчасти субъективны, и при желании можно приспособиться. Но: регулярно забывает сохранить измененные файлы перед сборкой, хотя все галочки где надо проставлены; при принудительном ручном сохранении не видит изменений, и инкрементальная сборка получается неактуальной; полной пересборки (Clean and Build) как единой команды нет, при этом после принудительной очистки через Clean Project сборка происходит с ошибками (файлового доступа ?), и успешно завершается только с 4-й попытки — это разумному объяснению уже не поддается. Даже у ранних Бета-релизов MPLAB X на базе древнего NetBeans 6.xx не было таких проблем 10 лет назад, какие есть у официально поддерживаемой среды разработки под STM32 сегодня.
Кроме того, с SW4STM32 набирается уже 3 копии типовых IDE в системе, т. к. помимо него стоят еще намертво прибитый к NetBeans 8.0.1 и некоторым образом отгороженный MPLAB X (т. е. использовать его для других языков/платформ невозможно), и NetBeans 8.2 для Джавы и С/С++.
Получается, что настройка NetBeans 8.2 для работы с STM32 устранит описанные практические проблемы Eclipse, сократит кол-во копий IDE, и сведет к одной платформе, пусть и немного разных версий.
NetBeans 8.2 и GNU ARM Tools
NetBeans лучше использовать 32-битный, т. к. помимо удвоенного расхода памяти, отличий 64-битной версии обнаружить не удалось.
Гуглом быстро нашлось руководство по настройке. Принципиальная разница заключалась только в ОС (Linux у них против Win7 x64 у меня), поэтому призовой игрой стала установка *nix-окружения MSYS, которое входит в пакет MinGW. Настройки тулчейна должны получиться примерно такие:
Важный момент — при добавлении тулчейна GNU ARM выбрать семейство «GNU MinGW», в таком случае NetBeans будет правильно вызывать MSYS. Если на машине уже установлен Cygwin, логично будет использовать его, соответственно семейство для GNU ARM надо выбрать «GNU Cygwin».
Добиться успешной компиляции оказалось не просто, а очень просто. Поскольку SW4STM32 использует этот же компилятор, подсмотрев командную строку вызова компилятора в SW4STM32 и скопировав недостающие ключи в Project Properties > C Compiler > Additional Options
получаем в точности такой же выходной результат, но с важным практическим отличием — все собирается с первого раза, Clean and Build есть и отлично работает:
Но и этот результат можно улучшить, добавив опциональную Post-Build обработку. Открываем Makefile, и в раздел .build-post: .build-impl дописываем:
.build-post: .build-impl
cp ${CND_DISTDIR}/${CONF}/${CND_ARTIFACT_NAME_${CONF}} ${CND_ARTIFACT_NAME_${CONF}}
arm-none-eabi-objcopy -O ihex ${CND_ARTIFACT_NAME_${CONF}} ${CND_ARTIFACT_NAME_${CONF}}.hex
arm-none-eabi-size ${CND_ARTIFACT_NAME_${CONF}}
(важно — отступ должен быть одинарным символом табуляции, не пробелами)
Построчно: 1 — копирует объектный файл (.elf) из выходной папки в корень проекта, для упрощения доступа; 2 — генерирует HEX из elf (можно закомментить, если HEX не нужен); 3 — выводит объем занятой памяти по сегментам.
Конечный результат:
Пока все прекрасно.
OpenOCD – первые сложности
В упомянутых онлайн-руководствах программирование кристалла через OpenOCD протекает просто и буднично. Устанавливается последняя версия (0.10.0), берется конфигурационный файл (из комплекта OpenOCD или из папки проекта SW4STM32), в Project Properties > Run прописывается команда вида:
и все сразу работает. Действительно, так дело и обстоит для младших семейств типа STM32F103 и F407, но меня интересуют F7 и H7. С первыми официальная версия OpenOCD 0.10.0 вылетает с ошибками «auto_probe failed» и «undefined debug reason 7»; вторые не поддерживаются вообще. Перепробованы все доступные официальные сборки 0.10.0 от Янв 2017 и Янв 2018 — результат идентичный. Поиск по ключевым словам подтверждает существование проблемы, хотя массовой ее не назовешь; разбора и решения нет.
Но ведь существует версия, которая гарантированно работает — из комплекта SW4STM32. Естественно, оказывается улучшенной и дополненной, с новыми скриптами и поддержкой семейства H7. Также в ней изменена файловая структура, и в плагине ресурсы хранятся в отдельном модуле, поэтому, чтобы консолидированная в единой папке утилита увидела свои скрипты, потребовался ключ -s.
Board.cfg для NUCLEO-F767ZI, за вычетом комментов, и уплотненный:
set CHIPNAME STM32F767ZITx
source [find interface/stlink-v2-1.cfg]
transport select hla_swd
source [find target/stm32f7x.cfg]
set WORKAREASIZE 0x10000
set ENABLE_LOW_POWER 1
set STOP_WATCHDOG 1
reset_config srst_only srst_nogate connect_assert_srst
set CONNECT_UNDER_RESET 1
Наконец запуск через Run Main Project:
Прошивка успешна, код выполняется.
Отладка
Схема подразумевается самая традиционная: локальный GDB-сервер на OpenOCD, NetBeans подключается к нему через localhost:3333 по TCP. Cоответственно, для NetBeans потребуется плагин Gdbserver.
Упростить запуск OpenOCD можно через bat-скрипт, а поскольку после завершения сеанса он выходит в консоль, имеет смысл бесконечно зациклить перезапуск:
:start
openocd -f debug.cfg -s d:/Prog/openocd/scripts
goto start
Запуск:
В версии из SW4STM32 явно не пишется, но сервер ожидает подключения по TCP к порту 3333. В NetBeans необходимо выбрать Debug > Attach Debugger…, и установить:
Сессия активна. Терминал OpenOCD:
С виду все выглядит неплохо — отладочная сессия активна, код выполняется. Однако проблема все-таки существует.
Проблема
В режиме свободного прогона выполнение невозможно остановить.
Если перед запуском сессии поставить брейкпоинт, при входе в отладку произойдет остановка с обновлением контекста, будет работать пошаговое выполнение и просмотр / изменение переменных, т. е. в принципе — все базовые функции, необходимые для полноценной отладки:
Но только до следующего свободного запуска, после которого останется только закрыть и перезапустить сессию.
Еще одна неприятная мелочь связана с программными брейкпоинтами: ф-ция SYSTEM_Halt () определена как __asm__ («bkpt»), и ее срабатывание приводит к отображению ненужного диалога:
При нажатии Discard and Pause работает как требуется (т. е. останавливает выполнение), однако установить эту опцию по умолчанию и отключить отображение окна стандартными средствами невозможно.
До кучи, хотелось бы автоматизировать запуск OpenOCD и подключение отладчика прямо через NetBeans.
Однако, объективно, единственная функция, которой не хватает для более-менее полноценной отладки, — это остановка выполнения (она же требуется и для установки брейкпоинта на лету).
Отладка отладки
Поиск гуглом показал, что похожие проблемы с неработающей остановкой GDB в NetBeans существовали, но были исправлены несколько лет назад. За неимением лучшей идеи, были скачаны исходники NetBeans в надежде пройтись отладчиком по живому. На удивление, довольно быстро удалось локализовать проблему до вызова внешней утилиты GdbKillProc.exe, которая по сути является оберткой для DebugBreakProcess ( pid ) из WinAPI. Принцип работы утилиты сводится к неинтрузивному прерыванию процесса (аналог «kill -SIGINT [pid]» под *nix, или Ctrl+C в консоли).
Но она не работает.
Что опробовано
В консольном режиме GDB-клиент (arm-none-eabi-gdb.exe) на Ctrl+C реагирует правильно, т. е. останавливает выполнение кода без закрытия сессии, и ждет дальнейших указаний.
ps -W и Windows Process Explorer отображают процесс правильно, и PID совпадает с внутренней переменной в NetBeans.
Ручной вызов «kill -SIGINT [pid]» из пакета MSYS выдает ошибку «No such process».
Проверка через «taskkill /pid [pid]» выдает «The process … could not be terminated… This process can only be terminated forcefully...», что, похоже, свидетельствует о системной блокировке. С ключом /f процесс закрывается полностью, что тоже не годится.
По ходу дела выяснилось, что в Windows дела с генерацией сигналов прерывания обстоит неважно, а точнее — никак: стандартно поддерживается только аналог SIGTERM, что соответствует грубому вырубанию процесса, и общепринятого решения как бы нет.
На просторах интернета обнаружилась утилита windows-kill, созданная для эмуляции Ctrl+C и Ctrl+Brk. Процесс находит, прерывание отправляет без ошибок, но GDB-клиент по-прежнему не реагирует.
Эксперименты проводилась с использованием всех 32-битных версий (NetBeans, ARM Tools, MSYS 1.0), кроме windows-kill, которая 32-битная запускаться отказалась («...unable to start correctly...»). Возможно, проблема именно в этом, т. к., по обрывочным данным с форумов и багтрекеров, разрядности утилиты и процесса должны совпадать. Сложность здесь в том, что ARM не предлагает x64 версию тулчейна под Windows, т. ч. единственный путь устранить разнородность — заставить работать x32 версию windows-kill, что тоже не ясно, возможно ли под Win x64 в принципе.
В середине процесса была с нуля переустановлена ОС, и никаких изменений в поведении подопытных замечено не было, т. ч. с большой уверенностью особенности конкретной системы можно исключить.
Требуется помощь зала
Собственно, все вышенаписанное можно считать вступлением к этому параграфу.
Остался последний шаг, чтобы сделать отладку STM32 под NetBeans реальной:
требуется работающий программный механизм передачи сигнала прерывания SIGINT (Ctrl+C) в процесс GDB-клиента под Windows
Ниже приведена рекомендация по настройке минимальной конфигурации, достаточной для проверки / отладки вышеозначенной проблемы. Если / когда ее удастся разрешить, статья будет переделана в простое пошаговое руководство по настройке NetBeans + OpenOCD под несколько разных семейств и отладочных плат. Дальнейшую функциональность можно будет допиливать по желанию, уже имея работоспособное базовое решение.
Тестовая установка
В качестве аппаратной платформы предлагается использовать плату Blue Pill на основе STM32F103C8T6 и клон ST-Link V2.
Необходимо:
1. Установить GNU Arm Embedded Toolchain
2. Установить OpenOCD 0.10.0 (сборка под Win)
3. Прописать папки bin обоих пакетов в PATH (Control Panel > System > Advanced System Settings > Environment Variables… > Path).
4. В удобном месте создать файл board.cfg, скопировать содержимое:
source [find interface/stlink-v2.cfg]
source [find target/stm32f1x.cfg]
transport select "hla_swd"
reset_config none separate
set WORKAREASIZE 0x5000
set CHIPNAME STM32F103C8T6
set ENABLE_LOW_POWER 1
set STOP_WATCHDOG 1
set CLOCK_FREQ 4000
set CONNECT_UNDER_RESET 1
5. Подобрать подходящую тестовую прошивку (test.elf), главный критерий — чтобы явно было различимы выполнение и остановка. Скопировать в удобное место.
6. Из удобного места прошить плату:
openocd -f board.cfg -c "program test.elf verify reset exit"
Прошивка должна запуститься. Примерный вывод OpenOCD в консоль:
7. Из удобного места запустить GDB-сервер OpenOCD:
openocd -f board.cfg
Код все еще выполняется; примерный вывод (консоль остается заблокирована OpenOCD):
8. В другой консоли или напрямую запустить arm-none-eabi-gdb.exe из пакета GNU ARM Embedded Toolchain и выполнить команды:
target extended-remote localhost:3333
(код все еще выполняется)continue
(код выполняется, консоль заблокирована)Ctrl+C
(код остановлен, консоль активна)continue
(снова выполняется, консоль заблокирована)Задача — вывести GDB-клиент из состояния выполнения (т. е. по сути сэмулировать Ctrl+C) программным способом.
Для выяснения Process ID использовать «ps -W» из *nix-окружения, или Windows Process Explorer (устанавливается дополнительно).
Возможно, у кого-то отладка заработает правильно сразу после начальной настройки NetBeans – подобная информация тоже будет нелишней, особенно с подробным описанием системы и возможных особенностей.
Просьба делиться идеями в комментах, и, надеюсь, общими усилиями удастся превратить смелый эксперимент в полезное руководство по настройке еще более полезного инструмента.
Комментарии (37)
Shtucer
04.06.2018 09:34Пункт "Что не так в Eclipse / SW4STM32" меня очень удивил. Использую Эклипс для разработки под STM32 (CubeMX), Avr и прочий веб. Для каждого типа проекта имеется Custom Perspective, т.е. при работе с STM32 нету тулбаров от Avr и т.п. "полной пересборки (Clean and Build) как единой команды нет" называется Rebuild Project.
Но не без стакана дёгтя, разумеется, но он в обзор не вошёл...mundindel Автор
05.06.2018 20:46это прекрасно, что можно настроить под себя. проблема в том, что любая настройка, как отключение перспектив или восстановление скрытых пунктов меню, каждый раз требует гугла и 10 мин долботни. посмотрите, например, как включить тулбар по умолчанию, чтобы он не исчезал при перезапуске.
кстати в NB спец-окна и тулбары активируются при помощи плагинов, при этом основная раскладка остается прозрачной между всеми режимами.
Rebuild Project после вашей подсказки нашел, в горячих клавишах (которые тоже почему-то настраиваются в свойствах окон (Windows->Preferences), как и все остальные системные настройки). в меню Project, как и в контекстном меню проекта, этого пункта нет. ну ОК, забиндил на Ctrl+F10 как у себя в NB/MPLabX. в фокусе редактора вместо пересборки открывается контекстное меню, в фокусе панели проектов — активируется верхнее меню. с контекстным оказывается — конфликт горячей комбинации, о чем Eclipse вежливо умолчал. устраняю. теперь верхнее меню активируется всегда, по Ctrl+F10. пересборка так и не работает. все, спасибо.
с этим сталкиваешься в первые полчаса использования среды. копать глубже уже как-то и смысла по-моему нетShtucer
05.06.2018 21:51"Как включить тулбар.." ниразу не сталкивался, хотя вопрос довольно старый…
Ну и за первые полчаса можно включить, например, готовый набор горячих клавиш, в комплекте есть Visual Studio и что-то там еще, кажется, Emacs :). Ну и полнотекстовый поиск по горячим клавишам имеется. Сейчас проверил кейс с Ctrl-F10, результат:
- Linux — работатет как заказано.
- Windows — активируется меню. По Ctrl-F10 у меня во всех приложениях активируется меню… хмммм...
Вывод: Windows must чего-то там. Не агитирую за советскую власть, но любая среда требует настройки, и не по полчаса, а как бы в процессе работы. В своё время я "ниасилил" настройку NetBeans от слова совсем, примерно вот как вы сейчас неделю обсуждаете как пропатчить gdb под netbeans, так и я провозился с какой-то ерундой, которая очень мне мешала комфортно работать. Долботня она везде, но у каждого своя.
esaulenka
04.06.2018 10:34Вы не пробовали посмотреть, как устроен в этом месте https://gnu-mcu-eclipse.github.io/?
Тот же набор в виде OpenOCD (или Segger JLink, по желанию) + GDB, и всё "само" работает.mundindel Автор
04.06.2018 18:40вроде вариант решения нашелся (ниже), но если не сработает, буду иметь в виду.
в принципе, сам Eclipse можно наверное глянуть в исходниках, т.к. все отладочные платфрмы на GCC (даже десктопные) по идее должны использовать этот же механизм
ExIngus
04.06.2018 13:16Можно ещё делать так:
gdbinit:
shell start "OpenOCD" openocd
shell start "OpenOCD telnet" telnet localhost 4444
target extended-remote localhost:3333
openocd.cfg:
#Exit on gdb-detach
$_TARGETNAME configure -event gdb-detach { shutdown }
В telnet консоли можно вводить команды OpenOCD, в том числе init
Makefile удобнее свой использовать, или STM32CubeMX умеет генерироватьExIngus
04.06.2018 13:24Вместо Ctrl+C использую команду halt
Для перезапуска reset (halt/run/init)mundindel Автор
04.06.2018 18:34из консоли все работает, включая Ctrl+C для прерывания.
сложность была в том, чтобы управлять из-под NetBeansExIngus
04.06.2018 18:37У меня управляется всё из NetBeans, собственно кроме ввода halt.
При этом не нужно вручную запускать openocd и делать Attachmundindel Автор
04.06.2018 18:43а вот это полезная инфа, спсб
ExIngus
04.06.2018 19:01Есть ещё вариант использовать pipe
target remote | openocd -c "gdb_port pipe; log_output openocd.log"
GDB and OpenOCD
mundindel Автор
05.06.2018 11:32у меня при запуске OOCD через скрипт GDB как-будто сразу рвется связь. в окне Sessions — Process ID = -1 и Process State = Exited. кнопки отладки остаются, но все заблокированы, кроме завершения сеанса
irsdkv
04.06.2018 17:38Решали эту проблему для одного из проектов, в итоге остановились на самописном плагине, который создает «свою» кнопку останова (согласен, не лучший вариант), вызывающую следующую утилиту на C++ (и передающую ей PID arm-none-eabi-gdb процесса):
#include "stdafx.h" #include <windows.h> int main(int Argc, char** Argv) { if (Argc != 2) return 1; int consolePid = atoi(Argv[1]); FreeConsole(); AttachConsole(consolePid); GenerateConsoleCtrlEvent(0, 0); return 0; }
Исходный код плагина, к сожалению, показать не могу, но он (плагин) входит в состав SDK для платы MSTN-M100.
Предупреждаю — эта кнопка идет вместе с другим функционалом проекта, будьте осторожны :).
Если найдется человек, который сможет «перевесить» данный функционал на «родную» кнопку останова и выложит это в стандартный репозиторий NetBeans, думаю многие люди скажут ему спасибо, т.к. во многом только эта проблема останавливает людей от отказа от Eclipse в пользу NetBeans.mundindel Автор
04.06.2018 18:27Бинго!
Из консоли работает, сейчас смотрю как внедрить в стандартную установку с минимальным вмешательством
mundindel Автор
04.06.2018 20:49похоже, получилось.
попробуйте скомпилировать свою утилиту, переименовать в GdbKillProc.exe и подложить в
%User%/AppData/Roaming/NetBeans/8.2/bin
кстати, у самописных кнопок и консольной остановки есть фундаментальный недостаток — не позволяют ставить брейкпоинты на лету, т.к. для этого требуется кратковременная остановка стандартным способомExIngus
05.06.2018 08:34У меня на win10 GdbKillProc из \NetBeans 8.2\cnd\bin\
С костылём, всё работает, но нужно ещё разобраться с параметрами которые NetBeans передаёт в GdbKillProc.exemundindel Автор
05.06.2018 10:24это как раз просто:
if (exEnv.isLocal() && Utilities.isWindows()) { File f = InstalledFileLocator.getDefault().locate("bin/GdbKillProc.exe", "org.netbeans.modules.cnd.debugger.common2", false); // NOI18N //bz#258406 - NullPointerException at org.netbeans.modules.cnd.debugger.common2.utils.ExecutorCND.interrupt //looks loke GdbKillProc file could not be located, will just check for null if (f != null && f.exists()) { ProcessUtils.execute(exEnv, f.getAbsolutePath(), "-s", "INT", Long.toString(pid)); //NOI18N }
(комменты авторские как есть)
т.е.GdbKillProc.exe -s INT [pid]
кстати только сейчас понял, что утилита требует правки для использования argv[3] для получения [pid]. забыл пояснить, извиняюсь
скоро обновлю пост, и будет наверное еще пара вопросов по автоматизации запуска OOCD/GDB, что вы выше писалиExIngus
05.06.2018 10:28Да, только в моём случае NB передаёт в GdbKillProc несуществующий pid, судя по исходникам это pid запущенного под отладкой процесса, а не процесса gdb
ExIngus
05.06.2018 10:35NetBeans парсит ответ от gdb, gdb получает «магическое» значение pid от OpenOCD.
Этот pid и получает GdbKillProc.mundindel Автор
05.06.2018 11:10pid=42000?
ExIngus
05.06.2018 11:17Да, и похоже без модуля для NB не обойтись
mundindel Автор
05.06.2018 11:30С костылём, всё работает
а что тогда работает?ExIngus
05.06.2018 11:45Модифицированный GdbKillProc, который сам ищет среди процессов gdb.
Но это плохой вариант, т.к. сессий отладки может быть несколько.
ExIngus
05.06.2018 12:51using namespace std; #include <cstdlib> #include <windows.h> #include <Tlhelp32.h> #define STR_EQUAL 0 void signINT(int consolePid) { FreeConsole(); AttachConsole(consolePid); GenerateConsoleCtrlEvent(0, 0); } void onProcess(LPPROCESSENTRY32W proc) { if (lstrcmpiW(L"gdb.exe", proc->szExeFile) == STR_EQUAL || lstrcmpiW(L"arm-none-eabi-gdb.exe", proc->szExeFile) == STR_EQUAL) { signINT(proc->th32ProcessID); } } int main(int Argc, char** Argv) { //kill -s INT <PID> HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) { return 1; } PROCESSENTRY32W proc; proc.dwSize = sizeof (PROCESSENTRY32W); if (Process32FirstW(snapshot, &proc)) { onProcess(&proc); while (Process32NextW(snapshot, &proc)) { onProcess(&proc); } } CloseHandle(snapshot); return 0; }
mundindel Автор
05.06.2018 18:47OK.
а если написать внешнюю обертку для GDB, которая будет запускаться из NetBeans, стартовать его самого и OOCD (условно-параллельно), возвращая правильный PID самого GDB, и просто форвардить управляющий траффик?ExIngus
05.06.2018 18:52Думаю лучше делать плагин к NB.
В качестве костыля решение выше работает, просто оно «поставит на паузу» все сессии отладки через gdb.mundindel Автор
05.06.2018 19:50в долгосрочной перспективе — да. только это другой уровень сложности, плюс зависимость от NB API, интеграционное тестирование, публикация, поддержка, обновления,…
кстати, ставить всех на паузу может оказаться полезной ф-цией при отладке многоголовых систем или обмена с ПК
Costic
04.06.2018 17:55Чтобы избежать этих проблем я использую Keil uVision, раньше IAR использовал, там можно было выбрать скромную легальную лицензию (на месяц или с ограничением по коду).
k_sashka
06.06.2018 03:01Atollic TrueStudio не смотрели? Бесплатная для STM32, под винду ставится и работает вроде без танцев с бубнами.
Ryppka
Давно не читал столь подробного обоснования того, что винда — зло))))
mundindel Автор
интересный у вас фокус восприятия. вы уверены, что статья об этом?
анекдот один напоминает