Come configurare smartphone e PC. Portale informativo
  • casa
  • Sistemi operativi
  • STM32F407(STM32F4-DISCOVERY) - Approccio non standard - Libreria standard parte 1.

STM32F407(STM32F4-DISCOVERY) - Approccio non standard - Libreria standard parte 1.

Ancora una volta voglio scrivere di un semplice inizio con STM32, solo che questa volta senza utilizzare modelli o esempi di qualcun altro, con una spiegazione di ogni passaggio. Gli articoli avranno una numerazione continua dei passaggi.

1. Installa IAR

Costruire un progetto in IAR

1. Preprocessore

  1. cancella i commenti

2. Compilatore

3. Linker

3. Creare un nuovo progetto in IAR

Dopo aver avviato IAR, viene visualizzata una finestra del centro informazioni, di cui non abbiamo bisogno. Fare clic sul menu Progetto -> Crea nuovo progetto. Seleziona toolchain: ARM (è improbabile che tu abbia nient'altro in quell'elenco), Modelli di progetto: C –> principale.

Nella finestra di sinistra ("Area di lavoro"), fare clic con il pulsante destro del mouse per richiamare il menu e creare un nuovo gruppo (Aggiungi –>

Fare clic destro su CMSIS

Al gruppo avviare

Rifinito con CMSIS.

Al gruppo StdPeriphLib

Al gruppo utente

5. Configurare il progetto

  1. Opzioni generali –> Destinazione –>
Selezionare ST –> STM32F100 –> ST STM32F100xB. Questo è il nostro controllore. 2. Opzioni generali –> Configurazione libreria –> CMSIS: spuntare la casella Usa CMSIS. Quindi useremo la libreria CMSIS integrata nel compilatore. Dalla versione 6.30, IAR è stato fornito con CMSIS integrato e sembra essere migliore, ma ha introdotto una certa confusione con i progetti precedenti. 3. Compilatore C/C++ –>
$DIR_PROJ$\

* Debugger –> Setup –> Driver: selezionare ST-Link, poiché tale programmatore è integrato nella scheda Discovery. Ora configuriamo il programmatore stesso: * Debugger –> ST-LINK –> Interfaccia: selezionare SWD (il programmatore sulla scheda è collegato al controller tramite SWD, non tramite JTAG). * Debugger –>
#include "stm32f10x_conf.h" 

vuoto principale ()
{
mentre(1)
{
}
}

<1000000; i++);


per(i=0; io<1000000; i++);

#include "stm32f10x_conf.h"

vuoto principale ()
{





int io;
mentre(1)
{

per(i=0; io<1000000; i++);

<1000000; i++); } }

archivio con il progetto GPIO. Fortunatamente, puoi salvare questo progetto e usarlo come modello in modo da non dover ripetere tutte le impostazioni da capo. L'intero ciclo: 1. Porte I/O (/index.php/stm32-from_zero_to_rtos-2_timers/ "STM32 - da zero a RTOS. 2: Timer e interrupt") (/index.php/stm32-from_zero_to_rtos-3_timer_outputs/ " STM32 - Da zero a RTOS. 3: Uscite timer") [Ancora una volta voglio scrivere di un semplice inizio con STM32, solo che questa volta senza usare modelli o esempi di qualcuno - con una spiegazione di ogni passaggio. Gli articoli avranno una numerazione continua dei passaggi.

0. Ottieni la scheda STM32VLDiscovery

Compriamo nel negozio, costa 600 rubli. Dovrai installare i driver sulla scheda - penso che ciò non causerà difficoltà.

1. Installa IAR

Lavoreremo in IAR - un buon IDE con un eccellente compilatore. Manca la comodità di scrivere codice, ma per i nostri scopi è abbastanza. Sto usando IAR versione 6.50.3, prendi - sai dove.

2. Scarica la libreria delle periferiche

Non sono un sostenitore del lavoro con i registri in fase di formazione. Pertanto, suggerisco di scaricare la libreria delle periferiche da ST per ottenere comode funzioni di accesso a tutte le impostazioni necessarie.

Creiamo la cartella "STM32_Projects", aggiungiamo la cartella Librerie dall'archivio scaricato (stsw-stm32078.zip/an3268/stm32vldiscovery_package), contiene CMSIS (libreria ARM per tutti i microcontrollori Cortex, descrizione e indirizzi di tutte le risorse) e STM32F10x_StdPeriph_Driver - libreria periferica di ST con tutte le funzionalità.

Creiamo anche una cartella “1. GPIO", che conterrà il nostro primo progetto.

Albero delle cartelle - nell'immagine. Fai proprio questo, perché i percorsi relativi in ​​questo albero saranno molto importanti.

Bene, per capire cosa c'è in gioco, scarica il documento di 1100 pagine per questi controller.

Costruire un progetto in IAR

È necessario comprendere chiaramente l'essenza del processo di assemblaggio del progetto. Per comodità, suddividiamolo in fasi.

1. Preprocessore

Un preprocessore passa attraverso tutti i file .c del progetto (sia main.c che tutti i file nell'area di lavoro). Fa quanto segue:

  1. cancella i commenti
  2. espande le direttive #include, sostituendole con il contenuto del file specificato. Questo processo viene eseguito in modo ricorsivo, partendo dal file .c e andando in ogni #include .h incontrato, e se le direttive #include si trovano anche nel file .h, anche il preprocessore entrerà in esse. Si scopre un tale albero di inclusioni. Nota: non gestisce la situazione della doppia inclusione, cioè lo stesso file .h può essere incluso più volte se è #incluso in più punti del progetto. Questa situazione deve essere gestita da defines.
  3. esegue la sostituzione delle macro - espande le macro
  4. raccoglie le direttive del compilatore.

Il preprocessore genera file .i, che sono abbastanza utili quando si cercano errori di compilazione, se non altro perché tutte le macro sono completamente espanse al loro interno. Il salvataggio di questi file può essere abilitato nelle impostazioni del progetto.

A questo punto, il builder ha tutti i file .c nel progetto pronti per essere compilati - come file .i. Non ci sono ancora collegamenti tra i file.

2. Compilatore

Dopo il passaggio del preprocessore, il compilatore ottimizza e compila ogni file .i, producendo un codice binario. Qui è dove è necessario specificare il tipo di processore, la memoria disponibile, il linguaggio di programmazione, il livello di ottimizzazione e cose simili.

Che cosa fa il compilatore quando incontra una chiamata di funzione in un file .c che non è descritto in questo file? Lo cerca nei titoli. Se le intestazioni dicono che la funzione si trova in un altro file .c, lascia semplicemente un puntatore a quell'altro file in quella posizione.

A questo punto, il builder ha tutti i file .c del progetto compilati in file .o. Sono chiamati moduli compilati. Ora ci sono collegamenti tra i file sotto forma di puntatori nei punti in cui vengono chiamate le funzioni "estranee", ma si tratta ancora di diversi file diversi.

3. Linker

Quasi tutto è pronto, devi solo controllare tutti i collegamenti tra i file - passare attraverso main.o e sostituire i puntatori alle funzioni di altre persone - moduli compilati. Se una funzione delle librerie non viene utilizzata, non verrà compilata affatto nella fase precedente o non verrà sostituita dal linker da nessuna parte (a seconda del metodo del raccoglitore). In ogni caso, non entrerà nel codice binario finito.

Il linker può anche eseguire alcune azioni finali con il binario, ad esempio calcolarne il checksum.

Primo progetto: lavorare con le porte I/O

3. Creare un nuovo progetto in IAR

Dopo aver avviato IAR, viene visualizzata una finestra del centro informazioni, di cui non abbiamo bisogno. Fare clic sul menu Progetto -> Crea nuovo progetto. Seleziona toolchain: ARM (è improbabile che tu abbia nient'altro in quell'elenco), Modelli di progetto: C –> principale.

Ora hai un nuovo progetto C vuoto e un file main.c.

4. Ci colleghiamo al progetto della biblioteca

Nella finestra di sinistra ("Area di lavoro"), fare clic con il tasto destro per richiamare il menu e creare un nuovo gruppo (Aggiungi -> Aggiungi gruppo), chiamiamolo CMSIS. Creiamo i gruppi StdPeriphLib, Startup e User allo stesso modo. Ora aggiungi i file ai gruppi (sottolineerò tutti i file per renderlo più facile da seguire).

Fare clic destro su CMSIS, Aggiungi, Aggiungi file - vai a Librerie / CMSIS / CM3, dalla cartella DeviceSupport / ST / STM32F10x (supporto per i cristalli) prendi system_stm32f10x.c (questa è una descrizione della periferia di un particolare cristallo e un'impostazione dell'orologio). La cartella CoreSupport (supporto del kernel) contiene anche core_cm3.c (questa è una descrizione del core Cortex M3), ma non la prenderemo, perché è già nel compilatore. Ne scriverò ulteriormente.

Al gruppo avviare aggiungere il file startup_stm32f10x_md_vl.s dalla cartella Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/iar. Questi sono i passaggi da eseguire all'avvio. Quasi completamente questa è la configurazione di vari gestori di interrupt (gli stessi gestori saranno un po' più avanti). Ci sono ancora file per altri cristalli, ma è md_vl che ci interessa - questo significa media densità (media quantità di memoria, ci sono anche cristalli con piccoli e grandi volumi), value line (assessment line - il cristallo STM32F100 è inteso solo per valutare le possibilità e passare alle seguenti famiglie).

Rifinito con CMSIS.

Al gruppo StdPeriphLib aggiungere i file stm32f10x_rcc.c e stm32f10x_gpio.c dalla cartella Libraries/STM32F10x_StdPeriph_Driver/src. La prima riguarda le funzioni di lavoro con il sistema dell'orologio e la seconda funziona con i pin I/O.

Al gruppo utente trascina e rilascia il nostro main.c . È facoltativo, ma è più carino in questo modo.

Ora l'albero del progetto GPIO si presenta così:

L'area di lavoro è pronta, non aggiungeremo nient'altro.

Resta solo da inserire un altro file nella cartella del progetto, collegando le intestazioni a tutti i file della libreria periferica. Puoi scriverlo da solo, ma è più facile prenderlo già pronto. Vai a stsw-stm32078.zip/an3268/stm32vldiscovery_package/Project/Examples/GPIOToggle - lì prendiamo il file stm32f10x_conf.h (configurazione del progetto) e lo inseriamo nel file "1. GPIO. Questo è l'unico file pronto che prendiamo.

stm32f10x_conf.h - solo un dump di include i moduli necessari e le funzioni di asserzione. Questa funzione verrà chiamata in caso di errori nell'utilizzo delle funzioni di libreria periferiche: ad esempio, metti un po' di spazzatura nella funzione GPIO_WriteBit invece di GPIOC - in breve, ST è stato notevolmente riassicurato. In questa funzione, puoi semplicemente eseguire un ciclo infinito - while(1); Dobbiamo ancora entrare in stm32f10x_conf.h - per commentare le righe per includere file di periferiche non necessarie, lasciando solo stm32f10x_rcc.h, stm32f10x_gpio.h e misc.h - così potremmo scriverlo noi stessi.

5. Configurare il progetto

Fare clic con il pulsante destro del mouse nella finestra dell'area di lavoro sul nome del progetto:

  1. Opzioni generali –> Target –> Variante processore: selezionare “Dispositivo”, premere il pulsante a destra
Selezionare ST –> STM32F100 –> ST STM32F100xB. Questo è il nostro controllore. 2. Opzioni generali –> Configurazione libreria –> CMSIS: spuntare la casella Usa CMSIS. Quindi useremo la libreria CMSIS integrata nel compilatore. Dalla versione 6.30, IAR è stato distribuito con CMSIS integrato e sembra essere migliore, ma ha introdotto una certa confusione con i progetti precedenti. 3. Compilatore C/C++ –> Preprocessore. Qui scriviamo i percorsi delle cartelle della libreria:
$DIR_PROJ$\
$PROJ_DIR$\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
$PROJ_DIR$\..\Libraries\STM32F10x_StdPeriph_Driver\inc
La macro $PROJ_DIR$ indica la cartella corrente (cartella del progetto) e .. significa salire di un livello. Abbiamo scritto i percorsi della cartella con la descrizione del cristallo, così come i file di intestazione della libreria periferica, poiché tutti i file .c nel progetto includono le loro intestazioni e il compilatore deve sapere dove cercarli. Anche qui è necessario scrivere USE\_STDPERIPH\_DRIVER in Simboli definiti. Ciò includerà i file di configurazione richiesti (ad esempio, il menzionato stm32f10x_conf.h) al progetto. Quindi la scheda Preprocessore sarà simile a questa: * Debugger –> Setup –> Driver: selezionare ST-Link, poiché tale programmatore è integrato nella scheda Discovery. Ora configuriamo il programmatore stesso: * Debugger –> ST-LINK –> Interfaccia: selezionare SWD (il programmatore sulla scheda è collegato al controller tramite SWD, non tramite JTAG). * Debugger –> Download: spuntare la casella Usa caricatore/i flash, “Carica firmware su memoria flash”. Logicamente, senza di essa, nulla si allagherà.## 6. Scrivere il codice E per cominciare, scriverò cosa farà questo codice. Dimostrerà una cosa semplice, facendo lampeggiare un LED (PC8 sulla scheda Discovery) con una pausa in un ciclo infinito. Includiamo il file di intestazione di configurazione del progetto, stm32f10x\_conf.h. In esso troviamo la riga #include "stm32f10x \ _exti.h" - questa è la riga 35 e la commentiamo con due barre. Il fatto è che il nostro progetto non necessita del modulo EXTI. C'è già una funzione int main nel file main.c e l'unica azione al suo interno è return 0. Rimuovi questa riga (non restituiremo alcun valore), cambia il tipo di funzione in void (per lo stesso motivo) e scrivi un ciclo infinito:
#include "stm32f10x_conf.h" 

vuoto principale ()
{
mentre(1)
{
}
}

### Avvio del modulo GPIO Le porte I/O STM32 sono denominate GPIO - General Purpose Input/Output. Pertanto, abbiamo incluso la libreria stm32f10x_gpio.c. Tuttavia, non è tutto ciò di cui abbiamo bisogno, un po' di teoria: tutte le periferiche on-chip sono disabilitate di default, sia dall'alimentazione che dalla frequenza di clock. Per accenderlo, devi dare un segnale di clock. Questo è gestito dal modulo RCC e c'è un file stm32f10x_rcc.c con cui lavorare. Il modulo GPIO si blocca sul bus APB2. C'è anche AHB (un analogo del bus del ponte nord-processore) e APB1 (così come APB2 - un analogo del bus del ponte nord-ponte sud). Pertanto, la prima cosa che dobbiamo fare è attivare il clock del modulo GPIOC. Questo è il modulo responsabile di PORTC; c'è anche GPIOA, GPIOB e così via. Questo viene fatto in questo modo: RCC\_APB2PeriphClockCmd(RCC\_APB2Periph_GPIOC, ENABLE); È semplice: chiamiamo la funzione di fornire un segnale di clock dal bus APB2 al modulo GPIOC e quindi accendiamo questo modulo. Naturalmente, lo facciamo proprio all'inizio della funzione principale void. Qui ci sono solo le basi che devi capire. Ho anche un altro [articolo dettagliato sul modulo GPIO](/index.php/stm32-%e2%86%92-%d0%bf%d0%be%d1%80%d1%82%d1%8b- gpio / "STM32 → Porte GPIO"). ### Configurazione del modulo GPIOC Non è rimasto molto per configurare il modulo GPIOC. Impostiamo la gamba sull'uscita (c'è ancora un ingresso e funzioni alternative), regoliamo la nitidezza dei frontali (ai fini della compatibilità EM), il driver di uscita (push-pull o open source). Lo facciamo subito dopo l'inizializzazione della porta. GPIO\_InitTypeDef GPIO\_InitStructure; GPIO\_InitStructure.GPIO\_Velocità = GPIO\_Velocità\_2MHz; GPIO\_InitStructure.GPIO\_Mode = GPIO\_Mode\_Out_PP; GPIO\_InitStructure.GPIO\_Pin = GPIO\_Pin\_8; GPIO\_Init(GPIO, &GPIO\_InitStruttura); Questo è tutto, dopodiché il pin PC8 funzionerà come uscita push-pull con bordi relativamente lisci (la frequenza di commutazione massima è 2 MHz. Gli spigoli vivi sono 50 MHz). La levigatezza dei frontali non è evidente alla vista, ma si può notare sull'oscilloscopio. ### Accendere il LED Chiamare la funzione GPIO\_WriteBit(GPIOC, GPIO\_Pin\_8, Bit\_SET); Il LED si accende. ### Attiva e disattiva in un ciclo Nel ciclo while(1), scrivi il codice per accenderlo, mettere in pausa, spegnerlo e mettere nuovamente in pausa:

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);  per(i=0; io<1000000; i++);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET);
per(i=0; io<1000000; i++);

Pertanto, l'intero file main.c appare così:

#include "stm32f10x_conf.h"

vuoto principale ()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ABILITA);

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIO, &GPIO_InitStruttura);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);

int io;
mentre(1)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);
per(i=0; io<1000000; i++);

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); per(i=0; io<1000000; i++); } }

## 7. Lancio! Colleghiamo la scheda STM32VLDiscovery al computer tramite microUSB, clicchiamo sul pulsante Download e Debug in IAR. Il programma viene caricato sul microcontrollore (puoi notare una finestra con una barra di avanzamento che si chiude rapidamente - la dimensione del programma è così piccola) e inizia il debug. IAR si ferma alla prima istruzione del codice (questo è abbastanza comodo durante il debug), è necessario avviarlo con il pulsante Vai. Tutto dovrebbe funzionare - il LED blu PC8 sulla scheda STM32VLDiscovery dovrebbe Come sempre, puoi scaricare l'archivio con il progetto GPIO. Fortunatamente, puoi salvare questo progetto e usarlo come modello in modo da non dover ripetere tutta la configurazione da capo. L'intero ciclo: 1. Porte I/O (/index.php/stm32-from_zero_to_rtos-2_timers/ "STM32 - da zero a RTOS. 2: Timer e interrupt") (/index.php/stm32-from_zero_to_rtos-3_timer_outputs/ " STM32 - Da zero a RTOS. 3: Uscite timer")

](/index.php/stm32-from_zero_to_rtos-4_exti_nvic/ “STM32 - da zero a RTOS. 4: Interruzioni esterne e NVIC”) 5. Installa FreeRTOS

Quindi, ci siamo già alzati, nel senso che abbiamo tutto quello che ci serve collegato ai pin del microcontrollore sulla scheda Discovery STM32VL, abbiamo imparato a parlare, nel linguaggio di programmazione C, sarebbe ora di creare un progetto in prima classe.

Scrittura del programma

Terminata la creazione e configurazione del progetto, si può iniziare a scrivere il programma vero e proprio. Come è consuetudine per tutti i programmatori, il primo programma scritto per funzionare su un computer è un programma che visualizza sullo schermo la scritta "HelloWorld" e per tutti i microcontrollori, il primo programma per il microcontrollore fa lampeggiare il LED. Non faremo eccezione a questa tradizione e scriveremo un programma che controllerà il LED LD3 sulla scheda Discovery STM32VL.

Dopo aver creato un progetto vuoto in IAR, crea un codice di programma minimo:

Ora il nostro programma "girerà" sempre in un ciclo mentre.

Per poter controllare il LED, dobbiamo abilitare il clock della porta a cui è collegato e impostare il pin della porta del microcontrollore corrispondente su output. Come abbiamo discusso in precedenza nella prima parte, per consentire il port clocking DA risponde un po' IOPCEN Registrati RCC_APB2ENR. Secondo il documento " RM0041riferimentoManuale.PDF» per abilitare il clock del bus della porta DA richiesto nel registro RCC_APB2ENR imposta il bit IOPCEN per unità. Affinché quando si imposta questo bit, non si reimpostano gli altri impostati in questo registro, è necessario applicare l'operazione di addizione logica (OR logico) allo stato corrente del registro e quindi scrivere il valore risultante nel contenuto del Registrati. In accordo con la struttura della libreria ST, l'accesso al valore del registro per la lettura e la scrittura dello stesso avviene tramite un puntatore alla struttura RCC-> APB2 ENR. Quindi, ricordando il materiale della seconda parte, possiamo scrivere il codice seguente che esegue un po' di impostazione IOPCEN nel registro RCC_APB2ENR:

Come puoi vedere, dal file "stm32f10x.h", il valore del bit IOPCEN definito come 0x00000010, che corrisponde al quarto bit ( IOPCEN) Registrati APB2ENR e corrisponde al valore specificato nel foglio dati.

Ora impostiamo l'output allo stesso modo 9 porta DA. Per fare ciò, è necessario configurare questo pin della porta per l'output in modalità push-pull. Il registro è responsabile dell'impostazione della modalità porta per input/output GPIOC_CRH, lo abbiamo già considerato in , la sua descrizione è anche nella sezione “7.2.2 Registro configurazione porte alto” del datasheet. Per impostare l'uscita sulla modalità di uscita con una velocità massima di 2 MHz, è necessario nel registro GPIOC_CRH installare MODALITÀ9 a uno e resettare il bit MODALITÀ9 a zero. I bit sono responsabili dell'impostazione della modalità di funzionamento dell'uscita come funzione principale con l'uscita push-pull. CNF9 e CNF9 , per configurare la modalità di funzionamento richiesta, entrambi questi bit devono essere azzerati.

Ora il pin della porta a cui è collegato il LED è impostato su output, per controllare il LED è necessario modificare lo stato del pin della porta impostando l'uscita su un'unità logica. Esistono due modi per modificare lo stato di un pin della porta, il primo è scrivere direttamente nel registro dello stato della porta il contenuto modificato del registro della porta, proprio come abbiamo fatto per l'impostazione della porta. Questo metodo è sconsigliato in vista della possibilità di una situazione in cui un valore errato può essere scritto nel registro di porta. Questa situazione può verificarsi se durante un cambio di stato del registro, dal momento in cui lo stato del registro è già stato letto e fino al momento in cui lo stato modificato viene scritto nel registro, qualche periferica o interrupt modifica il stato di questo porto. Al termine dell'operazione di modifica dello stato del registro, il valore verrà scritto nel registro senza tener conto delle modifiche avvenute. Sebbene la probabilità di questa situazione sia molto bassa, vale comunque la pena utilizzare un altro metodo in cui la situazione descritta è esclusa. Per fare ciò, ci sono due registri nel microcontrollore. GPIOx_BSRR e GPIOx_BRR. Quando si scrive un'unità logica nel bit richiesto del registro GPIOx_BRR il pin della porta corrispondente verrà riportato a zero logico. Registrati GPIOx_BSRR può impostare e ripristinare lo stato dei pin della porta, per impostare il pin della porta su uno logico, è necessario impostare i bit BSn, corrispondenti al numero del bit richiesto, questi bit si trovano nei registri inferiori del byte. Per ripristinare lo stato del pin della porta su zero logico, è necessario scrivere i bit BRn pin corrispondenti, questi bit si trovano nei bit superiori del registro delle porte.

LED LD3 collegato all'uscita 9 porta DA. Per accendere questo LED, dobbiamo applicare un'unità logica al pin corrispondente della porta in modo da “accendere” il LED.

Aggiungiamo il codice per impostare l'uscita della porta LED al nostro programma e aggiungiamo anche una funzione di ritardo software per ridurre la frequenza di commutazione del LED:

//Non dimenticare di collegare il file di intestazione con una descrizione dei registri del microcontrollore

#include "stm32f10x.h"

vuoto Ritardo ( vuoto);

vuoto Ritardo ( vuoto)
{
non firmato lungo io;
per(io=0; io<2000000; i++);
}

//La nostra funzione principale

vuoto principale( vuoto)
{


RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

//cancella i bit MODE9 (reimposta i bit MODE9_1 e MODE9_0 a zero)
GPIOC->CRH &= ~GPIO_CRH_MODE9;

//Imposta il bit MODE9_1 per impostare l'uscita su un'uscita con una velocità di 2MHz
GPIOC->CRH |= GPIO_CRH_MODE9_1;

//cancella i bit CNF (impostati come output generico, simmetrico (push-pull))
GPIOC->CRH &= ~GPIO_CRH_CNF9;

mentre(1)
{

//Impostando il pin 9 della porta C su uno logico ("acceso" il LED)
GPIOC->BSRR = GPIO_BSRR_BS9;


ritardo();


GPIOC->BSRR = GPIO_BSRR_BR9;


ritardo();

}
}

È possibile scaricare l'archivio con il codice sorgente del programma scritto utilizzando il controllo diretto dei registri del microcontrollore al link.

Il nostro primo programma funzionante è stato scritto, in fase di scrittura, per lavorare e configurare le periferiche, abbiamo utilizzato i dati del datasheet ufficiale " RM0041riferimentoManuale.PDF”, questa fonte di informazioni sui registri del microcontrollore è la più accurata, ma per usarla è necessario rileggere molte informazioni, il che complica la scrittura dei programmi. Per facilitare il processo di configurazione delle periferiche del microcontrollore esistono vari generatori di codice, l'utility ufficiale di ST è Microxplorer, ma è ancora di poche funzionalità e per questo motivo sviluppatori di terze parti hanno creato un programma alternativo "STM32 Generatore di codice di programma » . Questo programma consente di ottenere facilmente il codice delle impostazioni della periferica tramite una comoda e intuitiva interfaccia grafica (vedi Fig. 2).


Riso. 2 Screenshot del generatore di codice STM32

Come puoi vedere dalla Figura 2, il codice generato dal programma per impostare l'uscita LED corrisponde al codice che abbiamo scritto in precedenza.

Per eseguire il programma scritto, dopo aver compilato il codice sorgente, è necessario caricare il nostro programma nel microcontrollore e vedere come funziona.

Video della modalità di debug del programma di lampeggio dei LED

Video del programma di lampeggio dei LED sulla scheda Discovery STM32VL

Funzioni di libreria per lavorare con le periferiche

Per semplificare il lavoro di impostazione dei registri delle periferiche del microcontrollore, la ST ha sviluppato delle librerie, grazie all'utilizzo delle quali non è necessario leggere il datasheet in modo così approfondito, perché quando si utilizzano queste librerie, il lavoro di scrittura di un programma diventa più vicino alla scrittura di programmi di alto livello, poiché tutte le funzioni di basso livello sono implementate a livello di funzioni di libreria. Tuttavia, non si dovrebbe abbandonare completamente l'uso del lavoro diretto con i registri del microcontrollore, in considerazione del fatto che le funzioni di libreria richiedono più tempo del processore per la loro esecuzione, di conseguenza, il loro utilizzo in sezioni del programma critiche dal punto di vista del tempo non è giustificato. Tuttavia, nella maggior parte dei casi, cose come l'inizializzazione delle periferiche non sono critiche per il runtime e la comodità dell'utilizzo delle funzioni di libreria è più preferibile.

Ora scriviamo il nostro programma usando la libreria ST. Il programma necessita di configurare le porte I/O, per utilizzare le funzioni di libreria per l'impostazione delle porte, è necessario collegare il file header " stm32f10x_gpio.h» (vedi tabella 1). Questo file può essere collegato decommentando la riga corrispondente nel file di configurazione dell'intestazione collegata " stm32f10x_conf.h". Alla fine del fascicolo stm32f10x_gpio.h» c'è un elenco di dichiarazioni di funzioni per lavorare con le porte. Una descrizione dettagliata di tutte le funzioni disponibili si trova nel file " stm32f10x_stdperiph_lib_um.chm”, una breve descrizione dei più comunemente utilizzati è riportata nella tabella 2.

Tabella 2. Descrizione delle principali funzioni delle impostazioni della porta

Funzione

Descrizione della funzione, parametri passati e restituiti

GPIO_DeInit(
GPIO_TypeDef* GPIOx)

Imposta i valori dei registri di configurazione della porta GPIOx sui valori predefiniti

GPIO_Init(
GPIO_TypeDef* GPIOx,

Imposta i registri di configurazione della porta GPIOx in base ai parametri specificati nella struttura GPIO_InitStruct

GPIO_StructInit(
GPIO_InitTypeDef* GPIO_InitStruct)

Riempie tutti i campi della struttura GPIO_InitStruct con valori predefiniti

uint8_t GPIO_ReadInputDataBit(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin);

Lettura del valore di input del pin GPIO_Pin della porta GPIOx

uint16_t GPIO_ReadInputData(
GPIO_TypeDef* GPIOx)

Leggi i valori di input di tutti i pin della porta GPIOx

GPIO_SetBits(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin)

Impostazione del valore di uscita del pin GPIO_Pin della porta GPIOx su uno logico

GPIO_ResetBits(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin)

Ripristino del valore di uscita del pin GPIO_Pin della porta GPIOx su zero logico

GPIO_WriteBit(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin,
BitAzione BitVal)

Scrivi il valore BitVal sul pin GPIO_Pin della porta GPIOx

GPIO_Write(
GPIO_TypeDef* GPIOx,
uint16_t PortVal)

Scrivere il valore PortVal sulla porta GPIOx

Come si può vedere dalla descrizione delle funzioni, come parametri per le impostazioni delle porte, ecc., alla funzione non vengono passati molti parametri individuali diversi, ma un'unica struttura. Le strutture sono dati aggregati che hanno una relazione logica. A differenza degli array, le strutture possono contenere dati di diversi tipi. In altre parole, una struttura rappresenta un insieme di variabili diverse con tipi diversi combinati in un'unica variabile. Le variabili che si trovano in questa struttura sono chiamate campi della struttura e vi si accede come segue, prima viene scritto il nome della struttura, quindi vengono scritti un punto e il nome del campo della struttura (il nome della variabile in questa struttura).

L'elenco delle variabili incluse nelle strutture per le funzioni di lavoro con le porte è descritto nello stesso file un po' più in alto rispetto alla descrizione delle funzioni. Quindi, ad esempio, la struttura GPIO_InitTypeDef" ha la seguente struttura:

struttura typedef
{

uint16_t PIN_GPIO; /*!< Specifies the GPIO pins to be configured.
Questo parametro può essere qualsiasi valore di @ref GPIO_pins_define */

GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
Questo parametro può essere un valore di @ref GPIOSpeed_TypeDef */

GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
Questo parametro può essere un valore di @ref GPIOMode_TypeDef */

)GPIO_InitTypeDef;

Il primo campo di questa struttura contiene la variabile " GPIO_ Spillo" genere non firmato breve, in questa variabile è necessario scrivere i flag dei numeri delle uscite corrispondenti per le quali si suppone di effettuare le impostazioni necessarie. È possibile configurare più uscite contemporaneamente impostando più costanti come parametro tramite l'operatore bit per bit OR(cm. ). Bitwise OR "raccoglierà" tutti quelli dalle costanti elencate e le costanti stesse sono una maschera, destinata solo a tale uso. Le definizioni delle macro delle costanti sono specificate nello stesso file di seguito.

Il secondo campo della struttura" GPIO_InitTypeDef» imposta la velocità di uscita della porta massima possibile. L'elenco dei possibili valori per questo campo è elencato sopra:

Descrizione dei possibili valori:

  • GPIO_Mode_AIN- ingresso analogico (in inglese Analog INput);
  • GPIO_Mode_IN_FLOATING- ingresso senza pull-up, penzolante (eng. Input float) in aria
  • GPIO_Mode_IPD- ingresso a tendina
  • GPIO_Mode_IPU- Ingresso Pull-up
  • GPIO_Mode_Out_OD- uscita con scarico aperto (Eng. Output Open Drain)
  • GPIO_Mode_Out_PP- uscita a due stati (uscita in inglese Push-Pull - avanti e indietro)
  • Modalità_GPIO_AF_OD- Uscita a scarico aperto per la funzione alternativa. Utilizzato nei casi in cui l'uscita deve essere controllata da periferiche collegate a questo pin della porta (ad esempio, pin USART1 Tx, ecc.)
  • GPIO_Mode_AF_PP- lo stesso, ma con due stati

Allo stesso modo, è possibile visualizzare la struttura delle variabili di altre strutture necessarie per lavorare con le funzioni di libreria.

Per lavorare con le strutture, queste, come le variabili, devono essere dichiarate e gli è stato assegnato un nome univoco, dopo di che è possibile accedere ai campi della struttura dichiarata con il nome ad essa assegnato.

//Dichiara struttura

/*
Prima di iniziare a compilare i campi della struttura, si consiglia di inizializzare il contenuto della struttura con i dati di default, questo per evitare di scrivere dati errati se per qualche motivo non tutti i campi della struttura sono stati compilati .

Per passare i valori di una struttura a una funzione, fai precedere il nome della struttura dal simbolo &. Questo simbolo dice al compilatore che è necessario passare alla funzione non i valori contenuti nella struttura, ma l'indirizzo di memoria dove si trovano questi valori. Ciò viene fatto per ridurre il numero di azioni necessarie del processore per copiare il contenuto della struttura e risparmiare anche RAM. Pertanto, invece di passare alla funzione molti byte contenuti nella struttura, ne verrà passato solo uno, contenente l'indirizzo della struttura.
*/

/* Scrivi il numero pin della porta nel campo GPIO_Pin della struttura GPIO_Init_struct, che configureremo ulteriormente */

GPIO_Init_struct.GPIO_Pin=GPIO_Pin_9;

/* Compila il campo GPIO_Speed ​​allo stesso modo */

/*
Dopo aver compilato i campi obbligatori della struttura, tale struttura dovrà essere passata alla funzione, che provvederà all'iscrizione necessaria negli appositi registri. Oltre alla struttura con le impostazioni per questa funzione, è necessario passare anche il nome della porta a cui sono destinate le impostazioni.
*/

Quasi tutte le periferiche sono configurate all'incirca allo stesso modo, le differenze sono solo nei parametri e nei comandi specifici di ogni dispositivo.

Ora scriviamo il nostro programma di lampeggio dei LED utilizzando solo le funzioni di libreria.

// Non dimenticare di collegare il file di intestazione con una descrizione dei registri del microcontrollore

#include "stm32f10x.h"
#include "stm32f10x_conf.h"

//dichiarando la funzione di ritardo del programma

vuoto Ritardo ( vuoto);

//funzione di ritardo del software stessa

vuoto Ritardo ( vuoto)
{
non firmato lungo io;
per(io=0; io<2000000; i++);
}

//La nostra funzione principale

vuoto principale( vuoto)
{

//Abilita il clock del bus della porta C
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ABILITA);

//Dichiara una struttura per configurare la porta
GPIO_InitTypeDef GPIO_Init_struct;

//Riempi la struttura con i valori iniziali
GPIO_StructInit(&GPIO_Init_struct);

/* Scrivi il numero pin della porta nel campo GPIO_Pin della struttura GPIO_Init_struct, che configureremo ulteriormente */
GPIO_Init_struct.GPIO_Pin = GPIO_Pin_9;

// Compila i campi GPIO_Speed ​​e GPIO_Mode in modo simile
GPIO_Init_struct.GPIO_Speed= GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode = GPIO_Mode_Out_PP;

//Passiamo la struttura completata per eseguire azioni per impostare i registri
GPIO_Init(GPIOC, &GPIO_Init_struct);

//Il nostro ciclo infinito principale
mentre(1)
{
//Impostazione del pin 9 della porta C su uno logico (LED "acceso")
GPIO_SetBits(GPIOC, GPIO_Pin_9);

// Aggiunge un ritardo software in modo che il LED si accenda per un po'
ritardo();

//Reimposta lo stato del pin 9 della porta C su zero logico
GPIO_ResetBits(GPIOC, GPIO_Pin_9);

//Aggiunta di nuovo il ritardo del software
ritardo();
}
}

collegamento .

Dall'esempio sopra, si può vedere che l'uso delle funzioni di libreria per lavorare con le periferiche consente di avvicinare i programmi di scrittura per il microcontrollore alla programmazione orientata agli oggetti e riduce anche la necessità di frequenti accessi al foglio dati per leggere la descrizione dei registri del microcontrollore, ma l'utilizzo delle funzioni di libreria richiede una maggiore conoscenza del linguaggio di programmazione. In considerazione di ciò, per le persone che non hanno particolare dimestichezza con la programmazione, un'opzione più semplice per scrivere programmi sarà un modo per scrivere programmi senza utilizzare le funzioni di libreria, con accesso diretto ai registri del microcontrollore. Per coloro che conoscono bene il linguaggio di programmazione, ma sono poco esperti di microcontrollori, in particolare STM32, l'uso delle funzioni di libreria semplifica notevolmente il processo di scrittura dei programmi.

Questa circostanza, oltre al fatto che ST ha curato un elevato grado di compatibilità, sia in termini hardware che software, dei suoi vari microcontrollori, contribuisce al loro più facile studio, non essendo necessario approfondire le caratteristiche strutturali di vari controllori della serie STM32 e permette di scegliere come microcontrollore da studio uno qualsiasi dei microcontrollori disponibili nella linea STM32.

Gestore di interruzioni

I microcontrollori hanno una notevole capacità - interrompere l'esecuzione del programma principale su un evento specifico e procedere all'esecuzione di una subroutine speciale - gestore degli interrupt. Le sorgenti di interrupt possono essere eventi esterni - interrupt per la ricezione/trasmissione di dati attraverso qualsiasi interfaccia di trasferimento dati, o un cambiamento nello stato dell'uscita, o interni - overflow del timer, ecc. Un elenco di possibili sorgenti di interrupt per i microcontrollori della serie STM32 è dato nella scheda tecnica" RM0041 Manuale di riferimento" nella sezione " 8 Interruzioni ed eventi».

Poiché anche il gestore di interrupt è una funzione, verrà scritto come una normale funzione, ma affinché il compilatore sappia che questa funzione è un gestore di interrupt specifico, è necessario scegliere nomi predefiniti come nome della funzione, a cui reindirizza il vettore di interrupt sono specificati. L'elenco dei nomi di queste funzioni con una breve descrizione è nel file assembler " startup_stm32f10x_md_vl.s". Possono esserci diverse fonti di interrupt per un gestore di interrupt, ad esempio, la funzione del gestore di interrupt " USART1_IRQHandler» può essere chiamato in caso di fine ricezione e fine trasmissione di un byte, ecc.

Per iniziare con gli interrupt, è necessario configurare e inizializzare il controller di interrupt NVIC. Nell'architettura Cortex M3, ogni interrupt può essere impostato sul proprio gruppo di priorità per i casi in cui si verificano più interrupt contemporaneamente. Quindi dovresti configurare la fonte di interruzione.

Il campo NVIC_IRQChannel indica quale interrupt vogliamo configurare. La costante USART1_IRQn designa il canale responsabile degli interrupt associati a USART1. È definito nel file stm32f10x.h”, vi sono definite altre costanti simili.

I due campi successivi indicano la priorità degli interrupt (i valori massimi di questi due parametri sono determinati dal gruppo di priorità selezionato). L'ultimo campo, infatti, prevede l'utilizzo degli interrupt.

In funzione NVIC_Init, oltre all'impostazione delle porte, viene trasmesso un puntatore a una struttura per applicare le impostazioni effettuate e scriverle nei registri corrispondenti del microcontrollore.

Ora, nelle impostazioni del modulo, è necessario impostare i parametri in base ai quali questo modulo genererà un'interruzione. Per prima cosa devi abilitare l'interrupt, questo viene fatto chiamando la funzione nome_ITConfig(), che si trova nel file di intestazione della periferica.

// Abilita gli interrupt alla fine del trasferimento di byte tramite USART1
USART_ITConfig(USART1, USART_IT_TXE, ABILITA);

// Abilita gli interrupt alla fine della ricezione di un byte da USART1
USART_ITConfig(USART1, USART_IT_RXNE, ABILITA);

Una descrizione dei parametri passati alla funzione si trova nel file del codice sorgente del dispositivo periferico, appena sopra la posizione della funzione stessa. Questa funzione abilita o disabilita gli interrupt su vari eventi dal modulo periferico specificato. Quando questa funzione viene eseguita, il microcontrollore sarà in grado di generare interrupt per gli eventi di cui abbiamo bisogno.

Dopo essere entrati nella funzione di gestione degli interrupt, dobbiamo verificare quale evento ha causato l'interruzione, quindi ripristinare il flag impostato, altrimenti, all'uscita dall'interruzione, il microcontrollore deciderà che non abbiamo elaborato l'interruzione, poiché il flag di interruzione è ancora impostato.

Per eseguire varie, piccole azioni ripetitive con un periodo preciso, i microcontrollori con un core Cortex-M3 dispongono di un timer di sistema appositamente progettato per questo. La funzione di questo timer include solo la chiamata di interruzione a intervalli di tempo strettamente specificati. Di norma, nell'interrupt chiamato da questo timer, viene inserito del codice per misurare la durata dei vari processi. La dichiarazione della funzione di impostazione del timer si trova nel file " nucleo_ cm3. h". L'argomento passato alla funzione specifica il numero di cicli del bus di sistema tra gli intervalli di chiamata del gestore di interrupt del timer di sistema.

SysTick_Config(clk);

Ora, dopo aver affrontato gli interrupt, riscriveremo il nostro programma, utilizzando il timer di sistema come elemento di impostazione dell'ora. Perché il timer SysTick”è di sistema e può essere utilizzato da vari blocchi funzionali del nostro programma, quindi sarebbe ragionevole spostare la funzione di gestione degli interrupt dal timer di sistema a un file separato, da questa funzione chiamare le funzioni per ciascun blocco funzionale separatamente.

Un esempio del file "main.c" del programma per far lampeggiare il LED utilizzando un interrupt:

//Collega il file header con la descrizione dei registri del microcontrollore

#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include "main.h"

int. non firmato LED_timer;

//Funzione chiamata dalla funzione di gestione degli interrupt del timer di sistema

vuoto SysTick_Timer_main( vuoto)
{
//Se la variabile LED_timer non ha ancora raggiunto 0,
Se(Timer_LED)
{
//Controlla il suo valore, se è superiore a 1500 accendi il LED
Se(Timer_LED>1500) GPIOC->BSRR= GPIO_BSRR_BS9;

//altrimenti, se minore o uguale a 1500, disabilitare
altro GPIOC->BSRR= GPIO_BSRR_BR9;

//Decrementiamo la variabile LED_timer
LED_timer--;
}

//Se il valore della variabile ha raggiunto lo zero, imposta un nuovo valore di 2000
altro LED_timer=2000;
}

//La nostra funzione principale

vuoto principale( vuoto)
{

//Abilita il clock del bus della porta C
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ABILITA);

//Dichiara una struttura per configurare la porta
GPIO_InitTypeDef GPIO_Init_struct;

//Riempi la struttura con i valori iniziali
GPIO_StructInit(&GPIO_Init_struct);

/* Scrivi il numero pin della porta nel campo GPIO_Pin della struttura GPIO_Init_struct, che configureremo ulteriormente */
GPIO_Init_struct.GPIO_Pin = GPIO_Pin_9;

// Compila i campi GPIO_Speed ​​e GPIO_Mode in modo simile
GPIO_Init_struct.GPIO_Speed= GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode = GPIO_Mode_Out_PP;

//Passiamo la struttura completata per eseguire azioni per impostare i registri
GPIO_Init(GPIOC, &GPIO_Init_struct);

// seleziona il gruppo di priorità per gli interrupt
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

//Configura il funzionamento del timer di sistema con un intervallo di 1 ms
SysTick_Config(24000000/1000);

//Il nostro ciclo infinito principale
mentre(1)
{
// Questa volta è vuoto, tutto il controllo dei LED avviene negli interrupt
}
}

Parte del codice sorgente nel file "stm32f10x_it.c":


#include "main.h"

/**
* @brief Questa funzione gestisce SysTick Handler.
* @paramNone
* @retvalNessuno
*/

vuoto SysTick_Handler( vuoto)
{
SysTick_Timer_main();
}

Un esempio di una bozza di lavoro del programma per far lampeggiare un LED utilizzando un'interruzione può essere scaricato dal collegamento.

Su questo, la mia storia sulle basi dello sviluppo di programmi per il microcontrollore STM32 può essere considerata completa. Ho fornito tutte le informazioni necessarie per la possibilità di ulteriore autoapprendimento dei microcontrollori STM32. Il materiale fornito è solo un inizio, poiché una descrizione completa del lavoro con i microcontrollori non può essere descritta nell'ambito di nessun articolo. Inoltre, studiare i microcontrollori senza acquisire esperienza pratica è impossibile e la vera esperienza arriva gradualmente con anni di lavoro, esperimenti, con l'accumulo di vari sviluppi software e hardware, oltre alla lettura di vari articoli e documentazione sui microcontrollori. Ma non lasciarti spaventare, perché le informazioni fornite nell'articolo sono sufficienti per creare il tuo primo dispositivo su un microcontrollore e puoi acquisire ulteriori conoscenze ed esperienze da solo, sviluppando ogni volta dispositivi sempre più complessi e migliori e migliorare le tue abilità.

Spero di essere stato in grado di interessarti allo studio dei microcontrollori e allo sviluppo di dispositivi basati su di essi, e il mio lavoro ti sarà utile e interessante.

Quando si crea la prima applicazione sul microcontrollore STM32, ci sono diversi modi per procedere. La prima, classica, prendiamo la descrizione esatta del controllore sul sito www.st.com, che compare sotto la denominazione "Manuale di riferimento" e leggiamo la descrizione dei registri periferici. Quindi proviamo a scriverli e vediamo come funzionano le periferiche. Leggere questo documento è molto utile, ma nella prima fase della padronanza del microcontrollore, questo può essere abbandonato, stranamente. Gli ingegneri di STMicroelectronics hanno scritto una libreria di driver per periferiche standard. Inoltre, hanno scritto molti esempi di utilizzo di questi driver, che possono ridurre la programmazione dell'applicazione alla pressione dei tasti Ctrl + C e Ctrl + V, seguiti da una piccola modifica nell'esempio del driver per soddisfare le tue esigenze. Pertanto, la connessione della libreria del driver periferico al progetto è il secondo metodo per creare un'applicazione. Oltre alla velocità di scrittura, ci sono altri vantaggi di questo metodo: l'universalità del codice e l'uso di altre librerie proprietarie, come USB, Ethernet, drive control, ecc., che sono fornite nei sorgenti e utilizzano un driver periferico standard. Ci sono anche degli svantaggi di questo metodo: dove puoi cavartela con una riga di codice, il driver della periferica STM32 standard scriverà 10. La libreria della periferica stessa è fornita anche come file sorgente, quindi puoi tracciare quale bit registra questo o quella funzione cambia. Volendo, sarà possibile passare dal secondo metodo di scrittura di un programma al primo commentando una parte del codice che utilizza da sola la libreria standard, che controlla direttamente il registro periferico. Come risultato di tale azione, guadagnerai velocità di controllo, quantità di RAM e ROM e perderai l'universalità del codice. In ogni caso, gli ingegneri Promelectronica consigliano di utilizzare la libreria delle periferiche standard almeno nella prima fase.

Le maggiori difficoltà attendono lo sviluppatore quando si collega la libreria al suo progetto. Se non sai come farlo, puoi dedicare molto tempo a questo evento, che contraddice l'idea stessa di utilizzare un driver già pronto. Il materiale è dedicato al collegamento della libreria standard a qualsiasi famiglia STM32.

Ciascuna famiglia STM32 dispone della propria libreria di periferiche standard. Ciò è dovuto al fatto che la periferia stessa è diversa. Ad esempio, le periferiche dei controller STM32L hanno una funzione di risparmio energetico come uno dei compiti, che comporta l'aggiunta di funzioni di controllo. Un classico esempio è l'ADC, che in STM32L ha la capacità di spegnere l'hardware, in assenza di un comando di conversione per molto tempo - una delle conseguenze del compito di risparmio energetico. I controller ADC delle famiglie STM32F non hanno tale funzione. Infatti, per la presenza di una differenza hardware in periferia, abbiamo diverse librerie di driver. Oltre all'ovvia differenza nelle funzioni del controller, c'è un miglioramento nelle periferiche. Quindi, le periferiche dei controllori delle famiglie che sono state rilasciate in seguito possono essere più ponderate e convenienti. Ad esempio, le periferiche dei controller STM32F1 e STM32F2 presentano differenze di controllo. A parere dell'autore, la gestione delle periferiche STM32F2 è più conveniente. E questo è comprensibile perché: la famiglia STM32F2 è stata rilasciata successivamente e questo ha permesso agli sviluppatori di tenere conto di alcune sfumature. Di conseguenza, per queste famiglie - biblioteche di controllo periferiche individuali. L'idea di quanto sopra è semplice: nella pagina del microcontrollore che andrete ad utilizzare, c'è una libreria periferica adatta ad esso.

Nonostante la differenza di periferiche nelle famiglie, i conducenti nascondono dentro di sé il 90% delle differenze. Ad esempio, la funzione di ottimizzazione dell'ADC sopra menzionata è la stessa per tutte le famiglie:

void ADC_Init(ADC_Nom, ADC_Param),

dove ADC_Nom è il numero ADC sotto forma di ADC1, ADC2, ADC3, ecc.

ADC_Param - puntatore alla struttura dati, come configurare l'ADC (da cosa partire, quanti canali digitalizzare, se farlo ciclicamente, ecc.)

Il 10% delle differenze familiari, in questo esempio, che dovranno essere corrette quando si passa da una famiglia STM32 all'altra, sono nascoste nella struttura ADC_Param. A seconda della famiglia, il numero di campi in questa struttura può essere diverso. La parte generale ha la stessa sintassi. Pertanto, il trasferimento di un'applicazione per una famiglia STM32, scritta sulla base di librerie periferiche standard, ad un'altra è molto semplice. In termini di universalizzazione di soluzioni basate su microcontrollori, STMicroelectronics è irresistibile!

Quindi, abbiamo scaricato la libreria per l'STM32 utilizzato. Qual è il prossimo? Successivamente, dobbiamo creare un progetto e collegare ad esso i file richiesti. Prendi in considerazione la creazione di un progetto utilizzando l'ambiente di sviluppo IAR Embedded Workbench come esempio. Avviamo l'ambiente di sviluppo e andiamo nella scheda "Progetto", selezioniamo la voce "Crea progetto" per creare il progetto:

Nel nuovo progetto che compare, entra nelle impostazioni passando con il mouse sopra il nome del progetto, premendo il tasto destro del mouse e selezionando "Opzioni" dal menu a tendina:

Aree di memoria di RAM e ROM:

Quando si fa clic sul pulsante "Salva", l'ambiente proporrà di scrivere un nuovo file di descrizione del controller nella cartella del progetto. L'autore consiglia di creare un singolo file *.icp per ogni progetto e di salvarlo nella cartella del progetto.

Se hai intenzione di eseguire il debug del tuo progetto in-circuit, cosa consigliata, inserisci il tipo di debugger da utilizzare:

Nella scheda del debugger selezionato, specificare l'interfaccia per collegare il debugger (nel nostro caso, ST-Link è selezionato) al controller:



D'ora in poi, il nostro progetto senza librerie è pronto per essere compilato e caricato nel controllore. Altri ambienti come Keil uVision4, Resonance Ride7, ecc. dovranno seguire gli stessi passaggi.

Se scrivi la riga nel file main.c:

#include "stm32f10x.h" o

#include "stm32f2xx.h" o

#include "stm32f4xx.h" o

#include "stm32l15x.h" o

#include "stm32l10x.h" o

#include "stm32f05x.h"

indicando la posizione di questo file, o copiando questo file nella cartella del progetto, alcune aree di memoria verranno associate ai registri periferici della famiglia corrispondente. Il file stesso si trova nella cartella della libreria periferica standard nella sezione: \CMSIS\CM3\DeviceSupport\ST\STM32F10x (o simile nel nome per altre famiglie). D'ora in poi, sostituisci l'indirizzo del registro periferico con un numero con il suo nome. Anche se non si utilizzano le funzioni della libreria standard, si consiglia di effettuare tale connessione.

Se intendi utilizzare gli interrupt nel tuo progetto, ti consigliamo di includere un file di avvio con estensione *.s, che si trova lungo il percorso \CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar, o simile, per altre famiglie. È importante notare che ogni ambiente ha il proprio file. Di conseguenza, se utilizziamo IAR EWB, dobbiamo prendere il file dalla cartella IAR. Ciò è dovuto a una leggera differenza nella sintassi degli ambienti. Pertanto, affinché il progetto possa iniziare immediatamente, gli ingegneri di STMicroelectronics hanno scritto diverse varianti di file di avvio per molti degli ambienti di sviluppo più diffusi. La maggior parte delle famiglie STM32 ha un file. La famiglia STM32F1 dispone di diversi file di avvio:

  • startup_stm32f10x_cl.s - per microcontrollori STM32F105/107
  • startup_stm32f10x_xl.s - per microcontrollori STM32F101/STM32F103 768kb o più
  • startup_stm32f10x_hd.s - per microcontrollori STM32F101/STM32F103 con memoria Flash 256-512 kb
  • startup_stm32f10x_md.s - per microcontrollori STM32F101/STM32F102/STM32F103 con memoria Flash 64-128 kb
  • startup_stm32f10x_ld.s - per microcontrollori STM32F101/STM32F102/STM32F103 con memoria Flash inferiore a 64kb
  • startup_stm32f10x_hd_vl.s per microcontrollori STM32F100 con memoria Flash 256-512 kb
  • startup_stm32f10x_md_vl.s per microcontrollori STM32F100 con memoria Flash 64-128 kb
  • startup_stm32f10x_ld_vl.s per microcontrollori STM32F100 con memoria Flash 32kb o inferiore

Quindi, a seconda della famiglia, della sottofamiglia e dell'ambiente di sviluppo, aggiungi il file di avvio al progetto:

È qui che finisce il microcontrollore all'avvio del programma. L'interrupt chiama in sequenza la funzione SystemInit() e quindi __iar_program_start. La seconda funzione reimposta o scrive i valori predefiniti delle variabili globali, dopodiché passa al programma utente main(). La funzione SystemInit() imposta l'orologio del microcontrollore. È lei che risponde alle domande:

  • Devo passare al cristallo esterno (HSE)?
  • Come moltiplicare la frequenza da HSI/HSE?
  • È necessario collegare la coda di download dei comandi?
  • Quale ritardo è necessario durante il caricamento di un comando (a causa della bassa velocità della memoria Flash)
  • Come suddividere la timbratura dei bus periferici?
  • Il codice deve essere inserito nella RAM esterna?

La funzione SystemInit() può essere scritta manualmente nel progetto. Se emetti questa funzione come vuota, il controller funzionerà su un generatore RC interno con una frequenza di circa 8 MHz (a seconda del tipo di famiglia). Opzione 2 - collegare il file system_stm32f10x.c al progetto (o simile nel nome a seconda del tipo di famiglia utilizzata), che si trova nella libreria lungo il percorso: Librerie\CMSIS\CM3\DeviceSupport\ST\STM32F10x. Questo file contiene la funzione SystemInit(). Prestare attenzione alla frequenza del cristallo HSE_VALUE esterno. Questo parametro è impostato nel file di intestazione stm32f10x.h. Il valore standard è 8 e 25 MHz, a seconda della famiglia STM32. Il compito principale della funzione SystemInit() è commutare l'orologio su un quarzo esterno e moltiplicare questa frequenza in un certo modo. Cosa succede se il valore di HSE_VALUE è 8MHz, il core dovrebbe avere un clock a 72MHz e infatti la scheda ha un quarzo a 16MHz? Come risultato di tali azioni errate, il core riceverà un clock di 144 MHz, che potrebbe essere al di là del funzionamento garantito del sistema su STM32. Quelli. quando si include il file system_stm32f10x.c, sarà necessario specificare il valore HSE_VALUE. Tutto ciò significa che i file system_stm32f10x.c, system_stm32f10x.h e stm32f10x.h (o simili nel nome per altre famiglie) devono essere individuali per ogni progetto. E

Gli ingegneri di STMicroelectronics hanno creato lo strumento di configurazione dell'orologio, che consente di configurare correttamente l'orologio di sistema. Questo è un file Excel che genera un file system_stm32xxx.c (simile nel nome a una determinata famiglia di famiglie) dopo aver impostato i parametri di input e output del sistema. Considera il suo lavoro sull'esempio della famiglia STM32F4.

Opzioni: oscillatore RC interno, oscillatore RC interno con moltiplicazione di frequenza o cristallo esterno con moltiplicazione di frequenza. Dopo aver selezionato la sorgente di clock, inseriamo i parametri della configurazione di sistema desiderata, come la frequenza di ingresso (quando si utilizza il quarzo esterno), la frequenza di core clock, i divisori di frequenza di clock dei bus periferici, il funzionamento del buffer di recupero delle istruzioni, e altri. Facendo clic sul pulsante "Genera", otteniamo una finestra


La connessione del file system_stm32f4xx.c e dei suoi analoghi richiederà la connessione di un altro file della libreria standard della periferica. Per controllare l'orologio, c'è un intero set di funzioni che vengono chiamate dal file system_stm32xxxxxx.c. Queste funzioni si trovano nel file stm32f10x_rcc.c e nella relativa intestazione. Di conseguenza, quando si collega il file system_stm32xxxxxx.c al progetto, è necessario includere stm32f10x_rcc.c, altrimenti il ​​linker dell'ambiente segnalerà che non esiste una descrizione della funzione con il nome RCC_xxxxxxx. Il file specificato si trova nella libreria periferica lungo il percorso: Libraries\STM32F10x_StdPeriph_Driver\src e la relativa intestazione \Libraries\STM32F10x_StdPeriph_Driver\inc.

I file di intestazione del driver periferico sono collegati nel file stm32f10x_conf.h, a cui fa riferimento stm32f10x.h. Il file stm32f10x_conf.h è semplicemente un insieme di file di intestazione del driver per specifiche periferiche del controller da includere nel progetto. Inizialmente, tutte le intestazioni "#include" sono contrassegnate come commenti. Il collegamento del file di intestazione della periferia consiste nel rimuovere il commento dal nome del file corrispondente. Nel nostro caso, questa è la riga #include "stm32f10x_rcc.h". Ovviamente, il file stm32f10x_conf.h è individuale per ogni progetto, perché progetti diversi utilizzano periferiche diverse.

E l'ultimo. È necessario specificare diverse direttive per il preprocessore del compilatore e percorsi per i file di intestazione.



I percorsi dei file header possono essere diversi, a seconda della posizione della libreria periferica relativa alla cartella del progetto, ma la presenza di “USE_STDPERIPH_DRIVER” è obbligatoria quando si collegano i driver delle periferiche della libreria standard.

Quindi, abbiamo collegato la libreria standard al progetto. Inoltre, abbiamo collegato uno dei driver periferici standard al progetto che controlla l'orologio di sistema.

Abbiamo imparato come appare il dispositivo della libreria dall'interno, ora alcune parole su come appare dall'esterno.



Pertanto, l'inclusione del file di intestazione stm32f10x.h in un'applicazione implica l'inclusione di altri file di intestazione e file di codice. Alcuni di quelli mostrati nella figura sono descritti sopra. Qualche parola sul resto. I file STM32F10x_PPP.x sono file di driver periferici. Un esempio di connessione di un tale file è mostrato sopra, questo è RCC - la periferia del controllo dell'orologio di sistema. Se vogliamo collegare i driver di altre periferiche, allora il nome dei file collegati si ottiene sostituendo “PPP” con il nome della periferica, ad esempio ADC - STM32F10x_ADC.s, o porte I/O STM32F10x_GPIO.s, oppure DAC - STM32F10x_DAC.s. In generale, è intuitivamente chiaro quale file deve essere collegato quando si collega una determinata periferica. I file "misc.c", "misc.h" sono generalmente lo stesso STM32F10x_PPP.x, controllano solo il kernel. Ad esempio, impostare i vettori di interrupt, che sono integrati nel kernel, o gestire il timer SysTick, che fa parte del kernel. I file xxxxxxx_it.c descrivono i vettori NMI del controller. Possono essere integrati con vettori di interrupt periferici. Il file core_m3.h descrive il core CortexM3. Questo core è standardizzato e può essere trovato in microcontrollori di altri produttori. Per l'universalizzazione multipiattaforma, STMicroelectronics ha lavorato per creare una libreria core CortexM separata, dopodiché ARM l'ha standardizzata e distribuita ad altri produttori di microcontrollori. Quindi il passaggio a STM32 dai controller di altri produttori con un core CortexM sarà un po' più semplice.

Quindi, possiamo collegare la libreria di periferiche standard a qualsiasi famiglia STM32. Chi ha imparato a farlo aspetta un premio: una semplicissima programmazione di microcontrollori. La libreria, oltre ai driver sotto forma di file sorgente, contiene molti esempi di utilizzo delle periferiche. Ad esempio, considera la creazione di un progetto che coinvolga le uscite di confronto del timer. Con l'approccio tradizionale, studieremo attentamente la descrizione dei registri di questa periferica. Ma ora possiamo studiare il testo del programma in corso. Entriamo nella cartella degli esempi di periferiche standard, che si trova lungo il percorso ProjectSTM32F10x_StdPeriph_Examples. Di seguito sono riportate cartelle di esempi con il nome delle periferiche utilizzate. Andiamo nella cartella "TIM". I timer in STM32 hanno molte funzioni e impostazioni, quindi è impossibile dimostrare le capacità del controller con un esempio. Pertanto, all'interno della directory specificata, ci sono molti esempi di utilizzo dei timer. Siamo interessati alla generazione di un segnale PWM da un timer. Andiamo nella cartella "7PWM_Output". All'interno c'è una descrizione del programma in inglese e una serie di file:

main.c stm32f10x_conf.h stm32f10x_it.h stm32f10x_it.c system_stm32f10x.c

Se il progetto non ha interruzioni, il contenuto si trova interamente nel file main.c. Copia questi file nella directory del progetto. Dopo aver compilato il progetto, otterremo un programma per l'STM32, che configurerà il timer e le porte I/O per generare 7 segnali PWM dal timer 1. Successivamente, possiamo adattare il codice già scritto al nostro compito. Ad esempio, ridurre il numero di segnali PWM, modificare il duty cycle, la direzione di conteggio, ecc. Le funzioni ei relativi parametri sono ben descritti nel file stm32f10x_stdperiph_lib_um.chm. I nomi delle funzioni ei loro parametri sono facilmente associabili al loro scopo per chi conosce un po' di inglese. Per chiarezza, ecco una parte del codice dell'esempio preso:

/* Configurazione della base dei tempi */ TIM_TimeBaseStructure.TIM_Prescaler = 0; // nessun contatore impulsi prescaler (registro a 16 bit) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // verso l'alto TIM_TimeBaseStructure TIM_Period = TimerPeriod; // contare fino al valore di TimerPeriod (costante nel programma) TIM_TimeBaseStructure.TIM_ClockDivision = 0; // nessuna predivisione dei contatori TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // contatore di overflow per la generazione di eventi (non utilizzato nel programma) TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // immissione dei valori TimeBaseStructure nei registri del timer 1 (l'immissione dei dati in questa // variabile è superiore) /* Canale 1, 2, 3 e 4 Configurazione in modalità PWM */ // configurazione delle uscite PWM TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2 ; // Modalità operativa PWM2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // abilita l'uscita dei segnali temporizzati PWM TIM_OCInitStructure.TIM_OutputNSate = TIM_OutputNSate_Enable; // abilita l'uscita timer PWM complementare TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; // larghezza dell'impulso Channel1Pulse è una costante nel programma TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // impostazione della polarità di uscita TIM_OCInitStructure TIM_OCNPolarità = TIM_OCNPolarità_High; // impostazione della polarità dell'uscita complementare TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; // imposta lo stato di uscita PWM sicuro TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; // imposta lo stato sicuro dell'uscita PWM complementare TIM_OC1Init(TIM1, &TIM_OCInitStructure); // inserire i valori della variabile TIM_OCInitStructure nei registri PWM del canale 1 // di timer1 TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; // modifica l'ampiezza dell'impulso nella variabile OCInitStructure e inseriscila in TIM_OC2Init(TIM1, &TIM_OCInitStructure); // timer1 canale 2 registri PWM TIM_OCInitStructure.TIM_Pulse = Channel3Pulse; // modifica l'ampiezza dell'impulso nella variabile OCInitStructure e inseriscila in TIM_OC3Init(TIM1, &TIM_OCInitStructure); // timer1 canale 3 registri PWM TIM_OCInitStructure.TIM_Pulse = Channel4Pulse; // modifica l'ampiezza dell'impulso nella variabile OCInitStructure e inseriscila in TIM_OC4Init(TIM1, &TIM_OCInitStructure); // timer1 canale 4 registri PWM /* abilitazione contatore TIM1 */ TIM_Cmd(TIM1, ENABLE); // avvia timer1 /* Abilita uscita principale TIM1 */ TIM_CtrlPWMOutputs(TIM1, ENABLE); // abilita il timer 1 per confrontare le uscite

Sul lato destro, l'autore ha lasciato un commento in russo per ogni riga del programma. Se apriamo lo stesso esempio nella descrizione delle funzioni delle librerie stm32f10x_stdperiph_lib_um.chm, vedremo che tutti i parametri di funzione utilizzati hanno un link alla propria descrizione, dove verranno indicati i loro possibili valori. Le funzioni stesse hanno anche un collegamento alla propria descrizione e codice sorgente. Questo è molto utile perché sapendo cosa fa la funzione, possiamo tracciare come lo fa, quali bit dei registri periferici e come influisce. Questa è, in primo luogo, un'altra fonte di informazioni per la padronanza del controller, basata sull'uso pratico del controller. Quelli. prima risolvi il problema tecnico, quindi studi la soluzione stessa. In secondo luogo, questo è un campo per l'ottimizzazione del programma per coloro che non sono soddisfatti della libreria in termini di velocità e dimensione del codice.



Ho fatto notare che la libreria standard è collegata al sistema. Infatti, CMSIS è collegato - un sistema di rappresentazione strutturale generalizzato per MK, così come SPL - una libreria periferica standard. Consideriamo ciascuno di essi:

CMSIS
È un insieme di file di intestazione e un piccolo insieme di codice per unificare e strutturare il lavoro con il core e le periferiche di MK. Infatti, senza questi file è impossibile lavorare normalmente con MK. Puoi ottenere la libreria sulla pagina per MK.
Questa libreria, secondo la descrizione, è stata creata per unificare le interfacce quando si lavora con qualsiasi MK della famiglia Cortex. Tuttavia, in realtà, si scopre che questo è vero solo per un produttore, ad es. passando al microcontrollore di un'altra azienda, sei costretto a studiarne la periferia quasi da zero.
Sebbene quei file che si riferiscono al core del processore MK siano identici per tutti i produttori (se non altro perché hanno un modello di core del processore, fornito sotto forma di blocchi IP da ARM).
Pertanto, lavorare con parti del kernel come registri, istruzioni, interruzioni e blocchi del coprocessore è standard per tutti.
Per quanto riguarda le periferiche, STM32 e STM8 (all'improvviso) sono quasi simili, e questo vale in parte anche per altri MK rilasciati da ST. Nella parte pratica mostrerò quanto sia facile usare CMSIS. Tuttavia, le difficoltà nell'utilizzarlo sono associate alla riluttanza delle persone a leggere la documentazione e comprendere il dispositivo MK.

SPL
Libreria periferica standard - libreria periferica standard. Come suggerisce il nome, lo scopo di questa libreria è creare un'astrazione per la periferia MK. La libreria è composta da file di intestazione in cui vengono dichiarate costanti comprensibili dall'uomo per la configurazione e l'utilizzo delle periferiche MK, nonché file di codice sorgente assemblati nella libreria stessa per le operazioni con le periferiche.
SPL è un'astrazione su CMSIS, che presenta all'utente un'interfaccia comune per tutti gli MK, non solo di un produttore, ma in generale per tutti gli MK con un core del processore Cortex-Mxx.
Si ritiene che sia più conveniente per i principianti, perché. permette di non pensare a come funzionano le periferiche, tuttavia la qualità del codice, l'universalità dell'approccio e la rigidità delle interfacce impongono allo sviluppatore alcune restrizioni.
Inoltre, la funzionalità della libreria non sempre consente di implementare con precisione la configurazione di alcuni componenti come USART (Universal Synchronous-Asynchronous Serial Port) in determinate condizioni. Nella parte pratica, descriverò anche come lavorare con questa parte della libreria.

Ciao. Come ricorderete, nell'ultimo articolo abbiamo configurato il pacchetto software per funzionare con i microcontrollori STM32 e compilato il primo programma. In questo post conosceremo l'architettura di questa scheda, il microcontrollore e le librerie disponibili per il lavoro.

Di seguito è riportato un disegno della tavola Scoperta STM32F3 , dove: 1 — Sensore MEMS. Giroscopio digitale a 3 assi L3GD20. 2 - Sistema MEMS in valigetta, contenente un accelerometro lineare digitale a 3 assi e un sensore geomagnetico digitale a 3 assi LSM303DLHC. 4 - LD1 (PWR) - alimentazione 3,3V. 5 - LD2 - LED rosso/verde. Il valore predefinito è rosso. Il verde indica la comunicazione tra ST-LINK/v2 (o V2-B) e il PC. Ho l'indicazione della porta personalizzata ST-LINK/v2-B e USB. 6. -LD3/10 (rosso), LD4/9 (blu), LD5/8 (arancione) e LD6/7 (verde). Nell'ultima voce, io e te abbiamo fatto lampeggiare il LED LD4. 7. - Due pulsanti: USER e RESET. 8. - UTENTE USB con connettore Mini-B.

9 - Debug/programmatore USB ST-LINK/V2. uno 0. - Microcontrollore STM32F303VCT6. 11. - Generatore esterno di alta frequenza 8 MHz. 12. - Dovrebbe esserci un generatore di bassa frequenza qui, purtroppo non saldato. 13. - SWD - interfaccia. 14. - I ponticelli per la selezione della programmazione dei controlli esterni o interni, nel primo caso, devono essere rimossi. 15 - Jumper JP3 - jumper, predisposto per collegare un amperometro per misurare i consumi del controllore. È chiaro che se viene rimosso, il nostro ciottolo non si avvia. 16. - STM32F103C8T6 ha una scheda di debug. 17. - LD3985M33R Regolatore con bassa caduta di tensione e livello di rumore, 150mA, 3,3V.

Ora diamo un'occhiata più da vicino all'architettura del microcontrollore STM32F303VCT6. Le sue caratteristiche tecniche: pacchetto LQFP-100, core ARM Cortex-M4, frequenza core massima 72 MHz, dimensione memoria programma 256 KB, tipo memoria programma FLASH, RAM SRAM 40 KB, RAM 8 KB, numero di ingressi/uscite 87, interfacce ( CAN, I²C, IrDA, LIN, SPI, UART/USART, USB), periferiche (DMA, I2S, POR, PWM, WDT), ADC/DAC 4*12 bit/2*12 bit, tensione di alimentazione 2...3,6 V , temperatura di lavoro -40 ...... +85 C. Nella figura sotto, la piedinatura, dove vediamo 87 porte I/O, di cui 45 Normal I/O (TC, TTa), 42 5-volt tolleranti I / Os (FT, FTf) - compatibile con 5 V. (sulla scheda a destra pin 5 V, a sinistra 3,3 V). Ciascuna linea di I/O digitale può funzionare come una linea di I/O comune.
destinazione o funzione alternativa. Man mano che i progetti avanzano, conosceremo costantemente la periferia.

Considera lo schema a blocchi di seguito. Il cuore è un core ARM Cortex-M4 a 32 bit fino a 72 MHz. Ha un'unità in virgola mobile FPU integrata e un'unità di protezione della memoria MPU, celle macro di traccia integrate - Embedded Trace Macrocell (ETM), che possono essere utilizzate per monitorare il processo di esecuzione del programma principale all'interno del microcontrollore. Sono in grado di emettere queste osservazioni continuamente attraverso i contatti ETM finché il dispositivo è in funzione. NVIC (Nested vectored interrupt controller) - modulo di controllo degli interrupt. TPIU (Trace Port Interface Unit). Contiene memoria FLASH: 256 KB, SRAM 40 KB, RAM 8 KB. Tra il core e la memoria c'è una matrice Bus (Matrice Bus), che consente di collegare direttamente i dispositivi. Qui vediamo anche due tipi di bus matrix AHB e APB, dove il primo è più produttivo e viene utilizzato per collegare componenti interni ad alta velocità, e l'ultimo è per periferiche (dispositivi I/O). Il controller dispone di 4 ADC (ADC) a 12 bit (5 Mbps) e un sensore di temperatura, 7 comparatori (GP Comparator1 ... 7), 4 amplificatori operazionali programmabili (OpAmp1 ... 4) (PGA (Programmable Gain Array)) , 2 DAC (DAC) canali a 12 bit, RTC (orologio in tempo reale), due timer watchdog - indipendenti e finestrati (WinWatchdog e Ind. WDG32K), 17 timer generici e multifunzionali.

In termini generali, abbiamo considerato l'architettura del controller. Consideriamo ora le librerie software disponibili. Dopo la revisione, si possono distinguere: CMSIS, SPL e HAL. Considera ciascuna applicazione in un semplice esempio di lampeggiamento di un LED.

1). CMSIS(Cortex Microcontroller Software Interface Standard) è la libreria standard per Cortex®-M. Fornisce il supporto del dispositivo e semplifica le interfacce di programmazione. CMSIS fornisce interfacce coerenti e semplici al kernel, alle sue periferiche e ai sistemi operativi in ​​tempo reale. Il suo utilizzo è un modo professionale di scrivere programmi, perché comporta la scrittura diretta dei registri e, di conseguenza, è necessaria una lettura e uno studio costanti delle schede tecniche. Indipendente dal produttore di hardware.
CMSIS include i seguenti componenti:
- CMSIS-CORE: avvio sistema coerente e accesso periferico (avvio sistema costante e accesso periferico);
- CMSIS-RTOS: esecuzione deterministica di software in tempo reale (esecuzione deterministica di software in tempo reale);
- CMSIS-DSP: implementazione rapida dell'elaborazione del segnale digitale (implementazione rapida dell'elaborazione del segnale digitale);
- CMSIS-Driver: Interfacce periferiche generiche per middleware e codice applicativo (Interfacce periferiche generali per middleware e codice applicativo);
- CMSIS-Pack: Facile accesso ai componenti software riutilizzabili (Facile accesso ai componenti software riutilizzabili);
- CMSIS-SVD: Visualizzazione coerente del dispositivo e delle periferiche (Visualizzazione coerente del dispositivo e delle periferiche);
- CMSIS-DAP: connettività a hardware di valutazione a basso costo. Software di debug.

Ad esempio, scriviamo un programma: lampeggia un LED. Per fare ciò, abbiamo bisogno della documentazione che descriva i registri. Nel mio caso RM0316 Manuale di riferimento STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xE MCU avanzati basati su ARM®, nonché una descrizione della parte specifica di cui è responsabile DS9118: MCU+FPU Cortex®-M4 32b basato su ARM®, fino a 256KB Flash+ 48KB SRAM, 4 ADC, 2 canali DAC, 7 comp, 4 PGA, timer, 2.0-3.6 V. Per cominciare, cronometraremo la porta nel programma, perché. Per impostazione predefinita, tutto è disabilitato, il che si traduce in un consumo energetico ridotto. Apri il Manuale di riferimento e guarda la sezione Reset e controllo dell'orologio, quindi la mappa del registro RCC e guarda quale registro è responsabile dell'abilitazione di IOPEEN

Passiamo alla descrizione della timbratura della periferia di questo registro Registro di abilitazione orologio periferico AHB (RCC_AHBENR), dove vediamo che questa porta è sotto il 21° bit. Abilitalo RCC->AHBENR|=(1<<21) . Далее сконфигурируем регистры GPIO. Нас интересует три: GPIOE_MODER и GPIOx_ODR . C помощью них повторим программу с предыдущей статьи, затактируем PE8. Первый отвечает за конфигурацию входа выхода, выбираем 01: General purpose output mode. GPIOE->MODERNO|=0×10000 . Il secondo per l'inserimento di un livello basso/alto sulla gamba. Di seguito il programma:

#include "stm32f3xx.h " // File di intestazione del microcontrollore
int i senza segno;
ritardo nullo()(
per (i=0;i<500000;i++);
}
int principale(vuoto)(
RCC->AHBENR|=(1<<21);
GPIOE->MODER|=0×10000;
mentre (1)(
ritardo();
GPIOE->ODR|=0×100;
ritardo();
GPIOE->ODR&=~(0×100);
} }

2). SPL(Libreria di periferiche standard)- questa libreria è progettata per combinare tutti i processori di ST Electronics. Progettato per rendere il codice più portabile e rivolto principalmente allo sviluppatore alle prime armi. ST ha lavorato su una sostituzione SPL chiamata "low layer" compatibile con HAL. I driver Low Layer (LL) sono progettati per fornire uno strato orientato agli esperti quasi leggero che è più vicino all'hardware rispetto a HAL. Oltre a HAL, sono disponibili anche API LL. Un esempio dello stesso programma in SPL.

#includere
#includere
#includere
#definisci LED GPIO_Pin_8
int main() (
lungo io;
GPIO_InitTypeDef gpio;
// Il LED blu è collegato alla porta E, pin 8 (bus AHB)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ABILITA);
// Configura la porta E (LED)
GPIO_StructInit(&gpio); //dichiarazione e inizializzazione della variabile della struttura dati
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_Pin = LED;
GPIO_Init(GPIOE, &gpio);
// LED lampeggianti
mentre (1) (
// Su
GPIO_SetBits(GPIOE, LED);
per (i = 0; io< 500000; i++);
// Tutto spento
GPIO_ResetBits(GPIOE, LED);
per (i = 0; io< 500000; i++);
} }

Ciascuna funzione è descritta nella documentazione tecnica UM1581 Manuale utente Descrizione della libreria di periferiche standard STM32F30xx/31xx. Qui includiamo tre file di intestazione che contengono i dati, le strutture, le funzioni di controllo di ripristino e sincronizzazione necessarie, nonché per la configurazione delle porte I/O.

3). HAL- (Livello di accesso hardware, livello di astrazione hardware)- Un'altra libreria di sviluppo condivisa. Con cui è uscito il programma CubeMX per la configurazione che abbiamo utilizzato nell'ultimo articolo. Nello stesso posto, abbiamo scritto un programma per far lampeggiare un LED usando questa libreria. Come puoi vedere nella figura seguente, il cubo genera driver HAL e CMSIS. Bene, descriviamo i principali file utilizzati:
- system_stm32f3x.ce system_stm32f3x.h- fornire insiemi minimi di funzioni per la configurazione del sistema di timbratura;
- core_cm4.h - fornisce l'accesso ai registri del core e della sua periferia;
- stm32f3x.h - file di intestazione del microcontrollore;
- startup_system32f3x.s - codice di avvio, contiene una tabella di vettori di interrupt, ecc.

#include "main.h"
#include "stm32f3xx_hal.h"
void SystemClock_Config(void); /*Dichiara le funzioni di configurazione dell'orologio*/
statico vuoto MX_GPIO_Init(void); /*Inizializzazione I/O*/
int principale(vuoto)(
/*Reset di tutte le periferiche, Inizializza l'interfaccia Flash e Systick.*/
HAL_Init();
/* Configura l'orologio di sistema */
SystemClock_Config();
/* Inizializza tutte le periferiche configurate */
MX_GPIO_Init();
mentre (1) (
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8); //Cambia lo stato della gamba
HAL_Ritardo(100); )
}
void SystemClock_Config(void){
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLSate = RCC_PLL_NONE;
se (HAL_RCC_OscConfig (&RCC_OscInitStruct) != HAL_OK){

}
/**Inizializza gli orologi dei bus CPU, AHB e APB */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
se (HAL_RCC_ClockConfig (&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK){
_Gestione errori (__FILE__, __LINE__);
}
/**Configura il tempo di interruzione Systick*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configura il Systick */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* Configurazione dell'interrupt SysTick_IRQn */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/** Configura i pin come uscita ingresso analogico EVENT_OUT EXTI */
statico vuoto MX_GPIO_Init(vuoto){
GPIO_InitTypeDef GPIO_InitStruct;
/* Abilitazione orologio porte GPIO */
__HAL_RCC_GPIOE_CLK_ENABLE();
/*Configura il livello di uscita del pin GPIO */
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configura pin GPIO: PE8 PE9 PE10 PE11 PE12 PE13 PE14 PE15 */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed ​​= GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
void _Error_Handler(char * file, int line){
mentre (1) (
} }
#ifdef USE_FULL_ASSERT

Void assert_failed (file uint8_t*, riga uint32_t){
}
#finisci se
Qui, così come nell'esempio precedente, possiamo visualizzare, ad esempio, la descrizione di ciascuna funzione nella documentazione UM1786 Manuale utente Descrizione dei driver STM32F3 HAL e low-layer.

Possiamo riassumere che la prima opzione, utilizzando CMSIS, è meno ingombrante. Ogni biblioteca dispone di documentazione. Nei progetti successivi utilizzeremo HAL e CMSIS, utilizzando il programma di configurazione STCube e, se possibile, utilizzeremo i registri direttamente, senza wrapper software. È qui che ci fermeremo oggi. Nel prossimo articolo considereremo i principi di base per costruire una casa intelligente. Per ora è tutto.

Articoli correlati in alto