Spisu treści:

Część 1 ARM Assembly TI RSLK Robotics Learning Curriculum Lab 7 STM32 Nucleo: 16 kroków
Część 1 ARM Assembly TI RSLK Robotics Learning Curriculum Lab 7 STM32 Nucleo: 16 kroków

Wideo: Część 1 ARM Assembly TI RSLK Robotics Learning Curriculum Lab 7 STM32 Nucleo: 16 kroków

Wideo: Część 1 ARM Assembly TI RSLK Robotics Learning Curriculum Lab 7 STM32 Nucleo: 16 kroków
Wideo: Part 1 ARM Assembly TI RSLK Robotics Learning Curriculum Lab 7 STM32 Nucleo 2024, Lipiec
Anonim
Image
Image

W centrum tego Instructable jest mikrokontroler STM32 Nucleo. Motywacją do tego jest możliwość stworzenia projektu montażu z gołych kości. Pomoże nam to zagłębić się i zrozumieć projekt Launchpad MSP432 (TI-RSLK), który był już tematem kilku Instructables.

Nie ma zbyt dużej pomocy online, aby utworzyć projekt tylko do montażu dla MSP432 przy użyciu Code Composer Studio. Do tej pory po prostu kopiowaliśmy/wklejaliśmy z istniejącego projektu montażu. Takie podejście dobrze nam służyło.

Jednak teraz, w przypadku Lab 7, napotkaliśmy mały problem. A przynajmniej chwilowa czkawka. Laboratorium 7 wprowadza maszyny o skończonych stanach, a pierwszą rzeczą, z którą się spotykamy, jest potrzeba tworzenia i używania szeregu wartości. Ponieważ kurs TI wykorzystuje głównie programowanie w C - nie stanowi to problemu. Ale te instrukcje skupiały się na montażu, a nie na C.

Ponadto, ponieważ tablica ma wartości tylko do odczytu, dobrze byłoby umieścić ją w pamięci flash, a nie w pamięci RAM.

Wydaje się, że jest dużo więcej pomocy online dla projektów montażu przy użyciu MCU STM32, dlatego zaczynamy od tego Instructable, aby wykorzystać to, czego się nauczyłem, a następnie zastosować do MSP432 i Code Composer Studio.

Na drodze do tego celu zdobędziemy również doświadczenie z jeszcze jednym, popularnym mikrokontrolerem.

Krok 1: Wstępny test urządzenia

Wstępny test urządzenia
Wstępny test urządzenia
Wstępny test urządzenia
Wstępny test urządzenia
Wstępny test urządzenia
Wstępny test urządzenia

Znowu, po co w szczególności wybierać STM32 Nucleo?

Szczerze mówiąc? Ponieważ szukałem dobrych artykułów na temat projektów montażu gołego metalu dla kontrolerów ARM i natknąłem się na tę serię. A także dlatego, że STM32 wydaje się być popularnym MCU.

Zrobiłem kilka badań (jest wiele wersji do wyboru - patrz obrazek powyżej), ale w końcu stało się to, co właściwie mogę uzyskać, ponieważ zamierzałem korzystać z Amazona (w USA).

Jest dostępny w prostym, ale profesjonalnym pakiecie, z kilkoma instrukcjami uruchamiania. To było trochę zabawne, że demo wypalone w kontrolerze było prawie dokładnie tym, co zrobiliśmy w poprzednich Instructables - dioda LED miga i zmienia prędkość w zależności od naciśnięcia przycisku.

Wygląda na to, że ta płytka rozwojowa jest bardzo podobna do MSP432 pod tym względem, że zawiera 2 diody LED i jeden przycisk użytkownika. MSP432 posiada 2 przyciski użytkownika.

Jak widać na zdjęciach, trochę mnie zdziwiło, że płyta ma mini, a nie micro USB. Musiał zabraknąć kupić przewód.

Innym dobrym testem jest to, że kiedy podłączasz go do swojego komputera (używam Linuksa), pojawia się w moim menedżerze plików jako system plików o nazwie "NODE_F303RE". Otwarcie, które pokazuje dwa pliki, jeden HTML i jeden tekst.

To wszystko, ale przynajmniej mówi również, że łączność wydaje się dość łatwa.

Teraz jesteśmy gotowi do rozpoczęcia.

Postaram się nie powtarzać żadnej dobrej informacji z serii artykułów IVONOMICON Bare Metal, a raczej ją wzbogacić.

Krok 2: Podstawy

Pierwszą rzeczą, jakiej potrzebujemy, jest kompilator.

A potem potrzebujemy debuggera:

devchu@chubox:~$ sudo apt-get install gdb-arm-none-eabiCzytanie list pakietów… Gotowe Budowanie drzewa zależności Odczytywanie informacji o stanie… Gotowe Zostaną zainstalowane następujące NOWE pakiety: gdb-arm-none-eabi 0 zaktualizowanych, 1 nowo zainstalowany, 0 do usunięcia i 8 nie zaktualizowany. Potrzebujesz 2 722 kB archiwów. Po tej operacji zostanie wykorzystane 7 738 kB dodatkowej przestrzeni dyskowej. Pobierz:1 https://us.archive.ubuntu.com/ubuntu xenial/universe amd64 gdb-arm-none-eabi amd64 7.10-1ubuntu3+9 [2, 722 kB] Pobrano 2, 722 kB w ciągu 1 s (1, 988 kB/s) Zaznaczenie wcześniej niezaznaczonego pakietu gdb-arm-none-eabi. (Odczytywanie bazy danych … 262428 aktualnie zainstalowanych plików i katalogów.) Przygotowanie do rozpakowania …/gdb-arm-none-eabi_7.10-1ubuntu3+9_amd64.deb … Rozpakowywanie gdb-arm-none-eabi (7.10-1ubuntu3+9) … Przetwarzanie wyzwalacze dla man-db (2.7.5-1) … Konfigurowanie gdb-arm-none-eabi (7.10-1ubuntu3+9) …

Krok 3: Podstawy - Windows

Powyższy krok zakładał, że używamy Linuksa. A jeśli używamy systemu Windows?

Możesz przejść do strony dewelopera ramienia i dostępnych jest kilka opcji pobierania. Używam komputera z systemem Windows 8.

Podczas instalacji zdecydowałem się zainstalować go na głównym dysku "C:\" zamiast na Program Files tylko dlatego, że używam również cygwin i łatwiej było utworzyć link z mojego lokalnego bin do głównego folderu C: niż wszystkie bałagan w ścieżce do Program Files (ze spacjami itp.).

Tak więc moje środowisko i ścieżka cygwina itp. wygląda tak:

C:\cygwin64\home\bin\arm-none-eabi-gcc, gdzie arm-none-eabi-gcc jest linkiem do C:\GNUToolsArmEmbedded\7.2018.q2.update\bin\arm-none-eabi- gcc.

Następnie utworzyłem folder "dev" w cygwin home i tam umieściłem plik core. S i uruchomiłem polecenie kompilatora. (zobacz dalej poniżej rzeczy kompilatora).

Dokładnie to samo zrobiłem dla gdb (arm-none-eabi-gdb).

Krok 4: Jakie są podstawy

Czym więc jest "gcc-arm-none-eabi" ?

Kompilator gnu (GCC) skompiluje języki programowania (takie jak C) do kodu natywnego dla maszyny, na której działa. Na przykład, jeśli miałbyś skompilować jakiś kod C przy użyciu GCC na komputerze z systemem Windows, zostałby on zbudowany tak, aby działał na komputerze z systemem Windows. Wygenerowany plik wykonywalny nie będzie (zwykle) działał na mikrokontrolerze ARM.

Tak więc, aby budować programy do pobrania i wypalenia w mikrokontrolerze ARM (w naszym obecnym przypadku byłby to STM32 Nucelo), musimy dać GCC coś jeszcze: możliwość „skrośnej kompilacji”. Oznacza to możliwość generowania pliku wykonywalnego nie dla swojego systemu natywnego (i procesora), ale dla systemu docelowego (mikrokontrolera ARM). W tym miejscu w grę wchodzi „gcc-arm-none-eabi”.

Czym więc jest "gdb-arm-none-eabi" ?

Po pobraniu i wypaleniu (flashowaniu) nowo wygenerowanego pliku wykonywalnego do mikrokontrolera, prawdopodobnie będziemy chcieli go debugować - krok po kroku wiersz po wierszu kodu. GDB jest debuggerem gnu i on również potrzebuje sposobu na wykonanie swojej pracy, ale jest skierowany do innego systemu.

Zatem gdb-arm-none-eabi jest dla GDB, czym gcc-arm-none-eabi jest dla GCC.

Inną sugerowaną instalacją pakietu była "libnewlib-arm-none-eabi". Co to jest?

Newlib to biblioteka C i biblioteka matematyczna przeznaczona do użytku w systemach wbudowanych. Jest to konglomerat kilku części bibliotecznych, wszystkie objęte licencjami wolnego oprogramowania, dzięki którym można z nich łatwo korzystać w produktach osadzonych.

I wreszcie pakiet "libstdc++-arm-none-eabi". To dość oczywiste; jest to biblioteka C++ dla kompilatora skrośnego; dla wbudowanych mikrokontrolerów ARM.

Krok 5: Plik konsolidatora

Plik konsolidatora
Plik konsolidatora
Plik konsolidatora
Plik konsolidatora

Stwórzmy skrypt linkera.

Jedną kluczową częścią lub blokiem w tym pliku będzie polecenie MEMORY.

--- z sourceware.org:

Domyślna konfiguracja linkera pozwala na alokację całej dostępnej pamięci. Możesz to zmienić za pomocą polecenia MEMORY. Polecenie MEMORY opisuje lokalizację i rozmiar bloków pamięci w systemie docelowym. Możesz go użyć do opisania, które regiony pamięci mogą być używane przez linker, a które regiony pamięci musi unikać. Następnie możesz przypisać sekcje do określonych regionów pamięci. Linker ustawi adresy sekcji w oparciu o regiony pamięci i będzie ostrzegał o regionach, które stają się zbyt pełne. Linker nie będzie tasował sekcji, aby dopasować się do dostępnych regionów. Skrypt linkera może zawierać wiele zastosowań polecenia MEMORY, jednak wszystkie zdefiniowane bloki pamięci są traktowane tak, jakby były określone w jednym poleceniu MEMORY. Składnia dla MEMORY jest:

PAMIĘĆ

{ nazwa [(atr)]: POCHODZENIE = pochodzenie, DŁUGOŚĆ = len … }

Przykład w artykule:

/* Zdefiniuj koniec pamięci RAM i limit pamięci stosu *//* (4 KB SRAM w linii STM32F031x6, 4096 = 0x1000) */ /* (RAM zaczyna się pod adresem 0x20000000) _estack = 0x20001000;

PAMIĘĆ

{ FLASH (rx): ORIGIN = 0x08000000, LENGTH = 32K RAM (rxw): ORIGIN = 0x20000000, LENGTH = 4K }

Musimy więc dowiedzieć się, ile FLASH (dla naszego programu i stałych, itp.) i RAM (do wykorzystania przez program; sterta i stos, itp.) dla naszej konkretnej płyty. To staje się trochę interesujące.

Ładna mała karta dostarczana z Nucleo mówi, że ma pamięć flash o pojemności 512 KB, a SRAM na 80 KB. Jednak po podłączeniu go do USB jest montowany jako system plików z dwoma plikami, a zarówno menedżer plików, jak i GParted wskazują, że ma ponad 540 KB miejsca. (BARAN?).

ALE próba usunięcia dwóch plików za pomocą menedżera plików, rozłączenie, a następnie ponowne podłączenie urządzenia, nadal pokazuje dwa pliki. (a menedżer plików rozpoznał coś, ponieważ na każdym pliku znajduje się mała ikona „kłódki”.

Przejdźmy więc do liczb na karcie. Więc teraz bierzemy powyższy przykład i konwertujemy go na naszą konkretną tablicę.

Możesz użyć czegoś takiego jak ten konwerter pamięci online, aby przejść z ogólnej liczby KB do określonej liczby bajtów.

Następnie możesz użyć internetowego konwertera dziesiętnego na szesnastkowy.

/* Zdefiniuj koniec pamięci RAM i limit pamięci stosu */

/* (4KB SRAM na linii STM32F031x6, 4096 = 0x1000) *//* przykład*/

/* krok 1: (80KB SRAM na STM32F303RE, 81920 = 0x14000) *//* nasza płyta */

/* krok 2, dodaj rozmiar szesnastkowy do adresu początkowego szesnastkowego (poniżej). */

/* (RAM zaczyna się od adresu 0x20000000) */

_stos = 0x20001000; /* przykład */

_stos = 0x20014000; /* nasza tablica */

PAMIĘĆ {

FLASH (rx): POCHODZENIE = 0x08000000, DŁUGOŚĆ = 512K

RAM (rxw): POCHODZENIE = 0x20000000, DŁUGOŚĆ = 80K

}

Nazwijmy powyższy plik "linker.script.ld".

Krok 6: Tabela wektorów

Tabela wektorów
Tabela wektorów

Teraz utworzymy mały plik asemblera (z dyrektywami), aby wykonać bardzo podstawową obsługę przerwań. Podążając za przykładem artykułu, utworzymy plik o nazwie „core. S”.

Ponownie, oto przykładowa zawartość pliku, ale wprowadziłem zmianę dla naszej konkretnej tablicy:

// Te instrukcje definiują atrybuty naszego chipa i

// język asemblera, którego użyjemy:.syntax unified /* Zobacz poniżej po tym obszarze kodu */ /*.cpu cortex-m0 */ /* skomentuj ten wiersz przykładu */.cpu cortex-m4 /* zamiast tego dodaj korę naszej tablicy. zobacz powyższy obrazek w tym kroku */ /*.fpu softvfp */ /* skomentuj ten wiersz przykładu */.fpu vfpv4 /* dodaj zamiast tego nasze forum; ma FPU */.thumb // Globalne lokalizacje pamięci..global vtable.global reset_handler /* * Rzeczywista tabela wektorów. * Dla uproszczenia uwzględniono tylko rozmiar pamięci RAM i obsługę funkcji 'reset'. */.type vtable, %object vtable:.word _stos.word reset_handler.size vtable,.-vtable

Hmm.. Brak dyrektywy „wyrównaj”

Jednak to nie jest krytyczne. Więcej o tym (może) później.

.składnia ujednolicona

.syntax [ujednolicony | podzielony]

Ta dyrektywa ustawia składnię zestawu instrukcji zgodnie z opisem w sekcji ARM-Instruction-Set

9.4.2.1 Składnia zestawu instrukcji Dwie nieco różne składnie obsługują instrukcje ARM i THUMB. Domyślny, podzielony, używa starego stylu, w którym instrukcje ARM i THUMB miały własną, osobną składnię. Nowa, ujednolicona składnia, którą można wybrać za pomocą dyrektywy.syntax.

.fpu vfpv4

Kompilator GCC może generować pliki binarne z kilkoma opcjami dotyczącymi zmiennoprzecinkowych: soft - odpowiedni do pracy na procesorze bez FPU - obliczenia są wykonywane w oprogramowaniu przez wygenerowany przez kompilator softfp - odpowiedni do pracy na procesorze z lub bez FPU - użyje FPU, jeśli jest obecny. W naszym konkretnym przypadku (musisz przeprowadzić własne badania), FPU tej konkretnej płyty jest zgodny z vfpv4. Być może będziesz musiał się z tym bawić. Lub nawet zostaw to w softfp.

.kciuk (a.ramię)

Te mikrokontrolery ARM mają w rzeczywistości mieszankę zestawów instrukcji. Jedna to ARM, druga to KCIUK. Jedną różnicą są instrukcje 16-bitowe w porównaniu z instrukcjami 32-bitowymi. Tak więc ta dyrektywa mówi kompilatorowi, aby traktował kolejne instrukcje jako KCIUK lub ARM.

Zamierzamy po prostu wziąć resztę pliku bez zmian, ponieważ te instrukcje nie zagłębiły się jeszcze w programowanie w assemblerze sterowane przerwaniami.

Krok 7: Wersja asemblera programu „Hello World”

Do utworzonego wcześniej pliku „core. S” mogą również przejść następujące elementy. To znowu pochodzi z przykładu w artykule.

/* * Obsługa resetowania. Wezwany po zresetowaniu. */.type reset_handler, %function reset_handler: // Ustawia wskaźnik stosu na koniec stosu. // Wartość '_estack' jest zdefiniowana w naszym skrypcie linkera. LDR r0, =_stack MOV sp, r0

// Ustaw kilka fikcyjnych wartości. Kiedy widzimy te wartości

// w naszym debuggerze będziemy wiedzieć, że nasz program // jest załadowany do układu i działa. LDR r7, =0xDEADBEEF MOVS r0, #0 main_loop: // Dodaj 1 do rejestru „r0”. DODAJE r0, r0, #1 // Pętla wsteczna. B main_loop.size reset_handler,.-reset_handler

Tak więc celem powyższego programu jest załadowanie rozpoznawalnego wzoru do jednego rejestru rdzenia MCU (w tym przypadku R7), a wartość zwiększająca się od zera do innego rejestru rdzenia MCU (w tym przypadku R0). Jeśli przejdziemy przez wykonujący się kod, powinniśmy zobaczyć przyrost danych R0.

Jeśli śledziłeś wraz z instrukcjami dotyczącymi MSP432 i kursu/laboratorium TI-RSLK, to prawie cały powyższy program powinien być ci znany.

Jedyną nową rzeczą, którą widzę, jest użycie „=” podczas ładowania „DEADBEEF” do rejestru R7. Nie użyliśmy tego.

Załączony plik "core. S" zawiera teraz pełne źródło.

Krok 8: Kompilacja kodu

Czas zrobić kilka rzeczy z wiersza poleceń. Nareszcie coś prawdziwego.

Jednak nie do końca tam jesteśmy. Ponownie musimy dostosować polecenie podane w artykule i zmodyfikować je do naszej własnej sytuacji.

Oto przykładowy kod:

arm-none-eabi-gcc -x assembler-z-cpp -c -O0 -mcpu=cortex-m0 -mthumb -Ściana core. S -o core.o

Jeśli przejdziemy do strony gnu.org dla GCC (w tym przypadku wersja 7.3),

x

-x służy do określenia języka. W przeciwnym razie, jeśli nie -x, kompilator spróbuje odgadnąć, używając rozszerzenia pliku. (w naszym przypadku *. S).

Powyższy przykład z artykułu określa assembler-with-cpp, ale moglibyśmy po prostu zrobić asembler.

C

Opcja -c mówi skompiluj, ale nie linkuj.

O0

-O służy do ustawienia poziomu optymalizacji. Użycie -O0 (oh-zero) mówi "skróć czas kompilacji i spraw, aby debugowanie przyniosło oczekiwane rezultaty. Jest to ustawienie domyślne".

mcpu=kora-m0

-mcpu określa nazwę procesora docelowego. W naszym przypadku byłaby to kora m4.

mkciuk

Opcja -mthumb określa wybór pomiędzy generowaniem kodu, który wykonuje stany ARM i THUMB.

Ściana

-Wall jest oczywiście bardzo popularny i dobrze znany. Włącza wszystkie flagi ostrzegawcze.

Wreszcie na końcu polecenia mamy plik wejściowy core. S i plik wyjściowy core.o.

Oto wynikowa nowa linia poleceń, która pasuje do naszego konkretnego przypadku.

arm-none-eabi-gcc -x assembler -c -O0 -mcpu=cortex-m4 -mthumb -Ściana core. S -o core.o

I to skompilowane.

Krok 9: Łączenie programu

Bezpośrednio z przykładu w artykule mamy to:

arm-none-eabi-gcc core.o -mcpu=cortex-m0 -mthumb -Wall --specs=nosys.specs -nostdlib -lgcc -T./STM32F031K6T6.ld -o main.elf

Większość z powyższych widziałeś. Poniżej przedstawiamy nowości.

specs=nosys.specs

To jest trochę trudne do wyjaśnienia.

Ma to związek z „semihostingiem” i „retargetingiem” oraz z wejściem/wyjściem. Ma to również związek z wywołaniami systemowymi i bibliotekami.

Zazwyczaj systemy wbudowane nie zapewniają standardowych urządzeń wejścia/wyjścia. Wpłynęłoby to na wywołania systemowe lub biblioteczne (przykład: printf()).

Semihosting oznacza, że debugger (patrz obraz kroku 11 z fragmentem debuggera zakreślonym na czerwono) ma specjalny kanał i używa protokołu semihostingu, i możesz zobaczyć wyjście printf() na maszynie hosta (poprzez debugger).

Z drugiej strony retargeting oznacza, że te same wywołania systemowe lub biblioteczne oznaczają coś innego. Robią coś innego, co ma sens dla systemu wbudowanego. W pewnym sensie, powiedzmy dla printf(), jest nowa implementacja, retargetowana implementacja tej funkcji.

Powiedziawszy to wszystko, --specs=nosys.specs oznacza, że nie będziemy semi-hostingiem. To normalnie oznaczałoby, że retargetujemy. To prowadzi nas do następnej flagi.

nostdlib

Opcja konsolidatora -nostdlib służy do łączenia programu przeznaczonego do samodzielnego uruchamiania. -nostdlib implikuje indywidualne opcje -nodefaultlibs i -nostartfiles. Poniżej omawiamy te dwie opcje osobno, ale najbardziej typowym zastosowaniem jest po prostu nostdlib do zakupów w jednym miejscu. Podczas łączenia hostowanego programu, standardowe biblioteki systemowe, takie jak libc, są domyślnie łączone, dając programowi dostęp do wszystkich standardowych funkcji (printf, strlen i przyjaciele). Opcja linkera -nodefaultlibs wyłącza linkowanie z tymi domyślnymi bibliotekami; jedyne połączone biblioteki to dokładnie te, które jawnie nazwałeś linkerowi za pomocą flagi -l.

lgcc

libgcc.a to standardowa biblioteka, która dostarcza wewnętrzne podprogramy do przezwyciężenia niedociągnięć poszczególnych maszyn. Na przykład procesor ARM nie zawiera instrukcji dzielenia. Wersja biblioteki libgcc.a dla ARM zawiera funkcję dzielenia, a kompilator emituje wywołania tej funkcji w razie potrzeby.

T

To jest tylko sposób na powiedzenie linkerowi, aby używał tego pliku jako skryptu linkera. W naszym przypadku nazwa pliku to linker.script.ld.

o main.elf

Na koniec mówimy linkerowi, jaka będzie nazwa końcowego pliku obrazu wyjściowego, który zostanie wypalony/flashowany na naszym urządzeniu.

Oto nasza wersja kompletnego wiersza poleceń, zmodyfikowana dla naszej konkretnej sytuacji:

arm-none-eabi-gcc core.o -mcpu=cortex-m4 -mthumb -Wall --specs=nosys.specs -nostdlib -lgcc -T./linker.script.ld -o main.elf

Upewniamy się, że plik skryptu i plik core.o znajdują się w tym samym katalogu, w którym uruchomimy powyższy wiersz poleceń.

I łączy się bez problemów.

Sprawdzenie

Następnie uruchamiamy:

arm-none-eabi-nm main.elf

i otrzymujemy:

devchu@chubox:~/Development/Atollic/TrueSTUDIO/STM32_workspace_9.1$ arm-none-eabi-nm main.elf 20014000 A _stack 08000010 t main_loop 08000008 T reset_handler 08000000 T vtable

Wygląda dobrze. Polecenie arm-none-eabi-nm jest sposobem na wyświetlenie listy symboli w plikach obiektowych.

Krok 10: Testowanie połączenia z STM32 Nucleo-64

TestowaniePołączenie z STM32 Nucleo-64
TestowaniePołączenie z STM32 Nucleo-64
TestowaniePołączenie z STM32 Nucleo-64
TestowaniePołączenie z STM32 Nucleo-64

Twoja pierwsza misja, jeśli zdecydujesz się ją zaakceptować, to sprawić, by Twój system zobaczył tablicę rozwojową.

Korzystanie z systemu Windows

W systemie Windows zdecydowałem się zainstalować TrueSTUDIO firmy Atollic (wersja darmowa). Była to bezbolesna instalacja i automatycznie zainstalowała sterownik, dzięki czemu mogłem użyć st-link do przetestowania połączenia. Gdy zainstalowałem TrueSTUDIO i menedżer urządzeń zobaczył urządzenie, pobrałem narzędzia texan/stlink sugerowane w artykule Bare Metal, który śledziliśmy. Ponownie umieściłem folder bezpośrednio pod "C:\" i ponownie utworzyłem kilka linków z mojego lokalnego domowego kosza cygwin do poleceń.

ln -s /c/STM32. MCU/stlink-1.3.0-win64/bin/st-info.exe ~/bin/st-info

Jako wstępny test, aby sprawdzić, czy naprawdę możemy komunikować się z urządzeniem, uruchomiłem:

st-info -- sonda

I wróciłem:

Znaleziono 1 programistów stlink

Więc teraz wiemy, że możemy porozmawiać/zapytać naszą tablicę rozwojową.

Korzystanie z Linuksa

W przypadku Linuksa tak naprawdę nie potrzebujesz sterownika. Ale w przypadku Debiana będziesz musiał zbudować narzędzia st ze źródeł.

klon git

Upewnij się, że masz zainstalowane libusb-1.0-0-dev.

odpowiednia lista | grep -E "*libusb.*dev*"

Powinieneś zobaczyć:

libusb-1.0-0-dev/xenial, teraz 2:1.0.20-1 amd64 [zainstalowany]

czy jakoś tak.

Aby go zainstalować:

sudo apt-get zainstaluj libusb-1.0-0-dev

Zauważ, że powyższe to nie to samo, co:

sudo apt-get zainstaluj libusb-dev

Poprawny brakujący program libusb może powodować problemy z cmake.

Błąd CMake: w tym projekcie używane są następujące zmienne, ale mają one wartość NOTFOUND. Ustaw je lub upewnij się, że są poprawnie ustawione i przetestowane w plikach CMake: LIBUSB_INCLUDE_DIR (ADVANCED)

Przejdź do katalogu głównego projektu (…blah/blah /stlink). Zrób „wypuść”.

Po tym kompilacji narzędzia powinny znajdować się w ".. /build/Release".

Następnie możesz uruchomić "st-info --probe". Oto wyjście z podłączonym Nucleo, to nie.

devchu@chubox:~/Development/stlink$./build/Release/st-info --probeZnaleziono numer seryjny programisty 1 stlink: 303636414646353034393535363537 openocd: "\x30\x36\x36\x41\x46\x46\x35\x30\x34\ x39\x35\x35\x36\x35\x37" flash: 524288 (rozmiar strony: 2048) sram: 65536 chipid: 0x0446 descr: F303 urządzenie wysokiej gęstości devchu@chubox:~/Development/stlink$./build/Release/st- info --probe Znaleziono 0 programistów stlink devchu@chubox:~/Development/stlink$

Krok 11: Użyjmy GDB z Linuksem

Użyjmy GDB z Linuksem
Użyjmy GDB z Linuksem
Użyjmy GDB z Linuksem
Użyjmy GDB z Linuksem

Jeśli próbowałeś tego wszystkiego i zaszedłeś tak daleko - świetnie! Świetny. Pobawmy się teraz trochę.

Kiedy kupujesz te płytki rozwojowe ARM, niezależnie od tego, czy są to Launchpad MSP432 firmy Texas Instruments, czy ten, o którym teraz rozmawiamy, Nucleo-F303 (STM32 Nucleo-64), zwykle są one już z flashowaniem z uruchomionym programem, zazwyczaj jakiś migający program, który obejmuje również naciśnięcie przełącznika, aby zmienić szybkość, z jaką migają diody LED.

Zanim tak szybko to nadpiszemy, zobaczmy, co jest do zrobienia i zobaczenia.

W Linuksie otwórz terminal, zmień katalog na projekt stlink git, który właśnie zbudowaliśmy, i znajdź narzędzie st-util.

devchu@chubox:~/Development/stlink$ znajdź. -nazwa st-util

./build/Release/src/gdbserver/st-util

Uruchom to narzędzie. Ponieważ już wcześniej testowaliśmy nasze połączenie za pomocą st-info --probe, powinniśmy otrzymać takie dane wyjściowe:

st-util 1.4.0-50-g7fafee2 2018-10-20T18:33:23 INFO common.c: Ładowanie parametrów urządzenia…. 2018-10-20T18:33:23 INFO common.c: Podłączone urządzenie to: urządzenie wysokiej gęstości F303, id 0x10036446 2018-10-20T18:33:23 INFO common.c: Rozmiar SRAM: 0x10000 bajtów (64 KiB), Flash: 0x80000 bajtów (512 KiB) na stronach 2048 bajtów 2018-10-20T18:33:23 INFO gdb-server.c: Chip ID to 00000446, Core ID to 2ba01477. 2018-10-20T18:33:23 INFO gdb-server.c: Słucham na *:4242…

To jest uruchomiony serwer GDB, który widzi naszą płytę rozwojową, a co ważniejsze, nasłuchuje na porcie 4242 (port domyślny).

Teraz jesteśmy gotowi do uruchomienia klienta GDB.

W systemie Linux otwórz inny terminal, wprowadź to:

ramię-none-eabi-gdb -tui

To tak samo, jak uruchamianie gdb w wierszu poleceń, jednak zamiast tego tworzy terminal tekstowy (domyślam się, że używa curses).

Mamy uruchomionego klienta GDB i serwer GDB. Jednak klient nie jest połączony z serwerem. W tej chwili nic nie wie o naszym Nucleo (lub wybranej przez Ciebie desce). Musimy to powiedzieć. W terminalu twój znak zachęty powinien mieć teraz postać „(gdb)”. Wejść:

pomoc cel

Da ci listę. Zauważ, że ten, którego szukamy, to cel rozszerzony-zdalny - użyj komputera zdalnego za pośrednictwem linii szeregowej.

Ale musimy też podać lokalizację. W wierszu polecenia (gdb) wpisz:

(gdb) docelowy rozszerzony zdalny lokalny host: 4242

Powinieneś otrzymać odpowiedź podobną do tej:

(gdb) docelowy rozszerzony zdalny lokalny host: 4242

Zdalne debugowanie przy użyciu localhost:4242 0x080028e4 w ?? ()

Tymczasem na terminalu, na którym działa st-util gdbserver, otrzymaliśmy to:

2018-10-20T18:42:30 INFO gdb-server.c: Znaleziono 6 rejestrów przerwań sprzętowych

2018-10-20T18:42:30 INFO gdb-server.c: GDB podłączony.

Krok 12: Powtórzmy, z Windows i Flashem Nasz program

Powtórzmy, z Windows i Flashem Nasz program
Powtórzmy, z Windows i Flashem Nasz program
Powtórzmy, z Windows i Flashem Nasz program
Powtórzmy, z Windows i Flashem Nasz program
Powtórzmy, z Windows i Flashem Nasz program
Powtórzmy, z Windows i Flashem Nasz program

Kroki do uruchomienia st-util gdbserver i klienta arm-none-eabi-gdb są zasadniczo takie same, jak w poprzednim kroku. Otwierasz dwa terminale (cygwin, DOS cmd lub Windows Powershell), znajdujesz lokalizację narzędzia st-util, uruchamiasz go. W drugim terminalu uruchom klienta arm-none-eabi-gdb. Jedyna różnica polega na tym, że tryb -tui (widok tekstowy oparty na terminalu) najprawdopodobniej nie jest obsługiwany.

Jeśli powyższe zadziałało w systemie Windows, prawdopodobnie będziesz musiał przestać (tylko klient). W tym momencie w jakiś sposób będziesz musiał uruchomić klienta GDB, w którym znajduje się twój plik kompilacji ("core.out"), lub dodać całą ścieżkę do tego pliku jako argument do klienta GDB.

Uprościłem sobie życie, używając cygwin i tworząc linki z mojego lokalnego katalogu $HOME//bin do miejsca, w którym znajdują się oba te narzędzia.

Ok, skompilowaliśmy i zlinkowaliśmy tak jak poprzednio i mamy plik main.elf gotowy do flashowania.

W jednym oknie działa st-util. Restartujemy klienta GDB, tym razem wykonujemy:

arm-none-eabi-gdb main.elf

Pozwalamy mu się uruchomić, czekamy na znak zachęty (gdb), wykonujemy to samo polecenie połączenia z serwerem GDB (st-util) i jesteśmy gotowi do flashowania pliku wykonywalnego. Jest bardzo antyklimatyczny:

(gdb) obciążenie

Działając z terminalami cygwin, istnieje znany problem z czasami, gdy polecenia konsoli nie są wyświetlane. Tak więc w naszym przypadku okno z serwerem było całkowicie ciche. Ten, na którym uruchomiliśmy klienta, na którym uruchomiliśmy ładowanie, wyświetla to:

Sekcja ładowania.text, rozmiar 0x1c lma 0x8000000Adres początkowy 0x8000000, rozmiar ładowania 28 Szybkość transferu: 1 KB/s, 28 bajtów/zapis.

Krok 13: Flashowanie z Linuksem - więcej satysfakcji:D

Flashowanie z Linuksem - więcej satysfakcji:D
Flashowanie z Linuksem - więcej satysfakcji:D

Krok 14: Zanurzmy się trochę głębiej

Jeśli dotarłeś tutaj, świetnie. Przejdźmy dalej.

Dlaczego nie zajrzeć do wnętrza pliku main.elf, wykonywalnego? Uruchom następujące:

arm-none-eabi-objdump -d main.elf

Powinieneś zobaczyć wynik podobny do tego:

main.elf: format pliku elf32-littlearm

Demontaż sekcji.text:

08000000:

8000000: 00 40 01 20 09 00 00 08.@. ….

08000008:

8000008: 4802 ldr r0, [szt., #8]; (8000014) 800000a: 4685 mov sp, r0 800000c: 4f02 ldr r7, [pc, #8]; (8000018) 800000e: 2000 movs r0, #0

08000010:

8000010: 3001 dodaje r0, #1 8000012: e7fd b.n 8000010 8000014: 20014000.word 0x20014000 8000018: deadbeef.word 0xdeadbeef

Jakie małe bryłki możemy uzyskać z powyższego wyniku?

Jeśli pamiętasz, kiedy omawialiśmy i tworzyliśmy plik linker.script.ld, stwierdziliśmy, że te urządzenia ARM mają pamięć RAM zaczynającą się od 0x20000000, a pamięć FLASH zaczyna się od 0x08000000.

Widzimy więc, że rzeczywiście program jest taki, że wszystko znajduje się w pamięci FLASH.

Następnie, powyżej, ale późniejszy krok, kiedy omawialiśmy część "Hello World", było oświadczenie, w którym ładujemy natychmiastową, stałą, dosłowną wartość ("0xDEADBEEF") do rejestru podstawowego MCU ("R7").

Oświadczenie brzmiało:

LDR R7, =0xDEADBEEF

W naszym kodzie jest to jedyne miejsce, w którym wspominamy o DEADBEEF. Nigdzie indziej. A jednak, jeśli spojrzysz na powyższe zdemontowane/zrekonstruowane instrukcje itp., jest tam więcej związanych z DEADBEEF, niż myśleliśmy, że zrobiliśmy.

Tak więc kompilator/linker w jakiś sposób zdecydował się na stałe sflashować wartość DEADBEEF do adresu FLASH, w lokalizacji 0x8000018. A potem kompilator zmienił naszą powyższą instrukcję LDR na:

LDR R7, [PC, #8]

To nawet wygenerowało dla nas komentarz. Jak miło. I każe nam wziąć bieżącą wartość licznika programu (rejestr PC), dodać 0x8 do tej wartości, i to jest miejsce, w którym spalono DEADBEEF, pobrać tę wartość i umieścić ją w R7.

Oznacza to również, że licznik programu (PC) wskazywał adres 0x8000010, który jest początkiem pętli głównej, oraz że wartość DEADBEEF znajduje się pod dwoma adresami po zakończeniu pętli głównej.

Krok 15: Na koniec krótkie spojrzenie na przebieg programu

Nawet jeśli wyjdziesz z GDB, po prostu ponownie wprowadź polecenie. Nie musisz nawet dawać mu żadnego pliku; już nie migamy, po prostu to uruchamiamy.

Po ponownym połączeniu klienta GDB z serwerem GDB w wierszu polecenia (gdb):

(gdb) rejestry informacyjne

Powinieneś zobaczyć coś takiego:

r0 0x0 0

r1 0x0 0 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0x0 0 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0x0 0 r12 0x0 0 sp 0x20014000 0x20014000 lr 0xffffffx 00081000 cffffx 000894008967295

Ale potem, w monicie (gdb) wpisz:

(gdb) kontynuuj

I bardzo szybko wciskamy CTRL-C. To powinno zatrzymać program. Wpisz ponownie komendę „info registers”.

Tym razem wygląda to inaczej:

(gdb) rejestry informacyjne

r0 0x350ffa 3477498 r1 0x0 0 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0xdeadbeef 3735928559 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0x0 0 r12 0x0r spxx000x001072ffx1000 l 16777216

Co się stało? Dokładnie to, czego chcieliśmy. DEADBEEF został załadowany do R7, a R0 był (niezwykle szybki) inkrementowany. Jeśli powtórzysz, zobaczysz ponownie R0 z inną wartością.

Krok 16: Chcieliśmy stworzyć tablicę tylko do odczytu we Flashu

Jednym ze sposobów utworzenia odpowiednika tablicy za pomocą zestawu i dyrektyw jest:

.type myarray, %object // nazwa lub etykieta 'myarray' jest zdefiniowana jako typ obiektu.

myarray: // to jest początek deklaracji 'myarray' // (z czego będzie się składać)..word 0x11111111 //pierwszy element lub wartość zawarta w 'myarray'..word 0x22222222 //druga wartość (sąsiadujące adresy)..word 0x333333333 //i tak dalej..size myarray,.-myarray // kompilator/asembler wie teraz, gdzie kończy się // granica 'myarray'.

Teraz, gdy ustawiliśmy go w pamięci FLASH, możemy go używać w programie. Poniżej znajduje się porcja:

LDR R1, myarray // ładuje dane zawarte w pierwszej lokalizacji 'myarray'.' // to nie jest to, czego chcemy.

LDR R1, =myarray // ładuje samą wartość lokalizacji (pierwszy adres), // nie dane.. // to jest to, czego chcemy.

MOV R2, #0 // R2 będzie liczyć, aby upewnić się, że nie odejdziemy

// koniec tablicy. LDR R3, =myarrsize // R3 będzie odpowiednikiem 'myarrsize'.

// R0 będzie przechowywać nasze dane

główna pętla:

LDR R0, [R1] // Załaduj dane wskazywane przez R1 („myarray”) do R0. CMP R2, R3 // Czy osiągnęliśmy limit tablicy? BEQ main_loop // Jeśli tak, to koniec, więc po prostu będziemy zapętlać w nieskończoność.

DODAJ R2, #1 // W przeciwnym razie możemy kontynuować iterację przez tablicę.

DODAJ R1, #4 // Dodaj 4 do rejestru R1, aby wskazywał poprawnie na następny

// adres..

B main_loop // Zapętl wstecz.

Film przechodzi przez to wszystko i jest w nim błąd. To jest dobre; pokazuje, że jest to ważny kod uruchamiania i debugowania. Pokazuje klasyczny przypadek odejścia od końca tablicy.

Zalecana: