In questo articolo di approfondimento imparerete come sviluppare firmware embedded per i microcontrollori STM32 Cortex-M a 32 bit utilizzando i vari strumenti di sviluppo.
La serie STM32 sono alcuni dei microcontrollori più popolari utilizzati in una vasta gamma di prodotti. Hanno anche un’eccellente base di supporto da più forum di sviluppo del microcontrollore.
Questa famiglia di microcontrollori di STMicroelectronics si basa sul core del processore ARM Cortex-M a 32 bit.
I microcontrollori STM32 offrono un gran numero di periferiche di comunicazione seriale e parallela che possono essere interfacciate con tutti i tipi di componenti elettronici tra cui sensori, display, telecamere, motori, ecc. Tutte le varianti STM32 sono dotate di memoria flash interna e RAM.
La gamma di prestazioni disponibili con STM32 è piuttosto ampia. Alcune delle varianti più basilari includono le sotto-serie STM32F0 e STM32F1 che iniziano con una frequenza di clock di soli 24 MHz e sono disponibili in pacchetti con un minimo di 16 pin.
All’altro estremo delle prestazioni, l’STM32H7 funziona fino a 400 MHz ed è disponibile in pacchetti con ben 240 pin.
I modelli più avanzati sono disponibili con unità in virgola mobile (FPU) per applicazioni con gravi esigenze di elaborazione numerica. Questi modelli più avanzati offuscano la linea tra un microcontrollore e un microprocessore.
Infine, la sub-serie STM32L è progettata specificamente per applicazioni portatili a bassa potenza in esecuzione da una piccola batteria.
Strumenti di sviluppo
Gli strumenti di sviluppo sono necessari per sviluppare il codice, programmare il microcontrollore e testare/eseguire il debug del codice. Gli strumenti di sviluppo includono:
- Compilatore
- Debugger
- In-Circuit Serial Programmer (ICSP)
Programmazione STM32 tramite il In-Circuit-Serial-Programmer (ICSP).
Sono disponibili diversi strumenti di sviluppo software per lo sviluppo del codice sui microcontrollori STM32. Gli strumenti software sono disponibili come ambienti di sviluppo integrati (IDE) che combina tutti gli strumenti necessari in un ambiente integrato.
Due pacchetti di sviluppo comuni includono:
- Keil MDK ARM (uVison5 IDE) – Il MDK ARM IDE è un ambiente di sviluppo molto stabile che può essere scaricato gratuitamente. Consente lo sviluppo di codice fino a una dimensione del programma di 32 KB. Per lo sviluppo di programmi più grandi è necessario acquistare una versione con licenza qui.
- CoIDE-Una catena di strumenti gratuiti che si basa su una versione ridotta dell’IDE Eclipse integrata insieme a una versione ARM incorporata del compilatore GCC gratuito.
Ci sono anche molti altri IDE che sono disponibili per l’uso con i microcontrollori STM32. Tuttavia, questo articolo si concentra sullo sviluppo e lampeggiante un programma utilizzando il molto popolare Keil MDK ARM uVision5 IDE.
Oltre agli strumenti software, è necessario un programmatore seriale in-Circuit (ICSP) per programmare e testare il codice sul microcontrollore effettivo. L’ICSP è necessario per interfacciare il microcontrollore agli strumenti software per PC tramite una porta USB.
I microcontrollori ARM Cortex-M supportano due protocolli di programmazione: JTAG (denominato dall’electronics industry association il Joint Test Action Group) e Serial Wire Debug (SWD).
Ci sono diversi programmatori ICSP disponibili che supportano questi protocolli, tra cui:
- Keil U-Link 2
- Segger J-Link
- ST-Link
Sviluppare la prima applicazione
È sempre più semplice iniziare con un framework di codice di base facilmente disponibile. Quindi, aggiungere il codice richiesto per l’applicazione specifica e il modello di microcontrollore.
Fortunatamente, STMicroelectronics fornisce uno strumento grafico molto utile chiamato STM32CubeMx che aiuta a creare un progetto di applicazione di base per qualsiasi microcontrollore STM32 di vostra scelta. Può anche essere utilizzato per configurare le periferiche sui pin multiplex del microcontrollore.
Lo strumento STM32CubeMX può essere scaricato da qui. STM32Cube è dotato di un ampio set di driver per tutti i tipi di periferiche e supporto per un FreeRTOS opzionale (un sistema operativo in tempo reale gratuito) pre-integrato con il codice.
La sezione seguente descrive in dettaglio come creare una semplice applicazione UART per il microcontrollore STM32F030 che riecheggi tutto ciò che viene digitato su una finestra di terminale.
- Installare il software STM32CubeMX.
- Eseguire l’applicazione e selezionare Nuovo progetto. Aprirà quindi la finestra del selettore MCU come mostrato di seguito.
- Fare doppio clic per selezionare il modello di microcontrollore utilizzato. In questo caso stiamo usando STM32F030K6. Quindi ti porta alla pagina di piedinatura per il microcontrollore selezionato.
STM32F030K6 è un core ARM Cortex-M0 con 32KB di memoria Flash e 4KB di memoria RAM. Il codice di esempio abilita l’UART che utilizza i pin PA9 e PA10 per ricevere e trasmettere dati seriali come mostrato di seguito con i pin verdi.
Configura le impostazioni UART nella scheda Configurazione e scegli le impostazioni UART come mostrato di seguito. Attivare l’opzione di interrupt globale NVIC nella scheda Impostazioni NVIC.
Quindi, passare a Project–>Impostazioni per aggiungere il nuovo nome del progetto e selezionare l’IDE della catena di strumenti da utilizzare. Per questo esempio, impostare il nome del progetto su ‘UARTEcho’ e selezionare l’IDE Keil-MDK5 per lo sviluppo del progetto.
Infine, genera il codice del progetto facendo clic su Project- > Genera codice.
Costruire e lampeggiante il codice
Ora aprire il file di progetto MDK-ARM generato UARTEcho\MDK-ARM\UartEcho.uprojx.
Questo programma finora inizializza solo la periferica UART e si ferma in un ciclo infinito.
È importante notare che STM32Cube genera / * CODICE UTENTE BEGIN x * / e / * CODICE UTENTE END x * / blocchi di commento per implementare il codice specifico dell’utente. Il codice utente deve essere scritto all’interno di questi blocchi di commento. Ogni volta che il codice viene ri-generato con configurazioni modificate lo strumento STMCube mantiene il codice utente all’interno di questi blocchi di commento utente.
Quindi, definire una variabile globale per ricevere un byte dall’UART nel main.c file sorgente:
/* USER CODE BEGIN PV *//* Private variables ———————————————————*/static uint8_t recv_data;/* USER CODE END PV */
Dopo tutto il codice di inizializzazione, abilitare il driver per ricevere 1 byte. La seguente funzione abilita il bit di interrupt RXNE.
/* USER CODE BEGIN 2 */HAL_UART_Receive_IT(&huart1, &recv_data, 1);/* USER CODE END 2 */
Ora, aggiungere una funzione di callback per gestire l’interrupt di ricezione e trasmettere il byte ricevuto.
/* USER CODE BEGIN 0 */void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){HAL_UART_Transmit(huart, huart->pRxBuffPtr, 1, 1000);}/* USER CODE END 0 */
Infine, abbiamo bisogno di compilare il codice e flash (download) al microcontrollore.
Quando il Keil MDK ARM IDE è installato, i driver per ST-LINK V2, J-Link e Ulink2 sono disponibili. Il debugger ST-Link sarà selezionato per impostazione predefinita. Vai a Progetti – >Opzioni per Target e nella scheda Debug selezionare il programmatore ICSP utilizzato.
Flash il codice selezionando Flash – >Download.
Il microcontrollore ora echeggerà tutti i dati ricevuti tramite UART. Può essere collegato a un PC utilizzando un convertitore da USB a seriale. Sul PC aprire la porta COM con un’applicazione terminale utilizzando le impostazioni di 115200-8-N-1. Ora tutto ciò che viene inviato dal terminale riecheggerà attraverso il microcontrollore.
Sistema di interrupt
Il sistema di interrupt STM32 è basato sulla periferica NVIC ARM Cortex M core. Le MCU STM32 supportano più canali di interrupt mascherabili a parte i 16 canali di interrupt del core ARM.
Ad esempio la serie MCU STM32F0 supporta 32 interrupt mascherabili. L’eccezione e la tabella vettoriale di interrupt per questa famiglia di MCU sono riportate nella tabella seguente.
Interrupt | Description | Vector Address |
– | Reserved | 0x00000000 |
Reset | Reset | 0x00000004 |
NMI | Non maskable interrupt. The RCC clock security system (CSS) is linked to the NMI vector | 0x00000008 |
HardFault | All class of faults | 0x0000000C |
SVCall | System service call via SWI Instruction | 0x0000002C |
PendSV | Pendable request for system service | 0x00000038 |
SysTick | System tick timer | 0x0000003C |
WWDG | Window watchdog interrupt | 0x00000040 |
PVD_VDDIO2 | PVD and VDDIO2 supply comparator interrupt (combined with EXTI lines 16 and 31) | 0x00000044 |
RTC | RTC interrupts (combined EXTI lines 17, 19 and 20) | 0x00000048 |
Flash | Flash global interrupt | 0x0000004C |
RCC_CRS | RCC and CRS global interrupts | 0x00000050 |
EXTI0_1 | EXTI line interrupts | 0x00000054 |
EXTI2_3 | EXTI line interrupts | 0x00000058 |
EXTI4_15 | EXTI line interrupts | 0x0000005C |
TSC | Touch sensing interrupt | 0x00000060 |
DMA_CH1 | DMA channel 1 interrupt | 0x00000064 |
DMA_CH2_3 DMA2_CH1_2 |
DMA channels 2 and 3 interrupts DMA2 channel1 and 2 interrupts |
0x00000068 |
DMA_CH4_5_6_7 DMA2_CH3_4_5 |
DMA channel 4,5,6 and 7 interrupts DMA2 channel 3, 4, and 5 interrupts |
0x0000006C |
ADC_COMP | ADC and COMP interrupts (Combined EXTI lines 21 and 22) | 0x00000070 |
TIM1_BRK_UP_TRG_COM | TIM1 break, update, trigger and commutation interrupts | 0x00000074 |
TIM1_CC | TIM1 capture compare interrupt | 0x00000078 |
TIM2 | TIM2 global interrupt | 0x0000007C |
TIM3 | TIM3 global interrupt | 0x00000080 |
TIM6_DAC | TIM6 global interrupt and DAC underrun interrupt | 0x00000084 |
TIM7 | TIM7 global interrupt | 0x00000088 |
TIM14 | TIM14 global interrupt | 0x0000008C |
TIM15 | TIM15 global interrupt | 0x00000090 |
TIM16 | TIM16 global interrupt | 0x00000094 |
TIM17 | TIM17 global interrupt | 0x00000098 |
I2C1 | I2C1 global interrupt (combined with EXTI line 23) | 0x0000009C |
I2C2 | I2C2 global interrupt | 0x000000A0 |
SPI1 | SPI1 global interrupt | 0x000000A4 |
SPI2 | SPI2 global interrupt | 0x000000A8 |
USART1 | USART1 global interrupt (combined with EXTI line 25) | 0x000000AC |
UART2 | USART2 global interrupt (combined with EXTI line 26) | 0x000000B0 |
USART3_4_5_6_7_ 8 | USART3, USART4, USART5, USART6, USART7, USART8 global interrupts (combined with EXTI line 28) | 0x000000B4 |
CEC_CAN | CEC and CAN global interrupts (combined with EXTI line 27 | 0x000000B8 |
USB | USB global interrupt (combined with EXTI line 18) | 0x000000BC |
Extended Interrupts and Events Controller (EXTI)
Gli MCU STM32 dispongono di un Extended interrupts and Events controller che gestisce gli eventi/interrupt asincroni esterni ed interni e genera la richiesta di evento al controller CPU / Interrupt e una richiesta di wake-up al Power Manager.
Ciascuna delle una o più linee EXTI sono mappate su uno dei vettori di interrupt NVIC.
Per le linee di interrupt esterne, per generare un interrupt, la linea di interrupt deve essere configurata e abilitata. Questo viene fatto programmando i due registri trigger con il rilevamento dei bordi desiderato e abilitando la richiesta di interrupt scrivendo un’ 1 ‘ al bit corrispondente nel registro maschera di interrupt.
Interrupt esterno e mappatura GPIO
Ciascuno dei GPIO disponibili sul sistema può essere configurato per generare un interrupt. Ma ciascuna delle linee di interrupt EXTI è mappata su più pin GPIO. Ad esempio,PIO0 su tutte le porte GPIO disponibili (A,B, C, ecc.) sarà mappato alla linea EXTI0. PIO1 per tutte le porte verrà mappato alla linea EXTI1 e così via.
Alcune delle linee EXTI sono combinate in un singolo vettore NVIC. Ad esempio, EXTI4_15 è mappato su un singolo indirizzo vettoriale, quindi ci sarà una singola routine di interrupt per tutti gli interrupt da PIO4 a PIO15. Ma la fonte dell’interrupt può essere identificata leggendo il registro in sospeso di interrupt.
Una cosa importante da considerare durante la progettazione di un sistema utilizzando le MCU STM32 è la selezione dei pin GPIO per gli interrupt. L’MCU può avere più di 16 GPIO disponibili sul dispositivo, ma ci sono solo 16 linee di interrupt esterne disponibili.
Ad esempio, EXTI_0 può essere mappato su PA0 o PB0 ma non su entrambi. Quindi, mentre si scelgono i pin per gli interrupt esterni, dovrebbero essere scelti in modo tale che possano essere mappati in modo univoco su una delle linee EXTI.
La sezione seguente descrive come configurare un interrupt utilizzando il cubo STM32.
Selezionare la scheda Configurazione e scegliere il modulo hardware per il quale l’interrupt deve essere configurato. Si apre la finestra di configurazione del modulo.
Quindi selezionare la scheda Impostazioni NVIC e abilitare l’interrupt globale.
Il codice per abilitare l’interrupt per il modulo verrà generato in stm32f0xx_hal_msp.c nella funzione HAL_<modulo >_MSPInit (…).
/* USART1 interrupt Init */HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);
Il codice generato dal Cubo STM32 avrà l’implementazione IRQ_Handler di tutti gli interrupt. Quando l’interrupt è abilitato il codice verrà incluso nell’applicazione.
Di solito il codice generato gestisce già l’IRQ e cancella il flag che ha generato l’interrupt. Quindi chiama un callback dell’applicazione che corrisponde all’evento che ha generato l’interrupt per il modulo.
L’HAL STM32 (Hardware Abstraction Layer) implementa un callback per ciascuno dei tipi di evento all’interno di ciascun modulo come parte del driver. In questo esempio il callback completo del trasferimento Rx deve essere copiato da stm32f0xx_hal_UART.file C.
Le funzioni di callback all’interno del driver saranno implementate con un attributo __weak linker. L’utente deve implementare una copia della funzione di callback necessaria rimuovendo l’attributo _ _ weak in uno dei file dell’applicazione e quindi scrivendo la gestione specifica richiesta all’interno di tale funzione.
/*** @brief Rx Transfer completed callback.* @param huart UART handle.* @retval None*/__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){/* Prevent unused argument(s) compilation warning */UNUSED(huart);/* NOTE : This function should not be modified, when the callback is needed,the HAL_UART_RxCpltCallback can be implemented in the user file.*/}
Conclusione
Questo tutorial è un’introduzione alla scrittura di un’applicazione che funziona con la famiglia di microcontrollori STM32. Esistono diversi altri metodi per scrivere un’applicazione, ma STM32Cube discusso è un metodo facile e intuitivo per iniziare.
Questo strumento semplifica l’inizializzazione delle periferiche del microcontrollore. Migliora anche la manutenibilità del codice, specialmente quando ci sono revisioni hardware che richiedono la rimappatura dei segnali su diversi pin.
Un altro vantaggio dell’utilizzo dello strumento STM32Cube è che genera un report della configurazione utente per il microcontrollore. In questo rapporto descrive l’albero dell’orologio, la mappatura dei pin e la configurazione del modulo hardware che sono tutti molto utili.
Ci sono anche diverse altre librerie di codice e programmi di esempio disponibili per tutte le varianti STM32. È incluso anche il supporto per diversi IDE.
Se il tuo progetto richiede un sofisticato microcontrollore a 32 bit, consiglio vivamente la serie STM32. Non solo sono potenti e popolari, ma i microcontrollori STM32 sono anche abbastanza convenienti.
Hai bisogno di più formazione sulla programmazione dei microcontrollori STM32? Se è così, ecco un corso introduttivo più approfondito che dovresti controllare.
Questo articolo è stato scritto da Mohan Kashivasi da Vithamas Technologies. È anche uno degli esperti disponibili per aiutarti con il tuo prodotto all’interno dell’Hardware Academy.
Infine, non dimenticare di scaricare il tuo PDF gratuito: Guida definitiva per sviluppare e vendere il tuo nuovo prodotto hardware elettronico. Riceverai anche la mia newsletter settimanale in cui condivido contenuti premium non disponibili sul mio blog.
Altri contenuti che ti potrebbero piacere:
- Introduzione al STM32CubeIDE per STM32 Microcontrollori
- Come selezionare il microcontrollore per il vostro nuovo prodotto
- Utilizzando Arduino come piattaforma di sviluppo embedded
- Scheda tecnica Recensione: Entry-Level STM32 Cortex-M0 Microcontroller (Blog + Video)
- Introduction to the Ultra High-Performance STM32H7 32-bit Microcontroller