Использование Qbs для работы с STM8/SDCC в QtСreator (обновлено)

разделы: среда разработки , STM8 , дата: 29 июля 2022, обновлено 1 сентябряг.

Спустя долгое время, я решил узнать как обстоят дела с поддержкой STM8 в SDCC. Последней версией SDCC которой я пользовался был 3.6. За это время OpenOCD официально обзавелся поддержкой STM8. И т.к. в Linux я пользуюсь преимущественно IDE QtCreator, был соблазн перетащить туда проекты на STM8. Главная сложность возникла с системой управления сборки. QtCreator умеет работать только с qmake, qbs и cmake проектами. Qmake не поддерживает sdcc никоим образом. У CMake поддержка заявлена, но она чисто декларативная. Таким образом нам остается только Qbs.

Ранее для STM8 я пользовался STVD+Cosmic, но это требовало работы из виртуальной машины. STVD - отличная среда разработки, но в визуальном плане она устарела уже давно. Отсутствие темной темы и линтера конечно не принципиально, но без этого уже не комфортно работать. Кроме того Cosmic для проверки лицензии постоянно стучится в интернет. Я же предпочитаю отключать интернет в виртуалке.

Ок, но какие сложности нас ожидают?

  • Во-первых, придется порядком повозиться с этим с этим Qbs. Система плохо документированная и малоиспользуемая.
  • Во-вторых, отладка в QtCreator не такая комфортная как в STVD. Нет периферийных регистров, а ассемблерный код не сопровождается отладочной информацией. Что бы поставить, например, точку остановки на ассемблерное прерывание, сначала нужно узнать его адрес, и затем ставить break на физический адрес.
  • Ни и конечно приходится постоянно бороться с SDCC. Современный SDCC/STM8, в принципе, не так уж плох, но за ним приходиться постоянно следить, а проблемные участки кода переписывать в виде ассемблерных функций.

Итак, софт который нам понадобится для программирования микроконтроллеров STM8:

  1. stm8-binutils - Комплект утилит, который включает в себя: отладчик stm8-gdb, stm8-size, stm8-objdump и пр.
  2. OpenOCD - версии 0.11. Начиная с этой версии появилась поддержка SWIM протокола и чипов STM8
  3. QtCreator. Я использую версию 5.0.3 из SBo
  4. Qbs. Моя версия 1.16.0 так же из SBo. Она не самая свежая, но работает без вопросов.
  5. Для линтера в QtCreator потребуется плагин qt-creator-llvm и cppcheck.

Из аппаратного обеспечения нам достаточно будет платы c чипом STM8S103F3 и ST-Link V2 с поддержкой SWIM протокола.

Руководств и мануалов по использованию Qbs в baremetal-проектах почти нет. Есть несколько примеров для STM32, но так как там используется GCC, скрипт проекта будет отличаться от такового для SDCC.

Прежде чем начать что-то делать, настоятельно рекомендуется пройти по последним трем ссылкам, и изучить их содержимое.

Содержание:

I. Консольный Qbs проект для STM8+SDCC

  1. Знакомимся с Blink из примеров QBS
  2. Отладка прошивки
  3. Сборка прошивки в ELF-формат
  4. Раздельные профили компиляции "debug" и "release"
  5. Добавляем ассемблерный файл в проект

II. Работа с проектом в Qt Creator

  1. Настраиваем STM8 baremetal kit в QtCreator
  2. Качество кода SDCC

III. Бонус

  1. Qbs-скрипт для STM32F103C8-проекта на CMSIS (пошаговая инструкция)

IV. Добавленно позже

  1. Проблема с запуском OpenOCD для STM8

1) Знакомимся с Blink из примеров Qbs

Для начала нам нужно убедится, что конкретно наш Qbs поддерживает SDCC:

$ qbs config --list|grep sdcc
profiles.sdcc-4_1_0-mcs51.cpp.toolchainInstallPath: "/usr/bin"
profiles.sdcc-4_1_0-mcs51.qbs.architecture: "mcs51"
profiles.sdcc-4_1_0-mcs51.qbs.toolchainType: "sdcc"
profiles.sdcc-4_1_0-stm8.cpp.toolchainInstallPath: "/usr/bin"
profiles.sdcc-4_1_0-stm8.qbs.architecture: "stm8"
profiles.sdcc-4_1_0-stm8.qbs.toolchainType: "sdcc"

У меня старая версия Qbs, и здесь мы видим поддержку только STM8 и MCS51. В новых версиях была добавлена также поддержка HC08.

В examples с Qbs постовляется пример проекта для STM8S103F3:

$ tree  /usr/share/qbs/examples/baremetal/stm8s103f3/
/usr/share/qbs/examples/baremetal/stm8s103f3/
├── redblink
│   ├── README.md
│   ├── gpio.c
│   ├── gpio.h
│   ├── main.c
│   ├── redblink.qbs
│   └── system.h
└── stm8s103f3.qbs

Давайте скопируем этот пример куда-нибудь в домашний каталог, и посмотрим что это такое.

Файл "main.c"

#include "gpio.h"
#include "system.h"

static void some_delay(unsigned long counts)
{
    unsigned long index = 0u;
    for (index = 0u; index < counts; ++index)
       system_nop();
}

int main(void)
{
    gpio_init_red_led();

    while (1) {
        gpio_toggle_red_led();
        some_delay(20000u);
    }
}

Файл "gpio.c"

#include "gpio.h"
#include "system.h"

// A LED is connected to the pin #5 of Port B.
#define GPIO_RED_LED_PIN_POS   (0x20u)

void gpio_init_red_led(void)
{
    PB_ODR = 0x00; // Turn off all pins.
    PB_DDR = GPIO_RED_LED_PIN_POS; // Configure Pin as output.
    PB_CR1 = GPIO_RED_LED_PIN_POS; // Set Pin to Push-Pull.
    PB_CR2 = GPIO_RED_LED_PIN_POS; // Set Pin to Push-Pull.
}

void gpio_toggle_red_led(void)
{
    PB_ODR ^= GPIO_RED_LED_PIN_POS;
}

Пробуем собрать проект:

$ qbs build profile:sdcc-4_1_0-stm8
Build graph does not yet exist for configuration 'default'. Starting from scratch.
Resolving project for configuration default
Setting up build graph for configuration default
Building for configuration default
compiling main.c [stm8s103f3-redblink]
compiling gpio.c [stm8s103f3-redblink]
linking stm8s103f3-redblink.ihx [stm8s103f3-redblink]
Build done for configuration default.

Проект должен собраться в директорию default:

$ tree ./default/
./default/
├── default.bg
└── stm8s103f3-redblink.2e6c123b
    ├── 3a52ce780950d4d9
    │   ├── gpio.c.adb
    │   ├── gpio.c.asm
    │   ├── gpio.c.lst
    │   ├── gpio.c.rel
    │   ├── gpio.c.rst
    │   ├── gpio.c.sym
    │   ├── main.c.adb
    │   ├── main.c.asm
    │   ├── main.c.lst
    │   ├── main.c.rel
    │   ├── main.c.rst
    │   └── main.c.sym
    ├── stm8s103f3-redblink.ihx
    ├── stm8s103f3-redblink.lk
    └── stm8s103f3-redblink.map

2 directories, 16 files

Прошиваем прошивку програматором:

$ stm8flash -c stlinkv2 -p stm8s103f3 -w ./default/stm8s103f3-redblink.2e6c123b/stm8s103f3-redblink.ihx 
Determine FLASH area
STLink: v2, JTAG: v29, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "./default/stm8s103f3-redblink.2e6c123b/stm8s103f3-redblink.ihx" is considered as INTEL HEX format!
104 bytes at 0x8000... OK
Bytes written: 104

Замечательно, светодиод мигает!

Чтобы очистить проект, делаем так:

$ qbs clean
Restoring build graph from disk
Cleaning up for configuration default

2) Отладка прошивки

Давайте посмотрим как обстоят дела с отладкой STM8. Для этого нам следует скомпилировать прошивку в ELF-формат. Вручную это достигается последовательным вводом следующих команд:

$ sdcc -mstm8 --debug --out-fmt-elf  -I./ -D STM8S103 -c -o main.rel -c main.c
$ sdcc -mstm8 --debug --out-fmt-elf  -I./ -D STM8S103 -c -o gpio.rel -c gpio.c
$ sdcc -o blink.elf -mstm8 --debug --out-fmt-elf gpio.rel main.rel 

Подключаем ST-LinkV2 с микроконтроллером к компьютеру, и запускаем openocd:

$ /opt/bin/openocd-v11 -f /opt/share/openocd/scripts/interface/stlink-dap.cfg -f /opt/share/openocd/scripts/target/stm8s103.cfg -c "init" -c "reset halt"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
srst_only separate srst_gates_jtag srst_open_drain connect_deassert_srst

Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.278009
Info : clock speed 800 kHz
Info : starting gdb server for stm8s.cpu on 3333
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, pc: 0x00008000
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections

Контакт есть. Запускаем GDB:

Кроме Си-кода можно отлаживать ассемблерный код:

Убедились, что отладка работает, завершаем сеанс. Пока отладка нам ненужна. Сейчас нас будет интересовать как скомпилировать прошивку посредством Qbs.

3) Сборка прошивки в ELF-формат

Чтобы изменить сборку прошивки с ihx-формата на elf-формат, нужно править Qbs-скрипт сборки. Давайте посмотри что у нас есть изначально. Файл "redblink.qbs":

import qbs

CppApplication {
    condition: {
        if (!qbs.architecture.contains("stm8"))
            return false;
        return qbs.toolchain.contains("iar")
            || qbs.toolchain.contains("sdcc")
    }
    name: "stm8s103f3-redblink"
    cpp.positionIndependentCode: false

    //
    // IAR-specific properties and sources.
    //

    Properties {
        condition: qbs.toolchain.contains("iar")
        cpp.commonCompilerFlags: ["-e"]
        cpp.driverLinkerFlags: [
            "--config_def", "_CSTACK_SIZE=0x100",
            "--config_def", "_HEAP_SIZE=0x100",
        ]
    }

    Group {
        condition: qbs.toolchain.contains("iar")
        name: "IAR"
        prefix: "iar/"
        Group {
            name: "Linker Script"
            prefix: cpp.toolchainInstallPath + "/../config/"
            fileTags: ["linkerscript"]
            files: ["lnkstm8s103f3.icf"]
        }
    }

    //
    // SDCC-specific properties and sources.
    //

    Properties {
        condition: qbs.toolchain.contains("sdcc")
    }

    //
    // Common code.
    //

    Group {
        name: "Gpio"
        files: ["gpio.c", "gpio.h"]
    }

    Group {
        name: "System"
        files: ["system.h"]
    }

    files: ["main.c"]
}

Мы можем убрать код для IAR и тем самым существенно сократить скрипт:

import qbs

CppApplication {

    Group {
        name: "Gpio"
        files: ["gpio.c", "gpio.h"]
    }

    Group {
        name: "System"
        files: ["system.h"]
    }

    files: ["main.c"]
}

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

Для начала пусть будет так:

import qbs

Product {
    type: ["application"]
    Depends { name: "cpp" }
    name: "firmware"

    files: [
        "system.h",
        "gpio.h",
        "gpio.c",
        "main.c",
    ] 
}

Проверяем:

$ qbs clean; qbs build profile:sdcc-4_1_0-stm8
Restoring build graph from disk
Cleaning up for configuration default
Restoring build graph from disk
Building for configuration default
compiling gpio.c [firmware]
compiling main.c [firmware]
linking firmware.ihx [firmware]
Build done for configuration default.

Лично мне не нравятся эти скупые строки "compiling gpio.c [firmware]", я хочу видеть с какими параметрами вызывается компилятор. Для этого мы можем добавить флаг "--command-echo-mode command-line":

Теперь сборка у нас будет выглядеть следующим образом:

$ qbs clean && qbs build profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Restoring build graph from disk
Cleaning up for configuration default
Restoring build graph from disk
Building for configuration default
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug
/usr/bin/sdcc -mstm8 -o /tmp/redblink/default/firmware.9bcf18e4/firmware.ihx /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel
Build done for configuration default.

Кроме "command-line" допустимы так-же варианты: "silent", "summary" и "command-line-with-environment".

По логу сборки можно увидеть, что: во-первых, нам добавили в опции компиляции флаг "--debug".

Нашей задачей сейчас будет задать сборку в ELF-формате. Для того следует добавить флаги: "--debug","--out-fmt-elf" для компилятора и компоновщика:

import qbs

Product {
    type: ["application"]
    Depends { name: "cpp" }
    name: "firmware"

    cpp.driverFlags: ["--out-fmt-elf"]
    cpp.driverLinkerFlags: ["--debug","--out-fmt-elf"]
    cpp.executableSuffix: ".elf"

    files: [
        "system.h",
        "gpio.h",
        "gpio.c",
        "main.c",
    ] 
    
}

В данном случае я пропустил флаг "--debug" в опциях компиляции, т.к. Qbs добавляет его сама. Собираем:

$ qbs clean && qbs build profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Restoring build graph from disk
Cleaning up for configuration default
Restoring build graph from disk
Resolving project for configuration default
Building for configuration default
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug --out-fmt-elf
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug --out-fmt-elf
/usr/bin/sdcc -mstm8 -o /tmp/redblink/default/firmware.9bcf18e4/firmware.elf /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/default/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel --debug --out-fmt-elf
Build done for configuration default.

Проверяем ELF-файл:

$ file /tmp/redblink/default/firmware.9bcf18e4/firmware.elf
/tmp/redblink/default/firmware.9bcf18e4/firmware.elf: ELF 32-bit MSB executable, STMicroeletronics STM8 8-bit, version 1 (IRIX), statically linked, with debug_info, not stripped

Вроде бы всё Ок.

4) Раздельные профили компиляции "debug" и "release"

Теперь, когда у нас появилась возможность собирать прошивку как в elf-формат так и ihx, мы можем добавить раздельные опции компиляции для сборок "debug" и "release". Для этого следует все специфичные опции сборки собрать в своих "Properties":

import qbs

Product {
    type: ["application"]
    Depends { name: "cpp" }
    name: "firmware"

    files: [
        "system.h",
        "gpio.h",
        "gpio.c",
        "main.c",
    ]    

    Properties
    {
        condition: qbs.buildVariant === "debug"
        cpp.driverFlags: ["--out-fmt-elf"]
        cpp.driverLinkerFlags: ["--debug","--out-fmt-elf"]
        cpp.executableSuffix: ".elf"
    }

    Properties
    {
        condition: qbs.buildVariant === "release"
        cpp.cFlags: ["--opt-code-size"]
        cpp.executableSuffix: ".ihx"
    }
}

Чтобы указать используемый профиль qbs, к команде следует добавить: "config:имя_профиля".

Удаляем каталог с дефолтным профилем:

$ rm -r ./default 

Производим сборку с профилем "release"

$ qbs build config:release profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Build graph does not yet exist for configuration 'release'. Starting from scratch.
Resolving project for configuration release
Setting up build graph for configuration release
Building for configuration release
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size
/usr/bin/sdcc -mstm8 -o /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel
Build done for configuration release.

Проверяем:

$ find ./release -name *.ihx -exec file {} \;
./release/firmware.9bcf18e4/firmware.ihx: ASCII text

То же самое для профиля "debug":

$ qbs build config:debug profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Build graph does not yet exist for configuration 'debug'. Starting from scratch.
Resolving project for configuration debug
Setting up build graph for configuration debug
Building for configuration debug
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug --out-fmt-elf
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug --out-fmt-elf
/usr/bin/sdcc -mstm8 -o /tmp/redblink/debug/firmware.9bcf18e4/firmware.elf /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel --debug --out-fmt-elf
Build done for configuration debug.
$ find ./debug -name *.elf -exec file {} \;
./debug/firmware.9bcf18e4/firmware.elf: ELF 32-bit MSB executable, STMicroeletronics STM8 8-bit, version 1 (IRIX), statically linked, with debug_info, not stripped

Как видно, все работает как надо.

При очистке сборки, также следует указывать профиль:

$ qbs clean config:release ; qbs clean config:debug
Restoring build graph from disk
Cleaning up for configuration release
Restoring build graph from disk
Cleaning up for configuration debug

Еще бы хотелось сделать печать размера скомпилированной прошивки. Для этого к конец файла "redblink.qbs" следует добавить правило (Rule):

    Rule
    {
        inputs: ["application"]
        Artifact
        {
            filePath: product.name
            fileTags: "flash"
        }
        prepare:
        {
            var size_name=input.filePath
            var cmd=new Command("stm8-size",size_name)
            cmd.description = "print size of the firmware"
            return [cmd]
        }
    }

Также, в поле "Type"(сразу после Product) следует добавить "flash":

    type: ["application","flash"]

Теперь сборка будет выглядеть так:

$ qbs build config:release profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Restoring build graph from disk
Resolving project for configuration release
Building for configuration release
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size
/usr/bin/sdcc -mstm8 -o /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel
/usr/local/bin/stm8-size /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx
Build done for configuration release.
/usr/local/bin/stm8-size /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx
   text    data     bss     dec     hex filename
      0     104       0     104      68 /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx

Правило работает на оба профиля сборки. Сборка с профилем "debug", теперь будет выглядеть так:

$ qbs build config:debug profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Restoring build graph from disk
Resolving project for configuration debug
Building for configuration debug
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug --out-fmt-elf
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --debug --out-fmt-elf
/usr/bin/sdcc -mstm8 -o /tmp/redblink/debug/firmware.9bcf18e4/firmware.elf /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/debug/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel --debug --out-fmt-elf
/usr/local/bin/stm8-size /tmp/redblink/debug/firmware.9bcf18e4/firmware.elf
/usr/local/bin/stm8-size /tmp/redblink/debug/firmware.9bcf18e4/firmware.elf
   text    data     bss     dec     hex filename
    105       0       0     105      69 /tmp/redblink/debug/firmware.9bcf18e4/firmware.elf
Build done for configuration debug.

Тут по выводу "stm8-size" можно увидеть, что в случае ihx файла данные из секции .text почему-то попали в секцию .data.

Так же можно сделать вывод, что опция компиляции "--opt-code-size" не особо влияет на размер прошивки ;)

5) Добавляем ассемблерный файл в проект

Сейчас мы немного модифицируем проект, и в первую очередь, для примера, добавим простой ассемблерный файл с функцией задержки "utils.s":

.module UTILS
.area CODE
.globl _delay
delay:
    ld a, #0x06
    ldw y, #0x1a80          ; 0x61a80 = 400000 i.e. (2*10^6 MHz)/5cycles
loop:
    subw y, #0x01           ; decrement with set carry
    sbc a,#0x0              ; decrement carry flag i.e. a = a - carry_flag
    jrne loop
    ret

Файл "main.c" приводим к следующиму типу:

#include "gpio.h"
#include "system.h"

extern void delay();

int main(void)
{
    gpio_init_red_led();

    while (1) {
        gpio_toggle_red_led();
        delay();
    }
}

В файл проекта "redblink.qbs" потребуется добавить пару строк (выделено красным):

import qbs

Product {
    type: ["application","flash"]
    Depends { name: "cpp" }
    name: "firmware"
    cpp.assemblerFlags: ["-plosgffwy"]

    files: [
        "system.h",
        "gpio.h",
        "gpio.c",
        "main.c",
        "util.s",
    ]    

    Properties
    {
        condition: qbs.buildVariant === "debug"
        cpp.cFlags: ["--debug","--out-fmt-elf"]
        cpp.linkerFlags: ["--debug","--out-fmt-elf"]
        cpp.executableSuffix: ".elf"
    }

    Properties
    {
        condition: qbs.buildVariant === "release"
        cpp.cFlags: ["--opt-code-size"]
        cpp.executableSuffix: ".ihx"
    }

    Rule
    {
        inputs: ["application"]
        Artifact
        {
            filePath: product.name
            fileTags: "flash"
        }
        prepare:
        {
            var size_name=input.filePath
            var cmd=new Command("stm8-size",size_name)
            cmd.description = "print size of the firmware"
            return [cmd]
        }
    }
}

Собираем проект:

$ qbs clean config:release && qbs build profile:sdcc-4_1_0-stm8 config:release
Restoring build graph from disk
Cleaning up for configuration release
Restoring build graph from disk
Building for configuration release
sdasstm8 -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -plosgffwy -ol /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/util.s.rel /mnt/tmp/mcu/qbs/redblink/util.s [firmware]
sdcc /mnt/tmp/mcu/qbs/redblink/main.c -c -o /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size [firmware]
sdcc /mnt/tmp/mcu/qbs/redblink/gpio.c -c -o /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size [firmware]
sdcc -mstm8 -o /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/firmware.ihx /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/util.s.rel [firmware]
print size of the firmware [firmware]
/usr/local/bin/stm8-size /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/firmware.ihx
   text    data     bss     dec     hex filename
      0      85       0      85      55 /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/firmware.ihx
Build done for configuration release.

Прошиваем:

$ stm8flash -c stlinkv2 -p stm8s103f3 -w ./release/firmware.9bcf18e4/firmware.ihx 
Determine FLASH area
STLink: v2, JTAG: v29, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "./release/firmware.9bcf18e4/firmware.ihx" is considered as INTEL HEX format!
85 bytes at 0x8000... OK
Bytes written: 85

Если светодиод мигает, тогда все отлично.

Не лишним будет собрать с "debug" профилем. Проверить отладку. Напоминаю, что, чтобы поставить точку останова на ассемблерной функции следует ее стравить на физический адрес:

Хотя на первый взгляд, кажется что все работает как надо, на самом деле это не так. Тут есть еще подводный камень, который сразу и не увидишь. Проблема проявиться если мы переименуем файл "until.s" в скажем "delay.s". В "redblnk.qbs" также следует сменить имя файла.

Тогда сборка будет выглядеть так:

$ qbs clean config:release && qbs build profile:sdcc-4_1_0-stm8 config:release
Restoring build graph from disk
Cleaning up for configuration release
Restoring build graph from disk
Resolving project for configuration release
Building for configuration release 
sdasstm8 -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -plosgffwy -ol /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/delay.s.rel /mnt/tmp/mcu/qbs/redblink/delay.s [firmware]
sdcc /mnt/tmp/mcu/qbs/redblink/gpio.c -c -o /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size [firmware]
sdcc /mnt/tmp/mcu/qbs/redblink/main.c -c -o /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size [firmware]
sdcc -mstm8 -o /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/firmware.ihx /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/delay.s.rel /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel [firmware]
print size of the firmware [firmware]
/usr/local/bin/stm8-size /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/firmware.ihx
   text    data     bss     dec     hex filename
      0      85       0      85      55 /mnt/tmp/mcu/qbs/redblink/release/firmware.9bcf18e4/firmware.ihx
Build done for configuration release.
 

Я выделил красным нашу проблему, которая заключатся в порядке следования "rel" файлов в параметрах компоновщика. Qbs их сортирует в алфавитном порядке и приводит это к тому, если мы сейчас попробуем залить прошивку, то получим ошибку:

$ stm8flash -c stlinkv2  -p stm8s103f3 -w ./release/firmware.9bcf18e4/firmware.ihx
Determine FLASH area
STLink: v2, JTAG: v29, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "./release/firmware.9bcf18e4/firmware.ihx" is considered as INTEL HEX format!
Address 0000 is out of range at line 1
 

Флешер ругается на то, что адреса в прошивке начинаются с 0x0000. Причина этого кроется скорее всего в том, что компоновщику не хватет каких-то данных от наших ассемблерных исходников. И самым простым способом решить данную проблему будет такая сортировка rel-файлов, когда компоновщику сначала указываются rel-файлы от Си-модулей, а уже потом от ассемблерных.

Сделать это можно только путем правки скрипта "sdcc.js" который реализует поддержку SDCC в Qbs. К счастью, это не сложно. Нужный нам скрипт находится здесь:

/usr/share/qbs/modules/cpp/sdcc.js

Это JavaScript, он имеет 548 строк. Часть из них отвечает за сборку по архитектуру MSC051, часть за STM8, и какая-то часть общая. Я использую не самую последнюю версию Qbs версии 1.16, актуальную версию данного скрипта вы можете найти на github.com по адресу: https://github.com/qbs/qbs/blob/master/share/qbs/modules/cpp/sdcc.js

Итак, чтобы исправить нашу проблему, открываем файл "/usr/share/qbs/modules/cpp/sdcc.js", и в теле функции "linkerFlags() вносим следующие изменения:

        // Linker scripts.
        var scripts = inputs.linkerscript
            ? inputs.linkerscript.map(function(scr) { return "-f" + scr.filePath; }) : [];
        if (scripts)
            Array.prototype.push.apply(escapableLinkerFlags, scripts);
    } else {
        // Output.
        args.push(outputs.application[0].filePath);

        ////////// НАЧАЛО ИЗМЕННИЙ /////////////////////////////
        // Inputs.
//        if (inputs.obj)
//            args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));

        if (inputs.obj) {
            var asm_obj=[];
            inputs.obj.forEach(function(scr) {
            var l=scr.filePath;
                if (l.match(".s.rel"))
                    asm_obj.push(l);
                else
                    args.push(l);
            });
          args = args.concat(asm_obj);
        }
        //////// КОНЕЦ ИЗМЕНЕНИЙ //////////////////////////////

        // Library paths.
        args = args.concat(allLibraryPaths.map(function(path) { return "-k" + path }));

        // Linker scripts.
        // Note: We need to split the '-f' and the file path to separate
        // lines; otherwise the linking fails.
        inputs.linkerscript.forEach(function(scr) {
            escapableLinkerFlags.push("-f", scr.filePath);
        });
    }

Еще раз собираем:

$ qbs clean config:release && qbs build config:release profile:sdcc-4_1_0-stm8 --command-echo-mode command-line
Restoring build graph from disk
Cleaning up for configuration release
Restoring build graph from disk
Resolving project for configuration release
Building for configuration release
/usr/bin/sdasstm8 -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -plosgffwy -ol /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/delay.s.rel /tmp/redblink/delay.s
/usr/bin/sdcc /tmp/redblink/main.c -c -o /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size
/usr/bin/sdcc /tmp/redblink/gpio.c -c -o /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel -DNDEBUG -I/usr/share/sdcc/include/stm8 -I/usr/share/sdcc/include -mstm8 --opt-code-speed --opt-code-size
/usr/bin/sdcc -mstm8 -o /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/gpio.c.rel /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.rel /tmp/redblink/release/firmware.9bcf18e4/3a52ce780950d4d9/delay.s.rel
/usr/local/bin/stm8-size /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx
/usr/local/bin/stm8-size /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx
   text    data     bss     dec     hex filename
      0      85       0      85      55 /tmp/redblink/release/firmware.9bcf18e4/firmware.ihx
Build done for configuration release.

Теперь все на своем месте. Прошиваем:

$ stm8flash -c stlinkv2  -p stm8s103f3 -w ./release/firmware.9bcf18e4/firmware.ihx 
Determine FLASH area
STLink: v2, JTAG: v29, SWIM: v7, VID: 8304, PID: 4837
Due to its file extension (or lack thereof), "./release/firmware.9bcf18e4/firmware.ihx" is considered as INTEL HEX format!
85 bytes at 0x8000... OK
Bytes written: 85

На этот раз все правильно.

6) Настраиваем STM8 baremetal kit в QtCreator

Как настраивать Qt Creator для работы с микроконтроллерами я уже рассказывал на примере STM32: "Qt Creator + CMake + STM32". В случае STM8 все делается аналогично, единственное, что еще потребуется в настройки Qbs заглянуть.

Во-первых, вы должны включить плагин baremetal, после чего у вас должен появиться SDCC в списке доступных компиляторов:

Добавьте отладчик:

Настройте параметры запуска OpenOCD:

Здесь поле "Configuration file" полностью не влазит в скрин, там должен быть полный путь до скрипта интерфейса(отладчика), без всяких "-f".

Затем создайте девайс:

И настройте Kit:

Также потребуется заглянуть в настройки Qbs:

Для примера работы с Qt Creator мы возьмем более сложный пример чем простая мигалка. Пусть это будет эхо программа для интерфеса UART1. Структура проекта будет выглядеть так:

$ tree .
.
├── asm
│   ├── delay.s
│   └── stm8s103f.s
├── inc
│   ├── stm8s103f.h
│   ├── stm8s_clk.h
│   ├── stm8s_tim4.h
│   ├── stm8s_uart1.h
│   └── uart1.h
├── main.c
├── s103uart.qbs
└── src
    └── uart1.c

Исходники можно будет скачать из архива в конце статьи. В данном случае суть не в них. Сейчас нас будет интересовать только Qbs-файл, который будет выглядеть так:

import qbs

Product {
    type: ["application","flash"]
    Depends { name: "cpp" }
    name: "firmware"
    cpp.assemblerFlags: ["-plosgffwy"]
    cpp.includePaths:["inc"]

    files: [
        "main.c", 
        "inc/uart1.h",
        "src/uart1.c",
        "asm/delay.s",
    ]

    Properties
    {
        condition: qbs.buildVariant === "debug"
        cpp.cFlags: [--out-fmt-elf"]
        cpp.driverLinkerFlags: ["--debug","--out-fmt-elf"]
        cpp.executableSuffix: ".elf"
    }

    Properties
    {
        condition: qbs.buildVariant === "release"
        cpp.cFlags: ["--opt-code-size"]
        cpp.executableSuffix: ".ihx"
    }

    Rule
    {
        inputs: ["application"]
        Artifact
        {
            filePath: product.name
            fileTags: "flash"
        }
        prepare:
        {
            var cmd=new Command("stm8-size",input.filePath)
            cmd.description = "print size of the firmware"
            return [cmd]
        }
    }
}

Открываем этот проект в QtCreator и при конфигурации выбираем профиль "baremetal STM8":

Для печати строк компиляции проекта, в свойствах сборки проекта следует отметить галочку "Show command lines":

Попробуйте собрать проект:

Если посмотреть на лог сборки, то система нам в опции сборки добавила флаг "--opt-code-speed". А при сборке с профилем Debug, она добавляет флаг "--debug".

Для прошивки микроконтроллера из Release профиля(кликом на зеленый треугольник "Run"), в свойствах проекта добавим Custom Command со следующими параметрами:

Прошивка:

Из Debug-профиля должна быть доступна отладка. Для этого поставте точку остановки и кликните по зеленому треугольнику с жуком:

В режиме трассировки можно даже поставить точку остановки в на ассемблерный код:

Периферийных регистров нет, QtCreator принимает описания периферийных регистров в формате SVD. Это формат Keil для ARM микроконтроллеров. Для STM8 таких файлов нет, а табличку с регистрами в данный формат не преобразуешь. Он очень замороченный.

При отладке доступна консоль отладчика GDB, куда можно вводить любые команды отладчика или монитора. В частности меня смущала путаница с секциями .text и .data и ввел глобальную переменную с предустановленным значением чтобы понять, что инициализация глобальных переменных происходит корректно:

Ответ система выдает в такой замысловатой форме.

Я не нашел как сделать подсветку синтаксиса дизассемблерного кода в режиме отладки, но ассемблерный код проекта можно раскрасить на свой вкус:

Для этого в каталог "~/.config/QtProject/qtcreator/generic-highlighter/syntax/" помещается файл "stm8asm.xml" с XML-разметкой. Я на быструю руку создал универсальную разметку ассемблера для STM8 и ARM

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "language.dtd">
<language name="ARM Assembler" version="0.2" kateversion="5.0" section="Assembler" extensions="*.s;*.S;*.asm" mimetype="text/x-asmarm" author="flanker">

    <highlighting>

        <list name="keywords">
            <item> adcs </item>
            <item> push </item>
            <item> pop </item>
            <item> adds </item>
            <item> add </item>
            <item> adr </item>
            <item> ands </item>
            <item> asrs </item>
            <item> bics </item>
            <item> bkpt </item>
            <item> cmn </item>
            <item> cmp </item>
            <item> cpsid </item>
            <item> cpsie </item>
            <item> dmb </item>
            <item> dsb </item>
            <item> eors </item>
            <item> isb </item>
            <item> ldm </item>
            <item> ldr </item>
            <item> ldrb </item>
            <item> ldrh </item>
            <item> ldrsb </item>
            <item> ldrsh </item>
            <item> lsls </item>
            <item> lsrs </item>
            <item> mov </item>
            <item> mov.w </item>
            <item> movs </item>
            <item> mrs </item>
            <item> msr </item>
            <item> muls </item>
            <item> mvns </item>
            <item> snop </item>
            <item> orrs </item>
            <item> rev </item>
            <item> rev16 </item>
            <item> revsh </item>
            <item> rors </item>
            <item> rsbs </item>
            <item> sbcs </item>
            <item> sev </item>
            <item> stm </item>
            <item> str </item>
            <item> strb </item>
            <item> strh </item>
            <item> sub </item>
            <item> subs </item>
            <item> svc </item>
            <item> sxtb </item>
            <item> sxth </item>
            <item> tst </item>
            <item> uxtb </item>
            <item> uxth </item>
            <item> wfi </item>
            <item> wfe </item>

            <item> pushw </item>
            <item> popw </item>
            <item> ret </item>
            <item> call  </item>
            <item> subw </item>
            <item> sbc </item>
            <item> ld </item>
            <item> dec </item>
            <item> inc </item>
            <item> ldw </item>
            <item> decw </item>
            <item> incw </item>
            <item> iret </item>
            <item> clr </item>
            <item> clrw </item>
            <item> bset </item>
            <item> bres </item>
            <item> bcpl </item>
            <item> cp </item>
            <item> cpw </item>
            <item> div </item>
            <item> divw </item>
            <item> tnzw </item>
            <item> tnz </item>


        </list>

    <list name="branch instructions">
            <item> b </item>
            <item> bl </item>
            <item> blx </item>
            <item> bx </item>
            <item> beq </item>
            <item> bne </item>
            <item> bcs </item>
            <item> bcc </item>
            <item> bmi </item>
            <item> bmi.n </item>
            <item> bpl </item>
            <item> bvs </item>
            <item> bvc </item>
            <item> bhi </item>
            <item> bls </item>
            <item> bge </item>
            <item> ble </item>
            <item> blt </item>
            <item> bgt </item>
            <item> bal </item>


            <item> jreq </item>
            <item> jrnc </item>
            <item> jrne </item>
            <item> jra </item>
            <item> btjt </item>
            <item> btjf </item>
    </list>

        <list name="registers">
            <item> r0 </item>
            <item> r1 </item>
            <item> r2 </item>
            <item> r3 </item>
            <item> r4 </item>
            <item> r5 </item>
            <item> r6 </item>
            <item> r7 </item>
            <item> r8 </item>
            <item> r8 </item>
            <item> r10 </item>
            <item> r11</item>
            <item> r12 </item>
            <item> r13 </item>
            <item> r14 </item>
            <item> r15 </item>
            <item> sp </item>
            <item> pc </item>
            <item> lr </item>

            <item> a </item>
            <item> x </item>
            <item> y </item>
            <item> yl </item>
            <item> xl </item>
        </list>

        <contexts>
            <context name="Base" attribute="Normal Text" lineEndContext="#stay">
                <RegExpr String= "[{}\[\[()]" attribute = "Special Symbol" context="#stay"/>
                <RegExpr String= "^[a-zA-Z0-9_].*?:" attribute = "Label" context="#stay"/>
                <!--RegExpr String= "b[csxlmpne]*\ *[a-zA-Z0-9_]*" attribute = "Branch Instructions" context="#stay"/-->
                <RegExpr String="=0x[0-9a-fA-F]*" attribute="Number" context="#stay"/>
                <RegExpr String="=[0-9a-zA-Z_]*" attribute="Number" context="#stay"/>
                <RegExpr String="#0x[0-9a-fA-F]*" attribute="Number" context="#stay"/>
                <RegExpr String="#[0-9]*" attribute="Number" context="#stay"/>
                <RegExpr String="=[0-9]*" attribute="Number" context="#stay"/>
                <keyword attribute="Register" context="#stay" String="registers" />
                <keyword attribute="Directive" context="#stay" String="directives" />
                <keyword attribute="Keyword" context="#stay" String="keywords" />
                <keyword attribute="Branch Instructions" context="#stay" String="branch instructions"/>
                <RegExpr String= "\.asciz" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.balign" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.bss" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.data" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.end" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.global" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.long" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.section" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.text" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.word" attribute = "Data Type" context="#stay"/>

                <RegExpr String= "\.type" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.global" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.cpu" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "cortex-m0" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "cortex-m3" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "cortex-m4" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.weak" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.thumb" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.thumb_set" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.fpu\ softvfp" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.size" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.syntax\ unified" attribute = "Data Type" context="#stay"/>

                <RegExpr String= "\.area" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.globl" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.module" attribute = "Data Type" context="#stay"/>
                <RegExpr String= "\.include" attribute = "Data Type" context="#stay"/>

                <RegExpr String= ";.*" attribute = "Comment" context="#stay"/>

                <DetectChar attribute="String" context="string" char="&quot;" />
                <RegExpr String= "\@.*$" attribute = "Comment" context="#pop"/>
                <Detect2Chars attribute="Comment" context="Comment Type 2" char="/" char1="*" beginRegion="Comment"/>
            </context>
                <context attribute="Comment" lineEndContext="#stay" name="Comment Type 2">
                <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
            </context>
            <context attribute="String" lineEndContext="#stay" name="string" >
                <DetectChar attribute="String" context="#pop" char="&quot;" />
            </context>
        </contexts>

        <itemDatas>
            <itemData name="Label" color="#e1cb3b" />
            <itemData name="Normal Text" color="#333333" />
            <itemData name="Keyword" color="#32cc32" />
            <itemData name="Branch Instructions" color="#39f077" />
            <itemData name="Register" defStyleNum="dsKeyword" />
            <itemData name="Hex"  color="#ff4433"/>
            <itemData name="Number"  color="#ff5f1f"/>
            <itemData name="String" defStyleNum="dsString" />
            <itemData name="Comment" defStyleNum="dsComment" />
            <itemData name="Special Symbol" color="#ffffff" />
            <!--itemData name="Data Type" defStyleNum="dsComment" /-->
            <itemData name="Data Type" color="#f500f1" />
        </itemDatas>

    </highlighting>

    <general>
    </general>

</language>

7) Качество кода SDCC

Мне не хочется усложнять статью, но не могу не сказать в заключение пару слов о качестве кода SDCC. Пару месяцев я писал проект драйвера FM-приемника RDA5807m пока у меня не сгорел ST-Link. Потом я переносил проект на ATmega328 и я могу сравнить работу нормального компилятора (avr-gcc) с SDCC-STM8.

Первое и самое главное, что хочется сказать - "It's work!". Это работает. Мой проект под финал весил под 16КБ, это несколько тысяч строк исходного кода на Си и Ассемблере, и SDCC этот код успешно компилировал в рабочую прошивку:

Т.е. пользоваться SDCC можно, хотя конечно он постоянно выкидывал фортели. Если быть справедливым, большую часть моих претензий относится скорее к компоновщику нежели компилятору. Причем компилятор STM8 в SDCC в настоящее время вполне приличный. Какие-то сложные алгоритмы приходилось переписывать на ассемблере, но компилятор много раз срывал мои овации. Всего один пример - сдвиг влево на 8 бит:

uint16_t tuner=(uint16_t)(msb<<8) + (uint16_t)lsb;
      0009CA 7B 02            [ 1] 2756     ld  a, (0x02, sp)
      0009CC 95               [ 1] 2757     ld  xh, a
      0009CD 4F               [ 1] 2758     clr a
      0009CE 97               [ 1] 2759     ld  xl, a
      0009CF 0F 02            [ 1] 2760     clr (0x02, sp)
      0009D1 72 FB 02         [ 2] 2761     addw    x, (0x02, sp)

Здесь компилятор понял, что от него не требуется что-то сдвигать, а что нужно поменять местами младший и старший байт, при этом очистив младший.

Есть еще множество примеров с указателями, но как уже сказал, не хочу усложнять статью.

В тоже время компоновщик постоянно норовил что-нибудь выкинуть. Если ваша функция слишком длинная и локальная переменная перестает быть видимой. Это совершенно нормально. Поэтому длинный главный цикл не приветствуется ;). В одном модуле у меня "отвалилось" деление. Пришлось быстро писать функции деления на ассемблере и далее вызывать уже эти функции:

    .globl _div_func
_div_func:
    ldw x,(0x03,sp)
    ld a,(0x05,sp)
    div x,a
    ret

.globl _divmod_func
_divmod_func:
    ldw x,(0x03,sp)
    ld a,(0x05,sp)
    div x,a
    ret

Использование статических переменных запрещено:

Вот этот код SDCC мне так и не простил:

void (*command)(void)=task[state];
command();

постоянно жаловался на него, хотя и компилировал его абсолютно нормально:

warning 244: pointer types incompatible 
from type 'void generic* fixed'
  to type 'void function ( ) code* auto'

avr-gcc был не таким ворчливым ;)

Если говорить про эффективность оптимизации кода в SDCC, то могу примести следующие цифры. Первый вариант драйвера мню был написан в 2019-ом году на SDCC версии 3.9. Он весит:

stm8-size ./firmware.ihx 
   text    data     bss     dec     hex filename
      0    6422       0    6422    1916 ./firmware.ihx

Около полутора килобайт здесь строковые константы. Давайте пересоберем ее в SDCC v4.2:

   text    data     bss     dec     hex filename
      0    6039       0    6039    1797 firmware.ihx

Во-первых проект собрался. но я не уверен что он в рабочем состоянии, т.к. в SDCC-4.x поменяли формат вызова функций на совместимый с Cosmic. Тем не менее, прогресс есть. Прошивка стала весить меньше. А теперь давайте используем опцию оптимизации "--opt-code-size"

text    data     bss     dec     hex filename
    0    6027       0    6027    178b firmware.ihx

Прошивка уменьшилась еще на целых ДВЕНДЦАТЬ байт?!

Делайте выводы сами.

Когда в проекте столкнулся с необходимостью выводить информацию на кириллице (подразумевалось работа с меню), встал вопрос с работой в кодировках отличных от юникода. И в QtCreator обнаружилась замечательная возможность использовать пользовательские кодировки для выбранных файлов:

На этом тему заканчиваю. Надеюсь данная информация будет кому-то полезной, или хотя бы развлечет)) Just for fun!

ZIP-архив с исходниками обоих используемых в статье проектов, а также итоговый "sdcc.js" можно скачать здесь.

8) Qbs-скрипт для STM32F103C8-проекта на CMSIS (пошаговая инструкция)

Я не планировал писать про использование Qbs для STM32 проектов, т.к. использую CMake, но мне показалось, что без этого тема использования Qbs будет раскрыта не полностью.

С одной стороны составвление Qbs-скрипта для STM32 проще чем для STM8, т.к. не придется допиливать скрипты Qbs. С другой стороны, в сравнении с STM8, проекты для ARM сложнее по структуре, за счет широкого использования библиотек: CMSIS, HAL, SPL, USB FS Device Lib, RTOS, и пр.

В качестве примера возьмем проект мигалки на CMSIS для Bluepill. Его структура будет выглядеть так:

$ tree .
.
├── CMSIS
│   ├── core
│   │   ├── core_cm3.c
│   │   └── core_cm3.h
│   └── device
│       ├── stm32f10x.h
│       └── system_stm32f10x.h
├── SPL
│   └── inc
│       ├── stm32f10x_gpio.h
│       └── stm32f10x_rcc.h
├── asm
│   └── init.s
├── bluepill.qbs
├── inc
│   └── main.h
├── main.c
├── script.ld
└── src
    ├── my_misc.c
    └── startup.c

8 directories, 13 files

Исходники можно скачать здесь, сейчас не в них суть.

Здесь у нас четыре наших рабочих файла + qbs-скрипт. Остальные файлы нас не интересуют вообще, т.к. мы с ними работать не будем. Это библиотека CMSIS, заголовочные файлы от SPL и скрипт компоновщика. Тем не менее, они должны быть прописаны в проекте сборки прошивки.

На финальном этапе наш рабочий проект должен выглядеть как-то так:

Вместо того чтобы выкладывать финальный Qbs-скрипт с каким-то пояснениям, я бы хотел сделать пошаговый мануал, т.к. от него будет больше пользы.

Поэтому опять на какое-то время перейдем в консоль.

Самый простой Qbs-скрипт для данного проекта, который я смог составить, будет выглядеть так:

import qbs

Product {
    type: ["application"]
    Depends { name: "cpp" }
    name: "firmware"

    cpp.cLanguageVersion: "c99"
    cpp.positionIndependentCode: false
    cpp.executableSuffix: ".elf"

    cpp.includePaths:[
        "CMSIS/device",
        "CMSIS/core",
        "SPL/inc",
        "inc",
    ]

    cpp.driverLinkerFlags: [
        "-specs=nosys.specs",
        "-specs=nano.specs",
    ]

    cpp.linkerFlags:
    [
        "--gc-sections",
        "-T" + path + "/script.ld",
    ]


    cpp.cFlags: [
        "-mthumb",
        "-mcpu=cortex-m3",
    ]

    cpp.defines: [
        "STM32F10X_MD",
        "SYSCLK_FREQ_72MHz",
    ]

   files: [
        "asm/init.s",
        "src/startup.c",
        "src/my_misc.c",
        "main.c",
    ]
}

Ищем ARM профиль компиляции:

$ qbs config --list|grep arm
profiles.arm-none-eabi-gcc-10_3.cpp.archiverPath: "/usr/local/bin/arm-none-eabi-ar"
profiles.arm-none-eabi-gcc-10_3.cpp.assemblerPath: "/usr/local/bin/arm-none-eabi-as"
profiles.arm-none-eabi-gcc-10_3.cpp.nmPath: "/usr/local/bin/arm-none-eabi-nm"
profiles.arm-none-eabi-gcc-10_3.cpp.objcopyPath: "/usr/local/bin/arm-none-eabi-objcopy"
profiles.arm-none-eabi-gcc-10_3.cpp.stripPath: "/usr/local/bin/arm-none-eabi-strip"
profiles.arm-none-eabi-gcc-10_3.cpp.toolchainInstallPath: "/usr/local/bin"
profiles.arm-none-eabi-gcc-10_3.cpp.toolchainPrefix: "arm-none-eabi-"
profiles.arm-none-eabi-gcc-10_3.qbs.toolchainType: "gcc"

Собираем проект:

$ qbs build profile:arm-none-eabi-gcc-10_3
Build graph does not yet exist for configuration 'default'. Starting from scratch.
Resolving project for configuration default
WARNING: Could not detect target platform ('linux' given)
Setting up build graph for configuration default
Building for configuration default
assembling init.s [firmware]
compiling my_misc.c [firmware]
compiling startup.c [firmware]
compiling main.c [firmware]
linking firmware.elf [firmware]
Build done for configuration default.

Чтобы увидеть опции компиляции добавим параметр "--command-echo-mode command-line":

$ qbs clean && qbs build profile:arm-none-eabi-gcc-10_3  --command-echo-mode command-line
Restoring build graph from disk
Cleaning up for configuration default
Restoring build graph from disk
Building for configuration default
/usr/local/bin/arm-none-eabi-as -g -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/2445be8241aabd34/init.s.o /mnt/tmp/mcu/qbs/stm32f103c8/asm/init.s
/usr/local/bin/arm-none-eabi-gcc -g -O0 -Wall -Wextra -specs=nosys.specs -specs=nano.specs -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/f27fede2220bcd32/startup.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/src/startup.c
/usr/local/bin/arm-none-eabi-gcc -g -O0 -Wall -Wextra -specs=nosys.specs -specs=nano.specs -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/f27fede2220bcd32/my_misc.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/src/my_misc.c
/usr/local/bin/arm-none-eabi-gcc -g -O0 -Wall -Wextra -specs=nosys.specs -specs=nano.specs -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/3a52ce780950d4d9/main.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/main.c
/usr/local/bin/arm-none-eabi-gcc -Wl,--gc-sections,-T/mnt/tmp/mcu/qbs/stm32f103c8/script.ld -specs=nosys.specs -specs=nano.specs -o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/firmware.elf /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/2445be8241aabd34/init.s.o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/3a52ce780950d4d9/main.c.o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/f27fede2220bcd32/my_misc.c.o /mnt/tmp/mcu/qbs/stm32f103c8/default/firmware.9bcf18e4/f27fede2220bcd32/startup.c.o
Build done for configuration default.

Нам осталось внести пару правок в Qbs-файл проекта, и его можно уже будет загружать в QtCreator.

Во-первых, надо добавить профили компиляции Debug и Release:

import qbs

Product {
    type: ["application"]
    Depends { name: "cpp" }
    name: "firmware"

    cpp.cLanguageVersion: "c99"
    cpp.positionIndependentCode: false
    cpp.executableSuffix: ".elf"

    cpp.includePaths:[
        "CMSIS/device",
        "CMSIS/core",
        "SPL/inc",
        "inc",
    ]

    cpp.driverLinkerFlags: [
        "-specs=nosys.specs",
        "-specs=nano.specs",
    ]

    cpp.linkerFlags:
   [
        "--gc-sections",
        "-T" + path + "/script.ld",
    ]


    cpp.cFlags: [
        "-mthumb",
        "-mcpu=cortex-m3",
    ]

    cpp.defines: [
        "STM32F10X_MD",
        "SYSCLK_FREQ_72MHz",
    ]
    
    Properties
    {
        condition: qbs.buildVariant === "debug"
        cpp.defines: outer.concat(["DEBUG=1"])
        cpp.debugInformation: true
        cpp.optimization: "none"
    }

    Properties
    {
        condition: qbs.buildVariant === "release"
        cpp.debugInformation: false
        cpp.optimization: "fast"
    }


   files: [
        "asm/init.s",
        "src/startup.c",
        "src/my_misc.c",
        "main.c",
    ]
}

Кроме режимов оптимизации "fast" (-O2) и "none" доступен также "small" (-0s).

Проверяем:

$ qbs build  config:release profile:arm-none-eabi-gcc-10_3 --command-echo-mode command-line
Build graph does not yet exist for configuration 'release'. Starting from scratch.
Resolving project for configuration release
WARNING: Could not detect target platform ('linux' given)
Setting up build graph for configuration release
Building for configuration release
/usr/local/bin/arm-none-eabi-as -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/2445be8241aabd34/init.s.o /mnt/tmp/mcu/qbs/stm32f103c8/asm/init.s
/usr/local/bin/arm-none-eabi-gcc -O2 -Wall -Wextra -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DNDEBUG -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/f27fede2220bcd32/my_misc.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/src/my_misc.c
/usr/local/bin/arm-none-eabi-gcc -O2 -Wall -Wextra -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DNDEBUG -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/main.c
/usr/local/bin/arm-none-eabi-gcc -O2 -Wall -Wextra -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DNDEBUG -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/f27fede2220bcd32/startup.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/src/startup.c
/usr/local/bin/arm-none-eabi-gcc -Wl,--gc-sections,-T/mnt/tmp/mcu/qbs/stm32f103c8/script.ld -o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/firmware.elf /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/2445be8241aabd34/init.s.o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/3a52ce780950d4d9/main.c.o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/f27fede2220bcd32/my_misc.c.o /mnt/tmp/mcu/qbs/stm32f103c8/release/firmware.9bcf18e4/f27fede2220bcd32/startup.c.o -specs=nosys.specs -specs=nano.specs
Build done for configuration release.

Последнее что нам нужно сделать: конвертирование прошивки в BIN формат и печать размера прошивки. Для этого добавляем следующее Rule(правило):

import qbs

Product {
    type: ["application","flash"]
    Depends { name: "cpp" }
    name: "firmware"

    cpp.cLanguageVersion: "c99"
    cpp.positionIndependentCode: false
    cpp.executableSuffix: ".elf"

    cpp.includePaths:[
        "CMSIS/device",
        "CMSIS/core",
        "SPL/inc",
        "inc",
    ]

    cpp.driverLinkerFlags: [
        "-specs=nosys.specs",
        "-specs=nano.specs",
    ]

    cpp.linkerFlags:
   [
        "--gc-sections",
        "-T" + path + "/script.ld",
    ]


    cpp.cFlags: [
        "-mthumb",
        "-mcpu=cortex-m3",
    ]

    cpp.defines: [
        "STM32F10X_MD",
        "SYSCLK_FREQ_72MHz",
    ]

    Properties
    {
        condition: qbs.buildVariant === "debug"
        cpp.defines: outer.concat(["DEBUG=1"])
        cpp.debugInformation: true
        cpp.optimization: "none"
    }

    Properties
    {
        condition: qbs.buildVariant === "release"
        cpp.debugInformation: false
        cpp.optimization: "fast"
    }


   files: [
        "asm/init.s",
        "src/startup.c",
        "src/my_misc.c",
        "main.c",
    ]


    Rule
    {
        inputs: ["application"]
        Artifact
        {
            filePath: product.name
            fileTags: "flash"
        }
        prepare:
        {
            var size_name=input.filePath
            var argsObjcopy = ["-O", "binary", input.filePath, output.filePath+".bin"]
            var cmd_bin=new Command("arm-none-eabi-objcopy",argsObjcopy)
            var cmd_size=new Command("arm-none-eabi-size",size_name)
            cmd_bin.description = "convert to binary format"
            cmd_size.description = "print size of the firmware"
            return [cmd_bin,cmd_size]
        }
    }
}

Проверем:

$ qbs clean config:debug && qbs build  config:debug profile:arm-none-eabi-gcc-10_3 --command-echo-mode command-line
Restoring build graph from disk
Cleaning up for configuration debug
Restoring build graph from disk
Building for configuration debug
/usr/local/bin/arm-none-eabi-as -g -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/2445be8241aabd34/init.s.o /mnt/tmp/mcu/qbs/stm32f103c8/asm/init.s
/usr/local/bin/arm-none-eabi-gcc -g -O0 -Wall -Wextra -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -DDEBUG=1 -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/f27fede2220bcd32/my_misc.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/src/my_misc.c
/usr/local/bin/arm-none-eabi-gcc -g -O0 -Wall -Wextra -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -DDEBUG=1 -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/f27fede2220bcd32/startup.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/src/startup.c
/usr/local/bin/arm-none-eabi-gcc -g -O0 -Wall -Wextra -pipe -fvisibility=default -mthumb -mcpu=cortex-m3 -DSTM32F10X_MD -DSYSCLK_FREQ_72MHz -DDEBUG=1 -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/device -I/mnt/tmp/mcu/qbs/stm32f103c8/CMSIS/core -I/mnt/tmp/mcu/qbs/stm32f103c8/SPL/inc -I/mnt/tmp/mcu/qbs/stm32f103c8/inc -std=c99 -o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/3a52ce780950d4d9/main.c.o -c /mnt/tmp/mcu/qbs/stm32f103c8/main.c
/usr/local/bin/arm-none-eabi-gcc -Wl,--gc-sections,-T/mnt/tmp/mcu/qbs/stm32f103c8/script.ld -o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/firmware.elf /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/2445be8241aabd34/init.s.o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/3a52ce780950d4d9/main.c.o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/f27fede2220bcd32/my_misc.c.o /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/f27fede2220bcd32/startup.c.o -specs=nosys.specs -specs=nano.specs
/usr/local/bin/arm-none-eabi-objcopy -O binary /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/firmware.elf /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/firmware.bin
/usr/local/bin/arm-none-eabi-size /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/firmware.elf
Build done for configuration debug.
/usr/local/bin/arm-none-eabi-size /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/firmware.elf
   text    data     bss     dec     hex filename
   1324       8    1056    2388     954 /mnt/tmp/mcu/qbs/stm32f103c8/debug/firmware.9bcf18e4/firmware.elf

Данный проект уже сейчас можно открывать в QtCreator, но я бы сделал еще кое-что. На данный момент, у нас все рабочие файл свалены в одну кучу. Это:

   files: [
        "asm/init.s",
        "src/startup.c",
        "src/my_misc.c",
        "main.c",
    ]

Я бы предложил вместо данного списка файлов ввести группы. Тогда Qbs-скрипт примет такой, окончательный вид:

import qbs

Product {
    type: ["application","flash"]
    Depends { name: "cpp" }
    name: "firmware"
    
    property string Home: path
    property string Inc: Home + "/inc"
    property string Src: Home + "/src"
    property string Asm: Home + "/asm"
    
    cpp.cLanguageVersion: "c99"
    cpp.positionIndependentCode: false
    cpp.executableSuffix: ".elf"

    cpp.includePaths:[
        "CMSIS/device",
        "CMSIS/core",
        "SPL/inc",
        "inc",
    ]

    cpp.driverLinkerFlags: [
        "-specs=nosys.specs",
        "-specs=nano.specs",
    ]

    cpp.linkerFlags:
   [
        "--gc-sections",
        "-T" + path + "/script.ld",
    ]


    cpp.cFlags: [
        "-mthumb",
        "-mcpu=cortex-m3",
    ]

    cpp.defines: [
        "STM32F10X_MD",
        "SYSCLK_FREQ_72MHz",
    ]

    Properties
    {
        condition: qbs.buildVariant === "debug"
        cpp.defines: outer.concat(["DEBUG=1"])
        cpp.debugInformation: true
        cpp.optimization: "none"
    }

    Properties
    {
        condition: qbs.buildVariant === "release"
        cpp.debugInformation: false
        cpp.optimization: "fast"
    }

    
    Group {
        name: "Assembly"
        fileTags: ["asm"]
        files: [
            Asm  + "/*.s",
        ]
    }

    Group {
        name: "Inc"
        files: [
            Inc + "/*.h",
        ]
    }

    Group {
        name: "Src"
        files: [
            Src + "/*.c",
        ]
    }


   files: [
        "main.c",
    ]
    


    Rule
    {
        inputs: ["application"]
        Artifact
        {
            filePath: product.name
            fileTags: "flash"
        }
        prepare:
        {
            var size_name=input.filePath
            var argsObjcopy = ["-O", "binary", input.filePath, output.filePath+".bin"]
            var cmd_bin=new Command("arm-none-eabi-objcopy",argsObjcopy)
            var cmd_size=new Command("arm-none-eabi-size",size_name)
            cmd_bin.description = "convert to binary format"
            cmd_size.description = "print size of the firmware"
            return [cmd_bin,cmd_size]
        }
    }
}

В массивах вида: file [Prefix + "/*.h"] можно использовать как перечень файлов, так и маски имен. На ваш вкус.

Теперь, прежде чем загружать проект в QtCreator, следует создать BareMetal Kit для микроконтроллеров STM32F103xx

Он создается аналогично таковому для STM8, но с одним отличием. Нам нужно будет указать файл в формате SVD с описанием периферийных регистров ввода-вывода:

Он нужен для того, чтобы в режиме отладки мы могли бы просматривать значения этих регистров. Данный файл берется с сайта Keil: https://www.keil.com/dd2/pack/

Скачанный пак является ZIP-архивом в котором содержатся среди прочего SVD-файлы. В архиве, который можно скачать по ссылке: stm8_qbs.zip, уже содержится файл "STM32F103xx.svd".

После этого открываем проект в QtCreator. Отладка должна работать сразу, для прошивки из профиля release создаем "custom executable":

Если нужна печать флагов компиляции при сборке проекта, в свойствах проекта, во вкладке "Build Steps" отмечаем чекбокс "Show command lines". Это нужно делать для обоих профилей компиляции, для Release и Debug:

При отладке если выбрать вкладку с периферийными регистрами, то в ней будет поначалу пустое окно. Нужно щелкнуть по нему провой кнопкой миши и выбрать группу периферийных регистров:

Теперь при трассировке у вас будет отображаться значения выбранной группы:

К сожалению, одновременно выбирается только одна группа РВВ.

9) Проблема с запуском OpenOCD для STM8

С отладкой STM8 к сожалению все не так просто как хотелось бы, и часто OpenOCD отказывается запускаться. Выглядит это так:

$ openocd -f interface/stlink-dap.cfg -f target/stm8s105.cfg -c "init" -c "reset halt"
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
srst_only separate srst_gates_jtag srst_open_drain connect_deassert_srst

Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.274194
Error: stlink_swim_enter_failed (unable to connect to the target)

Какой-то определенной причины. когда возникает данный бег я выявить пока не смог. Поначалу думал, что отладка для STM8 еще "слишком сырая". Потом я думал, что OpenOCD не поддерживает чипы STM8S207, и т.д. Определенно могу сказать, что от чипа это не зависит, она возникала и на stm8s105 и на stm8s103.

Решается проблема достаточно просто, прошивкой какого-нибудь простейшего Blink, возможно поможет очистка флеш-памяти. После этого отладка снова начинает работать, до следующего подключения платы с stlink к компьютеру.

Архив с файлам к статье stm8_qbs.zip