Программируем микроконтроллеры stm32 при помощи QtCreator


    Как-то так незаметно получилось, что программист, который разрабатывал нам прошивку для микроконтроллера, стал банально не успевать и в некоторые моменты я начинал перехватывать инициативу и самостоятельно браться за исправление ошибок.
    Разработка велась в среде IAR, и многие согласятся со мной, что по сравнению с разработкой в QtCreator'е это боль и страдание.
    В какой-то момент мы решили, что быстрее нанять нового программиста и вместе с ним заново переписать прошивку контроллера stm32, так, как я к этому моменту уже немного сам смыслил в их программировании и к тому же обнаружил, что QtCreator умеет отладку на голом железе (плагин BareMetal), я решил принять в этом активное участие.
    Здесь я хочу поделиться шаблоном проекта для stm32f407 от Terra Electronica и рассказать об особенностях его настройки.

    Тестовый проект


    Этап первый — тулчейн

    Для разработки использовался Yagarto, или gcc-arm-embedded, который можно скачать здесь. Для платформ, отличных от Linux, существует существенное ограничение: gdb в них собран без поддержки Python'а, что делает невозможной отладку через QtCreator, оптимальным выходом будет все-таки использование Linux, или на худой конец OSX, где не составляет труда пересобрать gdb с поддержкой python.

    QtCreator можно использовать как из репозитория, если его версия не ниже, чем 3.1, или же скачать в составе QtSDK. Главное, чтобы в нем была включена поддержка qbs и BareMetal

    Этап второй — настройка IDE

    Включаем BareMetal и QbsProjectManager и перезагружаемся.



    Теперь мы можем зайти во вкладку «Устройства» и нажать на «добавить устройство», если вы все правильно сделали, то там должен появиться тип «Голое устройство». Для отладки будет использоваться gdb-server, поэтому необходимо убедиться, что его адрес и порт указаны верно.



    Во вкладке компиляторы добавляем наш arm-none-eabi-g++



    Аналогичным образом добавляем отладчик arm-none-eabi-gdb, после чего создаем новый комплект, в котором указываем тип устройства «Голое устройство» а также наш компилятор и отладчик.



    Этап третий — сборка тестового проекта

    Тут все просто, стягиваем шаблон с гитхаба, открываем файл stm32.qbs, выбираем созданный нами профиль arm-embedded и компилируем. На выходе должен получиться elf файл с прошивкой, который каким-то образом нужно будет запустить на устройстве.

    Этап четвертый — запуск

    Вот тут уже все зависит от типа отладчика, которым вы пользуетесь, я пользуюсь клоном jlink и запускаю его при помощи openocd, QtCreator пока не умеет самостоятельно поднимать сервер отладки, поэтому нам потребуется запустить его руками в папке с шаблоном есть файл openocdf.cfg, с профилем для jlink.



    После того, как вы запустите openocd, можно просто нажать на кнопку «Начать отладку» в QtCreator'е и заполучить уже полностью привычную среду. В данном шаблоне реализована работа с tcp посредством lwip и написан простенький tcp echo сервер, который находится по адресу 192.168.1.10 и слушает 7000 порт, а также отвечает на ping запросы. Если все успешно собралось и запустилось, то можно будет увидеть такую картину:



    Но думаю эта статья была бы не полной без описания внутренностей проекта.

    Что же у этого проекта под капотом?


    Для сборки используется qbs, достоинства которой уже обсуждались на Хабре.

    Чтобы не захламлять сам файл проекта, я создал модули Stm32Product, Stm32Application, в которых задал все необходимые для сборки ключи компилятора и линковщика

    Stm32Product
    import qbs
    
    Product {
        Depends { name: "cpp" }
        
        cpp.commonCompilerFlags: [
            "-mcpu=cortex-m4",
            "-mthumb",
            "-mfpu=fpv4-sp-d16", //включаем аппаратную поддержку плавающей точки
            "-mfloat-abi=softfp" //с програмной инициализацией
        ]
        cpp.linkerFlags: [
            "-mcpu=cortex-m4",
            "-mthumb",
            "-mfpu=fpv4-sp-d16",
            "-mfloat-abi=softfp",
        ]
    }
    


    Stm32Application
    Stm32Product {
        type: "application" // To suppress bundle generation on Mac
        consoleApplication: true
        
        cpp.positionIndependentCode: false
        cpp.executableSuffix: ".elf"
        cpp.linkerFlags: {
            base.push("-Xlinker");
            base.push("--gc-sections");
            return base;
        }
    }
    


    Собственно проектный файл app.qbs
    import qbs
    import Stm32Application
    import qbs.FileInfo
    import qbs.ModUtils
    
    Stm32Application {
        name: "Application"
    
        cpp.includePaths: [
            "app",
            "libs",
            "libs/cmsis",
            "libs/port/gcc",
            "libs/periphery",
            "libs/system",
            "libs/ethernet",
            "libs/lwip/src/",
            "libs/lwip/src/arch/",
            "libs/lwip/src/include",
            "libs/lwip/src/include/lwip",
            "libs/lwip/src/include/ipv4", //Use old internet protocol due ipv6 has experimental status
            "libs/lwip/src/include/netif",
            "libs/lwip/src/netif",
            "libs/lwip/include",
        ]
        cpp.defines: [
            "STM32F4XX", //дефайнами настраиваем cmsis на нашу плату
            "STM32F40_41xxx",
            "USE_STDPERIPH_DRIVER",
            "HSE_VALUE=168000000",
        ]
        Properties {
            condition: cpp.debugInformation
            cpp.defines: outer.concat("DEBUG")
        }
    
        cpp.linkerScripts: [ 
            "../ldscripts/libs.ld", //особенно интересный момент: скрипты линковщика, описывают структуру бинарника, секции, и так далее
            "../ldscripts/mem.ld",
            "../ldscripts/sections.ld",
        ]
    
        Group {
            name: "sources"
            prefix: "../**/"
            files: [
                "*.c",
                "*.cpp",
                "*.h",
                "*.s"
            ]
            excludeFiles: [
                "ipv6/*.*",
                "test/unit/**/*.*",
            ]
            cpp.cxxFlags: [ "-std=c++11" ] //gcc вполне позволяет использовать для программирования МК новый стандарт C++11
            cpp.cFlags: [ "-std=gnu99" ]
            cpp.warningLevel: "all"
        }
        Group {
            name: "ldscripts"
            prefix: "../ldscripts/"
            files: "*.ld"
        }
    }
    


    В общем, как мне кажется, данный шаблон будет не слишком трудно адаптировать и под другие микроконтроллеры, главное, чтобы для них имелась поддержка gcc и доступные в исходных кодах библиотеки для работы с периферией. Я надеюсь, что многим, как и мне, удобнее работать в QtCreator, чем в таких IDE, как Eclipse, Keil или IAR.
    Поделиться публикацией

    Комментарии 35

      –10
      <сарказм>Да и микроскопом тоже можно забивать гвозди</сарказм>
        +5
        Неуместный «сарказм». Не вижу причин заниматься мазохизмом и программировать микроконтроллеры в Notepad или в Keil (что почти одно и то же). Разработка под ARM Cortex-M3 ничуть не проще любой другой разработки на C или C++, в одном проекте может использоваться до десятка библиотек (для TCP/IP, SD-карт, радио и т.п.), да и количество своего кода зачастую довольно большое, если устройство достаточно сложное. Я даже для HelloWorld завёл бы проект в IDE — как минимум для того, чтобы потом использовать в качестве шаблона.
          +1
          С радостью пишу в TrueStudio, коллега пишет в CooCox, ни кто и не говорил про Notepad.
          0
          Но ведь микроскоп очень удобный, грех не воспользоваться. ))
          0
          Возможно я чего-то не знаю, но чем для вас QtCreator удобнее Eclipse?
            +12
            Во всяком случае быстрее.
              –1
              присоединяюсь к вопросу
                0
                Встречный вопрос, чем QtCretor хуже Eclipse?
                  0
                  Ну, лично меня отдельные мелочи раздражают. Например, файлы в проект добавляются только по одному и удаляются только по одному. Разделение на Headers и Sources в дереве проекта.

                  Еще, после Кейла, очень тяжело привыкнуть, что watch не обновляются, когда отладка запущена.
                  0
                  Мне теперь во всех IDE не хватает функциональности плагина Locator (по дефолту зовётся по Ctrl+K). Мегаудобная вещь. При том, что нечто похожее есть и в Sublime, и в IDEA, и, вроде, Eclipse, и в Kdevelop, но всё какое-то не такое — много суетливых движений нужно делать, что бы достичь того же эффекта как в локаторе.
                    0
                    Ага, очень удобная штука. Особенно если в проекте больше 300 файлов, а уж остальных сущностей и подавно… Главное работает моментально.
                  +1
                  Вопрос, а почему для C++ вы используете C++11, а для Си только gnu99? Есть же -std=c11 и -std=gnu11 (хотя новшеств там не так много, но вкусное есть). Просто праздный интерес.
                    0
                    Да я как-то стараюсь совместимость с Keilом сохранить, а он еле еле C99 умеет. А плюсов в проекте и не было, флаг для примера вставлен был.
                    0
                    gdb в них собран без поддержки Python'а, что делает невозможной отладку через QtCreator

                    Я с этой проблемой столкнулся, оказалось, что она решается очень просто и быстро. Как правило, под разные системы (ту же винду, в частности) есть собранные версии gdb с поддержкой python (например, под ту же винду есть собранный gdb на сайте qt-project http://download.qt-project.org/official_releases/gdb). А в настройках комплекта в QtCreator можно этот отладчик явно указать. Profit!
                      0
                      То есть mingwшный gdb будет работать с arm-eabi-none-gcc? Я вроде пробовал и меня он послал.
                        0
                        Я тоже пытался запустить (давно правда), не получилось.
                          0
                          Ну, тут можно поэкспериментировать с gdb, попробовать собрать самостоятельно или где-нибудь найти уже собранный, может кто-то этой проблемой уже заморачивался. Главное, что Creator позволяет без извращений подменить один gdb на другой, что дает простор для творчества.

                          З.Ы.
                          Например, вот здесь есть статейка, в конце которой упоминается собранный gdb с поддержкой arm и python, можно его попробовать.
                        –1
                        Видя человека с ARM-железякой в руках уже сложно его на что-то другое пересадить, если у него есть его любимая IDE и она с его задачами на отлично справляется.

                        Как-то задавался этим вопрос и получал ответы такого вида: «У нас нету много времени чтобы изучать Ваши новые IDE, Toolchains и тд., так как нас полностью устраивает то, что идет „с коробки“ от производителя. Все свое основное время мы лучше потратим на аппаратный мир (то есть, сам МК) и его изучение.»

                        Но пользуясь случаем хотел бы задать такие вопросы (извиняюсь за небольшой «оффтоп»):
                        • Как часто Вам приходиться «билдить» или «дебажить» с консоли?
                        • Была бы Вам интерестна платформа STM32 в PlatformIO?


                        Всем большое СПАСИБО!
                          +2
                          Наблюдая за кодом в cmsis я пришел к выводу, что МК скорее программируют инженеры с навыками программиста, чем прирожденные программисты. Качество кода обычно весьма посредственное, приходится продираться через портянки макросов, странных ifdef'ов и прочих ужасов. Сам размер заголовков в cmsis и в stdPeriph в stm32 пугает как меня, так и компилятор.
                          Видимо это тоже все как-то связанно с тем, что и инструментарий крайне неудобен для программирования (сравните Keil, IAR с тем же XCode).

                          • Билдить почти никогда, хотя qbs это прекрасно умеет, а вот дебажить пару раз приходилось.
                          • Я бы лучше дальше занялся улучшением поддержки embedded платформ со стороны qbs и QtCreator
                          +1
                          >> что МК скорее программируют инженеры с навыками программиста, чем прирожденные программисты.
                          Читаете мои мысли :)

                          >> Я бы лучше дальше занялся улучшением поддержки embedded платформ со стороны qbs и QtCreator
                          Спасибо. Буду следить.

                          P.S: Извиняюсь, ответ к #comment_7703313
                            0
                            "HSE_VALUE=168000000"
                            
                            HSE_VALUE — это не частота ядра, это частота кварца, которая чаще всего 8MHz (8000000) или 25MHz.
                              0

                                0
                                Вообще я портированием существующего кода занимался, а недостающие части брал из шаблонов в сети
                                  0
                                  Да в заголовке файла всё написано. Просто не доглядел я, потому и сообщение "удалил".
                                0

                                Почему может так быть что МК не прошивается? Но отладка работает. Код в МК не соответствует ожидаемому явно.

                                  0

                                  А прошивка же вообще другими методами осуществляется, я ее всегда через dfu-util делал.

                                    0

                                    Жуть. Это вы каждый раз перемычку BOOT0 BOOT1 дергаете, и имеете одельный USB или UART канал для заливания проши? Тяжкий путь, как по мне.

                                      0

                                      На самом деле режим dfu использовался для обновления на продакшене, чтобы можно было обойтись обычным usb кабелем, а не таскать отладчик. Во время отладки прошивал всегда через jlink клона.

                                    0

                                    В общем разрулил. У меня был прошитый ST-Link в J-Link. Очевидно что JLink гораздо круче по скорости и возможностям. Отладка работала но как-то туповато, точки останова не всегда срабатывали. Прошивка командой от GDB load. Приводила к фэйлу, но отладка начиналась, естественно не соответствуя .elf-файлу. Несколько раз я прозревал от разных фич:


                                    1. Блокировка флэш — защита от записи — write protection. Убирается командой


                                      (gdb) monitor flash protect bank sector_begin sector_end off  # sector=page - см.доки на МК. 

                                      Например STM32F1 имеет страницы размером по 2кБ, и для МК с 256кБ флэша нужно задать 0 0 127. Для STM32F4 с 1МБ, у него сектора разного размера будут 0 0 11. У каждого МК свой фокус.
                                      Посмотреть чё как


                                      monitor flash info 0   # bank=0

                                      При работе напрямую через openOCD, например задавая команды через xxx.cfg или опцию -c или telnet всё то же самое но без слова monitor


                                    2. Это помогло 1 раз. Короче прошивалось нормально через openocd -f board.cfg -c "program xxxx.elf" (это приблизительно). Дальше всё работает.

                                    Вердикт. Полез читать, оказалось JLink из ST-LInk совсем не J-Link, а подобие, и выходит что openocd с ним криво работает. Откатился к ST-link. Всё работает.
                                    ЗЫ. А вот Keil под виндой с этим JLink'ом давал жару на 50 МГц, вливая код за мгновения. И точек останова больше (неограничено типа, в st-link 6) и скорость хорошая.

                                    0
                                    Раз тема всплыла, скажу, что мне вполне успешно удалось настроить QtCreator под контроллеры Nuvoton (серия М451 в частности). С отладкой, правда, не разбирался.
                                    Это тоже Cortex M4, потому рецепты те же, что в статье. Единственное, Qbs не осилил, остановился на сmake, благо Creator с ним тоже прекрасно работает.
                                      0

                                      Аналогично остановился на CMake для Cyrpress FX3 (ARM926E-JS).

                                      0
                                      Аналогичным образом добавляем отладчик arm-none-eabi-gdb

                                      стоит отметить, что в данный момент, если GDB старше какой-то версии, то отладка не запускается. GDB 8.0 — не работает. GDB 7.7.1 — работает. Естественно оба с поддержкой Python.

                                      0
                                      Крик души! Помогите, пожалуйста, разобраться.

                                      Вопрос: В какой момент и каким образом происходит заливка прошивки?

                                      Настроил QtCreator и openocd по Вашей инструкции (и перепроверил на нескольких аналогичных), собрал проект, но я не вижу что либо в логах похожее на заливку. В итоге дебаг не идет толком, иногда стопается в рандомных точках в ассемблере, но в мейн так и не попал. Проект — простейшая моргалка. Пробовал 3 разные платы на stm32f103cb, stm32f407ve и stm32f407vg. последняя — честный Discovery с настоящим st-link-ом. Остальные пытаюсь скрестить с китайским двухбаксовым клоном
                                        0
                                        Сам спросил — сам отвечу.

                                        Заливка происходит командой load. Ее видно в окне настройки gdb сервера.

                                        Но основной затык был в том, что контроллер не переходил в режим программирования (или неправильно ресетился — не знаю). Я пытался запустить это в связке с openocd 0.10. После долгого гуглежа оказалось нужно добавить команду «monitor reset_config none separate» перед ресетом перед заливкой прошивки. В моем случае список команд для gdb сервера выглядит так (я добавил только одну, остальные там были по умолчанию)
                                        set remote hardware-breakpoint-limit 6
                                        set remote hardware-watchpoint-limit 4
                                        monitor reset_config none separate
                                        monitor reset halt
                                        load
                                        monitor reset halt


                                        Вдруг кому пригодиться

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

                                      Самое читаемое