Biblioteca periferică standard. Conectarea unei biblioteci periferice standard la orice familie STM32

Interacțiunea codului utilizatorului cu registrele nucleului și periferiei microcontrolerelor STM32 poate fi realizată în două moduri: folosind biblioteci standard sau folosind seturi de fragmente (sugestii software). Alegerea dintre ele depinde de volum propria memorie controler, viteza necesară, termenul limită de dezvoltare. Articolul analizează caracteristicile structurale, avantajele și dezavantajele seturi de fragmente pentru microcontrolere din familiile STM32F1 și STM32L0 produse de STMicroelectronics.

Unul dintre avantajele utilizării microcontrolerelor STMicroelectronics este o gamă largă de instrumente de dezvoltare: documentație, plăci de dezvoltare, software.

Software-ul pentru STM32 include software proprietar produs de STMicroelectronics, surse Open Source și software comercial.

Software-ul STMicroelectronics are avantaje importante. În primul rând, este disponibil pentru descărcare gratuită. În al doilea rând, biblioteci de software sunt prezentate sub formă de coduri sursă - utilizatorul poate modifica el însuși codul, ținând cont de restricțiile minore descrise în acordul de licență.

Bibliotecile STMicroelectronics respectă ANSI-C și pot fi împărțite după nivelul de abstractizare (Figura 1):

  • CMSIS (Core Peripheral Access Layer) – nivel de registru nucleu și periferic, bibliotecă ARM;
  • Hardware Abstraction Layer – biblioteci de nivel scăzut: biblioteci periferice standard, seturi de fragmente;
  • Middleware – biblioteci de nivel mediu: sisteme de operare în timp real (RTOS), sisteme de fișiere, USB, TCP/IP, Bluetooth, Display, ZigBee, Touch Sensing și altele;
  • Câmp de aplicație - biblioteci nivelul de aplicare: soluții audio, control motor, auto și industriale.

Figura 1 arată că pentru a interacționa cu nivelul CMSIS, STMicroelectronics oferă utilizarea a două instrumente principale - biblioteci standard și fragmente.

Biblioteca standard este un set de drivere. Fiecare driver oferă utilizatorului funcții și definiții pentru lucrul cu un anumit bloc periferic (SPI, USART, ADC și așa mai departe). Utilizatorul nu interacționează direct cu registrele de nivel CMSIS.

Seturile de fragmente sunt foarte eficiente exemple de programare, folosind accesul direct la registrele CMSIS. Dezvoltatorii de software pot folosi implementări ale funcțiilor din aceste exemple în propriul cod.

Fiecare metodă are avantaje și dezavantaje. Alegerea dintre ele se face ținând cont de cantitatea disponibilă de FLASH și RAM, viteza necesară, perioada de dezvoltare, experiența programatorilor și alte circumstanțe.

Nivelul CMSIS

Un microcontroler este un cip digital-analogic complex format din miezul procesorului, memorie, unități periferice, magistrale digitale și așa mai departe. Interacțiunea cu fiecare bloc are loc folosind registre.

Din punctul de vedere al programatorilor, un microcontroler reprezintă un spațiu de memorie. Conține nu numai RAM, FLASH și EEPROM, ci și registre de program. Fiecare registru hardware corespunde unei celule de memorie. Astfel, pentru a scrie date într-un registru sau pentru a scădea valoarea acestuia, programatorul trebuie să acceseze locația corespunzătoare din spațiul de adrese.

O persoană are anumite particularități ale percepției. De exemplu, numele simbolice sunt percepute de el mult mai bine decât adresele celulelor de memorie. Acest lucru este vizibil mai ales atunci când se utilizează un număr mare de celule. În microcontrolerele ARM, numărul de registre și, prin urmare, celulele utilizate, depășește o mie. Pentru a ușura lucrurile, este necesar să definiți indicatori simbolici. Această determinare se face la nivelul CMSIS.

De exemplu, pentru a seta starea pinilor portului A, trebuie să scrieți date în registrul GPIOA_ODR. Acest lucru se poate face în două moduri - utilizați un pointer cu adresa de celulă 0xEBFF FCFF cu offset 0x14 sau utilizați un pointer cu numele simbolic GPIOA și o structură gata făcută care definește offset-ul. Evident, a doua variantă este mult mai ușor de înțeles.

CMSIS îndeplinește și alte funcții. Este implementat sub formă grupul următor fisiere:

  • startup_stm32l0xx.s conține codul de pornire a asamblatorului pentru Cortex-M0+ și un tabel de vectori de întrerupere. După finalizarea inițializării de pornire, controlul este transferat mai întâi către funcția SystemInit() (explicațiile vor fi date mai jos), iar apoi către funcția principală int main(void);
  • stm32l0xx.h conține definiții necesare pentru a efectua operațiuni de bază pe biți și o definiție a tipului de microprocesor utilizat;
  • system_stm32l0xx.c/.h. După inițializarea inițială, funcția SystemInit() este executată. Efectuează configurarea inițială a perifericelor de sistem, temporizările blocului RCC;
  • stm32l0yyxx.h – fișiere de implementare pentru microcontrolere specifice (de exemplu, stm32l051xx.h). În ele sunt definite indicatori de caractere, structuri de date, constante de biți și decalaje.

Interacțiunea cu CMSIS. Biblioteci și fragmente standard

Numărul de registre pentru microcontrolerele STM32 în majoritatea modelelor depășește o mie. Dacă utilizați acces direct la registre, codul de utilizator va deveni ilizibil și complet inutilizabil pentru asistență și modernizare. Această problemă poate fi rezolvată prin utilizarea bibliotecii periferice standard.

Biblioteca standard de periferice este un set de drivere de nivel scăzut. Fiecare driver oferă utilizatorului un set de funcții pentru lucrul cu o unitate periferică. În acest fel, utilizatorul folosește funcțiile în loc să acceseze direct registrele. În acest caz, nivelul CMSIS este ascuns de programator (Figura 2a).

Orez. 2. Interacțiunea cu CMSIS folosind biblioteca standard (a) și fragmentele (b)

De exemplu, interacțiunea cu porturile I/O din STM32L0 este implementată folosind un driver realizat sub forma a două fișiere: stm32l0xx_hal_gpio.h și stm32l0xx_hal_gpio.c. stm32l0xx_hal_gpio.h oferă definițiile de bază ale tipurilor și funcțiilor, iar stm32l0xx_hal_gpio.c oferă implementarea acestora.

Această abordare are avantaje destul de evidente (Tabelul 1):

  • Creare rapidă de cod. Programatorul nu trebuie să studieze lista de registre. Începe imediat să lucreze la un nivel superior. De exemplu, pentru a interfața direct cu portul I/O de pe STM32L0, trebuie să cunoașteți și să puteți opera unsprezece registre de control/stare, dintre care majoritatea au până la 32 de biți configurabili. Când utilizați driverul de bibliotecă, este suficient să stăpâniți opt funcții.
  • Simplitatea și claritatea codului. Codul utilizatorului nu este înfundat cu nume de registru, poate fi transparent și ușor de citit, ceea ce este important atunci când lucrați cu o echipă de dezvoltare.
  • Nivel ridicat de abstractizare. Când utilizați biblioteca standard, codul se dovedește a fi destul de independent de platformă. De exemplu, dacă schimbați microcontrolerul STM32L0 cu microcontrolerul STM32F0, o parte din codul care funcționează cu porturile I/O nu va trebui schimbat deloc.

Tabelul 1. Comparația metodelor de implementare a codului personalizat

Parametru de comparație Când utilizați standard
biblioteci periferice
Când utilizați seturi de fragmente
Dimensiunea codului in medie minim
RAM costă in medie minim
Performanţă in medie maxim
Lizibilitatea codului excelent scăzut
Nivelul de independență a platformei in medie mic de statura
Viteza de creare a programului înalt scăzut

Prezența unui shell suplimentar sub formă de drivere are, de asemenea, dezavantaje evidente (Tabelul 1):

  • Creșterea volumului codului programului. Funcțiile implementate în codul bibliotecii necesită spatiu suplimentar in minte.
  • Costuri RAM crescute datorită creșterii numărului de variabile locale și utilizării structurilor voluminoase de date.
  • Performanță redusă datorită supraîncărcării crescute la apelarea funcțiilor bibliotecii.

Prezența acestor deficiențe a condus la faptul că utilizatorul a fost adesea forțat să optimizeze codul - să implementeze independent funcții pentru interacțiunea cu CMSIS, să optimizeze funcțiile bibliotecii prin eliminarea tuturor lucrurilor inutile, să copieze implementările funcțiilor bibliotecii direct în codul lor, utilizați directivele __INLINE pentru a crește viteza de execuție. Ca urmare, a fost alocat timp suplimentar pentru a rafina codul.

STMicroelectronics, întâlnindu-se cu dezvoltatorii la jumătatea drumului, a lansat colecții de fragmente STM32SnippetsF0 și STM32SnippetsL0.

Fragmentele sunt incluse în codul utilizatorului (Figura 2b).

Utilizarea fragmentelor oferă avantaje evidente:

  • creșterea eficienței și vitezei codului;
  • reducerea sferei de aplicare a programului;
  • Reducerea cantității de RAM utilizată și a încărcării stivei.

Cu toate acestea, merită remarcat dezavantajele:

  • reducerea simplității și clarității codului datorită „contaminarii” acestuia cu nume de registre și implementării independente a funcțiilor de nivel scăzut;
  • dispariția independenței platformei.

Deci alegerea dintre biblioteca standard și fragmente nu este evidentă. În cele mai multe cazuri, merită să vorbim nu despre concurență, ci despre utilizarea lor reciprocă. În fazele inițiale pentru construcție rapidă cod „frumos”, logic de folosit drivere standard. Dacă optimizarea este necesară, puteți apela la fragmente gata făcute pentru a nu pierde timpul dezvoltând propriile funcții optime.

Bibliotecile standard de drivere și fragmente STM32F0 și STM32L0 (Tabelul 2) sunt disponibile pentru descărcare gratuită de pe site-ul www.st.com.

Tabelul 2. Biblioteci de nivel scăzut pentru STM32F10 și STM32L0

O cunoaștere mai apropiată a fragmentelor, ca și în cazul oricărui software, ar trebui să înceapă prin a lua în considerare caracteristicile acordului de licență.

Acord de licențiere

Orice programator responsabil înainte de a utiliza o terță parte produse software studiază cu atenție acord de licențiere. În ciuda faptului că colecțiile de fragmente produse de ST Microelectronics nu necesită licență și sunt disponibile pentru descărcare gratuită, aceasta nu înseamnă că nu există restricții privind utilizarea lor.

Acordul de licență este inclus cu toate produsele descărcabile gratuit fabricate de STMicroelectronics. După încărcarea STM32SnippetsF0 și STM32SnippetsL0 în directorul rădăcină Este ușor de găsit documentul MCD-ST Liberty SW License Agreement V2.pdf, care introduce utilizatorul în regulile de utilizare a acestui software.

Dosarul Proiect conține subdirectoare cu exemple pentru anumite unități periferice, proiecte gata făcute pentru ARM Keil și EWARM, precum și fișiere main.c.

Lansare și caracteristici de utilizare a setului de fragmente STM32SnippetsF0 și STM32SnippetsL0

O caracteristică specială a acestor seturi de fragmente este dependența lor de platformă. Sunt proiectate să funcționeze cu plăci specifice. STM32SnippetsL0 utilizează placa Discovery STM32L053, iar STM32SnippetsF0 utilizează placa Discovery STM32F072.

La folosirea plăcilor propria dezvoltare codul și proiectele trebuie schimbate, acest lucru va fi discutat mai detaliat în ultima secțiune.

Pentru a rula exemplul, trebuie să parcurgeți o serie de pași:

  • rulați proiectul terminat din directorul cu exemplul necesar. Pentru simplitate, puteți folosi proiecte gata făcute pentru mediile ARM Keil sau EWARM, aflate în folderele MDK-ARM\, respectiv EWARM\;
  • porniți placa de dezvoltare STM32L053 Discovery/STM32F072 Discovery;
  • Conectați sursa de alimentare a plăcii de depanare la computer folosind un cablu USB. Datorită depanatorului ST-Link/V2 încorporat, nu este necesar niciun programator suplimentar;
  • deschideți, configurați și rulați proiectul;
    • Pentru ARM Keil:
      • proiect deschis;
      • compilați proiectul – Proiect → Reconstruiți toate fișierele țintă;
      • încărcați-l în controler – Debug → Start/Stop Debug Session;
      • rulați programul în fereastra Debug → Run (F5).
    • Pentru EWARM:
      • proiect deschis;
      • compilați proiectul – Proiect → Rebuild all;
      • încărcați-l în controler – Proiect → Depanare;
      • rulați programul în fereastra Debug → Go (F5).
  • efectuați testarea în conformitate cu algoritmul descris în principal.c.

Pentru a analiza codul programului, luați în considerare un exemplu specific din STM32SnippetsL0: Projects\LPUART\01_WakeUpFromLPM\.

Rularea unui exemplu pentru LPUART

O caracteristică distinctivă a noilor microcontrolere din familia STM32L0 bazate pe nucleul Cortex-M0+ este capacitatea schimbare dinamică consumul datorat un numar mare inovații. Una dintre aceste inovații a fost apariția perifericelor Low Power: timerul LPTIM pe 16 biți și transceiver-ul LPUART. Aceste blocuri au capacități de sincronizare care sunt independente de sincronizarea magistralei periferice principale APB. Dacă este necesar să se reducă consumul de energie, frecvența de operare a magistralei APB (PCLK) poate fi redusă, iar controlerul în sine poate fi comutat în modul de consum redus. În același timp, perifericele Low Power continuă să funcționeze la performanță maximă.

Să luăm în considerare un exemplu din directorul Projects\LPUART\01_WakeUpFromLPM\, care ia în considerare posibilitatea de funcționare independentă a LPUART în modul de consum redus.

La deschiderea unui proiect în mediul ARM Keil, sunt afișate doar trei fișiere: startup_stm32l053xx.s, system_stm32l0xx.c și main.c (Figura 4). Dacă ar fi folosită biblioteca standard, ar fi necesar să adăugați fișiere driver la proiect.

Funcționarea și analiza structurii fișierelor Main.c

Exemplul de program selectat este executat în mai multe etape.

După pornire, funcția SystemInit(), implementată în system_stm32l0xx.c, este lansată. Acesta configurează parametrii blocului de ceas RCC (timinguri și frecvențe de operare). Apoi, controlul este transferat la funcția principală int main(void). Inițializează perifericele utilizatorului - porturi de intrare/ieșire, LPUART - după care controlerul este comutat în modul STOP consum redus. În ea, periferia și nucleul obișnuit sunt oprite, doar LPUART funcționează. Așteaptă să înceapă transferul de date dispozitiv extern. Când sosește bitul de pornire, LPUART trezește sistemul și primește mesajul. Recepția este însoțită de pâlpâirea LED-ului plăcii de depanare. După aceasta, controlerul este comutat înapoi în starea STOP și așteaptă următorul transfer de date dacă nu au fost detectate erori.

Transferul de date are loc folosind un port COM virtual și software suplimentar.

Să ne uităm la main.c din proiectul nostru. Acest fișier este un fișier C standard. Caracteristica sa principală este auto-documentarea - prezența unor comentarii detaliate, explicații și recomandări. Partea explicativă conține mai multe secțiuni:

  • un titlu care indică numele fișierului, versiunea, data, autorul și o scurtă explicație a scopului;
  • descrierea secvenței de configurare a perifericelor de sistem (caracteristici specifice RCC): FLASH, RAM, sisteme de alimentare și de tac, magistrale periferice și așa mai departe;
  • lista resurselor microcontrolerului utilizate (MCU Resources);
  • scurtă explicație a utilizării acest exemplu(Cum se utilizează acest exemplu);
  • o scurtă explicație a testării exemplului și a algoritmului pentru implementarea acestuia (Cum se testează acest exemplu).

Funcția int main(void) are o formă compactă și este echipată cu comentarii, care în Lista 1, pentru o mai mare claritate, sunt traduse în rusă.

Listarea 1. Exemplu de implementare a funcției principale

int main(void)
{
/* Până la începutul execuției acestei părți, când unitățile de sistem au fost deja configurate în funcția SystemInit(), implementată în system_stm32l0xx.c. */
/* configurația unităților periferice */
Configure_GPIO_LED();
Configure_GPIO_LPUART();
Configurare_LPUART();
Configure_LPM_Stop();
/* verificați erorile în timpul recepției */
while (!eroare) /* buclă fără sfârșit */
{
/* așteptați ca LPUART să fie gata și treceți în modul STOP */
if((LPUART1->ISR & USART_ISR_REACK) == USART_ISR_REACK)
{
__WFI();
}
}
/* când apare o eroare */
SysTick_Config(2000); /* setarea perioadei de întrerupere a temporizatorului sistemului la 1 ms */
în timp ce(1);
}

Fișierul main.c declară și definește funcțiile de configurare periferică și două funcții de gestionare a întreruperilor. Să luăm în considerare caracteristicile lor.

Exemplul de mai jos folosește patru funcții de configurare (Listing 2). Toate nu au argumente și nu returnează valori. Scopul lor principal este de a inițializa rapid și cu cea mai mică cantitate de cod necesară pentru a inițializa perifericele. Acest lucru se realizează prin două caracteristici: utilizarea accesului direct la registre și utilizarea directivei __INLINE (Listing 3).

Lista 2. Declararea funcțiilor de configurare periferice

void Configure_GPIO_LED(void);
void Configure_GPIO_LPUART(void);
void Configure_LPUART(void);
void Configure_LPM_Stop(void);

Lista 3. Exemplu de implementare a funcției __INLINE cu acces direct la registrele LPUART

INLINE void Configure_LPUART(void)
{
/* (1) Activați ceasul interfeței de alimentare */
/* (2) Dezactivează registrul de protecție de rezervă pentru a permite accesul la domeniul de ceas RTC */
/* (3) LSE pe */
/* (4) Așteptați LSE gata */
/* (5) Activați registrul de protecție de rezervă pentru a permite accesul la domeniul de ceas RTC */
/* (6) LSE mapat pe LPUART */
/* (7) Activați ceasul periferic LPUART */
/* Configurați LPUART */
/* (8) supraeșantionare cu 16, 9600 baud */
/* (9) 8 biți de date, 1 bit de pornire, 1 bit de oprire, fără paritate, modul de recepție, modul de oprire */
/* (10) Setați prioritatea pentru LPUART1_IRQn */
/* (11) Activați LPUART1_IRQn */
RCC->APB1ENR |= (RCC_APB1ENR_PWREN); /* (1) */
PWR->CR |= PWR_CR_DBP; /* (2) */
RCC->CSR |= RCC_CSR_LSEON; /* (3) */
în timp ce ((RCC->CSR & (RCC_CSR_LSERDY)) != (RCC_CSR_LSERDY)) /*(4)*/
{
/* adăugați timp aici pentru o aplicație robustă */
}
PWR->CR &=~ PWR_CR_DBP; /* (5) */
RCC->CCIPR |= RCC_CCIPR_LPUART1SEL; /* (6) */
RCC->APB1ENR |= RCC_APB1ENR_LPUART1EN; /*(7) */
LPUART1->BRR = 0x369; /* (8) */
LPUART1->CR1 = USART_CR1_UESM | USART_CR1_RXNEIE | USART_CR1_RE | USART_CR1_UE; /* (9) */
NVIC_SetPriority(LPUART1_IRQn, 0); /* (10) */
NVIC_EnableIRQ(LPUART1_IRQn); /* (unsprezece) */
}

Operatorii de întrerupere de la temporizatorul de sistem și de la LPUART folosesc, de asemenea, acces direct la registre.

Astfel, comunicarea cu CMSIS se realizează fără o bibliotecă standard. Codul se dovedește a fi compact și foarte eficient. Cu toate acestea, lizibilitatea sa se va deteriora semnificativ din cauza abundenței acceselor la registre.

Folosind fragmente în propriile dezvoltări

Seturile de fragmente propuse au limitări: este necesar să se folosească placa Discovery STM32L053 pentru STM32SnippetsL0 și placa Discovery STM32F072 pentru STM32SnippetsF0.

Pentru a utiliza fragmente în dezvoltările dvs., va trebui să faceți o serie de modificări. În primul rând, trebuie să reconfigurați proiectul la procesorul necesar. Pentru a face acest lucru, trebuie să schimbați fișierul de pornire startup_stm32l053xx.s în fișierul altui controler și să definiți constanta necesară: STM32L051xx, STM32L052xx, STM32L053xx, STM32L062xx, STM32L063xx, STM32L063xx, STM32L061xx03, STM32L052xx, STM32L062xx 1 și altele. După aceasta, la compilarea stm32l0xx.h, fișierul necesar cu definiția perifericelor controlerului stm32l0yyxx.h (stm32l051xx.h/stm32l052xx.h/stm32l053xx.h/stm32l061xx.h/stm32l061xx.h/stm32l061xx.h/stm32l0610/stm32xxl0/stm32xx.h) va fi inclus automat. În al doilea rând, trebuie să selectați programatorul corespunzător în setările proprietăților proiectului. În al treilea rând, modificați codul funcțiilor din exemple dacă acestea nu îndeplinesc cerințele aplicației utilizator.

Concluzie

Seturile de fragmente și bibliotecile periferice standard produse de ST Microelectronics nu se exclud reciproc. Se completează reciproc, adăugând flexibilitate la crearea aplicațiilor.

Biblioteca standard vă permite să creați rapid cod clar, cu un nivel ridicat de abstractizare.

Fragmentele vă permit să îmbunătățiți eficiența codului - creșteți productivitatea și reduceți amprenta codului dvs. Memorie flashși RAM.

Literatură

  1. Rezumat de date. STM32SnippetsF0. Pachetul de firmware STM32F0xx Snippets. Rev. 1. – ST Microelectronics, 2014.
  2. Rezumat de date. STM32SnippetsL0. Pachetul de firmware STM32F0xx Snippets. Rev. 1. – ST Microelectronics, 2014.
  3. Acord de licență MCD-ST Liberty SW V2.pdf Relee electromecanice. Informații tehnice. – ST Microelectronics, 2011.
  4. Rezumat de date. 32L0538DISCOVERY Kit de descoperire pentru microcontrolere STM32L053. Rev. 1. – ST Microelectronics, 2014.
  5. http://www.st.com/.
Despre ST Microelectronics

Așadar, suntem deja din nou pe picioare, în sensul că avem tot ce ne trebuie conectat la pinii microcontrolerului de pe placa STM32VL Discovery, am învățat să vorbim în limbajul de programare C, este timpul să creăm un proiect pt. clasa întâi.

Scrierea unui program

După ce ați terminat de creat și configurat proiectul, puteți începe să scrieți programul propriu-zis. După cum este obișnuit pentru toți programatorii, primul program scris pentru a funcționa pe un computer este un program care afișează inscripția „HelloWorld” pe ecran, iar pentru toate microcontrolerele, primul program pentru un microcontroler produce LED-uri intermitente. Nu vom fi o excepție de la această tradiție și vom scrie un program care va controla LED-ul LD3 de pe placa STM32VL Discovery.

După crearea unui proiect gol în IAR, acesta produce cod de program minim:

Acum programul nostru se va „învârti” întotdeauna într-o buclă in timp ce.

Pentru a putea controla LED-ul, trebuie să activăm sincronizarea portului la care este conectat și să configuram ieșirea corespunzătoare a portului microcontrolerului. După cum am discutat mai devreme în prima parte, pentru a permite sincronizarea porturilor CU biți răspunsuri IOPCEN Inregistreaza-te RCC_APB2ENR. Potrivit documentului " RM0041Referinţămanual.pdf» pentru a activa sincronizarea magistralei portului CU cerute în registru RCC_APB2ENR set bit IOPCEN pe unitate. Pentru a ne asigura că atunci când acest bit este setat, nu resetam alții setate în acest registru, trebuie să aplicăm o operație logică de adăugare („SAU”) logică la starea curentă a registrului și apoi să scriem valoarea rezultată în conținutul Registrul. În conformitate cu structura bibliotecii ST, accesul la o valoare de registru pentru citire și scriere se face printr-un pointer către structură RCC-> APB2 ENR. Astfel, amintind materialul din partea a doua, puteți scrie următorul cod care setează bitul IOPCENîn registru RCC_APB2ENR:

După cum puteți vedea din fișierul „stm32f10x.h”, valoarea biților IOPCEN definit ca 0x00000010, care corespunde celui de-al patrulea bit ( IOPCEN) Inregistreaza-te APB2ENRși se potrivește cu valoarea indicată în fișa de date.

Acum să configuram ieșirea în același mod 9 port CU. Pentru a face acest lucru, trebuie să configuram acest pin de port pentru a ieși în modul push-pull. Registrul este responsabil pentru setarea modului de intrare/ieșire a portului GPIOC_CRH, ne-am uitat deja la el, descrierea sa este și în secțiunea „7.2.2 Port configuration register high” din fișa de date. Pentru a configura ieșirea în modul de ieșire cu o viteză maximă de 2 MHz, este necesar în registru GPIOC_CRH instalare MODE9 la unul și resetați bitul MODE9 la zero. Biții sunt responsabili pentru setarea modului de funcționare de ieșire ca funcție principală cu ieșire push-pull CNF9 Și CNF9 , pentru a configura modul de operare de care avem nevoie, ambii acești biți trebuie resetati la zero.

Acum pinul portului la care este conectat LED-ul este setat la ieșire, pentru a controla LED-ul trebuie să schimbăm starea pinului portului setând ieșirea la una logică. Există două moduri de a schimba starea pinului portului, prima este să scrieți direct în registrul de stare a portului conținutul modificat al registrului de porturi, la fel cum am configurat portul. Această metodă nu este recomandată din cauza posibilității unei situații în care o valoare incorectă poate fi scrisă în registrul portului. Această situație poate apărea dacă, în timpul unei modificări a stării registrului, din momentul în care starea registrului a fost deja citită și până în momentul în care starea modificată este scrisă în registru, un dispozitiv periferic sau întreruperea schimbă starea acestui port. . La finalizarea operațiunii de modificare a stării registrului, valoarea va fi scrisă în registru fără a ține cont de modificările intervenite. Deși probabilitatea apariției acestei situații este foarte scăzută, merită totuși să folosiți o altă metodă în care situația descrisă este exclusă. În acest scop, în microcontroler există două registre GPIOx_BSRRȘi GPIOx_BRR. Când scrieți unul logic la bitul de registru necesar GPIOx_BRR va avea loc resetarea ieșirea portului corespunzătoare la zero logic. Inregistreaza-te GPIOx_BSRR poate efectua atât setarea, cât și resetarea stării pinilor portului; pentru a seta pinul portului la o unitate logică, este necesar să setați biții BSN, corespunzător numărului de bit necesar, acești biți sunt localizați în registrele inferioare ale octetului. Pentru a reseta starea de ieșire a portului la zero logic, trebuie să scrieți biții BRn pinii corespunzători, acești biți sunt localizați în cei mai importanți biți ai registrului portului.

LED-ul LD3 este conectat la pin 9 port CU. Pentru a porni acest LED, trebuie să aplicăm unul logic pinului portului corespunzător pentru a „aprinde” LED-ul.

Să adăugăm cod pentru configurarea ieșirii portului LED în programul nostru și, de asemenea, să adăugăm o funcție de întârziere software pentru a reduce frecvența de comutare a LED-ului:

//Nu uitați să vă conectați fișier antet cu o descriere a registrelor microcontrolerului

#include „stm32f10x.h”

vidÎntârziere ( vid);

vidÎntârziere ( vid)
{
nesemnat lung eu;
pentru(i=0; i<2000000; i++);
}

//Funcția noastră principală

vid principal( vid)
{


RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

//Ștergeți biții MODE9 (resetați biții MODE9_1 și MODE9_0 la zero)
GPIOC->CRH &= ~GPIO_CRH_MODE9;

//Setați bitul MODE9_1 pentru a configura ieșirea la ieșire cu o viteză de 2MHz
GPIOC->CRH |= GPIO_CRH_MODE9_1;

//Ștergeți biții CNF (setați ca o ieșire de uz general, simetric (push-pull))
GPIOC->CRH &= ~GPIO_CRH_CNF9;

in timp ce(1)
{

//Setarea pinului 9 al portului C la cel logic („LED-ul aprins”)
GPIOC->BSRR = GPIO_BSRR_BS9;


Întârziere();


GPIOC->BSRR = GPIO_BSRR_BR9;


Întârziere();

}
}

Puteți descărca arhiva cu codul sursă al programului scris folosind controlul direct al registrelor microcontrolerului din link.

Primul nostru program lucrabil a fost scris; atunci când l-am scris, pentru a opera și configura perifericele, am folosit date din fișa de date oficială „ RM0041Referinţămanual.pdf„, această sursă de informații despre registrele microcontrolerului este cea mai precisă, dar pentru a o utiliza trebuie să recitiți o mulțime de informații, ceea ce complică scrierea programelor. Pentru a facilita procesul de configurare a perifericelor microcontrolerului, există diverse generatoare de cod, utilitate oficială de la compania ST este prezentat programul Microxplorer, dar este încă de puțină funcționalitate și din acest motiv a fost creat de dezvoltatori terți program alternativ„STM32 Program Code Generator » . Acest program vă permite să obțineți cu ușurință codul de configurare periferic folosind o interfață grafică convenabilă și intuitivă (vezi Fig. 2).


Orez. 2 Captură de ecran a programului generator de cod STM32

După cum se poate observa din Figura 2, codul de configurare a ieșirii LED generat de program coincide cu codul pe care l-am scris mai devreme.

Pentru a rula un program scris după compilare cod sursa, trebuie să încărcăm programul nostru în microcontroler și să vedem cum rulează.

Video cu LED-ul care clipește modul de depanare a programului

Video cu programul LED care clipește pe placa Discovery STM32VL

Funcții de bibliotecă pentru lucrul cu periferice

Pentru a simplifica munca de configurare a registrelor periferice ale microcontrolerului, compania ST a dezvoltat biblioteci, datorită cărora nu este nevoie să citiți atât de amănunțit fișa de date, deoarece atunci când utilizați aceste biblioteci, munca de scriere a unui program va deveni mai aproape de scrierea programelor de nivel înalt, având în vedere faptul că toate funcțiile de nivel scăzut sunt implementate la nivel de funcție de bibliotecă. Cu toate acestea, nu ar trebui să renunțăm complet la utilizarea lucrului direct cu registrele microcontrolerului, deoarece funcțiile de bibliotecă necesită mai mult timp de procesor pentru execuția lor și, ca urmare, utilizarea lor în secțiunile de timp critice ale programului nu este justificată. Dar totuși, în cele mai multe cazuri, lucruri precum inițializarea perifericelor nu sunt critice pentru timpul de execuție, iar comoditatea utilizării funcțiilor de bibliotecă este mai de preferat.

Acum să scriem programul nostru folosind biblioteca ST. Programul necesită configurarea porturilor de intrare/ieșire; pentru a utiliza funcțiile bibliotecii pentru configurarea porturilor, trebuie să conectați fișierul antet " stm32f10x_gpio.h„(vezi tabelul. 1). Acest fișier poate fi conectat prin decomentarea liniei corespunzătoare din fișierul de configurare antet conectat " stm32f10x_conf.h" La sfârșitul fișierului " stm32f10x_gpio.h» există o listă de declarații de funcții pentru lucrul cu porturi. O descriere detaliată a tuturor funcțiilor disponibile poate fi găsită în fișierul „ stm32f10x_stdperiph_lib_um.chm", o scurtă descriere a celor mai frecvent utilizate este dată în Tabelul 2.

Tabelul 2. Descrierea funcțiilor principale de configurare a porturilor

Funcţie

Descrierea funcției, parametrii trecuți și returnați

GPIO_DeInit(
GPIO_TypeDef* GPIOx)

Setează registrele de configurare a portului GPIOx la valorile implicite.

GPIO_Init(
GPIO_TypeDef* GPIOx,

Setează registrele de configurare a portului GPIOx în conformitate cu parametrii specificați în structura GPIO_InitStruct

GPIO_StructInit(
GPIO_InitTypeDef* GPIO_InitStruct)

Completează toate câmpurile structurii GPIO_InitStruct cu valori implicite

uint8_t GPIO_ReadInputDataBit(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin);

Citirea valorii de intrare a pinului GPIO_Pin al portului GPIOx

uint16_t GPIO_ReadInputData (
GPIO_TypeDef* GPIOx)

Citirea valorilor de intrare ale tuturor pinilor portului GPIOx

GPIO_SetBits(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin)

Setarea valorii de ieșire a pinului GPIO_Pin al portului GPIOx la unul logic

GPIO_ResetBits(
GPIO_TypeDef* GPIOx,
uint16_t GPIO_Pin)

Resetarea valorii de ieșire a pinului GPIO_Pin al portului GPIOx la zero logic

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

Scrierea valorii BitVal în pinul GPIO_Pin al portului GPIOx

GPIO_Write(
GPIO_TypeDef* GPIOx,
uint16_t PortVal)

Scrieți valoarea PortVal pe portul GPIOx

După cum se poate vedea din descrierea funcțiilor, ca parametri pentru setările portului etc., nu sunt trecuți mulți parametri individuali diferiți funcției, ci o singură structură. Structurile sunt date combinate care au o anumită relație logică. Spre deosebire de matrice, structurile pot conține date de diferite tipuri. Cu alte cuvinte, o structură reprezintă un set de variabile diferite cu tipuri variate, combinate într-o variabilă unică. Variabilele situate în această structură se numesc câmpuri ale structurii și sunt accesate în felul următor: mai întâi scrieți numele structurii, apoi scrieți un punct și numele câmpului structurii (numele variabilei din această structură).

Lista variabilelor incluse în structurile pentru funcțiile care lucrează cu porturi este descrisă în același fișier puțin deasupra descrierii funcțiilor. Deci, de exemplu, structura " GPIO_InitTypeDef„are următoarea structură:

typedef struct
{

uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
Acest parametru poate fi orice valoare a lui @ref GPIO_pins_define */

GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the viteza pentru pinii selectați.
Acest parametru poate fi o valoare a @ref GPIOSpeed_TypeDef */

GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
Acest parametru poate fi o valoare a @ref GPIOMode_TypeDef */

)GPIO_InitTypeDef;

Primul câmp al acestei structuri conține variabila " GPIO_ Pin" tip nesemnat mic de statura, în această variabilă este necesar să se înregistreze steagurile numerelor pinilor corespunzători pentru care se presupune că trebuie făcute setările necesare. Puteți configura mai multe ieșiri simultan prin specificarea mai multor constante ca parametru folosind operatorul biți SAU(cm. ). Un SAU pe biți le va „colecta” pe toate din constantele enumerate, iar constantele în sine sunt o mască doar destinată unei astfel de utilizări. Definițiile macro ale constantelor sunt listate în același fișier de mai jos.

Al doilea câmp al structurii " GPIO_InitTypeDef» setează viteza maximă posibilă a ieșirii portului. Lista de valori posibile a acestui domeniu listat mai sus:

Descrierea valorilor posibile:

  • GPIO_Mode_AIN- intrare analogică (în engleză: Analog INput);
  • GPIO_Mode_IN_FLOATING- intrare fără strângere, atârnând (intrare engleză float) în aer
  • GPIO_Mode_IPD- pull-down de intrare
  • GPIO_Mode_IPU- tragere de intrare
  • GPIO_Mode_Out_OD- Ieșire Open Drain
  • GPIO_Mode_Out_PP- ieșire în două stări (în engleză: Output Push-Pull - înainte și înapoi)
  • GPIO_Mode_AF_OD- ieșire de scurgere deschisă pentru funcții alternative (funcție alternativă în engleză). Folosit în cazurile în care ieșirea trebuie controlată de periferice atașate această concluzie port (de exemplu, pinul Tx USART1 etc.)
  • GPIO_Mode_AF_PP- același lucru, dar cu două stări

Într-un mod similar, puteți vizualiza structura variabilelor altor structuri necesare lucrului cu funcțiile de bibliotecă.

Pentru a lucra cu structuri, acestea, ca și variabilele, trebuie să fie declarate și atribuite acestora nume unic, după care puteți accesa câmpurile structurii declarate după numele atribuit acesteia.

//Declară structura

/*
Înainte de a începe să completați câmpurile structurii, se recomandă inițializarea conținutului structurii cu date implicite; acest lucru se face pentru a preveni scrierea datelor incorecte dacă, dintr-un anumit motiv, nu au fost completate toate câmpurile structurii. .

Pentru a transmite valorile unei structuri unei funcții, trebuie să precedați numele structurii cu simbolul &. Acest simbol spune compilatorului că este necesar să se transmită funcției nu valorile însele conținute în structură, ci adresa din memorie în care se află aceste valori. Acest lucru se face pentru a reduce numărul de acțiuni necesare procesorului pentru a copia conținutul structurii și, de asemenea, permite salvarea RAM. Astfel, în loc să treci mai mulți octeți conținuti în structură către funcție, se va trece doar unul care să conțină adresa structurii.
*/

/* Scrieți în câmpul GPIO_Pin al structurii GPIO_Init_struct numărul de pin al portului pe care îl vom configura în continuare */

GPIO_Init_struct.GPIO_Pin=GPIO_Pin_9;

/* Completați câmpul GPIO_Speed ​​​​în același mod */

/*
După ce am completat câmpurile necesare ale structurii, această structură trebuie trecută la o funcție care va face intrarea necesară în registrele corespunzătoare. Pe lângă structura cu setările pentru această funcție, este necesar să se treacă și numele portului pentru care sunt destinate setările.
*/

Aproape toate perifericele sunt configurate aproximativ în același mod; singurele diferențe sunt în parametrii și comenzile specifice fiecărui dispozitiv.

Acum să scriem programul nostru de LED intermitent folosind doar funcții de bibliotecă.

//Nu uitați să includeți fișierul antet cu o descriere a registrelor microcontrolerului

#include „stm32f10x.h”
#include „stm32f10x_conf.h”

//declară funcția de întârziere software

vidÎntârziere ( vid);

//funcția de întârziere software în sine

vidÎntârziere ( vid)
{
nesemnat lung eu;
pentru(i=0; i<2000000; i++);
}

//Funcția noastră principală

vid principal( vid)
{

//Permite sincronizarea magistralei portului C
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

//Declară o structură pentru configurarea portului
GPIO_InitTypeDef GPIO_Init_struct;

//Umpleți structura cu valorile inițiale
GPIO_StructInit(&GPIO_Init_struct);

/* Scrieți în câmpul GPIO_Pin al structurii GPIO_Init_struct numărul de pin al portului pe care îl vom configura în continuare */
GPIO_Init_struct.GPIO_Pin = GPIO_Pin_9;

// Completați câmpurile GPIO_Speed ​​​​și GPIO_Mode în același mod
GPIO_Init_struct.GPIO_Speed= GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode = GPIO_Mode_Out_PP;

//Treceți structura umplută pentru a efectua acțiuni de configurare a registrelor
GPIO_Init(GPIOC, &GPIO_Init_struct);

//Bucla noastră principală fără sfârșit
in timp ce(1)
{
//Setarea pinului 9 al portului C la cel logic (LED aprins)
GPIO_SetBits(GPIOC, GPIO_Pin_9);

//Adăugați o întârziere software, astfel încât LED-ul să lumineze pentru un timp
Întârziere();

//Resetați starea pinului 9 al portului C la zero logic
GPIO_ResetBits(GPIOC, GPIO_Pin_9);

//Adăugați din nou o întârziere software
Întârziere();
}
}

legătură.

Din exemplul de mai sus, este clar că utilizarea funcțiilor de bibliotecă pentru lucrul cu periferice vă permite să apropiați programele de scriere pentru un microcontroler de programarea orientată pe obiecte și, de asemenea, reduce nevoia de acces frecvent la fișa de date pentru a citi descrierile microcontrolerului. registre, dar utilizarea funcțiilor de bibliotecă necesită cunoștințe mai mari ale limbajului de programare. Având în vedere acest lucru, pentru persoanele care nu sunt deosebit de familiarizate cu programarea, o opțiune mai simplă de scriere a programelor va fi o modalitate de a scrie programe fără a utiliza funcții de bibliotecă, cu acces direct la registrele microcontrolerului. Pentru cei care cunosc bine limbajul de programare, dar sunt slab versați în microcontrolere, în special STM32, utilizarea funcțiilor de bibliotecă simplifică semnificativ procesul de scriere a programelor.

Această împrejurare, precum și faptul că ST s-a îngrijit de un grad ridicat de compatibilitate, atât în ​​hardware cât și în software, a diferitelor sale microcontrolere, facilitează studierea acestora, deoarece nu este nevoie să se aprofundeze caracteristicile structurale ale diverse controlere din seria STM32 și vă permite să selectați oricare dintre microcontrolerele disponibile în linia STM32 ca microcontroler pentru studiu.

Manager de întreruperi

Microcontrolerele au o capacitate remarcabilă - de a opri execuția programului principal pentru un anumit eveniment și de a trece la execuția unei subrutine speciale - handler de întrerupere. Sursele de întrerupere pot fi fie evenimente externe - întreruperi pentru primirea/transmiterea datelor prin orice interfață de transfer de date, fie o modificare a stării de ieșire, fie cele interne - overflow timer, etc. O listă cu posibile surse de întrerupere pentru microcontrolerele din seria STM32 este dată în fișa de date. " RM0041 Manual de referință„În capitolul” 8 Întreruperi și evenimente».

Deoarece handler-ul de întrerupere este și o funcție, va fi scrisă ca o funcție obișnuită, dar pentru ca compilatorul să știe că această funcție este un handler de întrerupere specific, ca nume de funcție ar trebui selectate nume predefinite, către care redirecționări vector de întrerupere. sunt specificate. O listă cu numele acestor funcții cu o scurtă descriere se află în fișierul de asamblare " startup_stm32f10x_md_vl.s" Un handler de întreruperi poate avea mai multe surse care cauzează întreruperi, de exemplu funcția de gestionare a întreruperilor " USART1_IRQHandler„poate fi apelat atunci când sfârșitul recepției și sfârșitul transmiterii unui octet etc.

Pentru a începe să lucrați cu întreruperi, trebuie să configurați și să inițializați controlerul de întrerupere NVIC. În arhitectura Cortex M3, fiecărei întreruperi i se poate atribui propriul grup de prioritate pentru cazurile în care apar mai multe întreruperi simultan. Apoi trebuie să configurați sursa de întrerupere.

Câmpul NVIC_IRQChannel indică ce întrerupere dorim să configuram. Constanta USART1_IRQn denotă canalul responsabil pentru întreruperile asociate cu USART1. Este definit în fișierul " stm32f10x.h„, acolo sunt definite și alte constante similare.

Următoarele două câmpuri indică prioritatea întreruperii (valorile maxime pentru acești doi parametri sunt determinate de grupul de prioritate selectat). Ultimul câmp permite de fapt utilizarea unei întreruperi.

A functiona NVIC_Init, la fel ca la configurarea porturilor, se trece un pointer către o structură pentru a aplica setările efectuate și a le scrie în registrele corespunzătoare ale microcontrolerului.

Acum, în setările modulului, trebuie să setați parametrii prin care acest modul va genera o întrerupere. Mai întâi trebuie să activați întreruperea; aceasta se face apelând funcția Nume_ITConfig(), care se află în fișierul antet dispozitiv periferic.

//Activați întreruperile la finalizarea transferului de octeți prin USART1
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);

//Activați întreruperile când se primește un octet prin USART1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

O descriere a parametrilor trecuți funcției poate fi găsită în fișierul de cod sursă al dispozitivului periferic, chiar deasupra locației funcției în sine. Această funcție activează sau dezactivează întreruperile pentru diverse evenimente de la modulul periferic specificat. Când această funcție este executată, microcontrolerul va putea genera întreruperi pentru evenimentele de care avem nevoie.

După ce intrăm în funcția de gestionare a întreruperilor, trebuie să verificăm de la ce eveniment a avut loc întreruperea și apoi să resetam flag-ul, altfel, la ieșirea din întrerupere, microcontrolerul va decide că nu am procesat întrerupere, deoarece flag-ul de întrerupere este încă setat.

Pentru a efectua diverse, mici, acțiuni repetate cu o perioadă precisă, microcontrolerele cu nucleu Cortex-M3 au un cronometru de sistem special conceput pentru aceasta. Funcțiile acestui cronometru includ doar apelarea unei întreruperi la intervale de timp strict specificate. De obicei, întreruperea apelată de acest temporizator conține cod pentru a măsura durata diferitelor procese. Declarația funcției de setare a temporizatorului se află în fișierul " miez_ cm3. h" Argumentul transmis funcției specifică numărul de cicluri de ceas al magistralei de sistem între intervalele de apelare a gestionarului de întrerupere a temporizatorului de sistem.

SysTick_Config(clk);

Acum că ne-am ocupat de întreruperi, să ne rescriem programul folosind temporizatorul de sistem ca element de sincronizare. De când cronometrul " SysTick” este unul de sistem și poate fi folosit de diferite blocuri funcționale ale programului nostru, atunci ar fi rezonabil să mutați funcția de gestionare a întreruperilor temporizatorului de sistem într-un fișier separat, iar din această funcție apelăm funcții pentru fiecare bloc funcțional separat.

Un exemplu de fișier „main.c” pentru un program care clipește LED folosind o întrerupere:

//Includeți un fișier antet care descrie registrele microcontrolerului

#include „stm32f10x.h”
#include „stm32f10x_conf.h”
#include „principal.h”

nesemnat int LED_timer;

//Funcția apelată de la funcția de gestionare a întreruperii temporizatorului de sistem

vid SysTick_Timer_main( vid)
{
//Dacă variabila LED_timer nu a ajuns încă la 0,
dacă(LED_timer)
{
//Verificați valoarea acestuia, dacă este mai mare de 1500, aprindeți LED-ul
dacă(LED_timer>1500) GPIOC->BSRR= GPIO_BSRR_BS9;

// în caz contrar, dacă este mai mic sau egal cu 1500, atunci dezactivați-l
altfel GPIOC->BSRR= GPIO_BSRR_BR9;

//Reduceți variabila LED_timer
LED_timer--;
}

//Dacă valoarea variabilei ajunge la zero, setați o nouă valoare de 2000
altfel LED_timer=2000;
}

//Funcția noastră principală

vid principal( vid)
{

//Permite sincronizarea magistralei portului C
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

//Declară o structură pentru configurarea portului
GPIO_InitTypeDef GPIO_Init_struct;

//Umpleți structura cu valorile inițiale
GPIO_StructInit(&GPIO_Init_struct);

/* Scrieți în câmpul GPIO_Pin al structurii GPIO_Init_struct numărul de pin al portului pe care îl vom configura în continuare */
GPIO_Init_struct.GPIO_Pin = GPIO_Pin_9;

// Completați câmpurile GPIO_Speed ​​​​și GPIO_Mode în același mod
GPIO_Init_struct.GPIO_Speed= GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode = GPIO_Mode_Out_PP;

//Treceți structura umplută pentru a efectua acțiuni de configurare a registrelor
GPIO_Init(GPIOC, &GPIO_Init_struct);

//selectați un grup prioritar pentru întreruperi
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

//Configurați temporizatorul de sistem cu un interval de 1 ms
SysTick_Config(24000000/1000);

//Bucla noastră principală fără sfârșit
in timp ce(1)
{
//De data aceasta este gol, toate comenzile LED au loc în întreruperi
}
}

O parte a codului sursă din fișierul „stm32f10x_it.c”:


#include „principal.h”

/**
* @brief Această funcție se ocupă de SysTick Handler.
* @param Nici unul
* @retval Nici unul
*/

vid SysTick_Handler( vid)
{
SysTick_Timer_main();
}

Un exemplu de schiță de lucru a unui program pentru clipirea unui LED folosind o întrerupere poate fi descărcat de pe link.

Aceasta încheie povestea mea despre elementele de bază ale dezvoltării programelor pentru microcontrolerul STM32. Am furnizat toate informațiile necesare pentru a activa în continuare auto-studiu Microcontrolere STM32. Materialul furnizat este doar un început, deoarece o descriere completă a lucrului cu microcontrolere nu poate fi descrisă în cadrul niciunui articol. În plus, studiul microcontrolerelor fără a dobândi experiență practică este imposibil, iar experiența reală vine treptat cu ani de muncă, experimente, cu acumularea de diverse dezvoltări software și hardware, precum și citirea diverselor articole și documentații despre microcontrolere. Dar nu lăsați acest lucru să vă sperie, deoarece informațiile furnizate în articol sunt destul de suficiente pentru a vă crea primul dispozitiv pe un microcontroler și puteți dobândi mai multe cunoștințe și experiență pe cont propriu, dezvoltând din ce în ce mai complex și cele mai bune dispozitiveși îmbunătățirea abilităților dvs.

Sper că am putut să vă interesez să studiați microcontrolere și să dezvolt dispozitive bazate pe acestea, iar lucrările mele vă vor fi utile și interesante.

Când tocmai începi să programezi microcontrolere sau nu ai făcut programare de mult timp, nu este ușor să înțelegi codul altcuiva. Întrebări „Ce este asta?” și „De unde a venit asta?” apar pe aproape fiecare combinație de litere și numere. Și cu cât înțelegerea logicii „ce? de ce? și unde?” vine mai rapid, cu atât este mai ușor să studiezi codul altor oameni, inclusiv exemple. Adevărat, uneori, pentru aceasta, trebuie să „săriți prin cod” și „căutați prin manuale” mai mult de o zi.

Toate microcontrolerele STM32F4xx au destul de multe periferice. Fiecărui dispozitiv periferic microcontroler îi este atribuită o zonă de memorie specifică, specifică și nerelocabilă. Fiecare zonă de memorie constă din registre de memorie, iar acești registre pot fi de 8 biți, 16 biți, 32 de biți sau altceva, în funcție de microcontroler. În microcontrolerul STM32F4, aceste registre sunt pe 32 de biți și fiecare registru are propriul său scop și propria sa adresă specifică. Nimic nu te împiedică să le accesezi direct în programele tale, indicând adresa. La ce adresă se află acest sau acel registru și ce dispozitiv periferic aparține este indicat pe cardul de memorie. Pentru STM32F4 un astfel de card de memorie se află în documentul DM00031020.pdf, care poate fi găsit pe st.com. Documentul este numit

RM0090
Manual de referință
STM32F405xx/07xx, STM32F415xx/17xx, STM32F42xxx și STM32F43xxx MCU avansate pe 32 de biți bazate pe ARM

În capitolul 2.3 Harta memoriei la pagina 64 un tabel începe cu adresele zonelor de registru și afilierea acestora cu dispozitivul periferic. În același tabel există un link către o secțiune cu alocarea mai detaliată a memoriei pentru fiecare periferic.

Tabelul din stânga arată gama de adrese, în mijloc este numele perifericului, iar în ultima coloană este situată o descriere mai detaliată a alocării memoriei.

Deci, pentru porturile I/O GPIO de uz general din tabelul de alocare a memoriei, puteți găsi că adresele sunt alocate pentru acestea începând de la 0x4002 0000. Portul I/O de uz general GPIOA ocupă intervalul de adrese de la 0x4002 000 la 0x4002 03FF. Portul GPIOB ocupă domeniul de adrese 0x4002 400 - 0x4002 07FF. Și așa mai departe.

Pentru a vedea mai multe distribuție detaliatăîn gama în sine, trebuie doar să urmați linkul.

Există și un tabel aici, dar cu o hartă de memorie pentru intervalul de adrese GPIO. Conform acestei hărți de memorie, primii 4 octeți aparțin registrului MODER, următorii 4 octeți aparțin registrului OTYPER și așa mai departe. Adresele de registru sunt numărate de la începutul intervalului aparținând unui anumit port GPIO. Adică, fiecare registru GPIO are o adresă specifică care poate fi utilizată la dezvoltarea programelor pentru microcontroler.

Dar utilizarea adreselor de registru este incomod și grea pentru oameni o cantitate mare erori. Prin urmare, producătorii de microcontrolere creează biblioteci standard care facilitează lucrul cu microcontrolere. În aceste biblioteci, adresele fizice sunt potrivite cu acestea desemnarea literei. Pentru STM32F4xx aceste corespondențe sunt specificate în fișier stm32f4xx.h. Fişier stm32f4xx.h aparține bibliotecii CMSISși se află în folderul Libraries\CMSIS\ST\STM32F4xx\Include\.

Să vedem cum este definit portul GPIOA în biblioteci. Orice altceva este determinat în mod similar. Este suficient să înțelegem principiul. Fişier stm32f4xx.h destul de mare și, prin urmare, mai bine să utilizați căutarea sau capacitățile oferite de lanțul dvs. de instrumente.

Pentru portul GPIOA, găsim linia care menționează GPIOA_BASE

GPIOA_BASE este definit prin AHB1PERIPH_BASE

AHB1PERIPH_BASE este, la rândul său, definit prin PERIPH_BASE

Și, la rândul său, PERIPH_BASE este definit ca 0x4000 0000. Dacă vă uitați la harta de distribuție a memoriei dispozitivelor periferice (în secțiunea 2.3 Harta memoriei la pagina 64), vom vedea această adresă chiar în partea de jos a tabelului. De la această adresă încep registrele tuturor perifericelor microcontrolerului STM32F4. Adică, PERIPH_BASE este adresa de pornire a întregii periferii a microcontrolerelor STM32F4xx în general și a microcontrolerului STM32F407VG în special.

AHB1PERIPH_BASE este definit ca suma (PERIPH_BASE + 0x00020000). (vezi pozele din spate). Aceasta va fi adresa 0x4002 0000. În cardul de memorie, porturile de intrare/ieșire GPIO de uz general încep la această adresă.

GPIOA_BASE este definit ca (AHB1PERIPH_BASE + 0x0000), adică este adresa de pornire a grupului de registre de porturi GPIOA.

Ei bine, portul GPIOA în sine este definit ca o structură de registre, a căror plasare în memorie începe cu adresa GPIOA_BASE (vezi linia #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE).

Structura fiecărui port GPIO este definită ca GPIO_TypeDef.

Astfel, bibliotecile standard, în acest caz fișierul stm32f4xx.h, pur și simplu umanizați adresarea mașinii. Dacă vedeți intrarea GPIOA->ODR = 1234, atunci aceasta înseamnă că numărul 1234 va fi scris la adresa 0x40020014. GPIOA are o adresă de pornire de 0x40020000 și registrul ODR are o adresă de 0x14 de la începutul intervalului, deci GPIOA->ODR are o adresă de 0x40020014.

Sau, de exemplu, nu vă place intrarea GPIOA->ODR, atunci puteți defini #define GPIOA_ODR ((uint32_t *) 0x40020014)și obțineți același rezultat scriind GPIOA_ODR = 1234;. Dar cât de oportun este acest lucru? Dacă doriți cu adevărat să vă introduceți propriile denumiri, atunci este mai bine să le reatribuiți pur și simplu pe cele standard. Puteți vedea cum se face acest lucru în fișier stm32f4_discovery.h De exemplu, așa este definit unul dintre LED-uri acolo:

#define LED4_PIN GPIO_Pin_12
#define LED4_GPIO_PORT GPIOD
#define LED4_GPIO_CLK RCC_AHB1Periph_GPIOD

O descriere mai detaliată a periferiei portului poate fi găsită în stm32f4xx_gpio.h

Până în acest punct am folosit bibliotecă standard nuclee - CMSIS. Pentru a configura orice port pornit modul dorit de lucru, a trebuit să apelăm pentru a găsi registrul responsabil pentru o anumită funcție și, de asemenea, să căutăm printr-un document mare și alte informații legate de acest proces. Lucrurile vor deveni și mai dureroase și de rutină atunci când începem să lucrăm cu un cronometru sau ADC. Numărul de registre de acolo este mult mai mare decât cel al porturilor I/O. Setare manuală necesită mult timp și crește șansa de a greși. Prin urmare, mulți oameni preferă să lucreze cu biblioteca periferică standard - StdPeriph. Ce dă? Este simplu - nivelul de abstractizare crește, nu trebuie să intri în documentație și să te gândești la registre în cea mai mare parte. În această bibliotecă, toate modurile și parametrii de funcționare ai periferiei MK sunt descriși sub formă de structuri. Acum, pentru a configura un dispozitiv periferic, trebuie doar să apelați funcția de inițializare a dispozitivului cu o structură umplută.

Mai jos este o imagine cu o reprezentare schematică a nivelurilor de abstractizare.

Am lucrat cu CMSIS (care este „cel mai aproape” de nucleu) pentru a arăta cum funcționează microcontrolerul. Urmatorul pas este biblioteca standard, pe care vom învăța să o folosim acum. Urmează driverele de dispozitiv. Ele sunt înțelese ca fișiere *.c \ *.h care oferă un mod convenabil interfata software pentru a controla orice dispozitiv. De exemplu, în acest curs vă vom oferi drivere pentru cipul max7219 și modulul WiFi esp8266.

Un proiect standard va include următoarele fișiere:


În primul rând, desigur, acestea sunt fișierele CMSIS care permit bibliotecii standard să lucreze cu nucleul, am vorbit deja despre ele. În al doilea rând, fișierele bibliotecii standard. Și în al treilea rând, fișierele utilizator.

Fișierele bibliotecii se găsesc pe pagina dedicată MK-ului țintă (pentru noi este stm32f10x4), în secțiunea Resurse de proiectare(în IDE-ul CooCox, aceste fișiere sunt descărcate din depozitul mediului de dezvoltare). Fiecare periferic corespunde a două fișiere - antet (*.h) și codul sursă (*.c). O descriere detaliată poate fi găsită în fișierul de suport, care se află în arhiva cu biblioteca de pe site.

  • stm32f10x_conf.h - fișier de configurare a bibliotecii. Utilizatorul poate conecta sau deconecta module.
  • stm32f10x_ppp.h - fișier antet periferic. În loc de ppp poate fi gpio sau adc.
  • stm32f10x_ppp.c - driver de dispozitiv periferic scris în limbaj C.
  • stm32f10x_it.h - fișier antet care include toți manipulatorii de întreruperi posibili (prototipurile acestora).
  • stm32f10x_it.c este un fișier cod sursă șablon care conține rutina de întrerupere a serviciului (ISR) pentru situații de excepție în Cortex M3. Utilizatorul își poate adăuga propriile ISR-uri pentru perifericele utilizate.

Biblioteca standard și perifericele au o convenție în denumirea funcțiilor și a notației.

  • PPP este un acronim pentru periferice, cum ar fi ADC.
  • Fișiere de sistem, antet și cod sursă - începeți cu stm32f10x_.
  • Constantele utilizate într-un fișier sunt definite în acel fișier. Constanțele utilizate în mai multe fișiere sunt definite în fișierele antet. Toate constantele din biblioteca periferică sunt cel mai adesea scrise cu majuscule.
  • Registrele sunt tratate ca constante și sunt numite și litere MAJUSCULE.
  • Numele de funcții specifice perifericelor includ un acronim, cum ar fi USART_SendData() .
  • Pentru a configura fiecare dispozitiv periferic, se folosește structura PPP_InitTypeDef, care este transmisă funcției PPP_Init().
  • Pentru a deinițializa (setați valoarea implicită), puteți utiliza funcția PPP_DeInit().
  • Funcția care vă permite să activați sau să dezactivați periferice se numește PPP_Cmd().
  • Funcția de activare/dezactivare a întreruperii se numește PPP_ITConfig.

CU lista plina puteți să vă uitați din nou la fișierul de suport al bibliotecii. Acum să rescriem LED-ul care clipește folosind biblioteca standard de periferice!

Înainte de a începe lucrul, să ne uităm la fișierul stm32f10x.h și să găsim linia:

#define USE_STDPERIPH_DRIVER

Dacă configurați proiectul de la zero folosind fișiere de bibliotecă din arhiva descărcată, atunci va trebui să decomentați această linie. Vă va permite să utilizați biblioteca standard. Această definiție (macro) va comanda preprocesorului să includă fișierul stm32f10x_conf.h:

#ifdef USE_STDPERIPH_DRIVER #include „stm32f10x_conf.h” #endif

Acest fișier conține module. Dacă aveți nevoie doar de unele specifice, dezactivați restul, acest lucru va economisi timp în timpul compilării. După cum probabil ați ghicit, avem nevoie module RTCși GPIO (cu toate acestea, în viitor veți avea nevoie și de _bkp.h , _flash , _pwr.h , _rtc.h , _spi.h , _tim.h , _usart.h ):

#include „stm32f10x_flash.h” // pentru init_pll() #include „stm32f10x_gpio.h” #include „stm32f10x_rcc.h”

Ca și data trecută, mai întâi trebuie să activați tactarea portului B. Acest lucru se face de funcția declarată în stm32f10x_rcc.h:

Void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

Enumerația FunctionalState este definită în stm32f10x.h:

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

Să declarăm o structură pentru configurarea piciorului nostru (o puteți găsi în fișierul stm32f10x_gpio.h):

LED GPIO_InitTypeDef;

Acum trebuie să o completăm. Să ne uităm la conținutul acestei structuri:

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

Toate enumerările și constantele necesare pot fi găsite în același fișier. Apoi, funcția init_leds() rescrisă va lua următoarea formă:

Void led_init() ( // Activați sincronizarea RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Declarați structura și completați-o GPIO_InitTypeDef LED; LED.GPIO_Pin = GPIO_Pin_0; LED.GPIO_Model_GPIO_Speed_2MHIO = GPIO_Speed_Speed. Out_PP; // Inițializați portul GPIO_Init( GPIOB, &LED); )

Să rescriem funcția main():

Int main(void) (led_init(); în timp ce (1) ( GPIO_SetBits(GPIOB, GPIO_Pin_0); delay(10000000); GPIO_ResetBits(GPIOB, GPIO_Pin_0); delay(10000000); ) )

Principalul lucru este să înțelegeți ordinea de inițializare: porniți ceasul periferic, declarați structura, completați structura, apelați metoda de inițializare. Alte dispozitive periferice sunt de obicei configurate într-un mod similar.

Încă o dată vreau să scriu despre un început simplu cu STM32, doar că de data aceasta fără a folosi șabloanele sau exemplele nimănui - cu o explicație a fiecărui pas. Articolele vor avea numerotarea continuă a pașilor.

1. Instalați IAR

Construirea unui proiect în IAR

1. Preprocesor

  1. sterge comentariile

2. Compilator

3. Linker

3. Creați un nou proiect în IAR

După lansarea IAR, apare o fereastră centru de informatii, de care nu avem nevoie. Faceți clic pe meniul Proiect -> Creare proiect nou. Selectați lanțul de instrumente: ARM (este puțin probabil să aveți altceva pe acea listă), șabloane de proiect: C –> principal.

În fereastra din stânga ("Workspace"), faceți clic dreapta pentru a apela meniul și creați grup nou(Adăugați –>

Faceți clic dreapta pe CMSIS

Pentru grup Lansare

Am terminat cu CMSIS.

Pentru grup StdPeriphLib

Pentru grup Utilizator

5. Stabilirea proiectului

  1. Opțiuni generale –> Țintă –>
Selectați ST –> STM32F100 –> ST STM32F100xB. Acesta este controlerul nostru. 2. Opțiuni generale –> Configurare bibliotecă –> CMSIS: bifați caseta de selectare Utilizare CMSIS. Deci vom folosi biblioteca CMSIS încorporată în compilator. Începând cu versiunea 6.30, IAR a început să fie livrat cu un CMSIS încorporat, iar acest lucru pare să fie mai bun - dar a introdus o oarecare confuzie cu proiecte mai vechi. 3. Compilatorul C/C++ –>
$PROJ_DIR$\

* Debugger –> Setup –> Driver: selectați ST–Link, deoarece acesta este programatorul încorporat în placa Discovery. Acum configuram programatorul in sine: * Debugger –> ST–LINK –> Interfata: selectati SWD (programatorul de pe placa este conectat la controler prin SWD, nu prin JTAG). * Depanator –>
#include „stm32f10x_conf.h” 

void main()
{
în timp ce (1)
{
}
}

<1000000; i++);


pentru(i=0; i<1000000; i++);

#include „stm32f10x_conf.h”

void main()
{





int i;
în timp ce (1)
{

pentru(i=0; i<1000000; i++);

<1000000; i++); } }

arhiva cu proiectul GPIO. Din fericire, puteți salva acest proiect și îl puteți folosi ca șablon, astfel încât să nu mai trebuiască să treceți din nou prin toate setările. Întregul ciclu: 1. Porturi I/O (/index.php/stm32-from_zero_to_rtos-2_timers/ "STM32 - de la zero la RTOS. 2: Timer și întreruperi") (/index.php/stm32-from_zero_to_rtos-3_timer_outputs/ " STM32 - de la zero la RTOS. 3: Timer outputs") [Încă o dată vreau să scriu despre un început simplu cu STM32, doar că de data aceasta fără a folosi șabloanele sau exemplele nimănui - cu o explicație a fiecărui pas. Articolele vor avea numerotarea continuă a pașilor.

0. Extragem placa STM32VLDiscovery

Îl cumpărăm în magazin, costă 600 de ruble. Va trebui să instalați drivere pe placă - cred că acest lucru nu va cauza dificultăți.

1. Instalați IAR

Vom lucra în IAR - un IDE bun cu un compilator excelent. Îi lipsește confortul de a scrie cod - dar pentru scopurile noastre este destul de suficient. Folosesc versiunea IAR 6.50.3, știți de unde să o obțineți.

2. Descărcați biblioteca de periferice

Nu sunt un fan să lucrez cu registre în timpul fazei de învățare. Prin urmare, vă sugerez să descărcați biblioteca de periferice din ST pentru a obține funcții convenabile pentru accesarea tuturor setărilor necesare.

Creați un folder „STM32_Projects”, puneți acolo folderul Biblioteci din arhiva descărcată (stsw-stm32078.zip/an3268/stm32vldiscovery_package), conține CMSIS (o bibliotecă de la ARM pentru toate microcontrolerele Cortex, descrierea și adresele tuturor resurselor) și STM_32FPeriph_StRiver10 - o bibliotecă periferică de la ST cu toate caracteristicile.

De asemenea, creăm acolo un folder „1. GPIO”, care va fi primul nostru proiect.

Arborele folderului este afișat în imagine. Fă-o astfel, pentru că mai târziu căile relative din acest arbore vor fi foarte importante.

Ei bine, pentru a înțelege despre ce vorbim, descărcați documentul de 1100 de pagini de pe aceste controlere.

Construirea unui proiect în IAR

Este necesar să înțelegeți clar esența procesului de asamblare a proiectului. Pentru comoditate, îl vom împărți în etape.

1. Preprocesor

Preprocesorul parcurge toate fișierele .c ale proiectului (atât main.c, cât și toate fișierele din spațiul de lucru). Face următoarele:

  1. sterge comentariile
  2. extinde directivele #include, înlocuindu-le cu conținutul fișierului specificat. Acest proces are loc recursiv, pornind de la fișierul .c și introducând fiecare #include .h întâlnit, iar dacă directivele #include sunt întâlnite și în fișierul .h, preprocesorul le va introduce și el. Acest lucru are ca rezultat un arbore de incluziuni. Vă rugăm să rețineți: nu se ocupă de situația dublelor incluziuni, adică. același fișier .h poate fi inclus de mai multe ori dacă este #inclus în mai multe locuri în proiect. Această situație trebuie tratată cu definiții.
  3. efectuează substituții de macro-uri - extinde macro-urile
  4. colectează directivele compilatorului.

Preprocesorul generează fișiere .i, care sunt destul de convenabile atunci când se caută erori de compilare - chiar dacă numai pentru că toate macrocomenzile sunt complet dezvăluite în ele. Salvarea acestor fișiere poate fi activată în setările proiectului.

În acest moment, constructorul are toate fișierele .c din proiect pregătite pentru a fi compilate - ca fișiere .i. Nu există încă conexiuni între fișiere.

2. Compilator

După trecerea prin preprocesor, compilatorul optimizează și compilează fiecare fișier .i, creând cod binar. Aici trebuie să specificați tipul procesorului, memoria disponibilă, limbajul de programare, nivelul de optimizare și lucruri similare.

Ce face compilatorul când întâlnește un apel de funcție într-un fișier .c care nu este descris în acest fișier? Îl caută în titluri. Dacă anteturile spun că funcția se află într-un alt fișier .c, pur și simplu lasă un pointer către acest alt fișier în acest loc.

În acest moment, constructorul are toate fișierele .c ale proiectului compilate în fișiere .o. Acestea se numesc module compilate. Acum există conexiuni între fișiere sub formă de pointeri în locurile în care sunt numite funcții „străine” - dar acestea sunt încă mai multe fișiere diferite.

3. Linker

Aproape totul este gata, trebuie doar să verificați toate conexiunile dintre fișiere - treceți prin main.o și înlocuiți pointerii către funcțiile altor persoane - module compilate. Dacă nu se utilizează o funcție din biblioteci, fie nu va fi compilată deloc în etapa anterioară, fie nu va fi înlocuită nicăieri de linker (în funcție de metoda de funcționare a asamblatorului). În orice caz, nu va fi inclus în codul binar terminat.

Linker-ul poate efectua, de asemenea, unele acțiuni finale asupra binarului, cum ar fi calcularea sumei de control.

Primul proiect lucrează cu porturi I/O

3. Creați un nou proiect în IAR

După lansarea IAR, apare o fereastră de centru de informare, de care nu avem nevoie. Faceți clic pe meniul Proiect -> Creare proiect nou. Selectați lanțul de instrumente: ARM (este puțin probabil să aveți altceva pe acea listă), șabloane de proiect: C –> principal.

Acum aveți un nou proiect C gol și un fișier main.c.

4. Conectați bibliotecile la proiect

În fereastra din stânga („Spatiu de lucru”), faceți clic dreapta pe meniu și creați un grup nou (Adăugați –> Adăugați grup), să-l numim CMSIS. Să creăm grupurile StdPeriphLib, Startup și User în același mod. Acum adăugăm fișiere la grupuri (voi sublinia toate fișierele pentru a fi mai ușor de urmărit).

Faceți clic dreapta pe CMSIS, Add, Add files - mergeți la Libraries/CMSIS/CM3, din folderul DeviceSupport/ST/STM32F10x (chip support) luați system_stm32f10x.c (aceasta este o descriere a periferiei unui anumit cristal și a setărilor de ceas). În folderul CoreSupport (suport kernel) există și core_cm3.c (aceasta este o descriere a nucleului Cortex M3), dar nu o vom lua - pentru că este deja în compilator. Voi scrie mai departe despre asta.

Pentru grup Lansare adăugați fișierul startup_stm32f10x_md_vl.s din folderul Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/iar. Acestea sunt acțiunile care trebuie efectuate la pornire. Aproape în întregime, este vorba despre configurarea diverșilor handler-uri de întrerupere (handlerii înșiși vor fi puțin mai departe). Există și fișiere pentru alte cristale, dar ne interesează md_vl - asta înseamnă densitate medie (volum mediu de memorie, există și cristale cu volum mic și mare), linie de valoare (linia de evaluare - cristalul STM32F100 este destinat doar aprecierii capabilități și trecerea la următoarele familii).

Am terminat cu CMSIS.

Pentru grup StdPeriphLib adăugați fișierele stm32f10x_rcc.c și stm32f10x_gpio.c din folderul Libraries/STM32F10x_StdPeriph_Driver/src. Primul este funcțiile de lucru cu sistemul de ceas, iar al doilea este lucrul cu pinii I/O.

Pentru grup Utilizator trageți principalul nostru.c . Acest lucru nu este necesar, dar este mai frumos.

Arborele de proiect GPIO arată acum astfel:

Spațiul de lucru este gata, nu vom mai adăuga nimic la el.

Tot ce rămâne este să puneți un alt fișier în folderul de proiect care conectează antetele la toate fișierele bibliotecii periferice. Îl poți scrie singur, dar este mai ușor să iei unul gata făcut. Mergem la stsw-stm32078.zip/an3268/stm32vldiscovery_package/Project/Examples/GPIOToggle - acolo luăm fișierul stm32f10x_conf.h (configurarea proiectului) și îl punem în folderul „1. GPIO". Acesta este singurul fișier gata făcut pe care îl luăm.

stm32f10x_conf.h este doar un dump de include ale modulelor necesare și funcții assert. Această funcție va fi apelată atunci când există erori când se lucrează cu funcțiile de bibliotecă periferice: de exemplu, introducerea unor gunoi în funcția GPIO_WriteBit în loc de GPIOC - pe scurt, ST a jucat în siguranță. În această funcție puteți începe pur și simplu o buclă infinită - while(1); Mai trebuie să intrăm în stm32f10x_conf.h - pentru a comenta liniile pentru includerea fișierelor de periferice inutile, lăsând doar stm32f10x_rcc.h, stm32f10x_gpio.h și misc.h - ca să le putem scrie noi înșine.

5. Stabilirea proiectului

Faceți clic dreapta pe numele proiectului în fereastra Spațiului de lucru:

  1. Opțiuni generale –> Țintă –> Varianta procesor: selectați „Dispozitiv”, apăsați butonul din dreapta
Selectați ST –> STM32F100 –> ST STM32F100xB. Acesta este controlerul nostru. 2. Opțiuni generale –> Configurare bibliotecă –> CMSIS: bifați caseta de selectare Utilizare CMSIS. Deci vom folosi biblioteca CMSIS încorporată în compilator. Începând cu versiunea 6.30, IAR a început să fie livrat cu un CMSIS încorporat, iar acest lucru pare să fie mai bun - dar a introdus o oarecare confuzie cu proiecte mai vechi. 3. Compilator C/C++ –> Preprocesor. Aici scriem căile către folderele bibliotecii:
$PROJ_DIR$\
$PROJ_DIR$\..\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x
$PROJ_DIR$\..\Libraries\STM32F10x_StdPeriph_Driver\inc
Macro-ul $PROJ_DIR$ înseamnă dosarul curent(dosarul de proiect) și.. - mutați cu un nivel mai sus. Am specificat căile către folderul cu o descriere a cristalului, precum și către fișierele de antet ale bibliotecii periferice, deoarece toate fișierele .c din proiect includ anteturile lor, iar compilatorul trebuie să știe unde să le caute. Aici trebuie să scrieți, de asemenea, USE\_STDPERIPH\_DRIVER în simboluri definite. Aceasta se va conecta fisierele necesare configurații (de exemplu, stm32f10x_conf.h menționat) la proiect. Deci fila Preprocesor va arăta astfel: * Debugger –> Setup –> Driver: selectați ST–Link, deoarece acesta este programatorul încorporat în placa Discovery. Acum configuram programatorul in sine: * Debugger –> ST–LINK –> Interfata: selectati SWD (programatorul de pe placa este conectat la controler prin SWD, nu prin JTAG). * Depanator –> Descărcare: bifați caseta Folosiți încărcător(e) flash, „Încărcați firmware în memoria flash”. Este logic, fără el nimic nu va inunda.## 6. Scrierea codului În primul rând, voi scrie ce va face acest cod. El va demonstra lucru simplu, LED intermitent (PC8 pe placa Discovery) cu o pauză într-o buclă nesfârșită. Includem fișierul antet de configurare a proiectului, stm32f10x\_conf.h. În el găsim linia #include „stm32f10x\_exti.h” - aceasta este linia 35 și o comentăm cu două bare oblice. Cert este că proiectul nostru nu va avea nevoie de modulul EXTI. Fișierul main.c are deja o funcție int main și singura acțiune din el este return 0. Ștergem această linie (nu vom returna nicio valoare), schimbăm tipul funcției la void (din același motiv), și scrieți o buclă infinită:
#include „stm32f10x_conf.h” 

void main()
{
în timp ce (1)
{
}
}

### Lansarea modulului GPIO Porturile de intrare/ieșire din STM32 se numesc GPIO - Intrare/Ieșire de uz general. De aceea am inclus biblioteca stm32f10x_gpio.c. Totuși, asta nu este tot ce ne trebuie, puțină teorie: Toate perifericele de pe cip sunt dezactivate implicit, atât de la sursa de alimentare, cât și de la frecvența ceasului. Pentru a-l porni, trebuie să trimiteți un semnal de ceas. Acesta este gestionat de modulul RCC și există un fișier stm32f10x_rcc.c pentru a lucra cu el. Modulul GPIO se blochează pe magistrala APB2. Există, de asemenea, AHB (un analog al magistralei procesor-northbridge) și APB1 (precum și APB2 - un analog al magistralei northbridge-southbridge). Prin urmare, primul lucru pe care trebuie să-l facem este să activăm sincronizarea modulului GPIOC. Acesta este modulul responsabil pentru PORTC; există și GPIOA, GPIOB și așa mai departe. Aceasta se face astfel: RCC\_APB2PeriphClockCmd(RCC\_APB2Periph_GPIOC, ENABLE); Este simplu - numim funcția de trimitere a unui semnal de ceas de la magistrala APB2 la modulul GPIOC și, prin urmare, pornim acest modul. Desigur, facem asta chiar de la început funcții de gol principal. Iată doar elementele de bază pe care trebuie să le înțelegeți. Mai am și un [articol detaliat despre modulul GPIO](/index.php/stm32-%e2%86%92-%d0%bf%d0%be%d1%80%d1%82%d1%8b- gpio / „STM32 → porturi GPIO”). ### Configurarea modulului GPIOC A mai rămas foarte puțin, trebuie să configurați modulul GPIOC. Instalăm piciorul de ieșire (există și o intrare și funcții alternative), ajustăm claritatea fronturilor (în scopul compatibilității EM) și driverul de ieșire (push-pull sau open source). Facem acest lucru imediat după inițializarea portului. GPIO\_InitTypeDef GPIO\_InitStructure; GPIO\_InitStructure.GPIO\_Speed ​​​​= GPIO\_Speed\_2MHz; GPIO\_InitStructure.GPIO\_Mode = GPIO\_Mode\_Out_PP; GPIO\_InitStructure.GPIO\_Pin = GPIO\_Pin\_8; GPIO\_Init(GPIOC, &GPIO\_InitStructure); Ei bine, asta este, după aceasta piciorul PC8 va funcționa ca o ieșire push-pull cu margini relativ netede ( frecventa maxima comutare 2 MHz. Marginile ascuțite sunt de 50 MHz). Nu vom observa netezimea fronturilor cu ochiul, dar poate fi văzută pe un osciloscop. ### Porniți LED-ul Apelați funcția GPIO\_WriteBit(GPIOC, GPIO\_Pin\_8, Bit\_SET); LED-ul se va aprinde. ### Porniți-l și opriți-l într-o buclă În bucla while(1), scriem codul pentru pornirea, întreruperea, oprirea și întreruperea din nou:

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

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

Astfel, întregul fișier main.c arată astfel:

#include „stm32f10x_conf.h”

void main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

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

GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);

int i;
în timp ce (1)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET);
pentru(i=0; i<1000000; i++);

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

## 7. Hai să lansăm! Conectam placa STM32VLDiscovery la computer prin microUSB, facem clic pe butonul Download and Debug din IAR. Programul este încărcat pe microcontroler (veți observa o fereastră cu o bară de progres care se închide rapid - dimensiunea programului este atât de mică) și începe depanarea. IAR se oprește la prima instrucțiune a codului (acest lucru este destul de convenabil la depanare), trebuie să îl porniți cu butonul Go. Totul ar trebui să funcționeze - LED-ul albastru PC8 de pe placa STM32VLDiscovery ar trebui. Ca întotdeauna, puteți descărca arhiva cu proiectul GPIO. Din fericire, puteți salva acest proiect și îl puteți folosi ca șablon, astfel încât să nu mai trebuiască să treceți din nou prin toate setările. Întregul ciclu: 1. Porturi I/O (/index.php/stm32-from_zero_to_rtos-2_timers/ "STM32 - de la zero la RTOS. 2: Timer și întreruperi") (/index.php/stm32-from_zero_to_rtos-3_timer_outputs/ " STM32 - de la zero la RTOS. 3: Ieșiri timer")

](/index.php/stm32-from_zero_to_rtos-4_exti_nvic/ „STM32 - de la zero la RTOS. 4: întreruperi externe și NVIC”) 5. Instalați FreeRTOS