STM32F407(STM32F4-DISCOVERY) - Approccio non standard - Libreria standard parte 1. Configurazione di STM32F10x SPL utilizzando le definizioni macro

Fino a questo punto abbiamo utilizzato la libreria standard del kernel: CMSIS. Per configurare una porta nella modalità operativa desiderata, abbiamo dovuto rivolgerci a per trovare il registro responsabile di una determinata funzione e anche cercare in un grande documento altre informazioni relative a questo processo. Le cose diventeranno ancora più dolorose e di routine quando inizieremo a lavorare con un timer o un ADC. Il numero di registri è molto maggiore di quello delle porte I/O. Impostazione manuale richiede molto tempo e aumenta la possibilità di commettere un errore. Pertanto, molte persone preferiscono lavorare con la libreria periferica standard: StdPeriph. Cosa dà? È semplice: il livello di astrazione aumenta, non è necessario entrare nella documentazione e pensare per la maggior parte ai registri. In questa libreria tutte le modalità operative e i parametri della periferia MK sono descritti sotto forma di strutture. Ora, per configurare un dispositivo periferico, è sufficiente richiamare la funzione di inizializzazione del dispositivo con una struttura piena.

Di seguito un'immagine con la rappresentazione schematica dei livelli di astrazione.

Abbiamo lavorato con il CMSIS (che è il "più vicino" al core) per mostrare come funziona il microcontrollore. Il passo successivo è la libreria standard, che impareremo a utilizzare ora. Poi arrivano i driver di dispositivo. Sono intesi come file *.c \ *.h che forniscono una comoda interfaccia software per il controllo di qualsiasi dispositivo. Ad esempio, in questo corso ti forniremo i driver per il chip max7219 e il modulo WiFi esp8266.

Un progetto standard includerà i seguenti file:


Innanzitutto, ovviamente, questi sono i file CMSIS che consentono alla libreria standard di funzionare con il kernel, ne abbiamo già parlato. In secondo luogo, i file della libreria standard. E in terzo luogo, i file dell'utente.

I file della libreria li trovate nella pagina dedicata al target MK (per noi è stm32f10x4), nella sezione Risorse di progettazione(nell'IDE CooCox, questi file vengono scaricati dal repository dell'ambiente di sviluppo). Ogni periferica corrisponde a due file: header (*.h) e codice sorgente(*.C). Descrizione dettagliata può essere trovato nel file di supporto, che si trova nell'archivio della biblioteca sul sito web.

  • stm32f10x_conf.h - file di configurazione della libreria. L'utente può connettere o disconnettere i moduli.
  • stm32f10x_ppp.h - file di intestazione della periferica. Al posto di ppp possono esserci gpio o adc.
  • stm32f10x_ppp.c - driver del dispositivo periferico scritto in linguaggio C.
  • stm32f10x_it.h - file di intestazione che include tutti i possibili gestori di interruzioni (i loro prototipi).
  • stm32f10x_it.c è un file di codice sorgente modello contenente routine di servizio di interruzione (ISR) per situazioni di eccezione in Cortex M3. L'utente può aggiungere i propri ISR ​​per le periferiche utilizzate.

La libreria standard e le periferiche hanno una convenzione nella denominazione delle funzioni e nella notazione.

  • PPP è l'acronimo di periferiche, come ADC.
  • File di sistema, intestazione e codice sorgente: iniziano con stm32f10x_.
  • Le costanti utilizzate in un file sono definite in quel file. Le costanti utilizzate in più di un file sono definite nei file di intestazione. Tutte le costanti nella libreria periferica sono spesso scritte in MAIUSCOLO.
  • I registri sono trattati come costanti e sono anche chiamati lettere MAIUSCOLE.
  • I nomi delle funzioni specifiche della periferica includono un acronimo, come USART_SendData() .
  • Per configurare ciascuna periferica viene utilizzata la struttura PPP_InitTypeDef, che viene passata alla funzione PPP_Init().
  • Per deinizializzare (impostare il valore su predefinito), è possibile utilizzare la funzione PPP_DeInit().
  • La funzione che permette di abilitare o disabilitare le periferiche si chiama PPP_Cmd().
  • La funzione di abilitazione/disabilitazione dell'interrupt si chiama PPP_ITConfig.

CON lista completa puoi nuovamente consultare il file di supporto della libreria. Ora riscriviamo il LED lampeggiante utilizzando la libreria periferica standard!

Prima di iniziare a lavorare, diamo un'occhiata al file stm32f10x.h e troviamo la riga:

#definisce USE_STDPERIPH_DRIVER

Se configuri il progetto da zero utilizzando i file di libreria dall'archivio scaricato, dovrai rimuovere il commento da questa riga. Ti consentirà di utilizzare la libreria standard. Questa definizione (macro) comanderà al preprocessore di includere il file stm32f10x_conf.h:

#ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif

Questo file contiene moduli. Se ne hai bisogno solo di specifici, disabilita il resto, questo farà risparmiare tempo durante la compilazione. Noi, come avrai intuito, abbiamo bisogno di moduli RTC e GPIO (ma in futuro avremo bisogno anche di _bkp.h, _flash, _pwr.h, _rtc.h, _spi.h, _tim.h, _usart.h):

#include "stm32f10x_flash.h" // per init_pll() #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h"

Come l'ultima volta, prima devi abilitare il clock della porta B. Questo viene fatto dalla funzione dichiarata in stm32f10x_rcc.h:

Void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

L'enumerazione FunctionalState è definita in stm32f10x.h:

Typedef enum (DISABLE = 0, ENABLE = !DISABLE) FunctionalState;

Dichiariamo una struttura per impostare la nostra gamba (puoi trovarla nel file stm32f10x_gpio.h):

GPIO_InitTypeDef LED;

Ora dobbiamo compilarlo. Diamo un'occhiata al contenuto di questa struttura:

Struttura Typedef ( uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; ) GPIO_InitTypeDef;

Tutte le enumerazioni e le costanti necessarie possono essere trovate nello stesso file. Quindi la funzione init_leds() riscritta assumerà la forma seguente:

Void led_init() ( // Abilita il clock RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Dichiara la struttura e riempila GPIO_InitTypeDef LED; LED.GPIO_Pin = GPIO_Pin_0; LED.GPIO_Speed ​​= GPIO_Speed_2MHz; LED.GPIO_Mode = GPIO_Mode_ Out_PP; // Inizializza la porta GPIO_Init( GPIOB, &LED); )

Riscriviamo la funzione main():

Int main(void) ( led_init(); while (1) ( GPIO_SetBits(GPIOB, GPIO_Pin_0); ritardo(10000000); GPIO_ResetBits(GPIOB, GPIO_Pin_0); ritardo(10000000); ) )

La cosa principale è avere un'idea dell'ordine di inizializzazione: accendere l'orologio periferico, dichiarare la struttura, riempire la struttura, chiamare il metodo di inizializzazione. Altri dispositivi periferici sono solitamente configurati in modo simile.

In questa pubblicazione cercherò di concentrarmi sui punti principali per iniziare rapidamente con i microcontrollori STM32F10x basati sulla libreria di periferiche standard dell'azienda produttrice STMicroelectronics.

L'articolo utilizzerà Eclipse CDT come ambiente di sviluppo. Poiché il focus principale sarà sul codice del programma, puoi tranquillamente eseguire tutte le manipolazioni in Code::Blocks.

La struttura generale del progetto per i microcontrollori ARM è descritta nel mio articolo.

Qui ti ricordo brevemente che per costruire un progetto per microcontrollori ARM (STM32F10x in particolare), avrai bisogno di uno script linker e di un file C-Startup.

Uno script linker è un file con istruzioni per inserire il codice e i dati del programma nella memoria del microcontrollore. Può comandare il caricamento del codice del programma nella memoria di programma Flash o nella memoria dati SRAM.

I microcontrollori con diverse quantità di memoria di programma e dati richiedono script di layout diversi. Possono essere ottenuti dal produttore del microcontrollore: STMicroelectronics.
Decomprimere la libreria periferica standard STM32F10x dall'archivio ARM_Toolchain/Lib/stm32f10x_stdperiph_lib.zip.
Contiene progetti di esempio per vari ambienti di sviluppo (IAR EWB, Keil uVision, Atollic True Studio, ecc.). Quello più vicino per noi è Atollic True Studio, poiché è una modifica di Eclipse.
Andare alla directory Project/StdPeriph_Template/TrueSTUDIO, lì sono presenti diverse sottodirectory, i cui nomi corrispondono ai nomi delle schede di sviluppo STM3210x-EVAL.

Scopri quale di queste schede utilizza la stessa linea di microcontrollori della tua. Copia il file stm32_flash.ld dalla directory appropriata al tuo progetto.

È anche possibile creare uno script universale in cui verrà modificata solo la quantità di memoria di programma e di dati in base al microcontrollore utilizzato.

Il codice di avvio (C-Startup) per i microcontrollori STM32 può essere scritto in C o Assembler.
Sebbene la libreria periferica standard STM32F10x (abbreviata STM32F10x SPL) sia spesso criticata per i suoi bug, è il modo più semplice per iniziare rapidamente quando si avvia la programmazione STM32.
Ma vuoi sempre che ci sia una sorta di alternativa. In effetti, ce ne sono molti, ad esempio, che programmano in linguaggio assembly :)

Questo è il percorso più difficile e inutile. Il secondo modo è utilizzare la libreria CMSIS, che fornisce la sintassi per accedere alle strutture del linguaggio C per accedere a varie periferiche del microcontrollore. Il modo più semplice e logico (secondo me) è utilizzare le librerie.

Se siete categoricamente contrari alla STM32F10x SPL, allora c'è un'altra alternativa appositamente per voi: la libreria libopencm3. In esso, la maggior parte degli esempi è concentrata sulla serie principale di microcontrollori STM32F10x, ma è solo questione di tempo prima che compaiano esempi per altre serie (STM32F2xx/4xx). Puoi sempre unirti al progetto libopencm3 e accelerare questo processo.

Anche lo standard CMSIS può essere utilizzato facoltativamente nei vostri programmi.
Puoi farne a meno dedicando tempo e impegno all'implementazione del livello HAL (Hardware Abstraction Layer) nel linguaggio di programmazione C.

In alcuni casi questo metodo potrebbe essere l’unico in modo accessibile. Ad esempio, la tua organizzazione utilizza chip personalizzati basati su core di elaborazione sviluppati da ARM e periferiche specifiche del settore.

Oppure è necessario implementare software in C per microcontrollori con core ARM9, per i quali i produttori puntano sull'utilizzo di prodotti già pronti sistemi operativi(Linux, QNX, Windows CE), pertanto i produttori non possono fornire librerie per la programmazione in linguaggio C in forma pura o in combinazione con un RTOS più leggero.

Fortunatamente, i produttori di microcontrollori basati sul core Cortex-M3 forniscono agli sviluppatori un gran numero di librerie di codici. Questo vale anche per i microcontrollori STM32.
Continuiamo la nostra considerazione della libreria STM32F10x SPL. Lo considereremo utilizzando un esempio.
Puoi aprire questo esempio o creare il tuo progetto da zero per comprendere meglio l'intero processo di ciò che sta accadendo.

Per il secondo caso elencherò i passaggi necessari:

  • Crea un nuovo progetto vuoto in Eclipse
  • Copia lo script di layout e avvia il file nel progetto
  • Crea un nuovo Makefile o copia un modello
  • Quando si utilizza il Makefile del mio esempio come modello, è necessario creare le directory src, inc, bin, obj all'interno del progetto e creare le sottodirectory Debug e Release all'interno delle directory bin e obj.
  • Copiare i file di origine e di intestazione necessari dalle librerie CMSIS e STM32F10x SPL.
  • Apporta le modifiche necessarie alla sezione delle impostazioni utente del modello Makefile, se utilizzato.
  • Creare nuovi target “Debug”, “cleanDebug”, “Release”, “cleanRelease”, “Program” nella finestra “make target” di Eclipse.
  • Avvia il target "Debug" e monitora la sua esecuzione nella finestra "Console".

Per una migliore comprensione del materiale, ho suddiviso l'articolo in più paragrafi indipendenti, ognuno dei quali descrive solo un aspetto del lavoro con la libreria STM32F10x SPL.

Configurazione di STM32F10x SPL utilizzando le definizioni macro

Per configurare la libreria vengono utilizzati macro valori predefiniti, che ora prenderemo in considerazione.
Possono essere impostati all'interno dei file header utilizzando una direttiva del preprocessore #definire oppure passare i valori delle definizioni delle macro tramite la chiave -D Compilatore GCC.
Nel mio esempio utilizzo il secondo metodo.
Nella variabile Makefile DEFINIRE contiene le macro necessarie per compilare la libreria STM32F10x SPL.
Definizione macro STM32F10X_MD specifica se il microcontrollore utilizzato appartiene alla linea Media densità.
Ciò include microcontrollori con memoria Flash da 64 a 128 kB.
La tabella seguente elenca i nomi delle macro per le diverse serie di microcontrollori:

Nome della serie Macro Descrizione
Linea Value a bassa densità STM32F10X_LD_VL con capacità di memoria Flash 16 - 32 kB
Bassa densità STM32F10X_LD
con capacità di memoria Flash 16 - 32 kB
Linea Value a media densità STM32F10X_MD_VL Memoria flash
64 - 128kB
Media densità STM32F10X_MD microcontrollori della serie STM32F101xx, STM32F102xx, STM32F103xx con memoria Flash 64 - 128 kB
Linea Value ad alta densità STM32F10X_HD_VL microcontrollori della serie STM32F100xx con volume
Flash - memoria 256 - 512kB
Alta densità STM32F10X_HD con volume
Memoria flash 256 - 512kB
Densità XL STM32F10X_XL
Memoria flash 512 - 1024 kB
Linea di connettività STM32F10X_CL

Per impostare la frequenza di clock del microcontrollore, è necessario rimuovere il commento dalla macro con il valore di frequenza di clock richiesto nel file system_stm32f10x.c.

#se definito (STM32F10X_LD_VL) || (definito STM32F10X_MD_VL) || (definito STM32F10X_HD_VL) #define SYSCLK_FREQ_24MHz 24000000 #else /* #define SYSCLK_FREQ_HSE HSE_VALUE */ /* #define SYSCLK_FREQ_24MHz 24000000 */ /* #define SYSCLK_FREQ_36MHz 3600 0000 */ / * #define SYSCLK_FREQ_48MHz 48000000 */ /* #define SYSCLK_FREQ_56MHz 56000000 * / #define SYSCLK_FREQ_72MHz 72000000 #endif

#se definito (STM32F10X_LD_VL) || (definito STM32F10X_MD_VL) || (definito STM32F10X_HD_VL)

/* #define SYSCLK_FREQ_HSE HSE_VALUE */

#define SYSCLK_FREQ_24MHz 24000000

#altro

/* #define SYSCLK_FREQ_HSE HSE_VALUE */

/* #define SYSCLK_FREQ_24MHz 24000000 */

/* #define SYSCLK_FREQ_36MHz 36000000 */

/* #define SYSCLK_FREQ_48MHz 48000000 */

/* #define SYSCLK_FREQ_56MHz 56000000 */

#define SYSCLK_FREQ_72MHz 72000000

#finisci se

Destinazione d'uso risuonatore al quarzo con una frequenza di 8 MHz per tutte le principali
serie di microcontrollori, ad eccezione della linea Connectivity, per la quale è necessario installare un risuonatore al quarzo da 25 MHz.
Se si utilizzano risonatori al quarzo con altri valori di frequenza, è necessario modificare il valore della macro HSE_VALUE nel file header stm32f10x.h e adattare di conseguenza tutte le funzioni dipendenti.
Lo scopo della macro USE_STDPERIPH_DRIVER non è difficile da indovinare: utilizzare la libreria periferica standard STM32F10x.
USE_FULL_ASSERT – utilizza la macro ASSERT per eseguire il debug del programma.

Utilizzando la macro assert_param nella libreria

Tutte le funzioni della libreria STM32F10x SPL utilizzano la macro assert_param per verificare i propri argomenti.
Questa macro controlla un'espressione che coinvolge l'argomento della funzione da testare per verificarne l'uguaglianza a zero. Se il valore dell'espressione è zero, viene chiamata la funzione di gestione degli errori dell'argomento assert_failed, altrimenti (l'espressione non è zero), il controllo dell'argomento ha esito positivo.
Devi implementare la funzione assert_failed nel tuo programma.
Visualizza il messaggio di errore, il nome del file e il numero della riga di codice che ha causato l'errore.
La macro debug_printf può essere generata tramite USART utilizzando la libreria standard new_lib o, ad esempio, la libreria del signor Chen.

#define debug_printf xprintf /* printf */ #ifdef USE_FULL_ASSERT void assert_failed(uint8_t* file, uint32_t riga) ( debug_printf("Valore parametro errato: file %s sulla riga %d\r\n", file, (int)riga) ; while (1) ( ) )/* assert_failed */ #endif/*USE_FULL_ASSERT*/

#define debug_printf xprintf /* printf */

#ifdef USE_FULL_ASSERT

void assert_failed (uint8_t * file, linea uint32_t)

debug_printf( "Valore parametri errato: file %s sulla riga %d\r\n", file , (int) riga );

mentre(1)

) /* asserzione_fallita */

#endif/*USE_FULL_ASSERT*/

La funzione assert_failed implementata nel codice viene utilizzata solo quando viene dichiarata la macro USE_FULL_ASSERT. In caso contrario, tutto il codice di debug verrà escluso dall'origine. Questa funzionalità è implementata nel file di intestazione delle impostazioni della libreria driver stm32f10x_conf.h.

#ifdef USE_FULL_ASSERT #define assert_param(expr) ((expr) ? (void)0: assert_failed((uint8_t *)__FILE__, __LINE__)) void assert_failed(uint8_t* file, uint32_t riga); #else #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */

#ifdef USE_FULL_ASSERT

#define assert_param(expr) ((expr) ? (void)0: assert_failed((uint8_t *)__FILE__, __LINE__))

void assert_failed (uint8_t*file, linea uint32_t);

#altro

#define assert_param(expr) ((void)0)

#endif /* USE_FULL_ASSERT */

Non c'è molto da spiegare qui. Diamo un'occhiata a un esempio di utilizzo di assert_param.

void set_param(uint8_t * param, uint8_t valore) ( ​​assert_param(param != NULL); *param = valore; )/*set_param*/

void set_param (uint8_t * param, valore uint8_t)

assert_param (param!= NULL) ;

*parametro = valore;

) /*set_param*/

La funzione imposta il valore del parametro tramite un puntatore passato come argomento. Se la macro USE_FULL_ASSERT non è dichiarata, allora possiamo supporre che le righe
assert_param(param != NULL) semplicemente non è nel codice, altrimenti il ​​parametro viene controllato in questa definizione.
Se il puntatore non è definito, il valore param != NULL sarà falso e verrà eseguita la funzione assert_failed, che restituirà il nome del file e il numero di riga con l'errore tramite USART, quindi si ripeterà, impedendo così che il valore venga assegnato a un indirizzo indefinito in memoria.
Non sei affatto obbligato a utilizzare la macro assert_param nel tuo codice, ma nel codice della libreria
STM32F10x SPL è utilizzato ovunque.
La funzione set_param può essere implementata con il controllo degli errori degli argomenti senza utilizzare assert_param.

#define ERRORE (-1) #define OK (0) int set_param(uint8_t * param, uint8_t valore) ( ​​int r = ERRORE; if (param == NULL) return r; *param = valore; r = OK; return r ; )/*set_param*/

#define ERRORE (-1)

#definire OK (0)

int set_param (uint8_t * param, valore uint8_t)

int r = ERRORE;

se (parametro == NULL )

ritorno r ;

*parametro = valore;

r = ok;

ritorno r ;

) /*set_param*/

File C-Startup nella libreria STM32F10x SPL

Nel codice iniziale, il microcontrollore viene inizialmente inizializzato, lo stack viene configurato, la sezione BSS viene ripristinata e viene chiamata la funzione principale main().
Il codice iniziale non ha alcuna relazione diretta con la libreria STM32F10x SPL. Tuttavia, in questo codice di avvio, prima di chiamare la funzione main() del programma, viene chiamata la funzione di inizializzazione del microcontrollore SystemInit(), che fa parte del CMSIS.
Può essere facilmente trovato nella libreria CMSIS.
Andare alla directory Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/TrueSTUDIO e copiare il file richiesto. Non resta che scoprire a quale linea appartiene il microcontrollore utilizzato nel tuo progetto.
Per fare ciò, guarda la seguente tabella:

Nome della serie Nome del file Descrizione
Linea Value a bassa densità startup_stm32f10x_ld_vl.s microcontrollori della serie STM32F100xx con volume
Memoria flash 16 - 32kB
Bassa densità startup_stm32f10x_ld.s microcontrollori serie STM32F101xx, STM32F102xx, STM32F103xx
con capacità di memoria Flash 16 - 32 kB
Linea Value a media densità startup_stm32f10x_md_vl.s microcontrollori serie STM32F100xx
Media densità startup_stm32f10x_md.s microcontrollori serie STM32F101xx, STM32F102xx, STM32F103xx
con capacità di memoria Flash 64 - 128 kB
Linea Value ad alta densità startup_stm32f10x_hd_vl.s microcontrollori serie STM32F100xx
Alta densità startup_stm32f10x_hd.s microcontrollori serie STM32F101xx, STM32F103xx
con capacità di memoria Flash 256 - 512 kB
Densità XL startup_stm32f10x_xl.s microcontrollori serie STM32F101xx, STM32F103xx
con capacità di memoria Flash 512 - 1024 kB
Linea di connettività startup_stm32f10x_cl.s microcontrollori delle serie STM32F105xx e STM32F107xx

Il file di avvio contiene i nomi dei gestori dei vettori di interruzione ed eccezione, ma è implementato solo il gestore del vettore di ripristino, all'interno del quale viene eseguita tutta l'inizializzazione iniziale prima di chiamare la funzione main().
L'implementazione di tutti gli altri gestori di eccezioni è responsabilità del programmatore dell'applicazione. Se il tuo programma non utilizza alcun gestore, non è necessario registrarli. Se si verifica un'eccezione, verrà utilizzato il gestore predefinito, ovvero il looping del codice del programma.

Composizione della libreria CMSIS

Come scritto in precedenza in questa pubblicazione, la libreria CMSIS fornisce l'accesso ai moduli periferici del microcontrollore utilizzando elementi delle strutture del linguaggio C.
L'implementazione di questa libreria è divisa in due parti. La prima parte fornisce l'accesso alla periferia del core Cortex-M3 e la seconda parte fornisce l'accesso alla periferia modello specifico microcontrollore.
Poiché lo standard CMSIS è lo stesso per tutti i microcontrollori con core Cortex-M3, l'implementazione della prima parte sarà la stessa per tutti i produttori, ma la seconda parte sarà diversa per ciascun produttore.
CMSIS include diversi file di intestazione e di origine. La prima parte comprende i file:

  • nucleo_cm3.h
  • nucleo_cm3.c

La seconda parte di CMSIS include il file C-Startup, nonché i seguenti file:

  • stm32f10x.h
  • system_stm32f10x.h
  • system_stm32f10x.c

Il file di intestazione stm32f10x.h contiene le definizioni delle macro per l'accesso ai moduli periferici dei microcontrollori stm32f10x.
I file system_stm32f10x.h e system_stm32f10x.c implementano l'inizializzazione iniziale del microcontrollore.

Composizione e configurazione della libreria STM32F10x SPL

La libreria è composta da file sorgente e file di intestazione con lo stesso nome dei moduli periferici con il prefisso stm32f10x_.
Ad esempio, l'implementazione dell'interazione con il modulo USART è contenuta nei file stm32f10x_usart.h e stm32f10x_usart.c.
Esistono convenzioni per la denominazione degli elementi della libreria e alcune regole di codifica, descritte nella documentazione.
La libreria contiene l'implementazione dei driver per i moduli del microcontrollore periferico.
I nomi degli elementi della libreria utilizzano i seguenti acronimi per i moduli periferici:

Acronimo Modulo periferico
ADC convertitore analogico-digitale
BKP registri di backup
POTERE Interfaccia CAN
CEC controllore dei consumi
CRC modulo per il calcolo del checksum
DAC convertitore digitale-analogico
DBGMCU debug del microcontrollore
DMA controllore di accesso diretto alla memoria
ESTI controller di interruzione esterno
FSMC controller di memoria esterno
VELOCE Memoria di programma flash
GPIO porte I/O per uso generale
I2C Interfaccia I2C
I2S Interfaccia I2S (audio).
IWDG timer watchdog indipendente
NVIC controller di interruzione nidificato
PWR regolatore di potenza
RCC reset e controller dell'orologio
RTC controller in tempo reale (orologio)
SDIO Interfaccia SDIO
SPI Interfaccia SPI
SysTick timer del sistema
TIM timer base o avanzato
USART seriale universale sincrono-asincrono
ricetrasmettitore
WWDG guardiano della finestra

Sulla base di questi acronimi vengono formati i nomi dei moduli software della biblioteca. Non è necessario utilizzare tutti i moduli nella libreria.
Per utilizzare solo i moduli necessari nel progetto, è necessario configurare la libreria.
Per questi scopi, ogni progetto che utilizza la libreria STM32F10x SPL deve avere un file di intestazione stm32f10x_conf.h.

#include "stm32f10x_gpio.h" //#include "stm32f10x_i2c.h" //#include "stm32f10x_iwdg.h" //#include "stm32f10x_pwr.h" #include "stm32f10x_rcc.h"

#include "stm32f10x_gpio.h"

//#include "stm32f10x_i2c.h"

//#include "stm32f10x_iwdg.h"

//#include "stm32f10x_pwr.h"

#include "stm32f10x_rcc.h"

Per abilitare il modulo richiesto, è necessario rimuovere il commento dalla direttiva #includere con i file di intestazione corrispondenti.
Il file di intestazione stm32f10x_conf.h è incluso in stm32f10x.h, quindi per utilizzare le funzioni della libreria STM32F10x SPL, devi solo includere un solo file di intestazione stm32f10x.h nel codice sorgente

// nel file stm32f10x.h #ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif

Ripeto che nel progetto devono essere definite anche le macro USE_STDPERIPH_DRIVER, USE_FULL_ASSERT ed una macro che specifichi la serie del microcontrollore utilizzato (ad esempio STM32F10X_MD per la linea Media densità).
Se utilizzi il valore di frequenza del quarzo standard e il controller funziona con una frequenza di clock massima di 72 MHz, non dovrai modificare nient'altro.
È necessario aggiungere un elenco di file di libreria da compilare al Makefile.
Per esempio:

SRC += stm32f10x_rcc.c SRC += stm32f10x_gpio.c

SRC += stm32f10x_rcc . C

SRC += stm32f10x_gpio . C

Utilizzando la libreria STM32F10x SPL. Meccanismi di funzionamento

Per iniziare a programmare utilizzando la libreria delle periferiche, il modo più semplice è guardare gli esempi forniti con la libreria. Tuttavia, per comprendere il codice di questi esempi, è necessario avere una conoscenza di base della sintassi e dell'utilizzo della libreria.
Tutti i moduli microcontrollori periferici precedentemente elencati sono inizialmente disattivati, non viene loro fornito il segnale di clock e non consumano energia elettrica.
Per utilizzare un modulo periferico è necessario prima fornirgli un segnale di clock. Il segnale dell'orologio è fornito dal modulo orologio e reset RCC.
A tal fine la Biblioteca svolge le seguenti funzioni:

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx, ABILITA); RCC_APB2PeriphClockCmd(RCC_APB2Periph_PPPx, ABILITA); RCC_APB1PeriphClockCmd(RCC_APB1Periph_PPPx, ABILITA);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_PPPx, ABILITA);

RCC_APB2PeriphClockCmd (RCC_APB2Periph_PPPx, ABILITA) ;

RCC_APB1PeriphClockCmd (RCC_APB1Periph_PPPx, ABILITA) ;

Qui PPP indica il nome del modulo con l'actonimo (ad esempio ADC o USART) e x è il numero del modulo periferico.
Prima di tutto devi scoprire a quale bus è collegato il modulo che stai utilizzando.
In totale, i microcontrollori con architettura core Cortex-M3 hanno tre bus:
bus istruzioni, bus dati e bus di sistema. Il bus delle istruzioni collega il core alla memoria del programma Flash. I bus dati e di sistema sono combinati in una matrice di bus AHB (ARM Hi-Speed ​​​​Bus), che funziona alla frequenza centrale. Tuttavia, la frequenza del bus AHB può essere ridotta installando divisori. Il bus AHB collega dispositivi ad alta velocità come il core e il modulo DMA.
I dispositivi I/O sono collegati al bus AHB tramite i bus intermedi APB1 e APB2 (ARM Peripheral Bus).
La frequenza operativa massima del bus APB2 è 72 MHz, la frequenza del bus APB1
limitato a 36 MHz.
Puoi scoprire a quale dei bus è collegato il modulo periferico che stai utilizzando dalla documentazione o cercare nel file di intestazione stm32f10x_rcc.h.
Apri questo file e cerca i valori RCC_AHBPeriph, RCC_APB1Periph e RCC_APB2Periph in sequenza.

#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001) #define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002) #define RCC_AHBPeriph_SRAM ((uint32_t)0x00000004) #define RCC_AHBPeriph_FLITF ((uint32_t) 0x00000010) #define RCC_AHBPeriph_CRC ((uint32_t)0x00000040)

#define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001)

#define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002)

#define RCC_AHBPeriph_SRAM ((uint32_t)0x00000004)

#define RCC_AHBPeriph_FLITF ((uint32_t)0x00000010)

#define RCC_AHBPeriph_CRC ((uint32_t)0x00000040)

Dai nomi delle macro determiniamo quali moduli sono collegati a quali bus. Puoi anche usare il buon senso per determinare quale pneumatico appartiene a uno dei tre. Ad esempio, il modulo USART è un dispositivo di input/output, il che significa che è collegato a uno dei bus APB. USART è un'interfaccia abbastanza a bassa velocità, quindi probabilmente è collegata al bus APB1.

#define RCC_APB1Periph_USART2 ((uint32_t)0x00020000) #define RCC_APB1Periph_USART3 ((uint32_t)0x00040000) #define RCC_APB1Periph_UART4 ((uint32_t)0x00080000) #define RCC_APB1Periph_UART5 ((uint32_t)0x00100000)

Dopo aver inviato un segnale di clock al modulo periferico, è possibile configurarne i parametri richiamando la funzione di inizializzazione:

PPP_Init(PPP, &Struttura PPP_Init);

PPP_Init (PPP, & amp; PPP_InitStructure) ;

Poiché molti parametri devono essere passati alla funzione di inizializzazione per inizializzare un modulo periferico, come argomento viene utilizzato un puntatore a una struttura. La struttura stessa con i parametri di inizializzazione deve essere creata in memoria prima di chiamare la funzione di inizializzazione; agli elementi della struttura devono essere assegnati i valori necessari:

PPP_InitTypeDef PPP_InitStructure = (val1, val2, ..., valN);/* inizializzazione della struttura quando dichiarata */

Puoi prima creare una struttura e poi assegnare i valori necessari ai suoi elementi:

PPP_InitTypeDef PPP_InitStructure; PPP_InitStructure.member1 = val1; PPP_InitStructure.member2 = val2; PPP_InitStructure.memberN = valN;

PPP_InitTypeDef PPP_InitStructure ;

PPP_InitStructure . membro1 = val1 ;

PPP_InitStructure . membro2 = val2 ;

PPP_InitStructure . membroN = valN ;

Diamo un'occhiata ad un esempio dal progetto stm32f10xQuickstart:

GPIO_InitTypeDef GPIO_InitStructure; #ifdef USE_STM32H_103 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GIOC, ABILITA); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed ​​= GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStruttura);

GPIO_InitTypeDef GPIO_InitStructure ;

#ifdef USE_STM32H_103

RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC, ABILITA) ;

GPIO_InitStructure . Pin_GPIO = Pin_GPIO_12 ;

GPIO_InitStructure . GPIO_Velocità = GPIO_Velocità_50MHz;

GPIO_InitStructure . Modalità_GPIO = Modalità_GPIO_Out_PP ;

GPIO_Init(GPIOC, e GPIO_InitStructure);

Agli elementi della struttura GPIO_InitStructure viene assegnato il valore del numero di pin, modalità e velocità della porta.
Chiamando la funzione GPIO_Init, viene inizializzata la linea 12 della porta GPIOC.
Il primo argomento della funzione GPIO_Init è un puntatore all'area di memoria della periferica GPIOC, convertito in un puntatore a una struttura GPIO_TypeDef.

// stm32f10x.h #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) #define PERIPH_BASE ((uint32_t)0x40000000) typede f struttura ( __IO uint32_t CRL; __IO uint32_t CRH ; __IO uint32_t IDR; __IO uint32_t ODR; __IO uint32_t BSRR; __IO uint32_t BRR; __IO uint32_t LCKR; ) GPIO_TypeDef;

// stm32f10x.h

#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE)

#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)

#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)

#define PERIPH_BASE ((uint32_t)0x40000000)

struttura typedef

IO uint32_t CRL;

IO uint32_tCRH;

IO uint32_t IDR;

IO uint32_tODR;

IO uint32_t BSRR ;

IO uint32_t BRR;

IO uint32_t LCKR ;

) GPIO_TypeDef ;

La struttura GPIO_InitStructure è di tipo GPIO_InitTypeDef, descritta nel file di intestazione
stm32f10x_gpio.h:

//stm32f10x_gpio.h typedef struct ( uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; )GPIO_InitTypeDef; typedef enum ( GPIO_Speed_10MHz = 1, GPIO_Speed_2MHz, GPIO_Speed_50MHz )GPIOSpeed_TypeDef; typedef enum ( GPIO_Mode_AIN = 0x0, GPIO_Mode_IN_FLOATING = 0x04, GPIO_Mode_IPD = 0x28, GPIO_Mode_IPU = 0x48, GPIO_Mode_Out_OD = 0x14, GPIO_Mode_Out_PP = 0x10, GPIO_Mode_AF_OD = 0x1C, GPIO_Mode_AF _PP = 0x18 )GPIOMode_TypeDef;

//stm32f10x_gpio.h

struttura typedef

uint16_t GPIO_Pin;

GPIOSpeed_TypeDef GPIO_Speed;

GPIOMode_TypeDef GPIO_Mode;

) GPIO_InitTypeDef ;

enumerazione typedef

GPIO_Speed_10MHz = 1,

GPIO_Velocità_2MHz,

GPIO_Velocità_50MHz

) GPIOSpeed_TypeDef ;

enumerazione typedef

(GPIO_Mode_AIN = 0x0,

GPIO_Mode_IN_FLOATING = 0x04,

GPIO_Mode_IPD = 0x28,

GPIO_Mode_IPU = 0x48,

GPIO_Mode_Out_OD = 0x14,

GPIO_Mode_Out_PP = 0x10,

GPIO_Mode_AF_OD = 0x1C,

GPIO_Mode_AF_PP = 0x18

) GPIOMode_TypeDef ;

Come puoi vedere, i tipi di dati della struttura inizializzata possono essere utilizzati come tipi personalizzati, come GPIOSpeed_TypeDef, e tipi di dati con valori specifici per la comodità di inizializzare i registri periferici, come GPIOMode_TypeDef.
Sono allocati 4 bit per la configurazione di ciascun pin GPIO.
L'immagine seguente mostra il formato per il bit zero del GPIO:

Mode – modalità operativa dell'uscita (ingresso/uscita). Più precisamente questi valori sono leggermente più grandi; le porte configurate come porte di uscita hanno una limitazione sulla frequenza massima del segnale di uscita.

Modalità Descrizione
00 Entrata
01 frequenza di uscita fino a 10 MHz
10 frequenza di uscita fino a 2 MHz
11 frequenza di uscita fino a 50 MHz

CNF – bit di configurazione dell'uscita. Dipende dalla modalità operativa:

Concordo sul fatto che con questa struttura del registro di configurazione dei pin, impostare da soli tutti i bit per la configurazione sarà estremamente scomodo. Sarà molto più semplice farlo utilizzando la funzione di libreria GPIO_Init.
Dopo aver inizializzato il modulo periferico, è necessario attivarlo utilizzando la funzione PPP_Cmd:

PPP_Cmd(PPP, ABILITA);

PPP_Cmd(PPP, ABILITA);

Questa funzione non esiste per i moduli GPIO; dopo l'inizializzazione è possibile utilizzare immediatamente i pin GPIO. Va ricordato che la libreria fornisce solo un'interfaccia con l'hardware del microcontrollore. Se il modulo hardware non dispone di un flag di attivazione/disattivazione, viene richiamata la funzione PPP_Cmd(PPP, ABILITA) impossibile.
Per controllare lo stato del pin GPIOx in modalità output e leggere il valore in modalità input o output, la libreria mette a disposizione le seguenti funzioni:

void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx); uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);

void GPIO_SetBits (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin) ;

void GPIO_ResetBits (GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin) ;

uint8_tGPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin) ;

uint16_tGPIO_ReadOutputData(GPIO_TypeDef* GPIOx) ;

uint8_tGPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_tGPIO_Pin) ;

uint16_tGPIO_ReadInputData(GPIO_TypeDef* GPIOx) ;

Il resto dei moduli periferici sono configurati e funzionano allo stesso modo. Tuttavia, esistono alcune differenze dovute alle specifiche del modulo hardware specifico, quindi consiglio vivamente di visualizzare prima gli esempi di utilizzo del modulo selezionato per la libreria STM32F10x SPL.

Gestione delle interruzioni e delle eccezioni

Il core Cortex-M3 include un controller di interrupt vettorizzato nidificato. Il controller supporta fino a 240 sorgenti che possono causare interruzioni del core del processore. Quanti vettori tra i 240 possibili sono implementati in uno specifico modello di microcontrollore dipende dal produttore. I microcontrollori Stm32f10x possono avere fino a 43 di questi vettori. Queste linee di interruzione sono chiamate mascherabili. Inoltre, sono presenti 15 vettori di interrupt core Cortex-M3 e un interrupt EXTI esterno non mascherabile.
Il controller supporta gli interrupt annidati, in cui può verificarsi un altro interrupt all'interno di un gestore. A questo proposito, ciascuna fonte di interruzione ha la propria priorità. Sono supportati 16 livelli di priorità di interruzione.
I vettori di interruzione del core Cortex-M3 hanno i valori di priorità più alti.
I tre livelli di interruzione più alti sono assegnati ai vettori e non possono essere modificati:

Numero Gestore Una priorità Descrizione
1 Reset_Handler -3(più alto) Reimposta vettore
2 NMI_Handler -2 Interruzione non mascherabile
3 HardFault_Handler -1 Condizioni di emergenza

A tutti gli altri vettori di interruzione possono essere assegnati livelli di priorità da 0 a 15.
Il livello di priorità più alto corrisponde a un valore più basso. Il livello di priorità può essere assegnato non solo ad un singolo vettore, ma anche ad un intero gruppo di vettori. Questa funzionalità semplifica il lavoro con un gran numero di vettori di interruzione.
Per impostare il gruppo di priorità viene utilizzata una funzione della libreria STM32F10x SPL.

Per molto tempo, anche molto tempo, non ci sono stati nuovi articoli sul nostro articolo, quindi è ora di recuperare 😉 Oggi inizieremo a studiare STM32F4. E, probabilmente, inizieremo creando un nuovo progetto per questi controller, anche se, a dire il vero, non volevo scrivere un articolo a riguardo, perché nuovo progetto qui viene creato, in linea di principio, allo stesso modo di STM32F103 (). Ma succede ancora che sorgano alcune difficoltà con l'STM32F4, quindi consideriamo comunque questo processo in dettaglio)

Quindi, lanciamo Keil, creiamo un nuovo progetto - Progetto -> Nuovo progetto uVision. Salviamo il nuovo progetto in qualche cartella, poi ci verrà chiesto di selezionare il microcontrollore da utilizzare. Bene, scegliamo, lascia che sia STM32F407VG:

Fatto, nella finestra di dialogo che appare, fai clic su "Sì" e il primo file verrà aggiunto al nostro progetto - startup_stm32f4xx.s. Proprio come prima, utilizzeremo le librerie CMSIS E Libreria periferica standard, ma, naturalmente, già per i controller STM32F4xx. Quindi dobbiamo assolutamente scaricarli e aggiungere i file necessari al nostro progetto ancora vuoto. A proposito, ho sentito più di una volta da persone diverse che si imbattono in alcune librerie "non così" per F4, e anche il progetto più semplice non viene messo insieme. Io stesso non l'ho riscontrato, tuttavia, ecco le librerie testate che utilizzo io stesso:

Quindi l'abbiamo scaricato, tutto è pronto, ora aggiungiamo i file al progetto. L'immagine mostra ciò di cui avrai bisogno:

Bene, la preparazione è completa, ora creiamo un nuovo file .c, che conterrà il nostro codice. Andiamo a File->Nuovo, si apre un file vuoto in Keil, fare clic File->Salva con nome e salvarlo ad esempio con il nome test.c. Durante il salvataggio, non dimenticare di specificare l'estensione del file (.c). Il file è stato creato, fantastico, ma dobbiamo anche aggiungerlo al nostro progetto. Beh, in realtà non c'è niente di complicato 😉 Scriviamo un programma di test vuoto in questo file:

#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" /*******************************************************************/ int main() ( while (1 ) ( __NOP() ; ) ) /*******************************************************************/

Quasi tutto è pronto, non resta che guardare le impostazioni del progetto - Progetto->Opzioni per destinazione... Si apre una finestra con molte schede, qui ci interessano solo alcune. Apri la scheda C/C++ e nel campo Definisci scriviamo:

Bene, giù in campo è necessario aggiungere percorsi assolutamente a tutti i file inclusi nel progetto. Dopo aver completato questo passaggio, puoi premere F7 (Build) e il progetto verrà creato senza errori o avvisi. Come puoi vedere, niente di complicato)

Ma in generale, personalmente faccio le cose in modo leggermente diverso. Considera gli svantaggi di questo approccio. Quindi abbiamo scaricato le librerie CMSIS e SPL da qualche parte, abbiamo aggiunto file da queste cartelle, abbiamo annotato i percorsi dei file, tutto è andato bene. MA! Il progetto non verrà creato su un altro computer, poiché i percorsi sono tutti assoluti, ovvero puntano a cartelle specifiche sul tuo computer. E su un'altra macchina dovrai effettivamente rieseguire i passaggi per creare un nuovo progetto. Questo è un enorme svantaggio. Pertanto, di solito creo una cartella separata per un nuovo progetto, in essa creo sottocartelle per CMSIS, SPL e altre librerie utilizzate, e in queste cartelle inserisco tutti i file di cui ho bisogno in ogni progetto specifico. Ad esempio, creiamo la cartella STM32F4_Test per il nostro nuovo progetto e le seguenti cartelle al suo interno:

Ho inserito tutti i file necessari che abbiamo aggiunto durante la creazione del progetto all'inizio dell'articolo nelle cartelle CMSIS e SPL. Ora lanciamo Keil, creiamo un nuovo progetto e lo salviamo nella nostra sottocartella Progetto in modo che tutti i file di progetto siano in un unico posto e non causino caos)

Il progetto è stato creato, ora, come prima, aggiungiamo semplicemente tutti i file dalle cartelle STM32F4_CMSIS e STM32F4_SPL. Inseriamo il nostro file .c di prova con la funzione main() nella cartella Source e lo aggiungiamo anche al progetto. Non resta che configurare le impostazioni =) È tutto uguale: nel campo di definizione scriviamo:

USE_STDPERIPH_DRIVER,STM32F4XX



Montiamo il progetto: non ci sono errori, il volo è normale! In linea di principio, alla fine abbiamo ottenuto la stessa cosa, ma ora il progetto verrà immediatamente assemblato su qualsiasi altro computer senza problemi, e questo è molto comodo e utile) Assolutamente tutti i file di progetto ora si trovano nelle vicinanze, nella stessa cartella, ei percorsi sono diventati relativi e non devono essere cambiati.
Questo è tutto, in realtà prossimamente faremo sicuramente qualcosa per programmare l'STM32F4, quindi a presto!;)

Progetto completo dall'articolo di esempio -

Ho indicato che la libreria standard è collegata al sistema. In effetti, CMSIS è collegato - il sistema di rappresentazione strutturale generalizzata di MK, nonché SPL - la libreria periferica standard. Diamo un'occhiata a ciascuno di essi:

CMSIS
È un insieme di file header e un piccolo insieme di codice per unificare e strutturare il lavoro con il nucleo e la periferia del MK. Infatti, senza questi file è impossibile lavorare normalmente con MK. È possibile ottenere la libreria nella pagina della documentazione allegata 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 vale solo per un produttore, vale a dire Passando a un microcontrollore di un'altra azienda, sei costretto a studiarne le periferiche quasi da zero.
Sebbene i file relativi al core del processore MK siano identici da tutti i produttori (se non altro perché hanno lo stesso modello di core del processore - fornito sotto forma di blocchi IP da ARM).
Pertanto, lavorare con parti del kernel come registri, istruzioni, interruzioni e unità coprocessore è standard per tutti.
Per quanto riguarda la periferia, l'STM32 e l'STM8 (improvvisamente) sono quasi simili, e questo è parzialmente vero anche per gli altri MK rilasciati dalla ST. Nella parte pratica mostrerò quanto è semplice utilizzare CMSIS. Tuttavia, le difficoltà nell'utilizzarlo sono associate alla riluttanza delle persone a leggere la documentazione e comprendere il design MK.

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

Ciao a tutti. Come ricorderete nell'ultimo articolo che abbiamo impostato pacchetto software per lavorare 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 è riportata un'immagine della scheda Scoperta STM32F3 , dove: 1 — Sensore MEMS. Giroscopio digitale a 3 assi L3GD20. 2 - Sistema MEMS in custodia contenente un accelerometro lineare digitale a 3 assi e un sensore geomagnetico digitale a 3 assi LSM303DLHC. 4 – LD1 (PWR) – Alimentazione 3,3 V. 5 – LD2 – LED rosso/verde. L'impostazione predefinita è rossa. Il verde significa comunicazione tra ST-LINK/v2 (o V2-B) e PC. Ho ST-LINK/v2-B e un display personalizzato porta USB. 6. -LD3/10 (rosso), LD4/9 (blu), LD5/8 (arancione) e LD6/7 (verde). Nell'ultimo post abbiamo fatto lampeggiare il LED LD4. 7. – Due pulsanti: UTENTE personalizzato e resettare RESET. 8. - UTENTE USB con connettore Mini-B.

9 - Debug/programmatore USB ST-LINK/V2. 1 0. - Microcontrollore STM32F303VCT6. 11. — Generatore esterno di alta frequenza 8 MHz. 12. – Qui dovrebbe esserci un generatore di bassa frequenza, purtroppo non è saldato. 13. – SWD – interfaccia. 14. – Ponticelli per la selezione della programmazione dei controllori esterni o interni, nel primo caso devono essere rimossi. 15 – Jumper JP3 – un jumper predisposto per collegare un amperometro per misurare il consumo del controller. È chiaro che se viene eliminato, la nostra pietra non verrà avviata. 16. – STM32F103C8T6 è presente una scheda di debug. 17. - LD3985M33R Regolatore a bassa caduta di tensione e livello di rumore, 150mA, 3,3V.

Ora diamo uno sguardo più da vicino all'architettura del microcontrollore STM32F303VCT6. Il suo specifiche tecniche: Case LQFP-100, core ARM Cortex-M4, frequenza core massima 72 MHz, capacità di memoria programma 256 KB, tipo di memoria Programmi FLASH, volume memoria ad accesso casuale SRAM 40 kbyte, RAM 8 kbyte, 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*12bit, tensione di alimentazione 2...3,6 V, temperatura operativa –40...+85 C. Nella figura sotto c'è una piedinatura, dove vediamo 87 porte di ingresso/uscita, 45 delle quali I/O normali (TC, TTa), 42 I/O tolleranti a 5 volt (FT, FTf) – compatibili con 5 V. (sulla scheda sono presenti pin 5 V a destra, 3,3 V a sinistra). Ciascuna linea I/O digitale può fungere da linea I/O generale.
destinazione o funzione alternativa. Man mano che i progetti avanzano, conosceremo gradualmente la periferia.

Consideriamo lo schema a blocchi qui sotto. Il cuore è un core ARM Cortex-M4 a 32 bit che opera fino a 72 MHz. Ha un'unità FPU a virgola mobile integrata e un'unità di protezione della memoria MPU, celle di tracciamento macro 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 continuamente queste osservazioni attraverso i contatti ETM finché il dispositivo è in funzione. NVIC (controller di interruzioni vettoriali nidificate) - modulo di controllo delle interruzioni. TPIU (unità di interfaccia porta traccia). Contiene memoria FLASH – 256 KB, SRAM 40 KB, RAM 8 KB. Tra il core e la memoria c'è una matrice Bus, che consente il collegamento diretto dei dispositivi. Anche qui vediamo due tipi di matrice bus AHB e APB, dove la prima è più produttiva e viene utilizzata per la comunicazione ad alta velocità componenti interni e il secondo è per le periferiche (dispositivi di input/output). Il controller dispone di 4 ADC (ADC) a 12 bit (5 Mbit/s) e un sensore di temperatura, 7 comparatori (GP Comparator1...7), 4 amplificatori operazionali programmabili (OpAmp1...4) (PGA (Programmable Gain Array) ), 2 canali DAC a 12 bit (DAC), RTC (orologio in tempo reale), due timer watchdog - indipendenti e con finestra (WinWatchdog e Ind. WDG32K), 17 timer generici e multifunzionali.

In termini generali, abbiamo esaminato l'architettura del controller. Ora guarda le librerie software disponibili. Fatta una panoramica possiamo evidenziare quanto segue: CMSIS, SPL e HAL. Esaminiamoli ciascuno utilizzando un semplice esempio di lampeggiamento di un LED.

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

Ad esempio, scriviamo un programma: facciamo lampeggiare un LED. Per questo 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 del ramo specifico di cui è responsabile DS9118: MCU+FPU 32b Cortex®-M4 basato su ARM®, fino a 256KB di Flash+ 48KB di SRAM, 4 ADC, 2 DAC ch., 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 consente di ridurre il consumo energetico. Apri il Manuale di riferimento e guarda la sezione Reset e controllo dell'orologio, quindi la mappa dei registri RCC e vedi quale registro è responsabile dell'abilitazione di IOPEEN

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

#include "stm32f3xx.h " //File di intestazione del microcontrollore
unsigned int i;
ritardo vuoto() (
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 periferiche standard)- questa libreria ha lo scopo di combinare tutti i processori di ST Electronics. Progettato per migliorare la portabilità del codice ed è rivolto principalmente agli sviluppatori principianti. La ST ha lavorato su un sostituto di SPL chiamato "low layer" compatibile con HAL. I driver Low Layer (LL) sono progettati per fornire un livello quasi leggero, orientato agli esperti, più vicino all'hardware rispetto a HAL. Oltre a HAL, sono disponibili anche le API LL. Un esempio dello stesso programma in SPL.

#includere
#includere
#includere
#definire LED GPIO_Pin_8
int principale() (
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); //dichiara e inizializza una 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; i< 500000; i++);
// Tutto spento
GPIO_ResetBits(GPIOE, LED);
per (i = 0; i< 500000; i++);
} }

Ogni funzione è descritta nella documentazione tecnica UM1581 Manuale utente Descrizione della libreria di periferiche standard STM32F30xx/31xx. Qui colleghiamo tre file header che contengono i dati necessari, le strutture, le funzioni di ripristino e controllo della sincronizzazione, nonché per la configurazione delle porte di ingresso/uscita.

3). HAL- (Livello di accesso hardware, Livello di astrazione hardware)- Un'altra libreria comune per lo sviluppo. Con cui è stato rilasciato anche il programma CubeMX per la configurazione che abbiamo utilizzato nello scorso articolo. Lì abbiamo anche scritto un programma per far lampeggiare un LED utilizzando questa libreria. Come vediamo nella figura seguente, il cubo genera driver HAL e CMSIS. Bene, descriviamo i principali file utilizzati:
- system_stm32f3x.c e system_stm32f3x.h- fornire set minimi di funzioni per la configurazione del sistema di cronometraggio;
— core_cm4.h – fornisce l'accesso ai registri del core e delle sue periferiche;
- stm32f3x.h - file di intestazione del microcontrollore;
— startup_system32f3x.s — codice di avvio, contiene una tabella di vettori di interruzione, ecc.

#include "main.h"
#include "stm32f3xx_hal.h"
void SystemClock_Config(void); /*Dichiara le funzioni di configurazione dell'orologio*/
vuoto statico MX_GPIO_Init(void); /*Inizializza I/O*/
int principale (vuoto) (
/*Reimposta tutte le periferiche, inizializza l'interfaccia Flash e il Systick.*/
HAL_Init();
/* Configura l'orologio di sistema */
Orologio_Sistema_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.PLLState = 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;
if (HAL_RCC_ClockConfig (&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK){
_Gestore_Errori(__FILE__, __LINE__);
}
/**Configura il tempo di interruzione del 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 ingresso uscita analogico EVENT_OUT EXTI */
vuoto statico 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 i 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 riga){
mentre (1) (
} }
#ifdef USE_FULL_ASSERT

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

Possiamo riassumere che la prima opzione, utilizzando CMSIS, è meno macchinosa. C'è documentazione per ogni biblioteca. Nei progetti successivi utilizzeremo HAL e CMSIS utilizzando il programma di configurazione STCube e, se possibile, utilizzeremo direttamente i registri, senza wrapper software. Fermiamoci qui oggi. Nel prossimo articolo esamineremo i principi di base della costruzione casa intelligente. Ciao a tutti.




Superiore