Padronanza rapida dei microcontrollori STM32. STM32 - microcontrollore per principianti dopo Arduino Scrittura di dati su un indirizzo in memoria

Pubblicato il 08/09/2016

Microcontrollori STM32 stanno diventando sempre più popolari grazie alla loro potenza, alle periferiche abbastanza diversificate e alla flessibilità. Inizieremo lo studio utilizzando una tavola di prova economica, il cui costo non supera i 2 dollari (dai cinesi). Avremo anche bisogno Collegamento ST programmatore, il cui costo è di circa $ 2,5 (dai cinesi). Tali importi di spesa sono accessibili sia agli studenti che agli scolari, quindi propongo di iniziare con questa opzione di budget.


Questo microcontrollore non è il più potente tra STM32, ma nemmeno il più debole. Ci sono varie schede con STM32, Compreso Scoperta che costa circa $ 20. Su tali schede, quasi tutto è uguale alla nostra scheda, più un programmatore. Nel nostro caso utilizzeremo il programmatore separatamente.

Microcontrollore STM32F103C8. Caratteristiche

  • Nucleo Cortex-M3 ARM a 32 bit
  • Frequenza massima 72 MHz
  • Memoria Flash da 64KB per i programmi
  • Memoria SRAM da 20Kb
  • Alimentazione 2,0…3,3V
  • 2 ADC a 12 bit (0...3,6 V)
  • controllore DMA
  • 37 ingressi/uscite tolleranti a 5 V
  • 4 timer a 16 bit
  • 2 timer di sorveglianza
  • I2C – 2 autobus
  • USART – 3 autobus
  • SPI – 2 autobus
  • Interfaccia USB 2.0 a piena velocità
  • RTC – orologio integrato

Disponibile sulla scheda STM32F103C8

  • Porte di uscita A0-A12, B0-B1, B3-B15, C13-C15
  • Micro USB attraverso il quale è possibile alimentare la scheda. La scheda ha uno stabilizzatore di tensione da 3,3 V. È possibile fornire alimentazione a 3,3 V o 5 V ai pin corrispondenti sulla scheda.
  • Pulsante Ripristina
  • Due ponticelli BOOT0 E AVVIO1. Lo useremo durante il flashing tramite UART.
  • Due quarzi da 8 MHz e 32768 Hz. Il microcontrollore è dotato di moltiplicatore di frequenza, quindi con un quarzo da 8 MHz possiamo raggiungere la frequenza massima del controller di 72 MHz.
  • Due LED. PWR– segnala l'avvenuta alimentazione. PC13– collegato all'uscita C13.
  • Connettore per programmatore Collegamento ST.

Quindi, iniziamo provando a eseguire il flashing del microcontrollore. Questo può essere fatto tramite USART o utilizzando un programmatore Collegamento ST.

È possibile scaricare il file di test per il firmware. Il programma fa lampeggiare il LED sulla scheda.

Firmware STM32 utilizzando l'adattatore USB-Uart per Windows

Nella memoria di sistema STM32 C'è Boot loader. Il bootloader viene registrato in fase di produzione e qualsiasi microcontrollore STM32 può essere programmato tramite interfaccia USART utilizzando un adattatore USART-USB. Tali adattatori sono spesso realizzati sulla base di microcircuiti popolari FT232RL. Prima di tutto collega l'adattatore al computer e installa i driver (se richiesti). È possibile scaricare i driver dal sito Web del produttore FT232RL– ftdichip.com. È necessario scaricare i driver VCP(porta COM virtuale). Dopo aver installato i driver, sul tuo computer dovrebbe apparire una porta seriale virtuale.


Connessione RX E Texas uscite ai pin corrispondenti USART1 microcontrollore. RX collegare l'adattatore a Texas microcontrollore (A9). Texas collegare l'adattatore a RX microcontrollore (A10). Poiché USART-USB ha uscite di alimentazione da 3,3 V, forniremo alimentazione alla scheda da essa.

Per mettere il microcontrollore in modalità di programmazione, è necessario impostare i pin BOOT0 E AVVIO1 allo stato desiderato e riavviarlo con il pulsante Ripristina oppure spegnere e riaccendere il microcontrollore. Per questo abbiamo i ponticelli. Combinazioni diverse forzano il microcontrollore in modalità diverse. Siamo interessati solo ad una modalità. Per fare ciò, il microcontrollore ha BOOT0 dovrebbe essercene uno logico e l'output AVVIO1– zero logico. Sulla scheda questa è la seguente posizione del jumper:

Dopo aver premuto il pulsante Ripristina oppure scollegando e collegando l'alimentazione, il microcontrollore deve entrare in modalità di programmazione.

Software del firmware

Se utilizziamo un adattatore USB-UART, il nome della porta sarà simile a questo /dev/ttyUSB0

Ottieni informazioni sul chip

Risultato:

Leggiamo dal chip nel file dump.bin

sudo stm32flash -r dump.bin /dev/ttyUSB0

Scrivi sul chip

sudo stm32flash -w dump.bin -v -g 0x0 /dev/ttyUSB0

Risultato:

Stm32flash 0.4 http://stm32flash.googlecode.com/ Utilizzo del parser: Raw BINARY Interface serial_posix: 57600 8E1 Version: 0x22 Opzione 1: 0x00 Opzione 2: 0x00 ID dispositivo: 0x0410 (densità media) - RAM: 20 KiB (512b riservato da bootloader) - Flash: 128 KiB (dimensione settore: 4x1024) - RAM opzionale: 16b - RAM di sistema: 2 KiB Scrittura in memoria Cancellazione della memoria Indirizzo scritto e verificato 0x08012900 (100,00%) Fatto. Avvio dell'esecuzione all'indirizzo 0x08000000... fatto.

Firmware STM32 utilizzando il programmatore ST-Link per Windows

Quando si utilizza un programmatore Collegamento ST conclusioni BOOT0 E AVVIO1 non vengono utilizzati e devono essere nella posizione standard per il normale funzionamento del controller.

(Libro in russo)

Marcatura STM32

Famiglia di dispositiviTipologia di prodottoSottofamiglia di dispositiviConteggio dei pinDimensioni della memoria flashPacchettoIntervallo di temperatura
STM32 =
Microcontrollore a 32 bit basato su ARM
F = Uso generale
L = Consumo ultrabasso
TS = touchscreen
W = sistema su chip wireless
60 = multitouch resistivo
103 = linea di prestazione
F = 20 pin
G = 28 pin
K = 32 pin
T = 36 pin
H = 40 perni
C = 48/49 pin
R = 64 pin
O=90 pin
V = 100 pin
Z = 144 pin
I = 176 pin
B = 208 pin
N = 216 pin
4 = 16 Kbyte di memoria Flash
6 = 32 Kbyte di memoria Flash
8 = 64 Kbyte di memoria Flash
B = 128 Kbyte di memoria Flash
Z = 192 Kbyte di memoria Flash
C = 256 Kbyte di memoria Flash
D = 384 Kbyte di memoria Flash
E = 512 Kbyte di memoria Flash
F = 768 Kbyte di memoria Flash
G = 1024 Kbyte di memoria Flash
I = 2048 Kbyte di memoria Flash
H = UFBGA
N=TFBGA
P = TSSOP
T = LQFP
U = V/UFQFPN
Y = WLCSP
6 = Intervallo di temperatura industriale, –40…+85 °C.
7 = Intervallo di temperatura industriale, -40…+ 105 °C.
STM32F103 C8 T6

Come rimuovere la protezione da scrittura/lettura?

Se hai ricevuto una scheda con STM32F103, ma il programmatore non la vede, significa che i cinesi hanno protetto la memoria Flash del microcontrollore. La domanda “perché?” ignoriamolo. Per rimuovere il blocco, collegheremo un adattatore UART e lo programmeremo. Impostiamo i jumper per la programmazione e partiamo:

Lo farò da Ubuntu utilizzando l'utilità stm32flash.

1. Controlla se il microcontrollore è visibile:

Sudo stm32flash /dev/ttyUSB0

Dovresti ottenere qualcosa del genere:

Stm32flash 0.4 http://stm32flash.googlecode.com/ Interfaccia serial_posix: 57600 8E1 Versione: 0x22 Opzione 1: 0x00 Opzione 2: 0x00 ID dispositivo: 0x0410 (densità media) - RAM: 20 KiB (512b riservati dal bootloader) - Flash: 128 KiB (dimensione settore: 4x1024) - RAM opzionale: 16b - RAM di sistema: 2 KiB

2. Rimuovere la protezione da lettura e successivamente la protezione da scrittura:

Sudo stm32flash -k /dev/ttyUSB0 stm32flash 0.4 http://stm32flash.googlecode.com/ Interfaccia serial_posix: 57600 8E1 Versione: 0x22 Opzione 1: 0x00 Opzione 2: 0x00 ID dispositivo: 0x0410 (densità media) - RAM: 20 KiB ( 512b riservato dal bootloader) - Flash: 128 KiB (dimensione settore: 4x1024) - RAM opzionale: 16b - RAM di sistema: 2 KiB Lettura-UnProtecting flash Fatto. sudo stm32flash -u /dev/ttyUSB0 stm32flash 0.4 http://stm32flash.googlecode.com/ Interfaccia serial_posix: 57600 8E1 Versione: 0x22 Opzione 1: 0x00 Opzione 2: 0x00 ID dispositivo: 0x0410 (densità media) - RAM: 20 KiB ( 512b riservato dal bootloader) - Flash: 128 KiB (dimensione settore: 4x1024) - RAM opzionale: 16b - RAM di sistema: 2 KiB Flash senza protezione da scrittura Fatto.

Ora puoi lavorare normalmente con il microcontrollore.

Negli ultimi anni, i microcontrollori (MCU) a 32 bit basati su processori ARM hanno rapidamente conquistato il mondo dell'elettronica. Questa svolta è dovuta alle loro elevate prestazioni, all'architettura avanzata, al basso consumo energetico, al basso costo e agli strumenti di programmazione avanzati.

STORIA BREVE
Il nome ARM è l'acronimo di Advanced RISC Machines, dove RISC (Reduced Instruction Set Computer) sta per architettura del processore con set di istruzioni ridotto. La stragrande maggioranza dei microcontrollori più diffusi, come le famiglie PIC e AVR, hanno anche un'architettura RISC, che ha aumentato le prestazioni semplificando la decodifica delle istruzioni e accelerandone l'esecuzione. L'emergere di microcontrollori ARM a 32 bit avanzati e produttivi ci consente di passare alla risoluzione di problemi più complessi che gli MCU a 8 e 16 bit non possono più affrontare. L'architettura del microprocessore ARM con core a 32 bit e set di istruzioni RISC è stata sviluppata dalla società britannica ARM Ltd, che sviluppa esclusivamente kernel, compilatori e strumenti di debug. L'azienda non produce MK, ma vende licenze per la loro produzione. MK ARM è uno dei segmenti in più rapida crescita del mercato MK. Questi dispositivi utilizzano tecnologie di risparmio energetico, quindi sono ampiamente utilizzati nei sistemi embedded e dominano il mercato dei dispositivi mobili per i quali è importante un basso consumo energetico. Inoltre, i microcontrollori ARM vengono utilizzati attivamente nelle comunicazioni, nei dispositivi portatili e integrati dove sono richieste prestazioni elevate. Una caratteristica dell'architettura ARM è il nucleo informatico del processore, che non è dotato di alcun elemento aggiuntivo. Ogni sviluppatore di processori deve dotare autonomamente questo core dei blocchi necessari per i propri compiti specifici. Questo approccio ha funzionato bene per i grandi produttori di chip, sebbene inizialmente fosse concentrato su soluzioni di processori classici. I processori ARM hanno già attraversato diverse fasi di sviluppo e sono ben noti per le famiglie ARM7, ARM9, ARM11 e Cortex. Quest'ultimo è suddiviso in sottofamiglie di classici processori CortexA, processori real-time CortexR e core di microprocessore CortexM. Sono stati i core CortexM a diventare la base per lo sviluppo di un'ampia classe di MCU a 32 bit. Differiscono dalle altre varianti dell'architettura Cortex principalmente nell'uso del set di istruzioni Thumb2 a 16 bit. Questo set combina le prestazioni e la compattezza delle istruzioni "classiche" ARM e Thumb ed è stato sviluppato appositamente per lavorare con i linguaggi C e C++, migliorando significativamente la qualità del codice. Il grande vantaggio dei microcontrollori basati sul core CortexM è la loro compatibilità software, che teoricamente consente l'uso di codici di programmazione in linguaggio di alto livello in modelli di diversi produttori. Oltre a indicare l'area di applicazione del core, gli sviluppatori MK indicano le prestazioni del core CortexM su una scala di dieci punti. Oggi le opzioni più popolari sono CortexM3 e CortexM4. Gli MCU con architettura ARM sono prodotti da aziende come Analog Devices, Atmel, Xilinx, Altera, Cirrus Logic, Intel, Marvell, NXP, STMicroelectronics, Samsung, LG, MediaTek, MStar, Qualcomm, SonyEricsson, Texas Instruments, nVidia, Freescale, Milander , HiSilicon e altri.
Grazie all'architettura ottimizzata, il costo degli MCU basati sul core CortexM è in alcuni casi addirittura inferiore a quello di molti dispositivi a 8 bit. I modelli “più giovani” possono attualmente essere acquistati per 30 rubli. per la carrozzeria, che crea concorrenza per le precedenti generazioni di MK. MICROCONTROLLORI STM32 Consideriamo l'MCU più conveniente e diffuso della famiglia STM32F100 di STMicroelectronics, che è uno dei principali produttori mondiali di MCU. L'azienda ha recentemente annunciato l'inizio della produzione di un MK a 32 bit che sfrutta i vantaggi dell'industrial
Core STM32 in applicazioni a basso costo. Gli MCU della famiglia della linea Value STM32F100 sono progettati per dispositivi in ​​cui le prestazioni degli MCU a 16 bit non sono sufficienti e la ricca funzionalità dei dispositivi "normali" a 32 bit è ridondante. La linea di MCU STM32F100 si basa su un moderno core ARM CortexM3 con periferiche ottimizzate per l'uso in applicazioni tipiche in cui venivano utilizzate MCU a 16 bit. Le prestazioni dell'MCU STM32F100 a 24 MHz sono superiori alla maggior parte degli MCU a 16 bit. Questa linea comprende dispositivi con vari parametri:
● da 16 a 128 kbyte di memoria flash programma;
● da 4 a 8 kbyte di RAM;
● fino a 80 porte di ingresso/uscita GPIO;
● fino a nove timer a 16 bit con funzioni avanzate;
● due timer watchdog;
● ADC a 12 bit ad alta velocità a 16 canali;
● due DAC a 12 bit con generatori di segnale integrati;
● fino a tre interfacce UART che supportano le modalità IrDA, LIN e ISO7816;
● fino a due interfacce SPI;
● fino a due interfacce I2C che supportano le modalità SMBus e PMBus;
● Accesso diretto alla memoria (DMA) a 7 canali;
● Interfaccia CEC (Consumer Electronics Control) inclusa nello standard HDMI;
● orologio in tempo reale (RTC);
● Controller di interrupt nidificato NVIC.

Lo schema funzionale dell'STM32F100 è mostrato nella Figura 1.

Riso. 1. Architettura della linea MK STM32F100

Un'ulteriore comodità è la compatibilità pin dei dispositivi, che permette, all'occorrenza, di utilizzare qualsiasi MK della famiglia con maggiori funzionalità e memoria senza rielaborare il circuito stampato. La linea di controller STM32F100 è prodotta in tre tipi di package LQFP48, LQFP64 e LQFP100, aventi rispettivamente 48, 64 e 100 pin. L'assegnazione dei pin è presentata nelle Figure 2, 3 e 4. Tali custodie possono essere installate su circuiti stampati senza l'uso di attrezzature speciali, il che è un fattore significativo nella produzione su piccola scala.


Riso. 2. MCU STM32 nel pacchetto LQFP48 Fig. 3. MCU STM32 nel pacchetto LQFP64


Riso. 4. MCU STM32 nel pacchetto LQFP100

STM32F100 è un dispositivo conveniente e ottimizzato basato sul core CortexM3, supportato da un ambiente di sviluppo avanzato per la famiglia di microcontrollori STM32, che contiene
Librerie gratuite per tutte le periferiche, incluso il controllo motore e le tastiere touch.

SCHEMA DI COLLEGAMENTO STM32F100C4
Consideriamo l'uso pratico di MK usando l'esempio del più semplice dispositivo STM32F100C4, che, tuttavia, contiene tutti i blocchi principali della linea STM32F100. Lo schema del circuito elettrico dell'STM32F100C4 è mostrato nella Figura 5.


Riso. 5. Schema di collegamento per MK STM32F100C4

Il condensatore C1 assicura che il MK venga ripristinato all'accensione e i condensatori C2-C6 filtrano la tensione di alimentazione. I resistori R1 e R2 limitano la corrente del segnale dei pin MK. L'oscillatore interno viene utilizzato come sorgente di clock, quindi non è necessario utilizzare un cristallo esterno.


Gli ingressi BOOT0 e BOOT1 consentono di selezionare il metodo di caricamento del MK all'accensione secondo la tabella. L'ingresso BOOT0 è collegato al bus a potenziale zero tramite il resistore R2, che protegge il pin BOOT0 da un cortocircuito quando utilizzato come porta di uscita di PB2. Utilizzando il connettore J1 e un ponticello, è possibile modificare il potenziale sull'ingresso BOOT0, determinando così come viene caricato l'MK: dalla memoria flash o dal bootloader integrato. Se è necessario caricare il MK dalla RAM, è possibile collegare un connettore simile con un ponticello all'ingresso BOOT1.
La programmazione del MK viene effettuata tramite la porta seriale UART1 o tramite programmatori speciali: debugger JTAG o STLink. Quest'ultimo fa parte del popolare dispositivo di debug STM32VLDISCOVERY, mostrato nella Figura 6. Sulla scheda STM32VLDIS COVERY, il connettore a 4 pin del programmatore - debugger STLink - è designato SWD. L'autore dell'articolo suggerisce di programmare l'MK tramite la porta seriale UART1, poiché è molto più semplice, non richiede attrezzature speciali e non ha una velocità inferiore a JTAG o ST Link. Qualsiasi personal computer (PC) che disponga di una porta COM seriale o di una porta USB con convertitore USBRS232 può essere utilizzato come dispositivo di controllo in grado di generare comandi e visualizzare i risultati del programma MK, oltre che come programmatore.

Per interfacciare la porta COM di un PC con un MK, è adatto qualsiasi convertitore di segnali RS232 in livelli di segnale logico da 0 a 3,3 V, ad esempio il microcircuito ADM3232. La linea di trasmissione TXD della porta seriale del computer, dopo il convertitore di livello, va collegata all'ingresso PA10 del microcontrollore, e la linea di ricezione RXD, tramite un convertitore simile, all'uscita PA9.

Se è necessario utilizzare un orologio MK non volatile, è necessario collegare ad esso una batteria CR2032 con una tensione di 3 V e un risonatore al quarzo con una frequenza di 32768 Hz. A questo scopo il MK è dotato di pin Vbat/GND e OSC32_IN/OSC32_OUT. Il pin Vbat deve essere prima scollegato dal bus di alimentazione da 3,3 V.

I restanti terminali liberi dell'MK possono essere utilizzati secondo necessità. Per fare ciò, dovrebbero essere collegati ai connettori che si trovano attorno al perimetro del circuito stampato per MK, per analogia con i popolari dispositivi Arduino e la scheda di debug STM32VLDISCOVERY.


Riso. 6. Dispositivo di debug STM32VLDISCOVERY


Schema elettrico STM32VLDISCOVERY.

Pertanto, a seconda dello scopo e del metodo di utilizzo dell'MK, è possibile collegarvi gli elementi necessari per utilizzare altri blocchi funzionali e porte, ad esempio ADC, DAC, SPI, I2C, ecc. In futuro, questi dispositivi saranno considerati in modo più dettagliato.

PROGRAMMAZIONE
Oggi molte aziende offrono strumenti per la creazione e il debug di programmi per microcontrollori STM32. Questi includono Keil di ARM Ltd, IAR Embedded Workbench per ARM, Atollic TrueStudio, CooCox IDE, GCC e Eclipse IDE. Lo sviluppatore può scegliere il software in base alle sue preferenze. Di seguito descriveremo il toolkit Keil uVision 4 dell'azienda Keil, che supporta un numero enorme di tipi di microcontrollori, dispone di un sistema sviluppato di strumenti di debug e può essere utilizzato gratuitamente con restrizioni sulla dimensione del codice generato di 32 kbyte ( che, di fatto, è il massimo per i microcontrollori considerati).

Avvio facile e veloce con CooCox CoIDE.

Quindi iniziamo. Vai al sito web ufficiale di CooCox e scarica l'ultima versione di CooCox CoIDE. Per scaricarlo è necessario registrarsi, la registrazione è semplice e gratuita. Quindi installa il file scaricato ed eseguilo.

CooCox CoIDE- un ambiente di sviluppo basato su Eclipse, che, oltre a STM32, supporta una serie di altre famiglie di microcontrollori: Freescale, Holtek, NXP, Nuvoton, TI, Atmel SAM, Energy Micro, ecc. Con ogni nuova versione di CoIDE, il l'elenco dei microcontrollori è costantemente aggiornato. Dopo aver installato con successo CoIDE, esegui:

Apparirà la finestra di avvio del Passo 1, nella quale dovrai selezionare il produttore del nostro microcontrollore. Premi ST e vai allo Step 2 (selezione di un microcontrollore), in cui devi selezionare un modello specifico. Abbiamo STM32F100RBT6B, quindi fai clic sul modello corrispondente:

A destra, la finestra della Guida mostra brevi caratteristiche di ciascun chip. Dopo aver selezionato il microcontrollore di cui abbiamo bisogno, procediamo al terzo passaggio, Passaggio 3, per selezionare le librerie necessarie per il lavoro:

Creiamo un semplice progetto per far lampeggiare un LED, come è consuetudine per l'apprendimento dei microcontrollori.

Per fare questo abbiamo bisogno della libreria GPIO, quando abilitata, CoIDE ti chiederà di creare un nuovo progetto. Fare clic su Sì su questa proposta, indicare la cartella in cui verrà archiviato il nostro progetto e il suo nome. Allo stesso tempo, CoIDE collegherà al progetto altri 3 necessari al funzionamento della biblioteca, e creerà anche tutta la struttura progettuale necessaria:

Un altro aspetto positivo di CoIDE è che ha la capacità di caricare esempi direttamente nell'ambiente di sviluppo. Nella scheda Componenti puoi vedere che ci sono esempi per quasi tutte le librerie, fai clic su GPIO (con 4 esempi) e guardali:

Puoi aggiungere i tuoi esempi lì. Come puoi vedere nello screenshot qui sopra, gli esempi contengono già il codice per far lampeggiare il LED GPIO_Blink. Puoi fare clic sul pulsante Aggiungi e verrà aggiunto al progetto, ma come file incluso, quindi lo faremo diversamente e copieremo semplicemente l'intero codice di esempio nel file main.c. L'unica cosa è sostituire la riga void GPIO_Blink(void) con int main(void). Quindi, premi F7 (o seleziona Progetto->Costruisci dal menu) per compilare il progetto e... niente di così fortunato!

L'ambiente necessita di un compilatore GCC, ma non ne abbiamo uno. Pertanto, vai alla pagina Strumenti GNU per processori incorporati ARM, seleziona il tipo di sistema operativo sulla destra e scarica l'ultima versione della toolchain. Quindi eseguiamo il file e installiamo la toolchain gcc. Successivamente, nelle impostazioni di CoIDE indicheremo il percorso corretto alla toolchain:

Premi nuovamente F7 (Progetto->Build) e verifica che la compilazione sia andata a buon fine:

Non resta che flashare il microcontrollore. Per fare ciò, colleghiamo la nostra scheda al computer tramite USB. Quindi, nelle impostazioni del debugger è necessario installare ST-Link; per fare ciò, selezionare Progetto->Configurazione nel menu e aprire la scheda Debugger. Selezionare ST-Link dall'elenco a discesa e chiudere la finestra:

Proviamo a flashare il MK. Nel menu, seleziona Flash->Download programma (o fai clic sull'icona corrispondente sulla barra degli strumenti) e verifica che il flash di MK sia stato eseguito correttamente:

Vediamo un LED lampeggiante sulla scheda, penso che non abbia senso fornire un video o una foto, perché... tutti lo hanno visto.

Inoltre, in CoIDE funzionano varie modalità di debug; per farlo, premi CTRL+F5 (o nel menu Debug->Debug):

È tutto. Come puoi vedere, configurare e lavorare con CoIDE è molto semplice. Spero che questo articolo ti incoraggi a studiare microcontrollori STM32 molto promettenti ed economici.

Il microcontrollore STM32 è una piattaforma popolare e molto ricercata che consente di creare soluzioni di automazione professionale nei... A differenza del conveniente Arduino, STM32 richiede un'immersione più profonda nei dettagli, è più difficile per i principianti e ci sono meno libri di testo in russo per questo. In questo articolo cercheremo di darti le informazioni di base sulla piattaforma, la sua storia, dirti dove puoi scaricare programmi e librerie e come scrivere il tuo primo schizzo.

STM32 è una piattaforma basata su microcontrollori STMicroelectronics basati su un processore ARM, vari moduli e periferiche, nonché soluzioni software (IDE) per lavorare con l'hardware. Le soluzioni basate su stm vengono utilizzate attivamente grazie alle prestazioni del microcontrollore, alla sua architettura di successo, al basso consumo energetico e al prezzo basso. Attualmente STM32 è già composto da diverse linee per diversi scopi.

Storia dell'apparenza

La serie STM32 è stata rilasciata nel 2010. Prima di ciò, la STMicroelectronics aveva già prodotto 4 famiglie di microcontrollori basati su ARM, ma avevano caratteristiche peggiori. I controller STM32 si sono rivelati ottimali in termini di proprietà e prezzo. Inizialmente furono prodotte in 14 versioni, divise in 2 gruppi: con una frequenza di clock fino a 2 MHz e con una frequenza fino a 36 MHz. Il software per entrambi i gruppi è lo stesso, così come la posizione dei contatti. I primi prodotti sono stati realizzati con memoria flash integrata da 128 kB e 20 kB di RAM. Ora la linea si è ampliata in modo significativo, sono comparsi nuovi rappresentanti con valori aumentati di RAM e memoria Flash.

Vantaggi e svantaggi di STM32

Principali vantaggi:

  • Basso costo;
  • Facilità d'uso;
  • Ampia selezione di ambienti di sviluppo;
  • I chip sono intercambiabili: se le risorse di un microcontrollore non sono sufficienti, può essere sostituito con uno più potente senza modificare il circuito e la scheda stessa;
  • Alte prestazioni;
  • Comodo debug del microcontrollore.

Screpolatura:

  • Elevata barriera all’ingresso;
  • Al momento non esiste molta letteratura su STM32;
  • La maggior parte delle librerie create sono già obsolete, è più semplice crearne di proprie.

Gli svantaggi di STM32 non consentono ancora al microcontrollore di sostituire Arduino.

In termini di caratteristiche tecniche, Arduino è inferiore a STM32. La frequenza di clock dei microcontrollori Arduino è inferiore: 16 MHz contro 72 MHz STM32. L'STM32 ha più pin GRIO. Anche la capacità di memoria di STM32 è maggiore. È impossibile non notare la compatibilità pin-to-pin di STM32: per sostituire un prodotto con un altro, non è necessario cambiare la scheda. Ma i concorrenti non possono sostituire completamente Arduino. Innanzitutto, ciò è dovuto all'elevata barriera all'ingresso: per lavorare con STM32 è necessario disporre di una base. Le schede Arduino sono più comuni e, se un utente ha un problema, può trovare una soluzione sui forum. Inoltre, per Arduino sono stati creati vari scudi e moduli che ne espandono le funzionalità. Nonostante i vantaggi, l'STM32 vince in termini di rapporto qualità/prezzo.

La famiglia di microcontrollori STM32 si differenzia dai suoi concorrenti per l'eccellente comportamento a temperature da -40°C a +80°C. Le prestazioni elevate non diminuiscono, a differenza di Arduino. Puoi anche trovare prodotti che funzionano a temperature fino a 105°C.

Panoramica delle linee di prodotti


La famiglia STM32 dispone di un'ampia gamma di prodotti che variano in termini di capacità di memoria, prestazioni, consumo energetico e altre caratteristiche.

Le serie STM32F-1, STM32F-2 e STM32L sono completamente compatibili. Ogni serie ha dozzine di microcircuiti che possono essere facilmente scambiati con altri prodotti. STM32F-1 era la prima linea, le sue prestazioni erano limitate. Per questo motivo, le caratteristiche dei controller hanno rapidamente raggiunto i prodotti della famiglia Stellaris e LPC17. Successivamente è stato rilasciato l'STM32F-2 con caratteristiche migliorate: la frequenza dell'orologio ha raggiunto 120 MHz. Presenta un'elevata potenza di elaborazione, ottenuta grazie alla nuova tecnologia di produzione a 90 nm. La linea STM32L è rappresentata da modelli realizzati utilizzando uno speciale processo tecnologico. La perdita dei transistor è minima, per cui i dispositivi mostrano i valori migliori.

È importante notare che i controller di linea STM32W non hanno compatibilità pin-to-pin con STM32F-1, STM32F-2 e STM32L. Il motivo è che la linea è stata sviluppata dalla società che ha fornito la parte RF. Ciò ha posto limitazioni allo sviluppo di ST.


Il chip STM32F100R4 ha un set minimo di funzioni. La memoria flash è di 16 KB, la RAM è di 4 KB e la frequenza di clock è di 12 MHz. Se avete bisogno di un dispositivo più veloce con una maggiore capacità di memoria flash fino a 128 KB, l'STM32F101RB è adatto. Il prodotto STM32F103RE dispone di un'interfaccia USB. Esiste un dispositivo simile, ma con un consumo inferiore: questo è STM32L151RB.

Software per lavorare con il controller


Molti ambienti di sviluppo sono stati sviluppati per l'architettura ARM. I più famosi e costosi sono gli strumenti di Keil e IAR System. I programmi di queste aziende offrono gli strumenti più avanzati per l'ottimizzazione del codice. Esistono anche vari sistemi aggiuntivi: stack USB, stack TCP/IP e altri. Utilizzando i sistemi Keil l'utente riceve un buon livello di supporto tecnico.

STM32 utilizza anche l'ambiente di sviluppo Eclipse e i sistemi Atollic TrueStudio (a pagamento) e CooCox IDE (CoIDE) (gratuito) basati su di esso. Solitamente viene utilizzato quest'ultimo. I suoi vantaggi rispetto ad altri ambienti di sviluppo:

  • Software gratis;
  • Facilità d'uso;
  • Ci sono molti esempi che puoi scaricare.

L'unico inconveniente dell'ambiente di sviluppo IDE CooCox è che l'assembly è disponibile solo per Windows.


È meglio iniziare a studiare il microcontrollore STM32 con la scheda Discovery. Ciò è dovuto al fatto che questa scheda ha un programmatore integrato. Può essere collegato a un computer tramite un cavo USB e utilizzato sia come microcontrollore programmabile che per dispositivi esterni. La scheda Discovery ha una piedinatura completa dal controller ai pin della scheda. È possibile collegare alla scheda vari sensori, microfoni e altri dispositivi periferici.

Cosa ti serve per connettere STM32 al tuo computer

Per iniziare, avrai bisogno dei seguenti componenti:

  • La stessa scheda STM32 Discovery;
  • Scheda tecnica del modello selezionato;
  • Manuale di riferimento per il microcontrollore;
  • Ambiente di sviluppo installato sul computer.

Ad esempio, il primo programma verrà considerato nell'IDE CooCox.

Primo programma


CooCox CoIDE

L'apprendimento dovrebbe iniziare con la cosa più semplice: Hello World. Per prima cosa devi installare CooCox IDE sul tuo computer. Installazione standard:

  • Scarica il programma dal sito ufficiale;
  • Lì devi inserire il tuo indirizzo email e iniziare a scaricare il file con estensione .exe;
  • È necessario aprire la scheda IDE CooCox Progetto, selezionare il percorso della toolchain;
  • Specificare il percorso del file;
  • Aprire nuovamente l'ambiente di sviluppo e fare clic su Visualizza -> Configurazione nella scheda Debugger;
  • Ora puoi registrare il programma.

Una volta installato il programma, è necessario aprirlo. Dovresti andare alla scheda Sfoglia nel repository e selezionare ST: il tuo microcontrollore.

Successivamente, sullo schermo verrà visualizzato l'elenco delle librerie che possono essere collegate. Il primo programma richiederà il core del sistema CMSIS e CMSIS Boot, una libreria per lavorare con il sistema di clock RCC, GPIO per lavorare con i pin.

Il programma stesso è scritto come per Arduino, è necessario conoscere le basi del linguaggio C.

Nella finestra Progetto, apri main.c. Nel codice, all'inizio, dovresti includere librerie diverse da CMSIS (sono già incluse automaticamente). Sono aggiunti come segue:

#include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h". //Per far lampeggiare il LED, è necessario impostare un ritardo: void Delay(int i) ( for (; i != 0; i--); )

Quindi l'orologio del porto viene aggiunto nella funzione principale main. Quale contatto è responsabile di ciò che può essere visualizzato nella scheda tecnica del microcontrollore.

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ABILITA);

Per configurare i parametri di output, dovresti scrivere il suo nome e mettere un punto. Un menu a comparsa elencherà tutte le caratteristiche. Possono essere corretti.

Successivamente, è necessario eseguire il loop in modo che il LED lampeggi fino allo spegnimento dell'alimentazione.

Una volta scritto il programma, è possibile caricarlo nel controller. Se disponi di una scheda di debug, devi collegarla tramite un cavo USB e fare clic su Scarica codice per flash. Se manca la scheda, sarà necessario un adattatore che dovrà essere collegato alla porta del computer. Il contatto BOOT 0 è collegato al positivo dell'alimentatore del controller, quindi l'alimentatore stesso del MK viene acceso. Successivamente, inizierà il firmware.

Per caricare il programma nel microcontrollore, è necessario seguire le istruzioni dell'applicazione. Innanzitutto viene scritto il codice della porta a cui è collegato il microcontrollore. Viene indicata anche la velocità. Si consiglia di assumere un valore basso per evitare errori. Il programma troverà il microcontrollore e dovrai fare clic sul pulsante "Avanti". Nella scheda Scarica sul dispositivo è necessario selezionare il programma scritto nel campo Scarica da file e fare clic su “Avanti”.

Successivamente, è necessario spegnere il controller STM32, chiudere Flash Loader Demonstrator e spegnere l'adattatore. Ora puoi riaccendere il microcontrollore in modalità normale. Una volta caricato il programma, il LED inizierà a lampeggiare.

Il lavoro in altri programmi funziona in modo simile. Vengono selezionate anche le librerie richieste e viene scritto il codice. Le utilità a pagamento hanno più funzionalità e puoi creare progetti più complessi.

Un giorno, quando mi sono trasferito in un altro appartamento in affitto, ho riscontrato un inconveniente abbastanza fastidioso: l'interruttore della luce nella stanza principale era dietro il pensile, che era avvitato al muro, e il suo spostamento era impossibile perché... ciò ha richiesto una notevole quantità di tempo e impegno. Volevo davvero risolvere questo problema e mi è venuto in mente un pensiero: crea un telecomando per controllare l'illuminazione!

È stato con l'idea di creare un proprio telecomando per controllare la luce della stanza che è iniziata la mia passione per l'elettronica, i microcontrollori e i vari dispositivi radio.

Successivamente, ho iniziato a studiare questo argomento, a conoscere le basi dell'elettronica, esempi di dispositivi e a scoprire come le persone implementano tali dispositivi. Dopo aver cercato informazioni su dove avrei potuto iniziare a studiare i microcontrollori, ho imparato cos'è Arduino, a cosa servono e come lavorarci. La soluzione semplice sembrava molto interessante perché, per quanto avevo capito allora, il codice poteva essere assemblato solo in una volta sola. Ma avendo concluso che non avrei saputo cosa stesse succedendo all'interno del microcontrollore oltre agli schizzi di Arduino, ho deciso di cercare un'opzione più interessante, che implicasse uno studio approfondito e un'immersione nella giungla della tecnologia dei microcontrollori.

L'azienda per cui lavoro ha un reparto di sviluppo e ho deciso di rivolgermi agli ingegneri in modo che potessero guidarmi sulla strada giusta e mostrarmi da dove avrei potuto iniziare a risolvere il mio problema. Fui decisamente dissuaso dallo studio di Arduino e mi ritrovai tra le mani una sciarpa verde sconosciuta e incomprensibile sulla quale erano visibili iscrizioni, lettere e vari componenti elettronici.

Tutto questo mi sembrava incomprensibilmente difficile in quel momento, e mi sentivo anche un po' confuso, ma non avrei rinunciato al compito. È così che ho conosciuto la famiglia di microcontrollori STM32 e la scheda STM32F0-Discovery, dopo aver studiato quale vorrei personalizzare il mio dispositivo per gli scopi di cui ho bisogno.

Con mia grande sorpresa, una community così numerosa, articoli, esempi e materiali vari su STM non erano così abbondanti come su Arduino. Naturalmente se cerchi troverai tanti articoli “per principianti” che descrivono come e da dove iniziare. Ma in quel momento mi è sembrato che tutto questo fosse molto difficile, non venivano raccontati molti dettagli che erano interessanti per la mente curiosa di un principiante, cose. Sebbene molti articoli fossero caratterizzati come "formazione per i più piccoli", non sempre è stato possibile ottenere il risultato richiesto con il loro aiuto, anche con esempi di codice già pronti. Ecco perché ho deciso di scrivere una breve serie di articoli sulla programmazione dell'STM32 alla luce della realizzazione di un'idea specifica: un pannello di controllo dell'illuminazione in una stanza.

Perché non AVR/Arduino?

Anticipando affermazioni secondo cui sarebbe troppo presto per un principiante inesperto precipitarsi immediatamente nello studio di un microcontrollore così complesso come l'STM32, vi dirò perché ho deciso di intraprendere questa strada, senza approfondire o conoscere la famiglia di processori Atmel e senza nemmeno considerare Arduino come un'opzione.

In primo luogo, il rapporto prezzo-funzionalità ha giocato un ruolo decisivo; la differenza è visibile anche tra uno dei MK più economici e semplici della ST e il piuttosto “grasso” ATMega:


Dopo aver notato differenze significative tra il prezzo e le capacità di AVR e STM32, ho deciso che non avrei utilizzato AVR nel mio sviluppo =)

In secondo luogo, ho prima cercato di determinare da solo l'insieme di competenze che avrei ricevuto una volta raggiunto il risultato richiesto. Se decidessi di utilizzare Arduino mi basterebbe copiare librerie già pronte, aggiungere uno schizzo e voilà. Ma capire come funzionano gli autobus digitali, come funziona un trasmettitore radio, come è tutto configurato e utilizzato, in questa situazione non ci sarei mai riuscito. Per quanto mi riguarda, ho scelto il percorso più difficile e spinoso, in modo da acquisire la massima esperienza e conoscenza sulla strada per ottenere risultati.

In terzo luogo, qualsiasi STM32 può essere sostituito da un altro STM32, ma con caratteristiche migliori. E senza modificare il circuito di commutazione.

In quarto luogo, le persone coinvolte nello sviluppo professionale sono più propense a utilizzare MCU a 32 bit e molto spesso si tratta di modelli di NXP, Texas Instruments e ST Microelectronics. Sì, e potrei in qualsiasi momento contattare i miei ingegneri del reparto sviluppo e scoprire come risolvere questo o quel problema e ottenere consigli su questioni che mi interessano.

Perché dovresti iniziare ad apprendere i microcontrollori STM32 utilizzando la scheda Discovery?

Come già sapete, inizieremo con voi, cari lettori, la nostra conoscenza e studio del microcontrollore STM32, utilizzando la scheda Discovery. Perché Discovery e non la tua tavola?

Di cosa abbiamo bisogno per lo sviluppo oltre alla scheda Discovery?

Nel nostro lavoro con la scheda Discovery, avremo bisogno di una serie di altre cose insostituibili di cui non possiamo fare a meno:

Iniziamo con la configurazione iniziale e la preparazione dell'IDE per il lavoro!

Dopo aver scaricato il file di installazione del nostro IDE, puoi iniziare l'installazione. Seguire le istruzioni del programma di installazione e completare il processo di installazione. Dopo che tutti i file necessari per il lavoro sono stati copiati, apparirà la finestra di installazione del pacchetto software per lo sviluppo Programma di installazione del pacchetto. Questo programma di installazione contiene librerie di basso livello, middleware e programmi di esempio che vengono regolarmente aggiornati e aggiornati.


Per iniziare a lavorare con la nostra scheda, dobbiamo installare una serie di pacchetti necessari per il lavoro e dobbiamo trovare un microcontrollore con cui lavoreremo. Puoi anche utilizzare la ricerca nella parte superiore della finestra. Dopo aver trovato il nostro MK, fai clic su di esso nella seconda metà della finestra e dobbiamo installare il seguente elenco di librerie:
  1. Keil::STM32F0xx_DFP– un pacchetto software completo per una famiglia specifica di microcontrollori, inclusi manuali, schede tecniche, file SVD, librerie del produttore.
  2. BRACCIO::CMSIS– Pacchetto Cortex Microcontroller Software Interface Standard, che include un set completo di librerie ARM per supportare il core Cortex.
  3. Keil::ARM_Compiler– l'ultima versione del compilatore per ARM.
Dopo aver installato i pacchetti richiesti, puoi procedere alla configurazione dell'IDE e del nostro debugger/programmatore. Per fare ciò, dobbiamo aprire la finestra principale di Keil e creare un nuovo progetto.


Per fare questo devi andare al menu Progetto -> Nuovo progetto uVision e seleziona la cartella in cui salvare il nostro progetto.

Successivamente Keil ci chiederà quale MK verrà utilizzato nel progetto. Seleziona il MK di cui abbiamo bisogno e fai clic OK.


E ancora apparirà una finestra che ci è già familiare in cui potremo collegare i moduli che ci interessano al progetto. Per il nostro progetto avremo bisogno di due moduli:
  1. Nucleo della libreria CMSIS, che dichiara le impostazioni, gli indirizzi dei registri e molto altro necessario per il funzionamento del nostro MK.
  2. File di avvio, che è responsabile dell'inizializzazione iniziale di MK all'avvio, della dichiarazione di vettori e gestori di interruzioni e molto altro.
Se tutte le dipendenze di coloro che si collegano sono soddisfatte, il gestore ce lo segnalerà in verde:


Dopo aver premuto il tasto OK possiamo iniziare a creare il nostro progetto.

Per configurare i parametri del progetto e configurare il nostro programmatore, è necessario fare clic con il tasto destro su Obiettivo 1 aprire il menu corrispondente.


Nel menu principale del progetto, impostare il parametro Xtal nel significato 8,0 MHz . Questo parametro è responsabile della frequenza operativa dell'oscillatore al quarzo del nostro MK:


Successivamente passiamo alla configurazione del nostro programmatore/debugger. Fare clic sulla scheda nella stessa finestra Debug e seleziona nel campo Utilizzo parametro Debug ST-Link e vai alle impostazioni:


Nelle impostazioni dovremmo vedere il modello del nostro ST-Link installato sulla scheda, il suo numero di serie, versione HW e IDCODE del MK che flasheremo:

Per comodità, è possibile configurare un parametro che assicuri che il MK venga ripristinato automaticamente dopo il lampeggio. Per fare ciò, devi spuntare la casella Ripristina ed esegui.


Successivamente, dobbiamo configurare un'altra opzione che ci permetterà di scrivere commenti in lingua russa sul codice dei nostri progetti. premi il bottone Configurazione e nel menu che si apre nel campo Codifica scegliere Russo Windows-1251 .


Tutto. Il nostro IDE e il nostro programmatore sono pronti a partire!

Keil dispone di un comodo navigatore di progetto, in cui possiamo vedere la struttura del progetto, i materiali di riferimento necessari per il lavoro, compresi quelli che abbiamo già scaricato in precedenza sul nostro computer (diagramma di scoperta, scheda tecnica, manuale di riferimento), elenco delle funzioni utilizzate nel progetto e modelli per inserire rapidamente diversi costrutti linguistici del linguaggio di programmazione.


Rinominiamo la cartella nella struttura del progetto con Gruppo di origini 1 SU Applicazione/Utente , indicando quindi che avremo i file dei programmi utente in questa cartella:


Aggiungiamo il file di programma principale tramite il navigatore del progetto eseguendo il comando Aggiungi un nuovo elemento al gruppo "App/Utente" .


È necessario selezionare dall'elenco fornito File C (.c) e dargli un nome principale.c :


Il file creato verrà automaticamente aggiunto alla struttura del progetto e aperto nella finestra principale del programma.

Bene, ora possiamo iniziare a creare il nostro programma.

Innanzitutto dobbiamo collegare il documento header della nostra famiglia di microcontrollori al nostro file eseguibile. Aggiungi al file principale.c linee con il seguente contenuto, questo programma farà lampeggiare alternativamente i nostri LED:

/* File header per la nostra famiglia di microcontrollori */ #include "stm32f0xx.h" /* Corpo del programma principale */ int main(void) ( /* Abilita il clock sulla porta GPIO */ RCC->AHBENR |= RCC_AHBENR_GPIOCEN ; /* Configura la modalità operativa delle porte PC8 e PC9 in Output*/ GPIOC ->MODER = 0x50000; /* Imposta il tipo di output su modalità Push-Pull */ GPIOC->OTYPER = 0; /* Imposta la velocità della porta su Bassa */ GPIOC->OSPEEDR = 0; while(1) ( /* Accende il LED PC8, spegne PC9 */ GPIOC->ODR = 0x100; for (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = 0x200; for (int i=0; i<500000; i++){} // Искусственная задержка } }
Dopo aver scritto il nostro programma, era il momento di compilare il codice e caricare il firmware sul nostro MK. Per compilare il codice e scaricarlo potete utilizzare questo menu:


Squadra Costruire (o il tasto F7) compilerà il codice e, se non ci sono errori, il programma visualizzerà nel log di compilazione il seguente messaggio indicando che non ci sono errori o avvisi:


Squadra Carico (o il tasto di scelta rapida F8) caricherà il codice compilato nel nostro MK e lo invierà automaticamente per l'esecuzione:


Dopo aver caricato il codice vedremo i led iniziare a lampeggiare ad intervalli regolari.


Evviva! Abbiamo fatto il primo passo verso la padronanza dei microcontrollori STM32! In questo articolo vedremo cosa sono le operazioni bit e logiche, come usarle e impareremo un'utilità molto utile per lavorare con MK, ma per ora possiamo goderci come lampeggiano allegramente i LED sulla nostra scheda Discovery.)

§> Domande generali. Variabili dichiarate dall'utente.

Quindi, il linguaggio C è un tipico rappresentante dei linguaggi di programmazione astratti, il che significa che non è affatto interessato al tipo di informazioni che elaboreremo, sia esso il contenuto di un file di computer o i registri di controllo interno di un microcontrollore.


L'oggetto principale della programmazione per il C classico è una variabile. Può trattarsi di una singola variabile o di un gruppo di variabili particolarmente correlate, come un array o una struttura. In sostanza, una variabile è una sorta di memoria per un numero che ha il suo nome univoco e un intervallo di valori accettabile, oltre il quale è estremamente indesiderabile andare oltre. E la prima cosa che dobbiamo fare prima di iniziare a usare il nome di una variabile nel testo del programma è introdurre il programma alle sue proprietà. In C, questo processo è chiamato dichiarazione di variabile.

Perché è necessario dichiarare le variabili?

Sebbene il linguaggio C sia astratto, il microcontrollore utilizzato dallo sviluppatore è, di regola, abbastanza concreto e dispone di un proprio spazio di indirizzi di memoria con proprietà specificate, in cui verrà archiviata la variabile dichiarata. La dichiarazione, oltre ad assegnare un nome alla variabile, obbliga il compilatore a collocarla ad un indirizzo specifico nella memoria del microcontrollore (nella maggior parte dei casi non ci interessa affatto quale indirizzo).

Come dovrebbero essere dichiarate le variabili?

La regola per la dichiarazione può essere formulata come segue: prima di utilizzare per la prima volta il nome di una variabile nel testo del nostro programma, è necessario inserire la sua dichiarazione nel seguente formato:

Digitare il nome; // Variabile con nome "nome" e tipo "tipo".

Qui: type è il cosiddetto identificatore di tipo variabile da un certo insieme di tipi standard;
nome - un nome di variabile arbitrario, purché non inizi con un numero, consiste solo di caratteri latini e non coincide con le parole del servizio del linguaggio C (il cui elenco non è così lungo che devi davvero impegnarti molto affrontare una situazione del genere).

Cos'è un identificatore di tipo e perché menzionarlo?

Per memorizzare le variabili, il microcontrollore utilizza celle di memoria, la cui dimensione è determinata dalla sua capacità in bit. Ad esempio, i microcontrollori della famiglia AVR sono a 8 bit, il che significa che utilizzano celle di memoria di un byte per memorizzare i dati, che sono in grado di memorizzare 256 diversi valori numerici. Se i valori attesi di una variabile possono superare questo numero, saranno necessarie due o più posizioni di memoria per memorizzarla. Poiché C, in senso stretto, non rappresenta quali valori intendiamo assegnare a una variabile, ci chiede di indicarne il tipo, che determina con precisione l'intervallo di valori consentito. Ciò è necessario per non riservargli quantità di memoria eccessive o inaccettabilmente piccole e anche per avvisarci se proviamo ad assegnare un valore troppo grande a una variabile che non è in grado di memorizzarlo. Per i microcontrollori a 8 bit, i tipi di dati interi più comunemente utilizzati sono:

In grado di memorizzare solo valori positivi (senza segno):
carattere senza segno - occupa un byte di memoria, valori 0...255
unsigned int - due byte, valori 0...65535
senza segno lungo: quattro byte, da 0 a (2^32)-1
in grado di memorizzare valori con segno (signed):
carattere con segno: occupa un byte di memoria, da -128...127
Signed int - due byte, valori -32768...32767
firmato lungo - richiede quattro byte, valori da -(2^31) a (2^31)

La parola chiave "unsigned", in generale, può essere omessa, poiché in C, per impostazione predefinita, un tipo per il quale questo attributo non è specificato è considerato unsigned.
Per lavorare con i numeri frazionari, C fornisce tipi a virgola mobile:

Float – 32 bit, valori da ±1.18E-38 a ±3.39E+38
double – 32 (±1.18E-38…±3.39E+38) o 64 bit (±2.23E-308…±1.79E+308) a seconda delle impostazioni del compilatore.

Nota: la dimensione della memoria per la memorizzazione delle variabili dei tipi specificati e l'intervallo di valori validi possono variare leggermente a seconda dell'ambiente di sviluppo o della famiglia di microcontrollore.

Affinché una variabile abbia già un valore specifico prima di usarla, spesso viene aggiunto un inizializzatore alla dichiarazione: un segno di uguale (in C è un operatore di assegnazione) e il valore iniziale della variabile.

Per esempio:

InterA=100; // Variabile denominata "A" di tipo int e valore iniziale pari a 100.

Esempio pratico: supponiamo di voler scrivere un programma che faccia lampeggiare un LED 5 volte. Per contare il numero di lampeggi avrete bisogno di una variabile il cui valore ovviamente non sarà mai negativo e non andrà oltre l'intervallo compreso tra 0 e 255, il che significa che in questo caso sarà più che sufficiente utilizzare il tipo char a byte singolo:

§> Ambito di visibilità variabile.

La fonte di molte difficoltà per i principianti è una proprietà del linguaggio chiamata scope di una variabile dichiarata. Il linguaggio C ha la capacità di limitare l'azione di una variabile ad un'area specifica del codice del programma, mentre in altre parti del programma diventa inaccessibile, liberando così memoria che può essere utilizzata da altre variabili in altre parti del programma. il programma. Tali variabili sono chiamate locali e il loro utilizzo è il modo principale per produrre codice efficiente.

Nella fase iniziale della formazione, è consigliabile dichiarare tutte le variabili come globali. Per fare ciò, le loro dichiarazioni devono essere poste all'inizio del programma, prima e all'esterno di qualsiasi funzione. In questo caso, puoi essere sicuro che saranno disponibili per lavorare ovunque nel programma all'interno del file corrente.

§> Area di posizionamento variabile.

Come sapete, i microcontrollori della famiglia AVR contengono tre aree di memoria, implementate utilizzando tecnologie diverse. Ognuno di essi ha il proprio scopo e spazio di indirizzi, numerati da zero al valore massimo per un modello specifico:


La RAM, la memoria EEPROM non volatile possono essere utilizzate per memorizzare le variabili utente e la memoria FLASH del microcontrollore può anche essere utilizzata per memorizzare costanti il ​​cui valore non può essere modificato durante il funzionamento del programma.

Per cominciare è utile sapere che le variabili dichiarate dall'utente senza l'utilizzo di parole chiave particolari come _eeprom o _flash si trovano nella RAM del microcontrollore, sotto forma di una o più celle SRAM. Durante il funzionamento, vengono periodicamente copiati nella memoria di registro veloce RON, che interagisce direttamente con l'unità aritmetico-logica ALU del microcontrollore.
I problemi relativi all'inserimento delle variabili all'interno della RAM, di norma, interessano solo nel contesto delle prestazioni del programma.

§> Registri per scopi speciali del microcontrollore SFR.

Quindi, abbiamo esaminato brevemente la dichiarazione delle variabili destinate all'organizzazione del processo di calcolo, che hanno poco a che fare con le specificità dell'hardware MK.

Il controllo e il monitoraggio del funzionamento del microcontrollore e dei suoi singoli moduli interni vengono effettuati scrivendo e leggendo celle di registro speciali nell'area di servizio della memoria RAM - registri per scopi speciali (Registro di funzioni speciali, di seguito semplicemente SFR).

L'idea di base che permette di utilizzare il C per programmare i microcontrollori è questa: i registri di valori speciali sono le stesse variabili del linguaggio C dichiarate dall'utente. A queste variabili possono essere assegnati valori per controllare il funzionamento del microcontrollore, oppure possono essere lette, ottenendo così informazioni sul suo stato attuale. Non è necessario dichiarare i registri del microcontrollore come variabili utente per diversi motivi. Innanzitutto la loro dimensione è nota in anticipo: in C per AVR si tratta di variabili a 8 bit senza segno. In secondo luogo, gli SFR hanno nomi e indirizzi in memoria rigorosamente definiti, essendo i cosiddetti registri I/O.

Tuttavia, è necessario familiarizzare il programma con registri per scopi speciali, e ciò avviene collegando i cosiddetti file di intestazione.

All'inizio di qualsiasi programma C possiamo vedere righe come:

#include "file1.h" // Include il contenuto del file "file1.h" nel codice.

#include è una direttiva (indicazione) che forza l'ambiente di sviluppo a posizionare il contenuto di un file chiamato file1.h in questo punto del programma. I file con estensione .h sono chiamati header o file h. Lo sviluppatore può creare i propri file h e posizionarli, a seconda del contenuto, ovunque nel programma. Tuttavia, per introdurre il programma in SFR per un dato tipo di microcontrollore, è necessario includere file di intestazione molto specifici. I loro nomi e numeri dipendono dall'ambiente di sviluppo specifico e dal tipo di microcontrollore utilizzato, ad esempio nell'IAR per Atmega64 è sufficiente scrivere le righe:

#include "iom64"
#include "inavr.h"

Dopo aver incluso i file h necessari nel testo, il programma riconoscerà i nomi SFR in esso menzionati, ad esempio il registro di stato del microcontrollore AVR con il nome SREG, il buffer di ricezione/trasmissione del modulo UART - UDR e Presto.

Un modello di programma per IAR, che non fa nulla, ma non "impreca più" sui nomi dei registri speciali del microcontrollore Atmega16, dovrebbe assomigliare a questo:

#include "iom16.h"
#include "inavr.h"
carattere senza segno ChisloMiganiy=0;
vuoto principale (vuoto)
{
// Qui inseriremo un programma che utilizza la variabile ChisloMiganiy
// e tutti i registri Atmega16 i cui nomi sono scritti nel file iom16.h.
}

Vorrei sperare che il lettore abbia familiarità con le regole per la formattazione dei commenti nel testo del programma. Si tratta di note che vengono ignorate dal linguaggio C e non vengono considerate parte del codice del programma se scritte su una o più righe racchiuse tra i caratteri /* e */, oppure su un'unica riga che inizia con la sequenza //.

§> Panoramica delle operazioni di registro standard.

È ora di passare a operazioni più serie su registri e variabili di programma. Il controllo del funzionamento di un microcontrollore nella maggior parte dei casi si riduce al seguente semplice insieme di azioni con i suoi registri:

1. Scrivere il valore richiesto nel registro.
2. Leggere il valore del registro.
3. Impostazione dei bit di registro richiesti su uno.
4. Reimpostazione dei bit del registro a zero.
5. Controllo del bit per uno logico o zero logico.
6. Cambiare lo stato logico di un bit di registro nel contrario.

Tutte queste azioni coinvolgono l'operatore di assegnazione del linguaggio C, scritto come segno di uguale. Il principio di funzionamento dell'operatore è primitivamente semplice: scrive in un registro o in una variabile situata a sinistra di esso il valore di ciò che è scritto a destra. A destra può esserci una costante, un altro registro, una variabile o un'espressione composta da essi, ad esempio:

A = 16; // Assegna la variabile A a 16;
A=B; // Leggi il valore della variabile B e assegna questo valore alla variabile A;
A = B+10; // Leggi il valore della variabile B, aggiungi 10 al valore letto, assegna il risultato alla variabile A (il valore della variabile B non cambia).

§> Scrivere e leggere registri.

Dagli esempi discussi è chiaro che l'operatore di assegnazione stesso risolve i primi due problemi: scrivere e leggere i valori del registro. Ad esempio, per inviare un byte al microcontrollore AVR tramite il bus UART, è sufficiente scriverlo in un registro di trasmissione denominato UDR:

UDR = 8; // Invia il numero 8 tramite UART;

Per ottenere un byte ricevuto tramite UART basta leggerlo dal registro UDR:

§> Impostazione dei bit di registro.

Il linguaggio C non include comandi per reimpostare o impostare direttamente i bit variabili, tuttavia esistono operazioni logiche bit per bit “AND” e “OR” che vengono utilizzate con successo per questi scopi.
L'operatore logico bit a bit "OR" è scritto come una barra verticale - "|" e può essere eseguita tra due variabili, nonché tra una variabile e una costante. Permettetemi di ricordarvi che l'operazione “OR” su due bit risulta in un bit se almeno uno dei bit sorgente è nello stato uno. Pertanto, per qualsiasi bit, un "OR" logico con un "1" risulterà in un "1", indipendentemente dallo stato di questo bit, e un "OR" con uno "0" logico lascerà lo stato originale po' invariato. Questa proprietà consente di utilizzare l'operazione OR per impostare l'ennesimo bit in un registro. Per fare ciò, è necessario calcolare una costante con un singolo N-esimo bit utilizzando la formula 2^N, chiamata maschera di bit, ed eseguire un "OR" logico tra essa e il registro, ad esempio, per impostare il bit n. 7 nel registro SREG:

(SREG | 128) - questa espressione legge il registro SREG e imposta il settimo bit nel valore letto, quindi il valore sufficientemente modificato viene nuovamente inserito nel registro SREG:

SREG = SREG | 128; // Imposta il bit #7 del registro SREG.

Tale lavoro con un registro viene solitamente chiamato "lettura - modifica - scrittura"; a differenza del semplice assegnazione, preserva lo stato dei bit rimanenti senza essere nominato.
Il codice di programma riportato sopra, impostando il settimo bit nel registro SREG, svolge un lavoro molto significativo: consente al microcontrollore di gestire gli interrupt software. L'unico inconveniente di questa notazione è che non è facile indovinare il settimo bit impostato nella costante 128, per cui molto spesso la maschera per l'N-esimo bit viene scritta nella seguente forma:

(1<

SREG = SREG | (1<<7);

O ancora più semplice utilizzando la forma abbreviata del linguaggio C:

SREG |= (1<<7);

Ciò significa: prendi il contenuto a destra del segno uguale, esegui l'operazione tra esso e il registro a sinistra che precede il segno uguale e scrivi il risultato nel registro o variabile a sinistra.

§> Resetta i bit nei registri.

Un'altra operazione logica del linguaggio C è l'“AND” bit a bit, scritto come il simbolo “&”. Come sapete, l'operazione logica “AND”, quando applicata a due bit, dà uno se e solo se entrambi i bit sorgente hanno un valore uno, questo ne consente l'utilizzo per reimpostare i bit nei registri. In questo caso viene utilizzata una maschera di bit in cui tutti i bit sono uno, tranne lo zero nella posizione di reset. Si può ottenere facilmente da una maschera con l'ennesimo bit impostato applicando ad essa un'operazione di inversione bit a bit:
~(1<

SREG = SREG & (~(1<<7)); или кратко: SREG &= ~ (1<<7);

Il file di intestazione menzionato in precedenza per un microcontrollore specifico contiene nomi standard di bit di registro per scopi speciali, ad esempio:

#definireOCIE0 1

Qui #define è un'istruzione al compilatore per sostituire la combinazione di caratteri "OCIE0" nel testo del programma con il numero 1, ovvero il nome standard del bit OCIE0, che fa parte del registro TIMSK del microcontrollore Atmega64 con il suo numero di sequenza in questo registro. Grazie a ciò l'impostazione del bit OCIE0 nel registro TIMSK può essere scritta più chiaramente come segue:

TIMSK|=(1<

È possibile impostare o reimpostare più bit di registro contemporaneamente combinando le maschere di bit nelle espressioni con l'operatore logico "OR":

PORTA |= (1<<1)|(1<<4); // Установить выводы 1 и 4 порта A в единицу;
PORTA&=~((1<<2)|(1<<3)); // Выводы 2 и 3 порта A сбросить в ноль.

Esempio di utilizzo con registri definiti in CMSIS:

DAC0->CTRL |= DAC_CTRL_DIFF; // installazione
DAC0->CTRL &= ~DAC_CTRL_DIFF; //Ripristina

§> Controllo dei bit di registro per zero e uno.

I registri speciali dei microcontrollori contengono molti bit di flag, i cosiddetti "flag", che notificano al programma lo stato corrente del microcontrollore e dei suoi singoli moduli. Il controllo del livello logico di un flag si riduce alla selezione di un'espressione che diventa vero o falso a seconda che sia impostato o che questo bit nel registro venga resettato. Questa espressione può essere un "AND" logico tra il registro e la maschera con il bit N impostato nella posizione del bit da controllare:

(REGISTRAZIONE & (1<

L'espressione di cui sopra può essere utilizzata in un'istruzione if condizionale (espressione) o in un operatore di ciclo while (espressione), che appartengono al gruppo logico, ovvero accettano valori vero e falso come argomenti. Poiché il linguaggio C, quando converte valori numerici in valori logici, percepisce qualsiasi numero diverso da zero come verità logica, il valore (REGISTR & (1<Se è necessario ottenere il valore logico “false” per la nostra espressione quando è impostato il bit N, è sufficiente integrarlo con un operatore di inversione logica sotto forma di punto esclamativo - !(REGISTR & (1<

Mentre (!(UCSRA & (1<

Qui, con il bit UDRE deselezionato, l'espressione (UCSRA & (1<!(UCSRA & (1<

§> Inversione dello stato di un bit di registro.

Questo problema, se così posso dire, viene risolto con successo dall'operazione logica del bit "EXCLUSIVE OR" e del corrispondente operatore C, scritto come il simbolo "^". Una regola di OR esclusivo a due bit è vera se e solo se uno dei bit è impostato e l'altro è azzerato. Non è difficile verificare che questo operatore, applicato tra la maschera di bit e il registro, copierà i bit opposti i bit di zero al risultato mascherano senza cambiare e invertono quelli posti di fronte alle 1. Ad esempio se: reg=b0001 0110 e mask=b0000 1111, allora reg^mask=b0001 1001. In questo modo potrete cambiare lo stato del LED collegato al quinto bit della porta A:

#define LED 5 // Sostituisci la combinazione di simboli LED nel programma con il numero 5 (uscita LED).

PORTA ^=(1<< LED); // Погасить светодиод, если он светится и наоборот.

§> Aritmetica e logica del linguaggio C.

Abbiamo esaminato una serie tipica di operazioni utilizzate quando si lavora con i registri del microcontrollore. Oltre a questi, il linguaggio ha una serie di semplici operazioni aritmetiche e logiche, le cui descrizioni possono essere trovate in qualsiasi libro di consultazione C, ad esempio:


Per un'introduzione più dettagliata alle operazioni sulle variabili e al linguaggio C in generale, consiglio il libro “The C Programming Language” di B. Kernighan, D. Ritchie.

La conversione dei tipi di variabile fa parte del funzionamento automatico interno del compilatore, che avviene in stretta conformità con le regole del linguaggio di programmazione. Lo sviluppatore stesso, quando scrive esplicitamente un programma, di solito non lo fa. Tuttavia, una dichiarazione imprecisa dei tipi di variabile, o l'assegnazione di un valore a una variabile che supera l'intervallo consentito, o anche un formato errato per la scrittura di una costante, possono portare alla perdita di dati e al funzionamento errato del programma, con il compilatore completamente silenzioso.
Quando avviene il type casting e di cosa si tratta? Ci sono molte di queste situazioni. Diamo un'occhiata ai più pericolosi.

§> Convertire il tipo di un'espressione prima di assegnarla ad una variabile.

Nella prima sezione abbiamo richiamato l'attenzione sulla necessità di indicare esplicitamente il tipo della variabile dichiarata. Ciò consente al compilatore di riservargli la giusta quantità di spazio di indirizzi e di determinare l'intervallo di valori che può memorizzare. Tuttavia non siamo immuni dalla possibilità che durante l'esecuzione del programma si tenti di scrivere in una variabile un valore superiore al valore massimo consentito. Nei casi più gravi, il compilatore ci darà un messaggio su un possibile errore. Ad esempio, se vuoi scrivere il numero 400 in una variabile di tipo unsigned char (intervallo da 0 a 255):

Carattere senza segno a=400; // visualizzerà un messaggio come "la conversione di numeri interi ha comportato un troncamento"

Il compilatore ci avverte che si è verificato un tentativo di scrivere un valore numerico che richiede due byte per essere memorizzato (400 è 1 nel byte alto e 144 nel byte basso) in una variabile da un byte. Tuttavia, nei casi in cui l'espressione di assegnazione contenga variabili e il compilatore notasse una possibile perdita di dati, si solleva da questa responsabilità, ad esempio:

Carattere senza segno x=200, y=200;
x=x+y;

Con questa opzione, nonostante anche il valore dell'espressione (x+y) sia uguale a 400, non seguirà alcun avviso da parte del compilatore. E nella variabile x verrà scritto solo il byte basso del numero 400, cioè 144. E qui è difficile incolpare il compilatore di qualcosa, perché invece di una variabile esplicitamente inizializzata nell'espressione, ad esempio, può essere utilizzato il registro di ricezione del bus UART, che può contenere qualsiasi valore, ricevuto da un dispositivo esterno.
Un altro esempio nello stesso spirito è l'assegnazione di un valore frazionario a una variabile di tipo intero:

Virgola mobile a=1,5; // È stata dichiarata una variabile in virgola mobile.

b=a*b; // Aspettatevi che b sia impostato su 4.5.

Di conseguenza, solo la parte intera del risultato a*b – il numero 4 – verrà memorizzata nella variabile b.

§> Converte il risultato di un'espressione nel tipo della variabile più precisa nell'espressione.

Con questa conversione, il compilatore è guidato dalla seguente regola: prima che l'espressione inizi a essere valutata, gli operatori di tipo “inferiore” vengono promossi a quelli “superiori” e anche il risultato viene convertito in un tipo “superiore”. Quale tipologia dovrebbe essere considerata “superiore”? Uno che può memorizzare qualsiasi valore valido di un altro tipo senza perdita di precisione. Quindi, nell'esempio precedente:

Virgola mobile a =1,5; // Viene dichiarata la variabile in virgola mobile a.
carattere b=3; // È stata dichiarata una variabile intera.

Nell'espressione (a*b), la variabile float a ha un tipo più alto perché può memorizzare qualsiasi valore intero compreso nell'intervallo 0...255 del tipo char. Il risultato dell'espressione (a*b) sarà di tipo float.
Un tipico esempio di sorpresa in questo caso è il tentativo di ottenere un numero frazionario dividendo due numeri interi:

Carattere a=3; // È stata dichiarata una variabile intera.
carattere b=4; // È stata dichiarata una variabile intera.
galleggiante c; // Dichiarata una variabile in virgola mobile "c" per memorizzare il risultato.
c=a/b; // "c" dovrebbe essere 0,75 (¾).

A differenza dell'esempio precedente, il risultato viene scritto in una variabile in grado di memorizzare numeri in virgola mobile, ma il compilatore, secondo la regola di casting, avendo ricevuto come risultato della divisione il numero 0,75, lo converte nel tipo di operandi interi , scartando la parte frazionaria. Di conseguenza, nella variabile “c” verrà scritto zero.
Un esempio più realistico nella vita reale è il calcolo della tensione misurata dal codice di uscita dell'ADC:

ADC int; // Variabile intera a due byte per memorizzare il codice ADC.
fluttuare U; // Variabile in virgola mobile per memorizzare il valore della tensione.
U=ADC*(5/1024); // Calcolo della tensione.

Ciò che qui viene trascurato è che anche una costante in C, come ogni variabile, ha un proprio tipo. Si consiglia di indicarlo esplicitamente o utilizzando l'apposito modulo di registrazione. Le costanti 5 e 1024 vengono scritte senza punto decimale e verranno interpretate dal linguaggio C come numeri interi. Di conseguenza, anche il risultato dell'espressione (5/1024) verrà ridotto a un numero intero - 0 invece del previsto 0,00489. Ciò non accadrebbe se l'espressione fosse scritta nel formato (5.0/1024).
Gli errori di cui sopra possono essere evitati anche utilizzando l'operatore di conversione del tipo esplicito delle espressioni del linguaggio C, che viene scritto come un nome di tipo racchiuso tra parentesi e influisce sull'espressione che lo segue. Questo operatore converte il risultato di un'espressione in un tipo specificato esplicitamente, indipendentemente dai tipi dei suoi operandi:

C= (flottante) a/b; // "c" dovrebbe essere 0,75 (¾);
U= ADC * ((virgola mobile)5/1024); // Calcolo della tensione.

§> Assegnazione delle funzioni.

Anche gli antichi programmatori hanno rivolto la loro attenzione a un fatto interessante: spesso un programma è costretto a eseguire più volte esattamente la stessa sequenza di azioni. Fu allora che nacque l'idea, dato un insieme sufficientemente ampio di tali azioni e delle loro ripetizioni, al fine di risparmiare memoria del programma, disporle sotto forma di un gruppo separato e quindi, se necessario, inviare semplicemente il programma per esecuzione. Questo pezzo di codice separato in C è chiamato funzione. Il nome stesso del termine "funzione" riflette originariamente un'altra proprietà di alcune funzioni: la capacità (come le funzioni matematiche) di trasformare determinati dati di input secondo un determinato algoritmo. Ma questo avverrà un po' più tardi.

Un altro scopo della funzione, che riflette pienamente il suo nome, è quello di separare in un gruppo separato di azioni relative a un obiettivo comune, ad esempio la funzione di inizializzazione della porta o la funzione di polling della tastiera. Questo è uno degli scopi aggiuntivi della funzione.
Tali funzioni possono essere chiamate solo una volta da un programma. Perché allora sono necessari? Per giustificare questo approccio, la letteratura cita spesso una frase di uno sconosciuto, ma apparentemente molto autorevole, programmatore dell'antica Roma: “Dividi e conquista!” E in effetti, un programma progettato sotto forma di blocchi funzionali target è molto più facile da comprendere, eseguire il debug e modifica successiva rispetto a un insieme di pezzi di codice separati, separati per scopo.
Riassumendo quanto detto possiamo formulare i prerequisiti formali per creare funzioni in un programma, questi sono:

1. La presenza di insiemi di azioni identici, sufficientemente ampi e ripetuti ripetutamente.
2. Il desiderio di strutturare il programma sotto forma di blocchi separati con uno scopo funzionale comune.

Qui dobbiamo subito fare un’importante avvertenza. Il fatto è che ad ogni transizione verso una funzione e ritorno da essa, il microcontrollore è costretto a salvare alcuni dati di sistema, ad esempio l'indirizzo del programma da cui è avvenuta la transizione alla funzione, e ciò richiede risorse di tempo aggiuntive. Devi tenere conto di questo fatto e cercare di non creare molte funzioni brevi nel tuo programma, se è possibile combinarle in una comune.

§> Struttura e disegno delle funzioni.

In qualsiasi funzione è strutturalmente facile distinguere due componenti: l'intestazione e il corpo della funzione.
L'intestazione è la prima riga di qualsiasi funzione del modulo:

Tipo di variabile di output Nome della funzione (tipi di variabili di input e relativi nomi separati da virgole)

Ignoriamo temporaneamente il contenuto dell'intestazione prima e dopo il nome e osserviamo le funzioni che non elaborano alcun dato. Sono progettati solo per eseguire determinate azioni. Nelle intestazioni di tali funzioni è necessario indicare i nomi del tipo vuoto – void (inglese: vuoto, vuoto):

Nome della funzione void (void)

Qualsiasi parola che rifletta il significato delle azioni eseguite dalla funzione può essere utilizzata come nome, purché non inizi con un numero. Ora possiamo chiamare l'esecuzione della nostra funzione da qualsiasi punto del programma. Per fare ciò, è necessario scrivere il nome della funzione, le parentesi e un punto e virgola. Ad esempio, una funzione con un titolo:
inizializzazione void (void)
può essere chiamato così:

inizializzazione();

Il corpo di una funzione è un insieme di comandi situati tra la prima parentesi graffa di apertura dopo l'intestazione e la corrispondente parentesi graffa di chiusura. Spiegazione: Gli insiemi di azioni racchiusi tra parentesi graffe sono chiamati blocchi in C. Collegano logicamente più azioni singole in un'azione complessa che viene completamente eseguita o completamente ignorata a seconda del contesto del programma. In questo caso il corpo della funzione è un blocco di comandi che la funzione deve eseguire dall'inizio alla fine. Pertanto, il programma, incontrando una transizione verso una funzione, eseguirà il contenuto del blocco e, dopo l'ultima parentesi di chiusura, tornerà al punto da cui è stato chiamato.
Ad esempio, la funzione:

Inizializzazione nulla (void)
{
DDRA=0xFF; //PORTA per uscire.
DDRB|=(1<<0)| (1<<3)| (1<<4); // PB0, PB3, PB4 на выход.
DDRC=0xF0; // PORTC tetrade alto per l'output.
}

Dopo aver inizializzato la direzione dei pin delle porte A, B e C, dopo la chiamata tornerà alla riga successiva.
Per cominciare è anche importante sapere che nel testo del programma ciascuna funzione deve essere posizionata separatamente, ovvero una funzione non può essere posizionata all'interno di un'altra o sovrapporsi parzialmente ad essa.

§> Elaborazione dei parametri tramite una funzione.

Tutte le funzioni possono elaborare e modificare i valori dei registri specializzati del microcontrollore e delle cosiddette variabili globali, cioè quelle dichiarate dall'utente all'inizio del programma al di fuori di qualsiasi funzione. Inoltre è possibile trasferire i dati ad una funzione per l'elaborazione direttamente al momento della sua chiamata. È semplicemente conveniente e niente di più.
Questi dati sono chiamati parametri passati alla funzione. Dovrebbero essere elencati, separati da virgole, insieme ai loro tipi nell'intestazione della funzione, tra parentesi dopo il nome:

Questo design dell'intestazione significherà che la funzione è in grado di accettare due numeri di carattere denominati FrameLength e StopBit come parametri. Ora, quando chiami una funzione, il compilatore non ti permetterà di lasciare le parentesi vuote e ti richiederà di passare valori specifici, separati da virgole, ad esempio:

InitUarte(8, 2);

Successivamente, all'interno della funzione, alle variabili denominate FrameLength e StopBit verranno assegnati valori specifici di 8 e 2, che potranno essere utilizzati, ad esempio, per configurare la lunghezza della trasmissione del modulo UART e il numero dei suoi bit di stop:

Void initUart(char FrameLength, char StopBit)
{
if (Lunghezza fotogramma==8) UCSR0C|=((1<<1)|(1<<2));
if (BitStop==2) UCSR0C|=(1<<3);
}

§> Funzioni specializzate.

Abbiamo esaminato le funzioni specificate dall'utente stesso. Oltre a questi, qualsiasi programma contiene funzioni che svolgono compiti specializzati e devono essere progettate secondo regole speciali.
La funzione più importante di questo tipo, come si evince dal nome stesso, è la funzione principale. È caratterizzato dal fatto che l'esecuzione gli viene trasferita dal microcontrollore stesso quando viene applicata l'alimentazione o dopo un riavvio, ovvero è da qui che inizia il lavoro di qualsiasi programma. Un'altra proprietà della funzione main è che quando viene eseguita fino alla fine, il programma andrà automaticamente all'inizio, cioè verrà eseguito in loop se l'utente non ha appositamente organizzato un loop infinito al suo interno.
Un'altra opzione per le funzioni di sistema sono i gestori di interrupt. Inoltre, non possono essere chiamati a livello di codice. Il microcontrollore trasferisce loro il controllo in modo indipendente in caso di condizioni hardware speciali - condizioni per la chiamata di interrupt.

Ovviamente qualsiasi programma è un insieme di azioni descritte secondo le regole di un linguaggio di programmazione e destinate ad essere eseguite da un dispositivo specifico, in questo caso un microcontrollore. Qual è la procedura per eseguire queste azioni nella pratica, cercheremo di capire in questa sezione.

§> Struttura generale del programma più semplice. Inizializzazione, sfondo.

Considerando un programma a livello di linguaggio C, possiamo dire che inizia il suo lavoro dalla prima riga della funzione principale (riga 001 in figura):

Struttura di un programma C
Successivamente, le righe 002, 003, 004 vengono eseguite in sequenza, unite da una proprietà comune: il programma le attraversa una sola volta, all'avvio del microcontrollore. Questa parte del programma è solitamente chiamata parte di inizializzazione. La parte di inizializzazione è il posto giusto in cui inserire le azioni per preparare le periferiche del microcontrollore a funzionare con i parametri specificati: impostazione delle porte per input o output, inizializzazione iniziale dei timer, impostazione della velocità e del formato frame dell'UART e così via per tutto che si prevede di utilizzare in futuro.

Poiché qualsiasi programma è progettato per funzionare in modo continuo, la sua normale modalità di funzionamento è una ripetizione continua del contenuto di un ciclo infinito in un cerchio. In pratica, un ciclo di questo tipo viene spesso implementato utilizzando il costrutto while(1) ( ), progettato per eseguire ripetutamente le azioni poste all'interno delle sue parentesi graffe. Il contenuto del ciclo infinito di un programma è chiamato sfondo. È qui che si svolge il grosso del lavoro: controllare lo stato dell'hardware e influenzarlo di conseguenza per ottenere il risultato desiderato.

Diamo un'occhiata alla struttura del programma descritta utilizzando un semplice esempio. Sia necessario: inviare il carattere * sul bus UART mentre è premuto il pulsante sul pin PA0 (livello del segnale zero). Il programma in questo caso (senza procedure non necessarie per sopprimere il rimbalzo dei pulsanti e altre cose) potrebbe assomigliare a questo:

Vuoto principale (vuoto)
{
PORTA|=(1<<0); // Притянуть вход кнопки PORTA.0 внутренним pull-up резистором.

UCSRB = (1<mentre (1)
{
se (! (PINA & (1<<0))) // Если кнопка нажата...
{
mentre(! (UCSRA & (1<UDR = "*"; // Inviare *.
}
// altri comandi in background:
00N
00N+1
...
}
}

Qui il costrutto if (...), situato in background del programma, interroga all'infinito il registro di input PINA e controlla il livello basso del pin PA0. Successivamente vengono eseguite altre azioni del processo in background, indicate dalle righe 00N, 00N+1 e così via.

Quali fattori, in relazione a questo programma, determinano i parametri più importanti del suo funzionamento: affidabilità e prestazioni?

L'esempio mostra che la frequenza di interrogazione dell'ingresso PA.0 è determinata dalla durata dell'esecuzione dei comandi in background, poiché prima di interrogare nuovamente il pulsante, il microcontrollore deve eseguire le seguenti righe 00N, 00N+1, ecc. Ovviamente, l'affidabilità di la registrazione di un evento esterno (pressione di un pulsante) in questo caso dipenderà dal rapporto tra la durata dell'impatto di questo evento e il periodo del suo rilevamento. La durata dello sfondo in questo programma sarà sicuramente molte volte inferiore alla durata della pressione del pulsante, che in pratica è di diverse decine di millisecondi. Tuttavia, con la crescita della parte in background del programma e il breve periodo di influenza esterna, l'affidabilità del suo tracciamento in un determinato momento diminuirà drasticamente. Per evitare che ciò accada, nonché per ridurre il tempo di risposta del programma ad un evento esterno, viene utilizzato un sistema di interrupt.

§> Interrompe.

Come funziona il meccanismo di interruzione? Molto semplice, soprattutto a livello di linguaggio C!

L'architettura dei microcontrollori AVR, così come qualsiasi altro, a livello hardware contiene la capacità di monitorare determinati "stati hardware interessanti" e impostare i bit di attributo corrispondenti. Tali stati sono chiamati condizioni di interruzione e gli attributi impostati sono chiamati flag di interruzione. Durante il funzionamento, il microcontrollore monitora continuamente lo stato di questi flag. Quando viene rilevato un flag di interruzione impostato, a condizione che sia abilitato attivando il bit corrispondente e che sia impostato il bit di abilitazione dell'interrupt globale (n. 7 nel registro SREG per AVR), l'esecuzione della parte principale del programma verrà temporaneamente sospesa (interrotta).

Poiché un'interruzione può verificarsi quando viene eseguito un comando arbitrario in background, il suo indirizzo viene memorizzato nel cosiddetto stack software. Dopodiché l'esecuzione viene trasferita ad una parte del programma appositamente scritta dallo sviluppatore per reagire all'evento che ha causato tale interruzione. Questa piccola parte del programma è chiamata gestore degli interrupt. Quando il gestore sarà completato fino alla fine, il programma, utilizzando l'indirizzo memorizzato nello stack del programma, tornerà al punto da cui è stato chiamato per gestire questa interruzione.

Qual è il ruolo del programmatore in questo processo? Quando si sviluppa in C, è ridotto al minimo.
Alcune azioni, come il monitoraggio dei flag di interruzione, sono implementate a livello hardware. L'altra parte, ad esempio, la protezione dalle modifiche nell'handler del registro di stato SREG importante per il programma, il salvataggio dell'indirizzo del programma nello stack e molto altro, è curata dal compilatore.
L'unica cosa che rimane necessaria è:

1. Consentire l'uso degli interrupt nel programma.
2. Consentire di richiamare l'interrupt di nostro interesse con un bit apposito nel registro corrispondente. La descrizione del microcontrollore ti dirà quale e dove.
3. Creare le condizioni affinché si verifichi un'interruzione, ad esempio, se si tratta di un overflow del timer, è banale avviarlo. Se si tratta di un'interruzione basata su un cambiamento nello stato di un pin esterno, impostare le condizioni necessarie per questo (fronte, fronte o livello zero).
4. Inserire un gestore di interrupt nel programma, formattandolo in conformità con i requisiti del compilatore.

In relazione al nostro esempio, è possibile organizzare l'invio all'UART a livello basso all'ingresso del pulsante utilizzando il cosiddetto interrupt esterno INT0. Questo interrupt viene attivato da un fronte di salita, da un fronte di discesa o dal livello zero sul pin INT0.

Spostiamo il pulsante sul pin PD.2 con funzione alternativa INT0. Nella parte di inizializzazione del programma, abiliteremo gli interrupt a livello globale e INT0 in particolare. Il microcontrollore è configurato per impostazione predefinita per generare un interrupt INT quando il segnale di ingresso è basso, quindi non sono necessarie impostazioni aggiuntive. Resta da dichiarare un handler INT0 esterno alla funzione main, inviando il carattere * all'UART:

Vuoto principale (vuoto)
{
PORTA|=(1<<2); // Притянуть вход кнопки PORTD.2 внутренним pull-up.
UBRRL=51; // La velocità UART è 9600 bps.
UCSRB = (1<SREG|=(1<<7); // Разрешить прерывания.
GICR|=(1<mentre (1)()
}

#pragma vector=INT0_vect // Gestore degli interrupt INT0/
__interrupt void INT0_INTPT()
{
se (! (PIND & (1<<2))) {while(! (UCSRA & (1< }

Qui il gestore degli interrupt viene dichiarato nel formato del compilatore IAR. Fondamentalmente contiene solo il nome del vettore di interruzione - INT0_vect; il compilatore lo sostituisce con l'indirizzo della memoria del programma a cui viene trasferita l'esecuzione del programma quando si verifica questa interruzione. Il nome del gestore stesso, INT0_INTPT, viene scelto arbitrariamente. I nomi dei vettori di tutte le possibili interruzioni per un dato MK sono descritti nei file h.

Ora il tempo di reazione alla pressione di un pulsante non dipende dalla durata dello sfondo del programma ed è di diversi cicli del microcontrollore e la probabilità di perdere questo evento è zero. Pertanto, un'interruzione è un ottimo modo per rispondere a un evento che richiede un'elaborazione immediata. Questo è il suo scopo principale.

Vorrei menzionare immediatamente una regola non detta riguardante i gestori di interrupt, sebbene si tratti di una questione piuttosto ristretta. Dovrebbero contenere solo ciò che è effettivamente necessario per rispondere rapidamente a un'interruzione. Tutte le altre azioni che possono essere rinviate devono essere messe in secondo piano.
A cosa è collegato questo?
Se gli eventi che causano un'interruzione si verificano con sufficiente frequenza, quando si verifica l'interruzione successiva, un gestore troppo lungo potrebbe non avere il tempo di completare la propria esecuzione. E questo è irto di conseguenze spiacevoli sotto forma di perdita di dati e interruzione della normale sequenza di azioni. Ad esempio, se è necessario ricevere un determinato array di byte tramite UART, nel gestore che viene chiamato dopo aver ricevuto ciascuno di essi, non è necessario studiare attentamente i dati ricevuti, ma riscriverli solo da un array pre-preparato. E dopo aver ricevuto l'ultimo, puoi impostare il segno appropriato nel gestore (dicono che tutto è accettato) e in background, dopo averlo rilevato, puoi iniziare con calma a esaminare l'intero array ricevuto.

Tratto dal sito
http://eugenemcu.ru/

Pubblicazioni sull'argomento