{"id":385250,"date":"2024-06-29T06:00:00","date_gmt":"2024-06-29T06:00:00","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=385250"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T06:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=385250","title":{"rendered":"<span>Setup of Qt Creator for programming and debugging of STM32 microcontrollers<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>Introduction<\/h3>\n<p>Hi,<\/p>\n<p>Currently I am investigating firmware development for STM32 microcontrollers and I would like to share with you my experience for doing it in Qt Creator IDE.<\/p>\n<p>There are a lot of IDEs, which are used for firmware development of STM32. Some of them, being quite comfortable, have restrictions for trial license. For example, the one of the most known IDE, IAR Embedded, suggests either a limited amount of product usage time (30 days) or the limited firmware size of 32 MB, which is not too much.<\/p>\n<p>Within this scope of the publication, we investigate the method of setting up an environment that allows developing the full value of the STM32 firmware in Qt Creator.<\/p>\n<p>First, we need to determine the components for the execution of experiments.<\/p>\n<h3>Hardware<\/h3>\n<p>I use the <strong>bluepill<\/strong> board based on the <strong>STM32F103C8T6<\/strong> microcontroller unit (MCU) to conduct the experiments. Sometimes, there could be issues with <strong>openocd<\/strong>, which claims the incorrect MCU number. It could be solved by 2 methods: either by replacing the number in the <strong>openocd<\/strong> configuration file or by changing the MCU to the original one. I chose the second option. For firmware burning and debugging purposes we use the <strong>ST-Link<\/strong> interface.<\/p>\n<h3>Software<\/h3>\n<p>The experiments are conducted on a Windows 10 virtual machine in Virtual Box. In addition, we will need to install the following software:<\/p>\n<ol>\n<li>\n<p><strong>Qt Creator<\/strong>. It could be installed with the online installer package from the Qt website. In this case, registration is required, and we do not need the Qt framework itself. For this reason, I download Qt Creator directly from the GitHub releases: https:\/\/github.com\/qt-creator\/qt-creator\/tags.<\/p>\n<\/li>\n<li>\n<p><strong>gcc-arm-none-eabi<\/strong>.<strong> <\/strong>https:\/\/developer.arm.com\/downloads\/-\/gnu-rm.<\/p>\n<\/li>\n<li>\n<p><strong>Git<\/strong>. https:\/\/git-scm.com\/downloads.<\/p>\n<\/li>\n<li>\n<p><strong>openocd<\/strong>. https:\/\/github.com\/openocd-org\/openocd\/tags. This software is designed for firmware burning and on-chip firmware debugging   .<\/p>\n<\/li>\n<li>\n<p><strong>make<\/strong>. https:\/\/gnuwin32.sourceforge.net\/packages\/make.htm<\/p>\n<\/li>\n<li>\n<p><strong>ST-LINK Utility<\/strong>. This utility is used for firmware burning and also contains a USB driver for ST-Link interface. https:\/\/www.st.com\/en\/development-tools\/stsw-link004.html<\/p>\n<\/li>\n<li>\n<p><strong>Python2.7<\/strong>. https:\/\/www.python.org\/downloads\/release\/python-2718\/<\/p>\n<\/li>\n<\/ol>\n<p>First, we need to download and unpack Qt Creator, and after that to install git, gcc compiler, openocd, python, make and ST-Link utility.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/222\/e0b\/526\/222e0b526b0722b539d67684643acb42.PNG\" alt=\"\" title=\"\" width=\"499\" height=\"388\"\/><\/figure>\n<p>I unpacked Qt Creator and openocd into C:\\Tools. All programs, except Qt Creator, should be added to <strong>Path<\/strong>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/286\/9aa\/849\/2869aa8496d4e9b9560382f598cd1993.PNG\" alt=\"\" title=\"\" width=\"527\" height=\"501\"\/><\/figure>\n<p>Then we need to check whether gcc and openocd are pushed to Path by calling them from the command line.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b2f\/d39\/5cd\/b2fd395cd8f2638653f8e514aea0cc97.PNG\" alt=\"\" title=\"\" width=\"979\" height=\"512\"\/><\/figure>\n<p>If gcc and openocd are available from cmd, we can move forward and tune the Qt Creator IDE.<\/p>\n<h3>Setup of Qt Creator<\/h3>\n<p>Now we can launch the IDE and install the necessary plugins: <strong>BareMetal<\/strong> and <strong>GenericProjectManager<\/strong>. To do this we need to go to <strong>Help->About Plugins<\/strong>, check the plugins and restart Qt Creator.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2e4\/d78\/cbb\/2e4d78cbbcb084654c682137f3e80e44.PNG\" alt=\"\" title=\"\" width=\"652\" height=\"432\"\/><\/figure>\n<p>The first thing we need to do is to go to <strong>Edit->Preferences<\/strong> and choose <strong>Kits<\/strong> from the list on left. Here we should see our kit for building an ARM, which was pulled from Path. Next, we go to <strong>Devices <\/strong>from the list on the left, on the<strong> Bare Metal<\/strong> tab, and add an <strong>OpenOCD<\/strong> server. In our case, I call it <strong>OpenOCD-STM32F103<\/strong>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3b4\/acb\/460\/3b4acb4608ce36608ee9bc1f25f2ce68.PNG\" alt=\"\" title=\"\" width=\"1011\" height=\"657\"\/><\/figure>\n<p>In <strong>Executable file<\/strong> we write the path to <strong>openocd <\/strong>(theoretically could be taken from <strong>Path<\/strong>).<\/p>\n<p>In the<strong> Root scripts<\/strong> directory we point the script directory for openocd.<\/p>\n<p>In <strong>Additional arguments<\/strong> we write the arguments for burning of firmware to MCU: config file for ST-Link and our target: <strong>stm32f1x<\/strong>. Then apply.<\/p>\n<p>Go to <strong>Devices<\/strong>. Click <strong>Add->Bare Metal Device<\/strong> and <strong>Start Wizard<\/strong>. In the appeared window we choose <strong>Debug server provider<\/strong> and previously added <strong>OpenOCD-STM32F103 <\/strong>server provider. Click Finish.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/87a\/83d\/e3b\/87a83de3b67e6a523cce838b3cd6b44c.PNG\" alt=\"\" title=\"\" width=\"1011\" height=\"657\"\/><\/figure>\n<p>Return to <strong>Kits<\/strong> and readjust our ARM kit. We need to choose previously configured <strong>Device type<\/strong> and <strong>Device \u2013 Bar Metal Device<\/strong>. Apply.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/207\/5ca\/60f\/2075ca60fd5db01b4377793c31036ae3.PNG\" alt=\"\" title=\"\" width=\"1011\" height=\"657\"\/><\/figure>\n<p>At this stage we postpone Qt Creator and move to setup of the project.<\/p>\n<h3>Setup of project<\/h3>\n<p>We will create the simplest project using only <strong>CMSIS<\/strong> library which will blink with LED.   <\/p>\n<p>First, we need to pull the necessary libraries as the submodules from the official repositories of ARM and ST. Open bash terminal, which usually goes together with git, and create led_blink_cmsis folder.<\/p>\n<pre><code>mkdir led_blink_cmsis &amp;&amp; cd led_blink_cmsis<\/code><\/pre>\n<p>Create inside <strong>build<\/strong>, <strong>lib and<\/strong> <strong>src<\/strong> folders.<\/p>\n<pre><code>mkdir build &amp;&amp; mkdir src &amp;&amp; mkdir lib<\/code><\/pre>\n<p>In the <strong>build<\/strong> folder we have the artifacts, in <strong>src<\/strong> &#8212; our source files and in <strong>lib<\/strong> &#8212; third party libraries. <\/p>\n<p>Initialize git.<\/p>\n<pre><code>git init<\/code><\/pre>\n<p>Next, add CMSIS libraries using <strong>git submodules<\/strong>. The first library is the ST fork from the ARM repo. The second has the settings and definitions for the STM32 F1 family.<\/p>\n<pre><code>git submodule add https:\/\/github.com\/STMicroelectronics\/cmsis_core lib\/cmsis_core &amp;&amp; cd lib\/cmsis_core git checkout v5.6.0 &amp;&amp; cd ..\/..<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1de\/9a4\/ec5\/1de9a4ec5563c82837bdc6b740131f2b.PNG\" alt=\"\" title=\"\" width=\"868\" height=\"594\"\/><\/figure>\n<pre><code>git submodule add https:\/\/github.com\/STMicroelectronics\/cmsis_device_f1 lib\/cmsis_device_f1 &amp;&amp; cd lib\/cmsis_device_f1 git checkout v4.3.3 &amp;&amp; cd ..\/..<\/code><\/pre>\n<p>After adding submodules we make git checkout to release versions of libraries. For cmsis_core and cmsis_device the current versions are correspondingly 5.6.0 and 4.3.3   .<\/p>\n<p>Create <strong>main.c<\/strong>, <strong>init.h<\/strong> and <strong>init.c<\/strong> in the<strong> src <\/strong>directory, which contain our source code.<\/p>\n<pre><code class=\"bash\">touch src\/main.c src\/init.h src\/init.c<\/code><\/pre>\n<p>Let us fill our files with code. In <strong>main.c<\/strong> there is a code which turns on and off the LED as well as calls the <strong>clock_init<\/strong> \u0438 <strong>port_init <\/strong>functions. These functions set up the clock frequency and external clocking from the quartz resonator as well the functioning of port C for output in low frequency push-pull mode. The init.c file<strong> <\/strong>also contains the implementation of the <strong>delay_ms<\/strong> function   . <\/p>\n<details class=\"spoiler\">\n<summary>main.c<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#include \"init.h\" int main(void) { clock_init();                                                \/\/Clock initialization port_init();                                                 \/\/Port initialization while(1) { GPIOC->BSRR = GPIO_BSRR_BR13;                            \/\/Pin 13 of PORT C on delay_ms(100); GPIOC->BSRR = GPIO_BSRR_BS13;                            \/\/Pin 13 of PORT C off delay_ms(100); } return 0; } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>init.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#ifndef STM32F103xB #define STM32F103xB #endif #include \"stm32f1xx.h\" int clock_init(); void port_init(); void delay_ms(uint16_t); <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>init.c<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#include \"init.h\" volatile uint32_t SysTickDelayMsec = 0; void SysTick_Handler(void) { --SysTickDelayMsec; } \/* OSC - 8 MHz PLLSRC - 8 MHz SYSCLK - 16 MHz PLL - 2 AHB - 1\/16 *\/ int clock_init() { RCC->CR |= RCC_CR_HSEON;                                    \/\/Start HSE generator for(volatile int StartUpCounter = 0; ; ++StartUpCounter)    \/\/Wait for successfull start or timeout { if(RCC->CR &amp; RCC_CR_HSERDY)                             \/\/If started successfully, break the cycle { break; } if(StartUpCounter > 4096)                               \/\/If not started - turn off and return an error { RCC->CR &amp;= ~RCC_CR_HSEON;                           \/\/Stop HSE return 1; } } RCC->CFGR = 0;                                              \/\/PLL mult is equal 2 RCC->CFGR |= RCC_CFGR_PLLSRC;                               \/\/Clock PLL from HSE RCC->CR |= RCC_CR_PLLON;                                    \/\/Start PLL for(volatile int StartUpCounter = 0; ; ++StartUpCounter)    \/\/Wait for successfull start or timeout { if(RCC->CR &amp; RCC_CR_PLLRDY)                             \/\/If started successfully, break the cycle { break; } if(StartUpCounter > 4096)                               \/\/If PLL didn't start , turn off everything and return an error { RCC->CR &amp;= ~RCC_CR_HSEON;                           \/\/Stop HSE RCC->CR &amp;= ~RCC_CR_PLLON;                           \/\/Stop PLL return 2; } } FLASH->ACR |= FLASH_ACR_LATENCY;                            \/\/0 cycles for flash, core clock 16 MHz RCC->CFGR |= RCC_CFGR_PPRE2;                                \/\/APB2 turned off (0 by default) RCC->CFGR |= RCC_CFGR_HPRE_3 | RCC_CFGR_HPRE_1 | RCC_CFGR_HPRE_0;                               \/\/AHB prescaler 16 RCC->CFGR |= RCC_CFGR_SW_1;                                 \/\/Switch to PLL while((RCC->CFGR &amp; RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);      \/\/Wait for switch to PLL RCC->CR &amp;= ~RCC_CR_HSION;                                   \/\/Turn off the internal clock for energy save SystemCoreClockUpdate();                                    \/\/Apply alterations to generator SysTick_Config(SystemCoreClock\/1000);                       \/\/Initialisation of interrupt (1 ms) return 0;                                                   \/\/Return 0 if success } void port_init() { RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;                         \/\/Turn on clock for GPIOC GPIOC->CRH = 0; GPIOC->CRH |= GPIO_CRH_MODE13;                              \/\/Pin 13 of PORT C in Push-Pull mode GPIOC->BSRR |= GPIO_BSRR_BR13;                              \/\/Pin 13 of PORT C reset } void delay_ms(uint16_t msec)                                     \/\/Delay function { SysTickDelayMsec = msec; while (SysTickDelayMsec); } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>Finally, we add the <strong>Makefile<\/strong>, which is used for building of firmware. The template of the file was taken from MXCube generator and adapted for the current project. It contains the following code.<\/p>\n<details class=\"spoiler\">\n<summary>Makefile<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>###################################### target ###################################### TARGET = mppt_firmware ###################################### building variables ###################################### DEBUG = 1 OPT = -O0 ####################################### paths ####################################### BUILD_DIR = build ###################################### source ###################################### C_SOURCES =   src\/main.c  src\/init.c  lib\/cmsis_device_f1\/Source\/Templates\/system_stm32f1xx.c ASM_SOURCES =   lib\/cmsis_device_f1\/Source\/Templates\/gcc\/startup_stm32f103xb.s ####################################### binaries ####################################### PREFIX = arm-none-eabi- ifdef GCC_PATH CC = (PREFIX)gcc AS = (PREFIX)gcc -x assembler-with-cpp CP = (PREFIX)objcopy SZ = (PREFIX)size else CC = $(PREFIX)gcc AS = $(PREFIX)gcc -x assembler-with-cpp CP = $(PREFIX)objcopy SZ = $(PREFIX)size endif HEX = $(CP) -O ihex BIN = $(CP) -O binary -S ####################################### CFLAGS ####################################### CPU = -mcpu=cortex-m3 MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI) AS_DEFS = C_DEFS =   -DSTM32F103xB AS_INCLUDES = C_INCLUDES =   -Isrc  -Ilib\/cmsis_core\/Include  -Ilib\/cmsis_device_f1\/Include ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections ifeq ($(DEBUG), 1) CFLAGS += -g -gdwarf-2 endif CFLAGS += -MMD -MP -MF\"$(@:%.o=%.d)\" ####################################### LDFLAGS ####################################### LDSCRIPT = lib\/cmsis_device_f1\/Source\/Templates\/gcc\/linker\/STM32F103XB_FLASH.ld LIBS = -lc -lm -lnosys LIBDIR = LDFLAGS = (LDSCRIPT) (BUILD_DIR)\/$(TARGET).map,--cref -Wl,--gc-sections ####################################### build the application ####################################### all: (TARGET).elf (TARGET).hex (TARGET).bin OBJECTS = (notdir $(C_SOURCES:.c=.o))) vpath %.c $(sort $(dir $(C_SOURCES))) OBJECTS += (notdir $(ASM_SOURCES:.s=.o))) vpath %.s $(sort $(dir $(ASM_SOURCES))) (BUILD_DIR)\/$(notdir $(&lt;:.c=.lst)) $&lt; -o $@ $(BUILD_DIR)\/%.o: %.s Makefile | $(BUILD_DIR) $(AS) -c $(CFLAGS) $&lt; -o $@ (TARGET).elf: $(OBJECTS) Makefile $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(SZ) $@ $(BUILD_DIR)\/%.hex: $(BUILD_DIR)\/%.elf | $(BUILD_DIR) $(BIN) $&lt; $@ $(BUILD_DIR)\/%.bin: $(BUILD_DIR)\/%.elf | $(BUILD_DIR) $(BIN) $&lt; $@ $(BUILD_DIR): mkdir $@ clean_build: (TARGET).elf (TARGET).bin find (TARGET).bin' -type f -exec rm -f {} + ####################################### clean up ####################################### clean: rm -fR $(BUILD_DIR)\/* <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>C_INCLUDES shows the path to .h files.<\/p>\n<\/li>\n<li>\n<p>C_SOURCES  shows the path to .c\/.cpp.<\/p>\n<\/li>\n<li>\n<p>DEBUG indicates the addition of debug symbols to firmware. In release version it should be <strong>0<\/strong>.<\/p>\n<\/li>\n<li>\n<p>OPT indicates the optimization level. For release it should be, for example, <strong>Os<\/strong>.<\/p>\n<\/li>\n<\/ul>\n<p>Instead of Makefile any other build system could be used. As an example, I can suggest <strong>CMake<\/strong> with the corresponding toolchain file for STM32.<\/p>\n<p>Add the <strong>build<\/strong> folder to.gitignore to exclude its contents from git tracking.<\/p>\n<pre><code class=\"bash\">echo build\/* > .gitignore<\/code><\/pre>\n<p>At this stage the project is set up, and it could be imported to Qt Creator.<\/p>\n<h3>Import and setup of the project in Qt Creator<\/h3>\n<p>Qt Creator is not very good with Makefile. If we try to use Autotools plugin, we will see only .c and .cpp files. For this reason, we import the project with a special interface from the <strong>GenericProjectManager<\/strong> plugin.<\/p>\n<p>Go to <strong>File->New Project<\/strong> and to <strong>Import Existing Project<\/strong> on the left.<\/p>\n<p>Name the project <strong>led_blink_cmsis<\/strong> and set the path to <strong>C:\\led_blink_cmsis<\/strong>. Next.<\/p>\n<p>Show which files should be imported. Currently we choose the <strong>src<\/strong> folder only. Next.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5b0\/8f6\/8ff\/5b08f68ff53ef83a7ec8903ea329cfe7.PNG\" alt=\"\" title=\"\" width=\"653\" height=\"723\"\/><\/figure>\n<p>Choose <strong>add to version control \u2013 none<\/strong> and click Finish. The files have been imported now.<\/p>\n<p>Go to the project explorer and add to <strong>led_blink_cmsis.config the<\/strong> definition of our MCU, <strong>#define STM32F103xB<\/strong>. The IDE autocompletion does not work without this definition because it is required to define the MCU type for CMSIS to include the necessary parts of code for project build.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1ce\/c5c\/cb2\/1cec5ccb2ba11dfaa958abbf8789747d.PNG\" alt=\"\" title=\"\" width=\"1025\" height=\"730\"\/><\/figure>\n<p>At this stage Qt Creator does not recognize our <strong>src<\/strong> files. Let us fix it.<\/p>\n<p>Add <strong>.c<\/strong> files to <strong>led_blink_cmsis.files<\/strong><\/p>\n<pre><code>src\/init.c src\/main.c<\/code><\/pre>\n<p>In <strong>led_blink_cmsis.inludes<\/strong> we need to set the path to <strong>.h<\/strong> files.<\/p>\n<pre><code>src\/ lib\/cmsis_core\/Include lib\/cmsis_device_f1\/Include<\/code><\/pre>\n<p>Now everything should be displayed correctly.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/41b\/83a\/d32\/41b83ad326a7cf1ce8ab614e3bd05a8a.PNG\" alt=\"\" title=\"\" width=\"1024\" height=\"729\"\/><\/figure>\n<p>Try to compile the project by clicking on the <strong>Build<\/strong> button. The project should compile, and at the end of the output we will see a table with the size of the firmware.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/42b\/44e\/e12\/42b44ee12d37049ef9d39396a15c42e9.PNG\" alt=\"\" title=\"\" width=\"1024\" height=\"729\"\/><\/figure>\n<p>Let us add the autodeploy of our firmware by clicking the <strong>Run<\/strong> button. Go to Projects on the left, choose our kit <strong>arm-baremetal-generic-elf-32bi<\/strong> and after <strong>Run<\/strong> on the bottom<strong>. <\/strong>On the right, in the deployment section, <strong>Add Deploy Step->Make<\/strong>. Repeat the step, choosing the <strong>Custom Process Step, <\/strong>and add the command to burn the MCU with <strong>ST_link CLI<\/strong>.<\/p>\n<pre><code>C:\\Program Files (x86)\\STMicroelectronics\\STM32 ST-LINK Utility\\ST-LINK Utility\\ST-LINK_CLI.exe -P \".\\build\\led_blink_cmsis.bin\" 0x08000000 \u2013Rst <\/code><\/pre>\n<p>Before burning of MCU connect ST-Link with MCU and PC. We burn the MCU starting from the 0x08000000 segment and reboot the MCU after burning.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0a9\/752\/9ce\/0a97529ce596575a1e225c862cda6980.PNG\" alt=\"\" title=\"\" width=\"1024\" height=\"728\"\/><\/figure>\n<p>Now, if we connect the MCU with ST-Link and click the <strong>Run<\/strong> button, the firmware should be compiled, loaded to the MCU. The LED on PC13 should start to blink, which we can see on the oscilloscope. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/402\/c9e\/0a9\/402c9e0a9360087a29538e625bf461ff.jpg\" alt=\"\" title=\"\" width=\"4624\" height=\"2604\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/402\/c9e\/0a9\/402c9e0a9360087a29538e625bf461ff.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>In this section we considered how to set up the project for STM32, to compile it and load into MCU. The next section is devoted to the setup of a debugger for comfortable STM32 debugging.<\/p>\n<h3>Setup of debugging<\/h3>\n<p>Previously we have already set the remove debugging using openocd, and it should work.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ce9\/dea\/6c8\/ce9dea6c8a12d1ae0597f3d5cf39a309.PNG\" alt=\"\" title=\"\" width=\"491\" height=\"254\"\/><\/figure>\n<p>However, it is quite good to have the possibility to see the current status of MCU registers. Let us add such a possibility. To do this we need <strong>.svd<\/strong> file for our MCU, <strong>STM32F103<\/strong>. I could be downloaded, for example, from this repo:<\/p>\n<blockquote>\n<p><a href=\"https:\/\/github.com\/fduignan\/stm32f103c8t6\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/fduignan\/stm32f103c8t6<\/a>   <\/p>\n<\/blockquote>\n<p>Next, go to <strong>QtCreator Edit->Preferences->Devices<\/strong>, to <strong>Bar Metal<\/strong> tab and choose the <strong>OpenOCD<\/strong>. Put to <strong>Peripheral description file field<\/strong> our path to <strong>.svd<\/strong> file.<\/p>\n<p>During run-time debug go to <strong>View->Views <\/strong>and enable<strong> PeripheralRegisters<\/strong>. Launching the debug window on the right and clicking the right mouse button at the <strong>Peripheral Registers window, we<\/strong> can choose a group of registers for display. For example, <strong>View Groups->GPIOC<\/strong>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eb0\/f70\/cb6\/eb0f70cb655da09656aebf2fd61b1fc0.PNG\" alt=\"\" title=\"\" width=\"1024\" height=\"729\"\/><\/figure>\n<p>Finally, we have setup all the necessary features for comfortable development and debugging of STM32 firmware in Qt Creator IDE.<\/p>\n<h3>Conclusion<\/h3>\n<p>Within the scope of the article we convinced the setup of Qt Creator IDE with GNU compilers for full-fledged development of software for STM32 MCU. I hope the article was interesting and useful.<\/p>\n<p>Thanks for your attention!<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/706198\/\"> https:\/\/habr.com\/ru\/articles\/706198\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h3>Introduction<\/h3>\n<p>Hi,<\/p>\n<p>Currently I am investigating firmware development for STM32 microcontrollers and I would like to share with you my experience for doing it in Qt Creator IDE.<\/p>\n<p>There are a lot of IDEs, which are used for firmware development of STM32. Some of them, being quite comfortable, have restrictions for trial license. For example, the one of the most known IDE, IAR Embedded, suggests either a limited amount of product usage time (30 days) or the limited firmware size of 32 MB, which is not too much.<\/p>\n<p>Within this scope of the publication, we investigate the method of setting up an environment that allows developing the full value of the STM32 firmware in Qt Creator.<\/p>\n<p>First, we need to determine the components for the execution of experiments.<\/p>\n<h3>Hardware<\/h3>\n<p>I use the <strong>bluepill<\/strong> board based on the <strong>STM32F103C8T6<\/strong> microcontroller unit (MCU) to conduct the experiments. Sometimes, there could be issues with <strong>openocd<\/strong>, which claims the incorrect MCU number. It could be solved by 2 methods: either by replacing the number in the <strong>openocd<\/strong> configuration file or by changing the MCU to the original one. I chose the second option. For firmware burning and debugging purposes we use the <strong>ST-Link<\/strong> interface.<\/p>\n<h3>Software<\/h3>\n<p>The experiments are conducted on a Windows 10 virtual machine in Virtual Box. In addition, we will need to install the following software:<\/p>\n<ol>\n<li>\n<p><strong>Qt Creator<\/strong>. It could be installed with the online installer package from the Qt website. In this case, registration is required, and we do not need the Qt framework itself. For this reason, I download Qt Creator directly from the GitHub releases: https:\/\/github.com\/qt-creator\/qt-creator\/tags.<\/p>\n<\/li>\n<li>\n<p><strong>gcc-arm-none-eabi<\/strong>.<strong> <\/strong>https:\/\/developer.arm.com\/downloads\/-\/gnu-rm.<\/p>\n<\/li>\n<li>\n<p><strong>Git<\/strong>. https:\/\/git-scm.com\/downloads.<\/p>\n<\/li>\n<li>\n<p><strong>openocd<\/strong>. https:\/\/github.com\/openocd-org\/openocd\/tags. This software is designed for firmware burning and on-chip firmware debugging   .<\/p>\n<\/li>\n<li>\n<p><strong>make<\/strong>. https:\/\/gnuwin32.sourceforge.net\/packages\/make.htm<\/p>\n<\/li>\n<li>\n<p><strong>ST-LINK Utility<\/strong>. This utility is used for firmware burning and also contains a USB driver for ST-Link interface. https:\/\/www.st.com\/en\/development-tools\/stsw-link004.html<\/p>\n<\/li>\n<li>\n<p><strong>Python2.7<\/strong>. https:\/\/www.python.org\/downloads\/release\/python-2718\/<\/p>\n<\/li>\n<\/ol>\n<p>First, we need to download and unpack Qt Creator, and after that to install git, gcc compiler, openocd, python, make and ST-Link utility.<\/p>\n<figure class=\"\"><\/figure>\n<p>I unpacked Qt Creator and openocd into C:\\Tools. All programs, except Qt Creator, should be added to <strong>Path<\/strong>.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>Then we need to check whether gcc and openocd are pushed to Path by calling them from the command line.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>If gcc and openocd are available from cmd, we can move forward and tune the Qt Creator IDE.<\/p>\n<h3>Setup of Qt Creator<\/h3>\n<p>Now we can launch the IDE and install the necessary plugins: <strong>BareMetal<\/strong> and <strong>GenericProjectManager<\/strong>. To do this we need to go to <strong>Help->About Plugins<\/strong>, check the plugins and restart Qt Creator.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>The first thing we need to do is to go to <strong>Edit->Preferences<\/strong> and choose <strong>Kits<\/strong> from the list on left. Here we should see our kit for building an ARM, which was pulled from Path. Next, we go to <strong>Devices <\/strong>from the list on the left, on the<strong> Bare Metal<\/strong> tab, and add an <strong>OpenOCD<\/strong> server. In our case, I call it <strong>OpenOCD-STM32F103<\/strong>.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>In <strong>Executable file<\/strong> we write the path to <strong>openocd <\/strong>(theoretically could be taken from <strong>Path<\/strong>).<\/p>\n<p>In the<strong> Root scripts<\/strong> directory we point the script directory for openocd.<\/p>\n<p>In <strong>Additional arguments<\/strong> we write the arguments for burning of firmware to MCU: config file for ST-Link and our target: <strong>stm32f1x<\/strong>. Then apply.<\/p>\n<p>Go to <strong>Devices<\/strong>. Click <strong>Add->Bare Metal Device<\/strong> and <strong>Start Wizard<\/strong>. In the appeared window we choose <strong>Debug server provider<\/strong> and previously added <strong>OpenOCD-STM32F103 <\/strong>server provider. Click Finish.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>Return to <strong>Kits<\/strong> and readjust our ARM kit. We need to choose previously configured <strong>Device type<\/strong> and <strong>Device \u2013 Bar Metal Device<\/strong>. Apply.<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>At this stage we postpone Qt Creator and move to setup of the project.<\/p>\n<h3>Setup of project<\/h3>\n<p>We will create the simplest project using only <strong>CMSIS<\/strong> library which will blink with LED.   <\/p>\n<p>First, we need to pull the necessary libraries as the submodules from the official repositories of ARM and ST. Open bash terminal, which usually goes together with git, and create led_blink_cmsis folder.<\/p>\n<pre><code>mkdir led_blink_cmsis &amp;&amp; cd led_blink_cmsis<\/code><\/pre>\n<p>Create inside <strong>build<\/strong>, <strong>lib and<\/strong> <strong>src<\/strong> folders.<\/p>\n<pre><code>mkdir build &amp;&amp; mkdir src &amp;&amp; mkdir lib<\/code><\/pre>\n<p>In the <strong>build<\/strong> folder we have the artifacts, in <strong>src<\/strong> &#8212; our source files and in <strong>lib<\/strong> &#8212; third party libraries. <\/p>\n<p>Initialize git.<\/p>\n<pre><code>git init<\/code><\/pre>\n<p>Next, add CMSIS libraries using <strong>git submodules<\/strong>. The first library is the ST fork from the ARM repo. The second has the settings and definitions for the STM32 F1 family.<\/p>\n<pre><code>git submodule add https:\/\/github.com\/STMicroelectronics\/cmsis_core lib\/cmsis_core &amp;&amp; cd lib\/cmsis_core git checkout v5.6.0 &amp;&amp; cd ..\/..<\/code><\/pre>\n<figure class=\"full-width\"><\/figure>\n<pre><code>git submodule add https:\/\/github.com\/STMicroelectronics\/cmsis_device_f1 lib\/cmsis_device_f1 &amp;&amp; cd lib\/cmsis_device_f1 git checkout v4.3.3 &amp;&amp; cd ..\/..<\/code><\/pre>\n<p>After adding submodules we make git checkout to release versions of libraries. For cmsis_core and cmsis_device the current versions are correspondingly 5.6.0 and 4.3.3   .<\/p>\n<p>Create <strong>main.c<\/strong>, <strong>init.h<\/strong> and <strong>init.c<\/strong> in the<strong> src <\/strong>directory, which contain our source code.<\/p>\n<pre><code class=\"bash\">touch src\/main.c src\/init.h src\/init.c<\/code><\/pre>\n<p>Let us fill our files with code. In <strong>main.c<\/strong> there is a code which turns on and off the LED as well as calls the <strong>clock_init<\/strong> \u0438 <strong>port_init <\/strong>functions. These functions set up the clock frequency and external clocking from the quartz resonator as well the functioning of port C for output in low frequency push-pull mode. The init.c file<strong> <\/strong>also contains the implementation of the <strong>delay_ms<\/strong> function   . <\/p>\n<details class=\"spoiler\">\n<summary>main.c<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#include \"init.h\" int main(void) { clock_init();                                                \/\/Clock initialization port_init();                                                 \/\/Port initialization while(1) { GPIOC->BSRR = GPIO_BSRR_BR13;                            \/\/Pin 13 of PORT C on delay_ms(100); GPIOC->BSRR = GPIO_BSRR_BS13;                            \/\/Pin 13 of PORT C off delay_ms(100); } return 0; } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>init.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#ifndef STM32F103xB #define STM32F103xB #endif #include \"stm32f1xx.h\" int clock_init(); void port_init(); void delay_ms(uint16_t); <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>init.c<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#include \"init.h\" volatile uint32_t SysTickDelayMsec = 0; void SysTick_Handler(void) { --SysTickDelayMsec; } \/* OSC - 8 MHz PLLSRC - 8 MHz SYSCLK - 16 MHz PLL - 2 AHB - 1\/16 *\/ int clock_init() { RCC->CR |= RCC_CR_HSEON;                                    \/\/Start HSE generator for(volatile int StartUpCounter = 0; ; ++StartUpCounter)    \/\/Wait for successfull start or timeout { if(RCC->CR &amp; RCC_CR_HSERDY)                             \/\/If started successfully, break the cycle { break; } if(StartUpCounter > 4096)                               \/\/If not started - turn off and return an error { RCC->CR &amp;= ~RCC_CR_HSEON;                           \/\/Stop HSE return 1; } } RCC->CFGR = 0;                                              \/\/PLL mult is equal 2 RCC->CFGR |= RCC_CFGR_PLLSRC;                               \/\/Clock PLL from HSE RCC->CR |= RCC_CR_PLLON;                                    \/\/Start PLL for(volatile int StartUpCounter = 0; ; ++StartUpCounter)    \/\/Wait for successfull start or timeout { if(RCC->CR &amp; RCC_CR_PLLRDY)                             \/\/If started successfully, break the cycle { break; } if(StartUpCounter > 4096)                               \/\/If PLL didn't start , turn off everything and return an error { RCC->CR &amp;= ~RCC_CR_HSEON;                           \/\/Stop HSE RCC->CR &amp;= ~RCC_CR_PLLON;                           \/\/Stop PLL return 2; } } FLASH->ACR |= FLASH_ACR_LATENCY;                            \/\/0 cycles for flash, core clock 16 MHz RCC->CFGR |= RCC_CFGR_PPRE2;                                \/\/APB2 turned off (0 by default) RCC->CFGR |= RCC_CFGR_HPRE_3 | RCC_CFGR_HPRE_1 | RCC_CFGR_HPRE_0;                               \/\/AHB prescaler 16 RCC->CFGR |= RCC_CFGR_SW_1;                                 \/\/Switch to PLL while((RCC->CFGR &amp; RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);      \/\/Wait for switch to PLL RCC->CR &amp;= ~RCC_CR_HSION;                                   \/\/Turn off the internal clock for energy save SystemCoreClockUpdate();                                    \/\/Apply alterations to generator SysTick_Config(SystemCoreClock\/1000);                       \/\/Initialisation of interrupt (1 ms) return 0;                                                   \/\/Return 0 if success } void port_init() { RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;                         \/\/Turn on clock for GPIOC GPIOC->CRH = 0; GPIOC->CRH |= GPIO_CRH_MODE13;                              \/\/Pin 13 of PORT C in Push-Pull mode GPIOC->BSRR |= GPIO_BSRR_BR13;                              \/\/Pin 13 of PORT C reset } void delay_ms(uint16_t msec)                                     \/\/Delay function { SysTickDelayMsec = msec; while (SysTickDelayMsec); } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>Finally, we add the <strong>Makefile<\/strong>, which is used for building of firmware. The template of the file was taken from MXCube generator and adapted for the current project. It contains the following code.<\/p>\n<details class=\"spoiler\">\n<summary>Makefile<\/summary>\n<div class=\"spoiler__content\">\n<pre><code>###################################### target ###################################### TARGET = mppt_firmware ###################################### building variables ###################################### DEBUG = 1 OPT = -O0 ####################################### paths ####################################### BUILD_DIR = build ###################################### source ###################################### C_SOURCES =   src\/main.c  src\/init.c  lib\/cmsis_device_f1\/Source\/Templates\/system_stm32f1xx.c ASM_SOURCES =   lib\/cmsis_device_f1\/Source\/Templates\/gcc\/startup_stm32f103xb.s ####################################### binaries ####################################### PREFIX = arm-none-eabi- ifdef GCC_PATH CC = (PREFIX)gcc AS = (PREFIX)gcc -x assembler-with-cpp CP = (PREFIX)objcopy SZ = (PREFIX)size else CC = $(PREFIX)gcc AS = $(PREFIX)gcc -x assembler-with-cpp CP = $(PREFIX)objcopy SZ = $(PREFIX)size endif HEX = $(CP) -O ihex BIN = $(CP) -O binary -S ####################################### CFLAGS<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-385250","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/385250","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=385250"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/385250\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=385250"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=385250"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=385250"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}