Sommario:

Parte 1 Assemblaggio del braccio TI RSLK Curriculum didattico di robotica Lab 7 STM32 Nucleo: 16 passaggi
Parte 1 Assemblaggio del braccio TI RSLK Curriculum didattico di robotica Lab 7 STM32 Nucleo: 16 passaggi

Video: Parte 1 Assemblaggio del braccio TI RSLK Curriculum didattico di robotica Lab 7 STM32 Nucleo: 16 passaggi

Video: Parte 1 Assemblaggio del braccio TI RSLK Curriculum didattico di robotica Lab 7 STM32 Nucleo: 16 passaggi
Video: VIDEO-CHOC: CARABINIERE AGGREDITO A ROMA 2024, Luglio
Anonim
Image
Image

Il focus di questo Instructable è il microcontrollore STM32 Nucleo. La motivazione per questo è essere in grado di creare un progetto di assemblaggio dalle ossa nude. Questo ci aiuterà ad approfondire e comprendere il progetto MSP432 Launchpad (TI-RSLK) che è già stato argomento di diversi Instructables.

Non c'è molto aiuto in linea per creare un progetto di solo assembly per MSP432, utilizzando Code Composer Studio. Finora abbiamo solo copiato/incollato da un progetto di assemblaggio preesistente. Questo approccio ci è servito bene.

Tuttavia, ora, per Lab 7, abbiamo riscontrato un piccolo problema. O almeno un singhiozzo temporaneo. Il Lab 7 introduce le macchine a stati finiti e la prima cosa che incontriamo è la necessità di creare e utilizzare una serie di valori. Poiché il corso TI utilizza principalmente la programmazione in C, questo non è un problema. Ma questi Instructables si sono concentrati sull'assemblaggio, non su C.

Inoltre, poiché l'array è di valori di sola lettura, sarebbe opportuno inserirlo nella memoria flash, non nella RAM.

Sembra che ci sia molto più aiuto online per i progetti di assemblaggio che utilizzano l'MCU STM32, quindi, iniziamo con questo Instructable, con l'obiettivo di utilizzare ciò che viene appreso, per poi applicarlo all'MSP432 e al Code Composer Studio.

Sulla strada per raggiungere questo obiettivo, avremo anche acquisito esperienza con un altro popolare microcontrollore.

Passaggio 1: test iniziale del dispositivo

Test iniziale del dispositivo
Test iniziale del dispositivo
Test iniziale del dispositivo
Test iniziale del dispositivo
Test iniziale del dispositivo
Test iniziale del dispositivo

Ancora una volta, perché scegliere l'STM32 Nucleo in particolare?

Onestamente? Perché stavo cercando buoni articoli su progetti di assemblaggio bare metal per controller ARM e mi sono imbattuto in questa serie. E anche perché l'STM32 sembra essere un MCU popolare.

Ho fatto alcune ricerche (ci sono molte versioni tra cui scegliere - vedi l'immagine sopra), ma alla fine è diventato ciò che posso effettivamente ottenere, dal momento che stavo per usare Amazon (negli Stati Uniti).

Viene fornito in un pacchetto semplice ma professionale, con alcune istruzioni per l'avvio. È stato un po' divertente vedere che la demo masterizzata nel controller era quasi esattamente ciò che abbiamo fatto negli Instructables passati: un LED lampeggia e cambia velocità in base alla pressione di un pulsante.

Sembra che questa scheda di sviluppo sia molto simile all'MSP432 in quanto ci sono 2 LED e un pulsante utente. L'MSP432 dispone di 2 pulsanti utente.

Come puoi vedere nelle foto, sono rimasto un po' sorpreso dal fatto che la scheda abbia un mini e non un micro USB. Ho dovuto correre per comprare un cavo.

Un altro buon test è che quando lo colleghi al tuo computer (sto usando una macchina Linux), viene visualizzato nel mio file manager, come un file system, chiamato "NODE_F303RE". Apertura che rivela due file, uno HTML e uno di testo.

Questo è tutto, ma almeno dice anche che la connettività sembra abbastanza facile.

Ora siamo pronti per iniziare.

Cercherò di non ripetere nessuna delle buone informazioni della serie di articoli IVONOMICON Bare Metal, ma piuttosto di aumentarla.

Passaggio 2: gli elementi essenziali

La prima cosa di cui abbiamo bisogno è un compilatore.

E poi, abbiamo bisogno di un debugger:

devchu@chubox:~$ sudo apt-get install gdb-arm-none-eabiLettura degli elenchi dei pacchetti… Fatto Creazione dell'albero delle dipendenze Lettura delle informazioni sullo stato… Fatto Verranno installati i seguenti NUOVI pacchetti: gdb-arm-none-eabi 0 aggiornati, 1 appena installato, 0 da rimuovere e 8 non aggiornato. È necessario ottenere 2.722 kB di archivi. Dopo questa operazione verranno utilizzati 7.738 kB di spazio su disco aggiuntivo. Ottieni:1 https://us.archive.ubuntu.com/ubuntu xenial/universe amd64 gdb-arm-none-eabi amd64 7.10-1ubuntu3+9 [2, 722 kB] Recuperato 2, 722 kB in 1s (1, 988 kB/s) Selezione del pacchetto precedentemente deselezionato gdb-arm-none-eabi. (Lettura del database … 262428 file e directory attualmente installati.) Preparazione alla decompressione …/gdb-arm-none-eabi_7.10-1ubuntu3+9_amd64.deb … Disimballaggio gdb-arm-none-eabi (7.10-1ubuntu3+9) … Elaborazione trigger per man-db (2.7.5-1) … Configurazione di gdb-arm-none-eabi (7.10-1ubuntu3+9) …

Passaggio 3: gli elementi essenziali - Windows

Il passaggio precedente presume che stiamo usando Linux. E se usiamo Windows?

Puoi andare al sito Arm Developer e ci sono diverse opzioni di download disponibili. Sto usando una macchina Windows 8.

Durante l'installazione, ho scelto di installarlo nell'unità root "C:\" invece di Program Files solo perché sto usando anche cygwin, ed è stato più facile creare un collegamento dal mio bin locale a una cartella root C: rispetto a tutti i pasticcio nel percorso dei file di programma (con spazi, ecc.).

Quindi, il mio ambiente e percorso cygwin, ecc., Si presenta così:

C:\cygwin64\home\bin\arm-none-eabi-gcc, dove arm-none-eabi-gcc è un collegamento a C:\GNUToolsArmEmbedded\7.2018.q2.update\bin\arm-none-eabi- gcc.

Ho quindi creato una cartella "dev" sotto cygwin home, ed è lì che ho inserito il file core. S ed eseguito il comando del compilatore. (vedi più avanti per le cose del compilatore).

Ho fatto la stessa identica cosa per gdb (arm-none-eabi-gdb).

Passaggio 4: quali sono gli elementi essenziali?

Quindi cos'è "gcc-arm-none-eabi"?

Il compilatore gnu (GCC) compilerà linguaggi di programmazione (come C) in codice nativo per la macchina su cui è in esecuzione. Ad esempio, se dovessi compilare del codice C usando GCC sulla tua macchina Windows, verrebbe creato per essere eseguito sulla macchina Windows. L'eseguibile generato non verrà (in genere) eseguito sul microcontrollore ARM.

Quindi, per costruire programmi da scaricare e masterizzare nel microcontrollore ARM (nel nostro caso sarebbe l'STM32 Nucelo), dobbiamo dare a GCC qualcos'altro: la capacità di "compilare in modo incrociato". Cioè, la capacità di generare un eseguibile, non per il suo sistema nativo (e processore), ma per il sistema di destinazione (il microcontrollore ARM). È qui che entra in gioco "gcc-arm-none-eabi".

Allora cos'è "gdb-arm-none-eabi"?

Una volta scaricato e masterizzato (flash) l'eseguibile appena generato nel microcontrollore, probabilmente vorremo eseguirne il debug, passo dopo riga del codice. GDB è il debugger di gnu e anche lui ha bisogno di un modo per fare il suo lavoro, ma mirando a un sistema diverso.

Quindi, gdb-arm-none-eabi sta a GDB, ciò che gcc-arm-none-eabi sta a GCC.

Un'altra installazione suggerita del pacchetto era "libnewlib-arm-none-eabi". Cos'è quello?

Newlib è una libreria C e una libreria matematica destinata all'uso su sistemi embedded. È un conglomerato di diverse parti di libreria, tutte sotto licenze di software libero che le rendono facilmente utilizzabili su prodotti incorporati.

E infine, il pacchetto "libstdc++-arm-none-eabi". Quello è abbastanza ovvio; è una libreria C++ per il compilatore incrociato; per microcontrollori ARM embedded.

Passaggio 5: il file linker

Il file linker
Il file linker
Il file linker
Il file linker

Creiamo uno script linker.

Una parte chiave o un blocco in questo file sarebbe il comando MEMORY.

--- da sourceware.org:

La configurazione predefinita del linker consente l'allocazione di tutta la memoria disponibile. È possibile sovrascriverlo utilizzando il comando MEMORY. Il comando MEMORY descrive la posizione e la dimensione dei blocchi di memoria nella destinazione. Puoi usarlo per descrivere quali aree di memoria possono essere utilizzate dal linker e quali aree di memoria deve evitare. È quindi possibile assegnare sezioni a particolari regioni di memoria. Il linker imposterà gli indirizzi delle sezioni in base alle regioni di memoria e avviserà delle regioni che diventano troppo piene. Il linker non rimescola le sezioni per adattarle alle regioni disponibili. Uno script del linker può contenere molti usi del comando MEMORY, tuttavia, tutti i blocchi di memoria definiti vengono trattati come se fossero specificati all'interno di un singolo comando MEMORY. La sintassi per MEMORY è:

MEMORIA

{ nome [(attr)]: ORIGIN = origine, LENGTH = len … }

L'esempio nell'articolo:

/* Definisce la fine della RAM e il limite della memoria dello stack *//* (4KB SRAM sulla linea STM32F031x6, 4096 = 0x1000) */ /* (la RAM inizia all'indirizzo 0x20000000) _estack = 0x20001000;

MEMORIA

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

Quindi dobbiamo capire quanta FLASH (per il nostro programma e le costanti, ecc.) E quanta RAM (per l'utilizzo da parte del programma; heap e stack, ecc.) per la nostra scheda particolare. Questo diventa un po' interessante.

La simpatica scheda fornita con il Nucleo dice che ha una memoria flash di 512 Kbyte e SRAM di 80 Kbyte. Tuttavia, collegandolo a USB, viene montato come un file system con due file e sia il file manager che GParted indicano che ha più di 540 Kbyte di spazio. (RAM?).

MA, tentando di eliminare i due file utilizzando il file manager, disconnettendo e ricollegando il dispositivo, vengono comunque visualizzati i due file. (e il file manager ha riconosciuto qualcosa perché c'è una piccola icona "lucchetto" su ogni file.

Quindi andiamo con le cifre sulla carta. Quindi ora prendiamo l'esempio sopra e lo convertiamo nella nostra scheda specifica.

Potresti voler usare qualcosa come questo convertitore di memoria online, per passare dal KB generale a un numero specifico di byte.

Quindi potresti voler utilizzare un convertitore online da decimale a esadecimale.

/* Definisce la fine della RAM e il limite della memoria dello stack */

/* (4KB SRAM sulla linea STM32F031x6, 4096 = 0x1000) *//* l'esempio*/

/* passaggio 1: (80KB SRAM su STM32F303RE, 81920 = 0x14000) *//* la nostra scheda */

/* passaggio 2, aggiungi la dimensione esadecimale all'indirizzo iniziale esadecimale (sotto). */

/* (la RAM inizia all'indirizzo 0x20000000) */

_estack = 0x20001000; /* l'esempio */

_stack = 0x20014000; /* il nostro tabellone */

MEMORIA {

FLASH (rx): ORIGINE = 0x08000000, LUNGHEZZA = 512K

RAM (rxw): ORIGINE = 0x20000000, LUNGHEZZA = 80K

}

Chiamiamo il file sopra "linker.script.ld".

Passaggio 6: la tabella vettoriale

La tabella vettoriale
La tabella vettoriale

Ora creeremo un piccolo file assembly (con direttive) per eseguire alcune semplici operazioni di gestione degli interrupt. Seguiremo l'esempio dell'articolo e creeremo un file denominato "core. S".

Di nuovo, ecco il contenuto del file di esempio, ma ho apportato una modifica per la nostra scheda specifica:

// Queste istruzioni definiscono gli attributi del nostro chip e

// il linguaggio assembly che useremo:.syntax unified /* Vedi sotto dopo questa area di codice */ /*.cpu cortex-m0 */ /*commenta questa riga dell'esempio */.cpu cortex-m4 /* aggiungi invece la corteccia della nostra tavola. vedi l'immagine sopra in questo passaggio */ /*.fpu softvfp */ /* commenta questa riga dell'esempio */.fpu vfpv4 /* aggiungi invece la nostra scheda; ha una FPU */.thumb // Posizioni di memoria globali..global vtable.global reset_handler /* * La tabella vettoriale attuale. * Sono inclusi solo la dimensione della RAM e il gestore 'reset', per semplicità. */.type vtable, %object vtable:.word _estack.word reset_handler.size vtable,.-vtable

Hmm.. Nessuna direttiva ".allinea"

Tuttavia, questo non è critico. Ne parleremo (forse) più avanti.

.sintassi unificata

.syntax [unificato | diviso]

Questa direttiva imposta la sintassi del set di istruzioni come descritto nella sezione ARM-Instruction-Set

9.4.2.1 Sintassi del set di istruzioni Due sintassi leggermente diverse supportano le istruzioni ARM e THUMB. Il valore predefinito, diviso, utilizza il vecchio stile in cui le istruzioni ARM e THUMB avevano le proprie sintassi separate. La nuova sintassi unificata, che può essere selezionata tramite la direttiva.syntax.

.fpu vfpv4

Il compilatore GCC può produrre binari con diverse opzioni relative alla virgola mobile: soft - adatto per l'esecuzione su CPU senza FPU - i calcoli vengono eseguiti nel software dal compilatore softfp generato - adatto per l'esecuzione su CPU con o senza FPU - utilizzerà una FPU se presente. Per il nostro caso specifico (dovrai fare le tue ricerche), l'FPU di questa particolare scheda è conforme a vfpv4. Potrebbe essere necessario giocare con questo. O anche lasciarlo su softfp.

.pollice (contro.braccio)

Questi microcontrollori ARM hanno in realtà un mix di set di istruzioni. Uno è ARM, un altro è THUMB. Una differenza sono le istruzioni a 16 bit rispetto alle istruzioni a 32 bit. Pertanto, questa direttiva dice al compilatore di trattare le istruzioni successive come THUMB o ARM.

Prenderemo semplicemente il resto del file così com'è poiché questi Instructables non hanno ancora approfondito la programmazione dell'assembly basata su interrupt.

Passaggio 7: la versione assembly di un programma "Hello World"

Quanto segue può anche andare nel file "core. S" creato in precedenza. Questo, ancora una volta, è tratto dall'esempio nell'articolo.

/* * Il gestore di ripristino. Chiamato al ripristino. */.type reset_handler, %function reset_handler: // Imposta il puntatore dello stack alla fine dello stack. // Il valore '_estack' è definito nel nostro script del linker. LDR r0, =_estack MOV sp, r0

// Imposta alcuni valori fittizi. Quando vediamo questi valori

// nel nostro debugger, sapremo che il nostro programma // è caricato sul chip e funziona. LDR r7, =0xDEADBEEF MOVS r0, #0 main_loop: // Aggiungi 1 al registro 'r0'. AGGIUNGI r0, r0, #1 // Torna indietro. B main_loop.size reset_handler,.-reset_handler

Quindi, la spinta del programma di cui sopra è caricare un pattern riconoscibile in un registro MCU principale (in questo caso R7) e un valore incrementale a partire da zero in un altro registro MCU principale (in questo caso R0). Se passiamo attraverso il codice in esecuzione, dovremmo vedere l'incremento dei dati di R0.

Se hai seguito gli Instructables relativi all'MSP432 e al corso/laboratorio TI-RSLK, allora praticamente tutto il programma di cui sopra dovrebbe esserti familiare.

L'unica cosa nuova che posso vedere è l'uso di "=" durante il caricamento di "DEADBEEF" per registrare R7. Non l'avevamo usato.

Il file "core. S" qui allegato ora contiene il sorgente completo.

Passaggio 8: compilazione del codice

È ora di fare alcune cose da riga di comando. Qualcosa di reale, finalmente.

Tuttavia, non ci siamo proprio. Dobbiamo nuovamente modificare il comando fornito nell'articolo e modificarlo in base alla nostra situazione.

Ecco il codice di esempio:

arm-none-eabi-gcc -x assembler-with-cpp -c -O0 -mcpu=cortex-m0 -mthumb -Wall core. S -o core.o

Se andiamo al sito gnu.org per GCC, (in questo caso la versione 7.3),

X

Il -x serve a specificare la lingua. Altrimenti se no -x, il compilatore proverà a indovinare usando l'estensione del file. (nel nostro caso, *. S).

L'esempio sopra dall'articolo specifica assembler-with-cpp, ma potremmo semplicemente fare assembler.

C

Il -c dice compila ma non collega.

O0

Il -O serve per impostare il livello di ottimizzazione. L'uso di -O0 (oh-zero) dice "riduci il tempo di compilazione e fai in modo che il debug produca i risultati previsti. Questa è l'impostazione predefinita".

mcpu=corteccia-m0

-mcpu specifica il nome del processore di destinazione. Nel nostro caso, sarebbe corteccia-m4.

mthumb

Il -mthumb specifica la selezione tra la generazione di codice che esegue gli stati ARM e THUMB.

Parete

Il -Wall è ovviamente molto comune e conosciuto. Attiva tutti i flag di avviso.

Infine, alla fine del comando abbiamo il file di input core. S e il file di output core.o.

Ecco la nuova riga di comando risultante per adattarsi al nostro caso specifico.

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

E questo compilato.

Passaggio 9: collegamento del programma

Direttamente dall'esempio nell'articolo, abbiamo questo:

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

La maggior parte di quanto sopra hai visto. Di seguito le novità.

specs=nosys.specs

Questo è un po' difficile da spiegare.

Ha a che fare con "semihosting" e "retargeting", e ha a che fare con input/output. Ha anche a che fare con le chiamate di sistema e le librerie.

In genere, i sistemi embedded non forniscono dispositivi di input/output standard. Ciò influenzerebbe le chiamate di sistema o di libreria (esempio: printf()).

Semihosting significa che il debugger (vedi l'immagine del passaggio 11 con la parte del debugger cerchiata in rosso) ha un canale speciale e utilizza il protocollo semihosting, e puoi vedere l'output di printf() sulla macchina host (tramite il debugger).

Retargeting, d'altra parte, significa che quelle stesse chiamate al sistema o alla biblioteca significano qualcos'altro. Fanno qualcos'altro, che ha senso per il sistema embedded. In un certo senso, diciamo per printf(), c'è una nuova implementazione, un'implementazione retargeting di quella funzione.

Detto questo, --specs=nosys.specs significa che non saremo semihosting. Normalmente ciò significherebbe che stiamo effettuando il retargeting. Questo ci porta alla prossima bandiera.

nostdlib

L'opzione del linker -nostdlib viene utilizzata per collegare un programma destinato a funzionare in modo autonomo. -nostdlib implica le singole opzioni -nodefaultlibs e -nostartfiles. Di seguito discutiamo le due opzioni separatamente, ma l'uso più tipico è solo nostdlib per lo shopping one-stop. Quando si collega un programma ospitato, le librerie di sistema standard come libc sono collegate per impostazione predefinita, dando al programma l'accesso a tutte le funzioni standard (printf, strlen e amici). L'opzione del linker -nodefaultlibs disabilita il collegamento con quelle librerie predefinite; le uniche librerie collegate sono esattamente quelle che chiami esplicitamente al linker usando il flag -l.

lgcc

libgcc.a è una libreria standard che fornisce subroutine interne per superare le carenze di particolari macchine. Ad esempio, il processore ARM non include un'istruzione di divisione. La versione ARM di libgcc.a include una funzione di divisione e il compilatore emette chiamate a tale funzione dove necessario.

T

Questo è solo un modo per dire al linker di usare questo file come script del linker. Nel nostro caso, il nome del file è linker.script.ld.

o main.elf

Infine, diciamo al linker quale sarà il nome del file immagine di output finale che verrà masterizzato/flash nel nostro dispositivo.

Ecco la nostra versione della riga di comando completa, modificata per la nostra situazione specifica:

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

Ci assicuriamo che il file di script e il file core.o siano entrambi nella stessa directory, dove eseguiremo la riga di comando sopra.

E si collega senza problemi.

Un controllo

Eseguiamo quindi:

arm-none-eabi-nm main.elf

e otteniamo:

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

Sembra buono. Il comando arm-none-eabi-nm è un modo per elencare i simboli all'interno dei file oggetto.

Passaggio 10: test della connessione a STM32 Nucleo-64

TestCollegamento a STM32 Nucleo-64
TestCollegamento a STM32 Nucleo-64
TestCollegamento a STM32 Nucleo-64
TestCollegamento a STM32 Nucleo-64

La tua prima missione, se dovessi scegliere di accettarla, è far sì che il tuo sistema veda la tua scheda di sviluppo.

Utilizzo di Windows

Per Windows, ho deciso di installare TrueSTUDIO da Atollic (versione gratuita). È stata un'installazione indolore e ha installato automaticamente il driver in modo da poter utilizzare st-link per testare la connessione. Una volta installato TrueSTUDIO e il gestore dispositivi ha visto il dispositivo, ho scaricato gli strumenti texan/stlink suggeriti dall'articolo Bare Metal che abbiamo seguito. Ho nuovamente posizionato la cartella direttamente sotto "C:\" e ho nuovamente creato alcuni collegamenti dal mio bin home cygwin locale ai comandi.

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

Come test iniziale per vedere se possiamo davvero comunicare con il dispositivo, ho eseguito:

st-info --probe

E sono tornato:

Trovato 1 programmatori strepitosi

Quindi ora sappiamo che possiamo parlare/interrogare la nostra scheda di sviluppo.

Usando Linux

Per Linux, non hai davvero bisogno di un driver. Ma per Debian, dovrai creare gli strumenti st dal sorgente.

git clone

Assicurati di aver installato libusb-1.0-0-dev.

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

Tu dovresti vedere:

libusb-1.0-0-dev/xenial, ora 2:1.0.20-1 amd64 [installato]

o qualcosa di simile.

Per installarlo:

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

Nota che quanto sopra non è lo stesso di:

sudo apt-get install libusb-dev

Il corretto dev libusb mancante può causare problemi a cmake.

Errore CMake: le seguenti variabili vengono utilizzate in questo progetto, ma sono impostate su NOTFOUND. Impostale o assicurati che siano impostate e testate correttamente nei file CMake: LIBUSB_INCLUDE_DIR (ADVANCED)

Passare alla directory principale del progetto (…blah/blah /stlink). Fai un "rilascio".

Dopo la compilazione, gli strumenti dovrebbero trovarsi in ".. /build/Release".

È quindi possibile eseguire "st-info --probe". Ecco l'output con il Nucleo collegato, quindi no.

devchu@chubox:~/Development/stlink$./build/Release/st-info --probeFound 1 stlink programmatori serial: 303636414646353034393535363537 openocd: "\x30\x36\x36\x41\x46\x46\x35\x30\x34\ x39\x35\x35\x36\x35\x37" flash: 524288 (dimensione pagina: 2048) sram: 65536 chipid: 0x0446 descr: dispositivo ad alta densità F303 devchu@chubox:~/Development/stlink$./build/Release/st- info --probe Trovati 0 programmatori stlink devchu@chubox:~/Development/stlink$

Passaggio 11: usiamo GDB con Linux

Usiamo GDB con Linux
Usiamo GDB con Linux
Usiamo GDB con Linux
Usiamo GDB con Linux

Se hai provato tutto questo e sei arrivato fino a questo punto, fantastico! Eccellente. Divertiamoci un po' adesso.

Quando acquisti queste schede di sviluppo ARM, che siano MSP432 Launchpad di Texas Instruments, o questa di cui stiamo discutendo ora, la Nucleo-F303 (STM32 Nucleo-64), di solito arrivano già lampeggiate con un programma in esecuzione, in genere qualche programma lampeggiante che include anche la pressione di un interruttore per modificare la velocità con cui i LED lampeggiano.

Prima di sovrascriverlo così velocemente, vediamo cosa c'è da vedere e da fare.

Con Linux, apri un terminale, cambia directory il progetto stlink git che abbiamo appena creato e trova lo strumento st-util.

devchu@chubox:~/Development/stlink$ find. -name st-util

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

Esegui quello strumento. Poiché abbiamo già testato in precedenza la nostra connessione con st-info --probe, dovremmo ottenere un output in questo modo:

st-util 1.4.0-50-g7fafee2 2018-10-20T18:33:23 INFO common.c: Caricamento parametri dispositivo…. 20-10-2018T18:33:23 INFO common.c: Il dispositivo connesso è: dispositivo ad alta densità F303, id 0x10036446 20-10-2018T18:33:23 INFO common.c: Dimensioni SRAM: 0x10000 byte (64 KiB), Flash: 0x80000 byte (512 KiB) in pagine di 2048 byte 2018-10-20T18:33:23 INFO gdb-server.c: l'ID chip è 00000446, l'ID core è 2ba01477. 20-10-2018T18:33:23 INFO gdb-server.c: Ascolto a *:4242…

Questo è il server GDB in esecuzione ora e vede la nostra scheda di sviluppo e, cosa più importante, è in ascolto sulla porta 4242 (la porta predefinita).

Ora siamo pronti per avviare il client GDB.

In Linux, apri un altro terminale, inserisci questo:

arm-none-eabi-gdb -tui

È proprio come eseguire gdb rigorosamente da riga di comando, tuttavia produce invece un terminale basato su testo (la mia ipotesi è che utilizzi le maledizioni).

Abbiamo il client GDB e il server GDB in esecuzione. Tuttavia, il client non è connesso al server. Al momento non sa nulla del nostro Nucleo (o tavola a tua scelta). Dobbiamo dirlo. Nel terminale, il prompt dovrebbe ora essere "(gdb)". Accedere:

obiettivo di aiuto

Ti darà una lista. Nota che quello che vogliamo è target extended-remote: usa un computer remoto tramite una linea seriale.

Ma dobbiamo anche dargli la posizione. Quindi, al prompt (gdb), inserisci:

(gdb) localhost remoto esteso di destinazione: 4242

Dovresti ricevere una risposta del genere:

(gdb) localhost remoto esteso di destinazione: 4242

Debug remoto utilizzando localhost:4242 0x080028e4 in ?? ()

Nel frattempo, al terminale che esegue st-util gdbserver, abbiamo ottenuto questo:

2018-10-20T18:42:30 INFO gdb-server.c: trovati 6 registri di breakpoint hw

20-10-2018T18:42:30 INFO gdb-server.c: GDB connesso.

Passaggio 12: ripetiamo, con Windows e Flash il nostro programma

Ripetiamo, con Windows e Flash il nostro programma
Ripetiamo, con Windows e Flash il nostro programma
Ripetiamo, con Windows e Flash il nostro programma
Ripetiamo, con Windows e Flash il nostro programma
Ripetiamo, con Windows e Flash il nostro programma
Ripetiamo, con Windows e Flash il nostro programma

I passaggi per eseguire st-util gdbserver e il client arm-none-eabi-gdb sono essenzialmente gli stessi del passaggio precedente. Apri due terminali (cygwin, DOS cmd o Windows Powershell), trova la posizione di st-util, eseguilo. Nell'altro terminale, esegui il client arm-none-eabi-gdb. L'unica differenza è che la modalità -tui (visualizzazione del testo basata su terminale) molto probabilmente non è supportata.

Se quanto sopra ha funzionato in Windows, probabilmente dovrai fermarti (solo il client). A questo punto, in qualche modo dovrai eseguire il client GDB in cui si trova il tuo file di build ("core.out") o aggiungere l'intero percorso a quel file come argomento per il client GDB.

Ho semplificato la mia vita usando cygwin e creando collegamenti dalla mia directory $HOME//bin locale a dove risiedono entrambi questi strumenti.

Ok, abbiamo compilato e linkato come prima, e abbiamo il file main.elf pronto per essere flashato.

Abbiamo st-util in esecuzione in una finestra. Riavviamo il client GDB, questa volta facciamo:

arm-none-eabi-gdb main.elf

Lasciamo che si avvii, aspettiamo il prompt (gdb), eseguiamo il nostro stesso comando di connessione al server GDB (st-util) e siamo pronti per eseguire il flashing dell'eseguibile. È molto anticlimatico:

(gdb) carico

In esecuzione con i terminali Cygwin, c'è un problema noto con i comandi della console a volte non vengono emessi. Quindi nel nostro caso, la finestra che esegue il server era completamente silenziosa. Quello che esegue il client, dove abbiamo eseguito il caricamento, emette questo:

Sezione di caricamento.text, dimensione 0x1c lma 0x8000000Indirizzo iniziale 0x8000000, dimensione di caricamento 28 Velocità di trasferimento: 1 KB/sec, 28 byte/scrittura.

Passaggio 13: lampeggiare con Linux - Più gratificante:D

Lampeggiante con Linux - Più gratificante:D
Lampeggiante con Linux - Più gratificante:D

Passaggio 14: immergiamoci un po' più a fondo

Se sei arrivato qui, eccellente. Andiamo avanti.

Perché non guardare all'interno del file main.elf, l'eseguibile? Esegui quanto segue:

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

Dovresti vedere un output simile a questo:

main.elf: formato file elf32-littlearm

Smontaggio della sezione.text:

08000000:

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

08000008:

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

08000010:

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

Quali piccole pepite possiamo ottenere dall'output di cui sopra?

Se ricordi quando abbiamo discusso e creato il file linker.script.ld, abbiamo affermato che questi dispositivi ARM hanno la RAM che inizia da 0x20000000 e che la memoria FLASH inizia da 0x08000000.

Quindi, possiamo vedere che effettivamente il programma è tale che risiede tutto nella memoria FLASH.

Quindi, sopra, ma un passaggio successivo, quando stavamo discutendo la parte "Hello World", c'era un'istruzione in cui caricavamo un valore letterale immediato, costante ("0xDEADBEEF") in un registro principale MCU ("R7").

La dichiarazione era:

LDR R7, =0xDEADBEEF

Nel nostro codice, questo è l'unico posto in cui menzioniamo anche DEADBEEF. Da nessun altra parte. Eppure, se guardi le istruzioni smontate/ricostruite di cui sopra, ecc., c'è molto di più relativo a DEADBEEF di quanto pensassimo di fare.

Quindi, il compilatore/linker ha deciso in qualche modo di eseguire il flashing permanente del valore di DEADBEEF in un indirizzo FLASH, nella posizione 0x8000018. E poi, il compilatore ha cambiato la nostra istruzione LDR sopra in:

LDR R7, [PC, #8]

Ha anche generato un commento per noi. Che carino. E ci dice di prendere il valore del contatore del programma corrente (il registro del PC), aggiungere 0x8 a quel valore, ed è lì che DEADBEEF è stato masterizzato, ottenere quel valore e inserirlo in R7.

Quindi questo significa anche che il program counter (PC) stava puntando all'indirizzo 0x8000010, che è l'inizio del main_loop, e che il valore DEADBEEF si trova a due indirizzi dopo la fine di main_loop.

Passaggio 15: infine, un breve sguardo al programma in esecuzione

Anche se esci da GDB, inserisci nuovamente il comando. Non devi nemmeno dargli alcun file; non stiamo più lampeggiando, solo eseguendolo.

Dopo aver ricollegato il client GDB al server GDB, al prompt dei comandi (gdb):

(gdb) info registri

Dovresti vedere qualcosa del genere:

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 0x21674ffffx 0x20014000 pc1000000 0x2001400000 lr

Ma poi, al prompt (gdb), inserisci:

(gdb) continua

E molto rapidamente premi CTRL-C. Questo dovrebbe mettere in pausa il programma. Immettere nuovamente il comando "registri info".

Questa volta sembra diverso:

(gdb) info registri

r0 0x350ffa 3477498 r1 0x0 0 r2 0x0 0 r3 0x0 0 r4 0x0 0 r5 0x0 0 r6 0x0 0 r7 0xdeadbeef 37359ffffff59 r8 0x0 0 r9 0x0 0 r10 0x0 0x0 0 r10 0x0 0 r11 0x0 0 0 012 rx 012 16777216

Quello che è successo? Esattamente quello che volevamo. DEADBEEF è stato caricato in R7 e R0 è stato incrementato (estremamente veloce). Se ripeti, vedrai di nuovo R0 con un altro valore.

Passaggio 16: volevamo creare un array di sola lettura in Flash

Un modo per creare l'equivalente di un array utilizzando assembly e direttive è il seguente:

.type myarray, %object // il nome o l'etichetta 'myarray' è definito come un tipo di oggetto.

myarray: // questo è l'inizio della dichiarazione di 'myarray' // (in cosa consisterà)..word 0x11111111 //il primo membro o valore contenuto in 'myarray'..word 0x22222222 //il secondo valore (indirizzi contigui)..word 0x33333333 //e così via..size myarray,.-myarray // il compilatore/assembler ora sa dove si trova la fine o // il confine di 'myarray'.

Ora che l'abbiamo impostato nella memoria FLASH, possiamo usarlo nel programma. Di seguito una porzione:

LDR R1, myarray // carica i dati contenuti nella prima posizione di 'myarray'.' // questo non è quello che vogliamo.

LDR R1, =myarray // questo carica il valore della posizione stesso (il primo indirizzo), // non i dati.. // questo è ciò che vogliamo.

MOV R2, #0 // R2 terrà un conteggio per assicurarci che non ce ne andiamo

// fine dell'array. LDR R3, =myarrsize // R3 sarà l'equivalente di 'myarrsize'.

// R0 manterrà i nostri dati

main_loop:

LDR R0, [R1] // Carica i dati a cui punta R1 ('myarray') in R0. CMP R2, R3 // Siamo al limite dell'array? BEQ main_loop // Se lo siamo, abbiamo finito, quindi eseguiremo il ciclo per sempre.

ADD R2, #1 // Altrimenti, possiamo continuare a scorrere l'array.

AGGIUNGI R1, #4 // Aggiungi 4 per registrare R1, in modo che punti correttamente al prossimo

// indirizzo..

B main_loop // Torna indietro.

Il video passa attraverso tutto questo, e c'è un bug in esso. Va bene; mostra che è importante eseguire ed eseguire il debug del codice. Mostra un classico caso di allontanamento dalla fine di un array.

Consigliato: