Portál AbcLinuxu, 30. dubna 2025 12:42
V dnešnom blogu si ukážeme prostriedky pre vývoj aplikácií na veľmi lacnej doske STM32F0 pod Linuxom. Nie som priaznivcom veľkých IDE, takže namiesto pohodlia IDE si zapatláme trochu ruky a pozrieme sa podrobnejšie na to aké nástroje sa používajú na najnižšej úrovni a ako medzi sebou spolupracujú.
Vývojová doska STM32F0 Discovery patrí k tomu najlacnejšiemu, čo na trhu pre vývoj ARM nájdeme. Do ceny približne 8€ sa okrem samotného balenia dostali:
Z periférií tu nájdeme:
Pre pripojenie k linuxu budeme potrebovať štandardný USB ovládač. O prácu so zariadením sa starajú utility v užívateľskom priestore. Po pripojení k linuxu by sa mal v dmesg objaviť výpis podobný tomuto:
usb 4-1: new full-speed USB device number 34 using uhci_hcd usb 4-1: New USB device found, idVendor=0483, idProduct=3748 usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 4-1: Product: STM32 STLink usb 4-1: Manufacturer: STMicroelectronics usb 4-1: SerialNumber: PÿoIwSU6V\xffffffc2\xffffff87
Aby bolo možné dosku používať musí užívateľ, ktorý spúšťa OpenOCD mať právo na čítanie a zápis zariadenia. V tomto prípade ide o zariadenie /dev/bus/usb/004/034
. Na väčšine distribúcii bude pravdepodobne stačiť pridať užívateľa do skupiny usb
. V prípade nedostatočných práv bude potrebné vytvoriť udev pravidlo v niektorom súbore v adresári /etc/udev/rules.d napr. /etc/udev/rules.d/41-arm.rules
. Pravidlo pre nastavenie skupiny USB a práv vyzerá nasledovne:
ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", GROUP="usb", MODE="0664"
Niektoré linuxové distribúcie majú v repozitároch pripravené nástroje pre kompiláciu ARM binárok. V názve balíkov pre ARM býva zvyčajne reťazec "arm-none-eabi". Tí menej menej mainstreamoví užívatelia budú musieť zvoliť niektorý z nasledujúcich spôsobov inštalácie, alebo ísť úplne vlastnou cestou
Na stránke MentorGraphics je možné stiahnuť SDK pre vývoj ARM aplikácií pod linuxom. Je dostupné v sekcii ARM processors (odkaz Download the GNU/Linux Release). Po stiahnutí stačí spustiť súbor ./arm-2012.09-63-arm-none-eabi.bin
a odklikať sprievodcu tlačidlom next.
Kompilátor a zvyšné nástroje nájdeme po inštalácii v adresári INSTALL_PREFIX/Sourcery_CodeBench_Lite_for_ARM_EABI/bin
. Tento adresár je vhodné pridať do premennej $PATH.
V distribúcii Gentoo je možné skompilovať si vlastný cross kompilátor príkazom crossdev -t arm-none-eabi
. Pre používateľov paludisu je situácia trochu komplikovanejšia. Najskôr je potrebné vytvoriť repozitár pre cross kompiláciu (súbor /etc/paludis/repositories/cross_overlay.conf
).
master_repository = gentoo format = e location = /usr/local/cross_overlay/ names_cache = /var/cache/paludis/names write_cache = /var/cache/paludis/metadata
Následne musíme vytvoriť adresárovú štruktúru overlaya:
mkdir -p /usr/local/cross_overlay/profiles/ echo "cross_overlay" > /usr/local/cross_overlay/profiles/repo_name echo "cross-arm" >> /usr/local/cross_overlay/profiles/categories mkdir -p /usr/local/cross_overlay/cross-arm/ ln -s /usr/portage/sys-devel/binutils /usr/local/cross_overlay/cross-arm/binutils ln -s /usr/portage/sys-devel/gcc /usr/local/cross_overlay/cross-arm/gcc ln -s /usr/portage/sys-libs/newlib /usr/local/cross_overlay/cross-arm/newlib ln -s /usr/portage/sys-devel/gdb /usr/local/cross_overlay/cross-arm/gdb
Pred kompiláciou ešte musíme nastaviť správne premenné prostredia v /etc/paludis/bashrc
if [[ "${CATEGORY}" == "cross-arm" ]] then CTARGET="arm-none-eabi" TARGET_CFLAGS="-mthumb -mcpu=cortex-m0 -mfloat-abi=soft -Wa,-mimplicit-it=thumb" EXTRA_ECONF="${EXTRA_ECONF} \ --with-sysroot=/usr/${CTARGET} \ --with-mode=thumb \ --disable-multilib \ --disable-interwork \ --disable-nls \ --disable-shared " if [[ "${PN}" == "gcc" ]] then EXTRA_ECONF="${EXTRA_ECONF} \ --with-newlib \ --disable-threads \ --disable-checking \ --disable-werror \ --disable-libmudflap \ --disable-libssp \ --disable-libgomp \ --disable-fortran \ --with-float=soft \ --with-dwarf2 \ --enable-version-specific-runtime-libs \ --with-cpu=cortex-m0" if [[ "${GCC_BOOTSTRAP}" == "1" ]] then EXTRA_ECONF="${EXTRA_ECONF} --enable-languages=c" else EXTRA_ECONF="${EXTRA_ECONF} --enable-languages=c,c++" fi fi if [[ "${PN}" == "newlib" ]] then EXTRA_ECONF="${EXTRA_ECONF} --disable-newlib-supplied-syscalls" fi if [[ "${PN}" != "gcc" && "${PN}" != "binutils" && "${PN}" != "newlib" ]] then CFLAGS="-Os ${TARGET_CFLAGS}" fi fi
Balíky nainštalujeme nasledujúcimi príkazmi (pravdepodobne bude potrebné odmaskovať cross-arm/newlib):
cave resolve cross-arm/binutils -x GCC_BOOTSTRAP=1 cave resolve cross-arm/gcc -x cave resolve cross-arm/newlib -x cave resolve cross-arm/gdb -x cave resolve cross-arm/gcc -x
Nasledujúcich pár riadkov nainštaluje toolchain do /opt/arm (súbory binutils-2.23.2.tar.bz2, gcc-4.8.0.tar.bz2, gdb-7.5.tar.bz2 a newlib-2.0.0.tar.gz musia byť stiahnuté v pracovnom adresári).
tar -xvjf binutils-2.23.2.tar.bz2 tar -xvjf gcc-4.8.0.tar.bz2 tar -xvjf gdb-7.5.tar.bz2 tar -xvzf newlib-2.0.0.tar.gz cd binutils-2.23.2 ./configure --target=arm-none-eabi --with-sysroot=/opt/arm --prefix=/opt/arm --disable-multilib --disable-interwork --with-gnu-as --with-gnu-ld --disable-nls --disable-shared make sudo make install cd .. mkdir gcc-build cd gcc-build ../gcc-4.8.0/configure --with-sysroot=/opt/arm --prefix=/opt/arm --build=x86_64-unknown-linux-gnu --host=x86_64-unknown-linux-gnu --target=arm-none-eabi --enable-languages=c -with-gnu-gcc --with-gnu-ld --with-gnu-as --with-newlib --disable-shared --enable-newlib --disable-multilib --disable-interwork --disable-threads --disable-nls --disable-checking --disable-werror --disable-libmudflap --disable-libssp --disable-libgomp --disable-fortran --with-dwarf2 --enable-version-specific-runtime-libs --with-cpu=cortex-m0 --with-mode=thumb make make install cd ../newlib-2.0.0 ./configure --target=arm-none-eabi --with-sysroot=/opt/arm --prefix=/opt/arm --disable-multilib --disable-interwork --with-gnu-as --with-gnu-ld --disable-nls --disable-shared --with-mode=thumb --disable-newlib-supplied-syscalls make make install cd ../gdb-7.5 ./configure --target=arm-none-eabi --with-sysroot=/opt/arm --prefix=/opt/arm --disable-multilib --disable-interwork --with-gnu-as --with-gnu-ld --disable-nls --disable-shared --with-mode=thumb make make install
Pre komunikáciu s vývojovou doskou sa používa softvér s veľmi výstižným názvom Open On-Chip Debugger. Doska používa pomerne nové rozhranie ST-LINK/V2. Musí sa preto použiť verzia min 0.6.1 skompilovaná s flagom --enable-stlink
(v gentoo je potrebné upraviť ebuild). Po inštalácii OpenOCD môžeme jeho funkčnosť vyskúšať príkazom openocd -f /usr/share/openocd/scripts/board/stm32f0discovery.cfg
. Program openocd sa ukončuje štandardne klávesovou skratkou Ctrl+C
.
Open On-Chip Debugger 0.6.1 (2013-03-28-21:42) Licensed under GNU GPL v2 For bug reports, read http://openocd.sourceforge.net/doc/doxygen/bugs.html adapter speed: 1000 kHz srst_only separate srst_nogate srst_open_drain Info : clock speed 1000 kHz Info : stm32f0x.cpu: hardware has 4 breakpoints, 2 watchpoints
Na ukážku použitia nástrojov vytvoríme jednoduchý program, ktorý bude prepínať 2 LED na portoch PC8 a PC9. Program bude vytvorený pre lepšiu názornosť bez použitia knižníc.
Hlavný program bude pracovať so vstupno výstupnými portmi (GPIO). Tie sú v prípade tejto vývojovej dosky namapované v pamäti od adresy 0x48000000
. Doska má niekoľko GPIO, ktoré sú označené celými písmenami (A, B, C …). Jednotlivé porty sú mapované s offsetom 0x400 takže GPIO C bude mať offset 0x48000800
.
Štruktúra registrov GPIO je v nasledujúcej tabuľke.
Názov | Popis | Offset |
---|---|---|
MODER | Režim pinu (2 bity pre každý pin): 00 - vstup, 01 - výstup, 10 - alternatívna funkcia, 11 - analógový režim | 0x00 |
OTYPER | Typ výstupu: 0 - push-pull, 1 - otvorený kolektor | 0x04 |
RESERVED0 | Rezervované | 0x06 |
OSPEEDR | Rýchlosť výstupu: X0 - 2 MHz, 01 - 10 MHz, 11 - 50 MHz | 0x08 |
PUPDR | Pull up / pull down rezistor: 00 - žiaden, 01 - pull-up, 10 - pull-down, 11 - rezervované | 0x0C |
IDR | Aktuálny stav vstupov | 0x10 |
RESERVED1 | Rezervované | 0x12 |
ODR | Zmena stavu výstupov | 0x14 |
RESERVED2 | Rezervované | 0x16 |
BSRR | Nastavenie alebo vynulovanie výstupu, zápis bitu 1 do horných 16 bitov výstup nuluje, do dolných 16 bitov nastavuje | 0x18 |
LCKR | Uzamknutie stavu registrov | 0x1C |
AFR | Alternatívna funkcia | 0x20 |
BRR | Vynulovanie výstupu | 0x28 |
RESERVED3 | Rezervované | 0x2A |
Na základe tejto tabuľky môžme zostrojiť štruktúru GPIO_Type
, pomocou ktorej sa bude dať pohodlne ovládať GPIO. Priamy prístup k pamäti sa dá získať pretypovaním adresy 0x48000000
na GPIO_Type *
. Nasleduje kompletný výpis C programu, ktorý sa stará o prepínanie LED.
// blink.c #include <stdint.h> #define GPIO_BASE 0x48000000 #define GPIOC ((GPIO_Type*) (GPIO_BASE + 0x00000800)) typedef struct { /* offset */ volatile uint32_t MODER; /* mode 0x00 */ volatile uint16_t OTYPER; /* output type 0x04 */ uint16_t RESERVED0; /* reserved 0x06 */ volatile uint32_t OSPEEDR; /* output speed 0x08 */ volatile uint32_t PUPDR; /* pull-up/pull-down 0x0C */ volatile uint16_t IDR; /* input data 0x10 */ uint16_t RESERVED1; /* reserved 0x12 */ volatile uint16_t ODR; /* output data 0x14 */ uint16_t RESERVED2; /* reserved 0x16 */ volatile uint32_t BSRR; /* bit set / reset 0x18 */ volatile uint32_t LCKR; /* config lock 0x1C */ volatile uint32_t AFR[2]; /* alternate function 0x20 */ volatile uint16_t BRR; /* bit reset 0x28 */ uint16_t RESERVED3; /* reserved 0x2A */ } GPIO_Type; void delay(void) { int i; for(i = 0; i < 1000000; ++i) {}; } int mainprog(void) { // Nastavenie pinov PC8 a PC9 do output režimu GPIOC->MODER = (1 << 16 | 1 << 18); // Nastavenie PC8 a PC9 do Push-pull režimu GPIOC->OTYPER = ~(1 << 8 | 1 << 9); // Vypnutie pull-up a pull-down rezistoru pre PC8 a PC9 GPIOC->PUPDR = ~(3 << 16 | 3 << 18); for(;;) { // Vypnutie PC8 a zapnutie PC9 GPIOC->BSRR = (1 << (16 + 8) | 1 << 9); delay(); // Vypnutie PC9 a zapnutie PC8 GPIOC->BSRR = (1 << (16 + 9) | 1 << 8); delay(); } return 0; }
Operačná pamäť je mapovaná od adresy 0x20000000
. Ak chceme spustiť náš program ako nerelokovateľný musíme definovať adresy, ktoré má linker používať pomocou skriptu memmap
. Skript pre linkovanie programu do oblasti RAM vyzerá nasledovne:
/* memmap */ MEMORY { ram : ORIGIN = 0x20000000, LENGTH = 0x1000 } SECTIONS { .text : { *(.text*) } > ram .rodata : { *(.rodata*) } > ram .bss : { *(.bss*) } > ram }
Náš program zámerne neobsahuje funkciu main. Pred spustením samotného programu musíme totiž nastaviť pozíciu stack pointra aby zásobník neprepisoval samotný program.
Nasledujúci kód nastaví najskôr zásobník. Ďalej povolí časovač GPIOC (ekvivalent volania RCC->AHBENR |= (1 << 19);
v C pri použití knižnice). Nakoniec spustí hlavný program.
;@ vectors.s ;@----------------------- .cpu cortex-m0 .thumb ;@----------------------- .thumb_func .global _start _start: ldr r0,stacktop ;@ načítanie konštanty 0x20001000 do r0 mov sp,r0 ;@ nastavenie stack pointra ;@----------------------- gpioc_enable: ;@ povolenie hodín GPIOC (RCC->AHBENR |= (1 << 19);) ldr r0,ahebnr ;@ do r0 0x40021000 (RCC) + offst 0x14 (AHEBNR) ldr r0,[r0] ;@ načítanie AHBENR ldr r1,ahebnrin ;@ 1<<19 do r1 orr r1,r0,r1 ;@ r1 = (1<<19) | RCC->AHBENR ldr r0,ahebnr ;@ adresa AHEBNR do R0 str r1,[r0] ;@ nastavenie AHEBNR ;@----------------------- start_main: bl mainprog ;@ zavolanie hlavného programu b hang ;@ ukončenie programu .thumb_func hang: b . ;@----------------------- .align stacktop: .word 0x20001000 ahebnr: .word 0x40021014 ahebnrin: .word 0x00080000 ;@----------------------- .end ;@-----------------------
Nasledujúci súbor po zadaní príkazu make skompiluje program do formy vhodnej pre upload do RAM. Pred spustením make
musí byť kompilátor a linker v cestách definovaných premennou PATH
.
ARCH = arm-none-eabi AS = $(ARCH)-as CC = $(ARCH)-gcc LD = $(ARCH)-ld OBJDUMP = $(ARCH)-objdump OBJCOPY = $(ARCH)-objcopy AFLAGS = --warn --fatal-warnings CFLAGS = -Wall -O0 -nostdlib -nostartfiles -ffreestanding -g -ggdb blink.thumb.bin: blink.thumb.list blink.thumb.elf $(OBJCOPY) blink.thumb.elf blink.thumb.bin -O binary blink.thumb.list: blink.thumb.elf $(OBJDUMP) -D blink.thumb.elf > blink.thumb.list blink.thumb.elf: memmap vectors.o blink.thumb.o $(LD) -o blink.thumb.elf -T memmap vectors.o blink.thumb.o blink.thumb.o: blink.c $(CC) $(CFLAGS) -mthumb -c blink.c -o blink.thumb.o vectors.o: vectors.s $(AS) $(AFLAGS) vectors.s -o vectors.o clean: rm -f blink.thumb.bin rm -f blink.thumb.list rm -f blink.thumb.elf rm -f blink.thumb.o rm -f vectors.o
Na to aby sme mohli s doskou komunikovať musíme spustiť OpenOCD príkazom openocd -f /usr/share/openocd/scripts/board/stm32f0discovery.cfg
. S OpenOCD môžme následne komunikovať cez telnet na porte 4444 príkazom telnet localhost 4444
.
Pred nahraním sa musí program zastaviť príkazom halt
. Nahrať program do RAM je možné príkazom load_image "/cesta/k/blink.thumb.elf"
. Program sa spúšťa príkazom resume 0x20000000
.
> halt target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x20000022 msp: 0x20000fe8 > load_image "blink.thumb.elf" 144 bytes written at address 0x20000000 downloaded 144 bytes in 0.006462s (21.762 KiB/s) > resume 0x20000000
Program OpenOCD automatiky pri spustení programu povolí jeho ladenie pomocou gdb a to na porte 3333. Gdb umožňuje nie len programy ladiť, ale aj nahrávať do pamäte MCU. Aby bolo ladenie pohodlnejšie napíšeme si najskôr skript pre gdb, ktorý uploadne program do MCU a nastaví break point na hlavnú funkciu.
# run.gdb target remote localhost:3333 monitor reset halt load break mainprog
Program sa spustí príkazom arm-none-eabi-gdb --batch --command=run.gdb blink.thumb.elf
. Zastaví sa na prvom riadku hlavnej funkcie a bude čakať na ďalšie príkazy gdb (napr. continue). Po tomto príkaze je možné spustiť rozhranie debuggeru napr. nemiver
(nemiver --remote=localhost:3333 --gdb-binary=`which arm-none-eabi-gdb` blink.thumb.elf
) a ladiť program priamo cez GUI. Nemiver bol však na mojom stroji príliš škaredý a nevošiel sa ani na monitor tak som tu hodil screenshot z QtCreatora .
Na vývoj aplikácii pre ARM som mal ešte zo školských čias a prostredia IAR zlé spomienky. Napriek tomu som neodolal vyskúšať si ARM na Linuxe a musím povedať, že je to úplne iný pocit. Prakticky všetko funguje tak ako má. Nie je potrebný žiaden neštandardný softvér okrem OpenOCD. Vyvíjať je tak možné v prakticky akomkoľvek IDE (alebo úplne bez IDE).
Vývojová doska STM32F0 Discovery poskytuje za lacný peniaz slušnú výbavu. Svojou cenou prekonáva arduino pričom ponúka skutočný 32-bitový procesor s množstvom periférií, ktoré sú rýchle vďaka použitiu DMA. Istou nevýhodou oproti arduinu je slabšia užívateľská základňa a s tým spojený aj menší počet knižníc.
Odkazy
Tiskni
Sdílej:
klobuk dole, pekne napisany článok... mohol by si spraviť aj anglicku verziu a dať tu na abičko.
ahebnrin: .word 0x001D0000
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.