Переходим с STM32 на GD32
Микроконтроллеры STM32 и GigaDevice GD32 часто сравнивают из-за схожей архитектуры и совместимости. GD32 является своеобразным «клоном» STM32, использующим такое же ядро ARM Cortex-M. Основное отличие между ними — это цена, так как GD32 обычно дешевле, что делает его привлекательным для проектов с ограниченным бюджетом. Однако несмотря на схожесть, существуют различия характеристиках и уровне поддержки, которые могут повлиять на выбор между этими двумя семействами микроконтроллеров.
Различия между STM32 и GD32
Хотя внешнее сходство и одинаковое наименование с микроконтроллерами STMicroelectronics могут навести на мысль о полном копировании, это не совсем так. У чипов от GigaDevice есть и принципиальные отличия от STM. Например:
- частота работы: до 108 МГц для семейства GD32F1 (против 72 МГц у STM32F1);
- объем флэш-памяти: до 3 МБ (STM32F2 поддерживает до 1 МБ);
- объем ОЗУ: до 256 КБ (STM32 предлагает до 128 КБ).
Реальный опыт
Несколько лет назад (не в Selectel) мы разработали контроллер автоматики на STM32F107RCTx. В проекте использовались UART, Ethernet и несколько GPIO (проект под NDA, поэтому секретов не раскрываем). Софт был написан в CubeIDE с использованием FreeRTOS, HAL и lwIP и нескольких сторонних библиотек. Устройство настраивалось через USB и веб-интерфейс. Проект был успешно завершен, а заказчик доволен. Однако начался дефицит микросхем (чиппагедон), контроллеры STM32 резко подорожали и исчезли со складов.
Нам предложили перейти на GD32F107, утверждая, что он полностью совместим pin-to-pin. Писать софт для GD32 можно было в Keil или IAR (необходимо поставить GD32Fx AddOn). Для удобства разработки доступно GD32Fx Firmware Library со всеми драйверами и примерами. Мы приступили к разработке в IAR, написав все ПО заново. Заказчик снова счастлив, все работает.
Но в процессе эксплуатации приходят новые идеи или обнаруживаются баги. В результате возникает необходимость доработки ПО, и начинается увлекательная поддержка двух железок в двух разных IDE.
Реальный опыт х2
Все ведь программисты такие и любят делать работу по два раза? Начинаю искать, а можно ли сделать одно ПО, которое бы работало на обеих железках одновременно. Аппаратно чипы похожи, регистры и ножки одинаковые. Чуть-чуть поискав, нахожу документ под названием GD32 and STM32 Compatibility (compatibility sumup between GD32 and STM32_V2.0.pdf).
В нем перечислены аппаратные отличия между чипами GD32 and STM32 и практические рекомендации по «обходу» этих отличий (своеобразное Errata). Прочитав документ и не увидев там особого «криминала», решаю, что можно прошить в GD бинарник от STM и посмотреть, что из этого получится.
На удивление софт сразу стартует, на дисплее рисуются циферки. Не заработал Ethernet, но это уже мелочи, с этим можно работать. А заработает ли дебаг? При попытке запуска отладки получаем ошибку Could not verify ST device! Ну как бы логично, перед нами не совсем ST.
Не работает дебаг, значит попробуем решить по-другому и будем отлаживать софт бесконтактным способом (собирать бинарники и прошивать). Углубляемся в compatibility sumup. Для начала надо научить софт определять, в каком железе он «проснулся». Это можно делать по Jtag ID.
uint16_t Get_JTAG_ID()
{
if(*(uint8_t *)(0xE00FFFE8) & 0x08)
{
return ((*(uint8_t *)(0xE00FFFD0)&0x0F)<<8) | ((*(uint8_t *)(0xE00FFFE4)&0xFF)>>3) | ((*(uint8_t *)(0xE00FFFE8)&0x07)<<5)+1;
}
return 0;
}
Если функция вернула 0x041 это ST, а если 0x7A3 то перед нами GD.
Поскольку самой серьезной «разницей» между чипами была рабочая частота, делаю отдельную функцию SystemClock_Config для GD. В ней с помощью PLL разгоняю частоту до 108 МГц (против 72 МГц у STM).
Собираю бинарник, прошиваю (спасибо, STM32 Link utility не делит чипы на свои и чужие и работает со всеми), дисплей и кнопки работают, Ethernet работает через раз. Путем телепатических усилий перебираю варианты, почему может не работать Ethernet. И примерно через 10 попыток собираю новый бинарник с отключенным autonegotiation, пусть сеть всегда работает на скорости на 100 Мбит (как по ТЗ). Догадка оказывается верной и интерфейс Ethernet начинает работать, IP-адрес по DHCP получается, данные передаются, лампочки моргают.
Дальше еще пара штрихов: надо добавить в веб-интерфейсе признак, что же за железка перед нами (это важно на случай потенциальных проблем и багов). Благо отличать железки по Jtag ID мы уже умеем.
Теперь единый проект теоретически готов, ПО одинаково работает в обеих железках. Но вдруг в будущем возникнут проблемы на GD? Как я буду их чинить без дебага?
Поиск способа отладки ПО, написанного в CubeIDE на железе GD
На YouTube нахожу экзотический вариант. В нем надо запустить несколько разных версий CubeIDE и путем манипуляций и переключений через меню запустить отладку. Автору, конечно, спасибо, но вариант явно не работает.
Дальше несколько раз на разных форумах попадается на глаза такая инструкция.
- Установить в настройках проекта Debug probe — OpenOCD.
- Отредактировать конфиг-файл C:\ST\STM32CubeIDE_1.3.0\STM32CubeIDE\plugins\ com.st.stm32cube.ide.mcu.debug.openocd_1.3.0.202002181050\ resources\openocd\st_scripts\target\stm32f1x.cfg, добавив туда строку set CPUTAPID 0.
Ноль сообщает OpenOCD, что нужно игнорировать идентификационные номера, а это значит, что все клоны или не STM-микроконтроллеры должны будут работать.
Скачиваю CubeIDE 1.3, благо под Windows можно одновременно поставить несколько разных версий IDE и они не будут друг другу мешать. Делаю новый тестовый проект. В настройках Debug probe выбираю OpenOCD, запускаю Debug. И — о чудо — вижу, что дебаг начался (на GD32F107x), можно походить по шагам и посмотреть переменные в памяти.
Теперь задача — перетащить рабочий проект, сделанный в старшей версии IDE. При открытии воркспейса получаем грозное сообщение, что версия не та и работоспособность не гарантируется. Принимаем на себя риски, открываем проект и видим сообщение про Missing nature (не разобрался, что это и зачем). CubeIDE будто бы предлагает решение, но оно не работает.
Раз проект, сделанный прямо в IDE 1.3, работает, а в более новой версии нет, попытаемся найти разницу в файлах. И она обнаруживается в файле .project. Вот, что у нас есть в проекте из 1.3:
com.st.stm32cube.ide.mcu.MCUCubeIdeServicesRevAProjectNature
А вот, что в проекте из 1.15:
com.st.stm32cube.ide.mcu.MCUCubeIdeServicesRevAev2ProjectNature
Руками меняем в боевом проекте одну строку на другую — и он сразу открывается. Уже лучше, пробуем собрать — опять проблема. Видим, что при сборке проект перестал влезать в отведенные ему области RAM. Но как же так? Что изменилось? Смотрим, кто же отвечает за сборку и компиляцию.
В IDE 1.15, где изначально был сделан проект, использовался toolchain gnu-tools-for-stm32.12.3.rel1, а единственная доступная в версии 1.3 — gnu-tools-for-stm32.7–2018-q2.
На портале community.st.com нахожу информацию, как можно добавить в CubeIDE альтернативную версию toolchain. Нужно просто скопировать с заменой все файлы отсюда:
C:\ST\STM32CubeIDE_1.15.0\STM32CubeIDE\plugins\ com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.win32_1.0.100.202403111256
После файлы нужно вставить сюда:
C:\ST\STM32CubeIDE_1.3.0\STM32CubeIDE\plugins\ com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.7–2018-q2-update.win32_1.0.0.201904181610
Также в настройках проекта принудительно необходимо установить Fixed-версию toolchain. Собираем проект — и успех! Запускаю отладку — и тоже успех. По шагам ходим, задача решена.
А как же установка CPUTAPID 0?
Отвечу: никак. Версия CubeIDE 1.3 при отладке через OpenOCD не задает лишних вопросов о происхождении чипа (если он GD32), все работает без этого лайфхака. Я попытался проделать трюк с прописываем CPUTAPID 0 в IDE с версиями 1.8, 1.7 и 1.5 и long story short — ни разу не получилось. Но уже было и не надо.
Заключение
Теперь у меня есть проект, сделанный в CubeIDE 1.3, который можно одинаково успешно запускать и отлаживать на железках на STM32 и GD32. Можно ли так делать в продакшене на реальных проектах? Скорее нет, чем да. По-моему, пользовательское соглашение CubeIDE такие вещи не приветствует. Или я не прав?
Так что все изложенное выше было сделано исключительно в исследовательских целях и в поля не пошло. А две версии ПО для данной железки мы так и продолжаем делать в двух разных IDE, внося все изменения по два раза, ведь все программисты такое любят. :-)