Cum se configurează smartphone-uri și PC-uri. Portal informativ

STM32F407(STM32F4-DISCOVERY) - Abordare non-standard - Bibliotecă standard partea 1.

Încă o dată vreau să scriu despre un început simplu cu STM32, doar că de data aceasta fără a folosi șabloanele sau exemplele altcuiva - 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ă 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 în acea listă), Șabloane de proiect: C –> principal.

În fereastra din stânga („Spatiu de lucru”), faceți clic dreapta pentru a apela meniul și a crea un nou grup (Adăugați ->

Faceți clic dreapta pe CMSIS

Pentru grup lansare

Terminat cu CMSIS.

Pentru grup StdPeriphLib

Pentru grup utilizator

5. Configurați proiectul

  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 Utilizare CMSIS. Deci vom folosi biblioteca CMSIS încorporată în compilator. Începând cu versiunea 6.30, IAR a fost livrat cu CMSIS încorporat și pare să fie mai bine - dar a introdus o oarecare confuzie cu proiecte mai vechi. 3. Compilatorul C/C++ –>
$PROJ_DIR$\

* Depanator –> Setup –> Driver: selectați ST-Link, deoarece un astfel de programator este î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, poți salva acest proiect și să-l folosești ca șablon, astfel încât să nu fii nevoit să faci toate setările din nou. Î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 cuiva - cu o explicație a fiecărui pas. Articolele vor avea numerotarea continuă a pașilor.

0. Obțineți placa STM32VLDiscovery

Cumpărăm din 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, ia - știi unde.

2. Descărcați biblioteca de periferice

Nu sunt un susținător al lucrului cu registre în etapa de pregătire. Prin urmare, vă sugerez să descărcați biblioteca de periferice din ST pentru a obține funcții de acces convenabile la toate setările necesare.

Creăm folderul „STM32_Projects”, adăugăm folderul Biblioteci din arhiva descărcată (stsw-stm32078.zip/an3268/stm32vldiscovery_package) acolo, acesta conține CMSIS (biblioteca ARM pentru toate microcontrolerele Cortex, descrierea și adresele tuturor resurselor) și STM_32DF -Periph_32DF -Periph_Stm32d bibliotecă periferică de la ST cu toate caracteristicile.

De asemenea, creăm un folder „1. GPIO”, care va conține primul nostru proiect.

Arborele folderelor - în imagine. Faceți asta, pentru că atunci căile relative din acest arbore vor fi foarte importante.

Ei bine, pentru a înțelege ce este în joc - descărcați documentul de 1100 de pagini pentru aceste controlere.

Construirea unui proiect în IAR

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

1. Preprocesor

Un preprocesor trece prin 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 rulează recursiv, pornind de la fișierul .c și mergând în fiecare #include .h întâlnit, iar dacă directivele #include se găsesc și în fișierul .h, preprocesorul va intra și ele. Se dovedește un astfel de arbore de incluziuni. Vă rugăm să rețineți: nu se ocupă de situația de dublă includere, de exemplu. 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 gestionată de definitori.
  3. efectuează substituția macro - extinde macrocomenzi
  4. colectează directivele compilatorului.

Preprocesorul generează fișiere .i, care sunt destul de utile atunci când se caută erori de compilare - chiar dacă numai pentru că toate macrocomenzile sunt complet extinse î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ă legături între fișiere.

2. Compilator

După trecerea preprocesorului, compilatorul optimizează și compilează fiecare fișier .i, producând un cod binar. Aici trebuie să specificați tipul de procesor, 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? O 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 acel alt fișier în acel loc.

În acest moment, constructorul are toate fișierele .c ale proiectului compilate în fișiere .o. Se numesc module compilate. Acum există legături între fișiere sub formă de pointeri la 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 legăturile 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ă de linker nicăieri (în funcție de metoda colectorului). În orice caz, nu va intra în codul binar terminat.

Linker-ul poate efectua, de asemenea, unele acțiuni finale cu binarul, de exemplu, calcularea sumei de control.

Primul proiect - lucrul 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 în acea listă), Șabloane de proiect: C –> principal.

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

4. Ne conectăm la proiectul bibliotecii

În fereastra din stânga („Workspace”), faceți clic dreapta pentru a apela meniul și a crea un nou grup (Adăugare –> Adăugare grup), să-l numim CMSIS. Să creăm grupurile StdPeriphLib, Startup și User în același mod. Acum adăugați fișiere în 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 Biblioteci / CMSIS / CM3, din folderul DeviceSupport / ST / STM32F10x (suport cristal) luați system_stm32f10x.c (aceasta este o descriere a periferiei unui anumit cristal și o setare a ceasului). Folderul CoreSupport (suport pentru kernel) conține și core_cm3.c (aceasta este o descriere a nucleului Cortex M3), dar nu îl 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. Aceștia sunt pașii care trebuie parcurși la pornire. Aproape în totalitate, aceasta este configurația diverșilor handler-uri de întrerupere (handlerii înșiși vor fi puțin mai departe). Mai există fișiere pentru alte cristale, dar md_vl ne interesează - asta înseamnă densitate medie (cantitate medie de memorie, există și cristale cu volume mici și mari), linie de valoare (linie de evaluare - cristalul STM32F100 este destinat doar pentru evaluarea posibilităților și trecerea la următoarele familii).

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 și plasați principalul nostru.c . Este optional, dar e mai frumos asa.

Acum arborele proiectului GPIO arată astfel:

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

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

stm32f10x_conf.h - doar un dump include modulele necesare și funcțiile assert. Această funcție va fi apelată în cazul unor erori în lucrul cu funcțiile bibliotecii periferice: de exemplu, puneți niște gunoi în funcția GPIO_WriteBit în loc de GPIOC - pe scurt, ST a fost reasigurat remarcabil. În această funcție, puteți rula 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. Configurați proiectul

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

  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 Utilizare CMSIS. Deci vom folosi biblioteca CMSIS încorporată în compilator. Începând cu versiunea 6.30, IAR a fost livrat cu CMSIS încorporat și pare să fie mai bine - 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ă folderul curent (dosarul de proiect), iar .. înseamnă urcarea cu un nivel. Am scris căile către folderul cu descrierea cristalului, precum și către fișierele antet ale bibliotecii periferice, deoarece toate fișierele .c din proiect includ anteturile lor, iar compilatorul trebuie să știe unde să le caute. Tot aici trebuie să scrieți USE\_STDPERIPH\_DRIVER în simboluri definite. Aceasta va include fișierele de configurare necesare (de exemplu, stm32f10x_conf.h menționate) pentru proiect. Deci fila Preprocesor va arăta astfel: * Depanator –> Setup –> Driver: selectați ST-Link, deoarece un astfel de programator este î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 Utilizați încărcător(e) flash, „Încărcați firmware în memoria flash”. În mod logic, fără ea, nimic nu va inunda.## 6. Scrierea codului Și pentru început, voi scrie ce va face acest cod. Va demonstra un lucru simplu, clipind un LED (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 ea 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 are nevoie de modulul EXTI. Există deja o funcție int main în fișierul main.c și singura acțiune din ea este return 0. Eliminați această linie (nu vom returna nicio valoare), schimbați 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)
{
}
}

### Pornirea modulului GPIO Porturile I/O STM32 se numesc GPIO - Intrare/Ieșire de uz general. Prin urmare, am inclus biblioteca stm32f10x_gpio.c. Cu toate acestea, nu este tot ce ne trebuie, puțină teorie: Toate perifericele pe cip sunt dezactivate implicit, atât de la putere, cât și de la frecvența de ceas. Pentru a-l porni, trebuie să daț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-punte de nord) și APB1 (precum și APB2 - un analog al magistralei podului nord-sud). Prin urmare, primul lucru pe care trebuie să-l facem este să pornim ceasul 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 furnizare a unui semnal de ceas de la magistrala APB2 la modulul GPIOC și, astfel, pornim acest modul. Desigur, facem acest lucru chiar la începutul funcției principale void. 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 Nu mai este mult de configurat modulul GPIOC. Setăm piciorul la ieșire (mai există o intrare și funcții alternative), ajustăm claritatea fronturilor (în scopul compatibilității EM), 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); Asta e tot, după aceea pinul PC8 va funcționa ca o ieșire push-pull cu margini relativ netede (frecvența maximă de comutare este de 2 MHz. Marginile ascuțite sunt de 50 MHz). Netezimea fronturilor nu este vizibilă pentru ochi, dar poate fi văzută la 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), scrieți codul pentru a-l porni, întrerupeți-l, opriți-l și întrerupeți 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. Lansare! Conectam placa STM32VLDiscovery la computer prin microUSB, facem clic pe butonul Download and Debug din IAR. Programul este încărcat pe microcontroler (puteț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, poți salva acest proiect și să-l folosești ca șablon, astfel încât să nu fii nevoit să faci toate setările din nou. Î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 - Zero la RTOS. 3: Ieșiri temporizatoare")

](/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

Deci, deja ne-am pus pe picioare, în sensul că avem tot ce ne trebuie conectat la pinii microcontrolerului de pe placa Discovery STM32VL, am învățat să vorbim, în limbajul de programare C, ar fi timpul să creăm un proiect în clasa întâi.

Scrierea programului

După ce ați terminat cu crearea și configurarea proiectului, puteți începe să scrieți programul real. 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 microcontroler clipește LED-ul. 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 creează un cod minim de program:

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

Pentru a controla LED-ul, trebuie să activăm sincronizarea portului la care este conectat și să setăm pinul corespunzător al portului microcontrolerului la ieșire. După cum am discutat mai devreme în prima parte, pentru a permite sincronizarea porturilor DIN raspunde putin IOPCEN Inregistreaza-te RCC_APB2ENR. Potrivit documentului " RM0041Referinţămanual.pdf» pentru a activa sincronizarea magistralei portului DIN cerute în registru RCC_APB2ENR setează bitul IOPCEN pe unitate. Pentru ca atunci când setăm acest bit, să nu resetam celelalte setate în acest registru, trebuie să aplicăm operația de adăugare logică („SAU”) la starea curentă a registrului și apoi să scriem valoarea rezultată în conținutul registrului. Inregistreaza-te. În conformitate cu structura bibliotecii ST, accesul la valoarea registrului pentru citire și scriere se face printr-un pointer către structură. RCC-> APB2 ENR. Astfel, amintindu-ne de materialul din partea a doua, putem scrie următorul cod care realizează o setare de biți 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 specificată în fișa de date.

Acum să setăm ieșirea în același mod 9 port DIN. 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 port pentru intrare/ieșire GPIOC_CRH, l-am luat deja în considerare în , descrierea sa se află și în secțiunea „7.2.2 Port configuration register high” din fișa de date. Pentru a seta 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 a ieșirii ca funcție principală cu ieșirea 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 o unitate logică. Există două moduri de a schimba starea unui pin de port, prima este să scrieți direct în registrul de stare a portului conținutul modificat al registrului de port, la fel cum am făcut setarea portului. Această metodă nu este recomandată având în vedere posibilitatea 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 schimbată este scrisă în registru, un dispozitiv periferic sau întrerupere va modifica 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 acestei situații este foarte mică, merită totuși să folosiți o altă metodă în care situația descrisă este exclusă. Pentru a face acest lucru, există două registre în microcontroler. GPIOx_BSRRȘi GPIOx_BRR. Când scrieți o unitate logică în bitul necesar al registrului GPIOx_BRR pinul portului corespunzător va fi resetat la zero logic. Inregistreaza-te GPIOx_BSRR poate seta și reseta starea pinii portului, pentru a seta pinul portului la unul logic, trebuie 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 pinului portului la zero logic, trebuie să scrieți biții BRn pinii corespunzători, acești biți sunt localizați în biții superiori ai registrului portului.

LED LD3 conectat la ieșire 9 port DIN. Pentru a porni acest LED, trebuie să aplicăm o unitate logică pinului corespunzător al portului pentru a „aprinde” LED-ul.

Să adăugăm codul pentru setarea 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ă conectați fișierul antet cu o descriere a registrelor microcontrolerului

#include „stm32f10x.h”

golÎntârziere ( gol);

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

//Funcția noastră principală

gol principal( gol)
{


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 seta ieșirea la o ieșire cu o viteză de 2MHz
GPIOC->CRH |= GPIO_CRH_MODE9_1;

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

in timp ce(1)
{

//Setarea pinului 9 al portului C la unul logic ("aprins" LED-ul)
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 la link.

Primul nostru program funcțional a fost scris, când l-am scris, pentru a funcționa și a configura periferice, 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 folosi 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, utilitatea oficială de la ST este Microxplorer, dar este încă de puțină funcționalitate și din acest motiv, dezvoltatorii terți au creat un program alternativ „STM32 Generator de coduri de program » . Acest program vă permite să obțineți cu ușurință codul de setări periferice folosind o interfață grafică convenabilă și intuitivă (vezi Fig. 2).


Orez. 2 Captură de ecran a generatorului de cod STM32

După cum puteți vedea din Figura 2, codul generat de programul pentru setarea ieșirii LED se potrivește cu codul pe care l-am scris mai devreme.

Pentru a rula programul scris, după compilarea codului sursă, trebuie să încărcați programul nostru în microcontroler și să vedeți cum rulează.

Video cu modul de depanare al programului LED care clipește

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

Funcții de bibliotecă pentru lucrul cu periferice

Pentru a simplifica munca de setare a registrelor perifericelor microcontrolerului, ST a dezvoltat biblioteci, datorită cărora nu este necesar 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, deoarece toate funcțiile de nivel scăzut sunt implementate la nivelul funcțiilor de bibliotecă. Cu toate acestea, nu ar trebui să renunțăm complet la utilizarea lucrului direct cu registrele microcontrolerului, având în vedere faptul că funcțiile de bibliotecă necesită mai mult timp de procesor pentru execuția lor, 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 trebuie să configureze porturile I/O, pentru a utiliza funcțiile bibliotecii pentru setarea 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 finalul dosarului 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 des utilizate este dată în tabelul 2.

Tabelul 2. Descrierea principalelor funcții ale setărilor portului

Funcţie

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

GPIO_DeInit(
GPIO_TypeDef* GPIOx)

Setează valorile registrelor de configurare a portului GPIOx la valorile implicite

GPIO_Init(
GPIO_TypeDef* GPIOx,

Setează registrele de configurare a portului GPIOx conform parametrilor 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)

Citiți valorile 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 una 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)

Scrieți valoarea 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 funcției diferiți parametri individuali, ci o singură structură. Structurile sunt date agregate care au o 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 diferite combinate într-o variabilă unică. Variabilele care se află în această structură se numesc câmpuri de structură și se accesează astfel, mai întâi se scrie numele structurii, apoi se scrie un punct și numele câmpului structurii (numele variabilei din această structură).

Lista variabilelor incluse în structurile pentru funcțiile de lucru cu porturi este descrisă în același fișier puțin mai mare decât descrierea 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 speed for the selected pins.
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ă scrieți steagurile numerelor ieșirilor corespunzătoare pentru care se presupune că trebuie să facă setările necesare. Puteți configura mai multe ieșiri simultan prin setarea mai multor constante ca parametru prin intermediul operatorului biți SAU(cm. ). Bitwise OR le va „colecta” pe toate din constantele enumerate, iar constantele în sine sunt o mască, doar destinate unei astfel de utilizări. Definițiile macro ale constantelor sunt specificate în același fișier de mai jos.

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

Descrierea valorilor posibile:

  • GPIO_Mode_AIN- intrare analogică (Intrare analogică engleză);
  • GPIO_Mode_IN_FLOATING- intrare fără tragere, atârnând (ing. Input float) în aer
  • GPIO_Mode_IPD- meniu de intrare
  • GPIO_Mode_IPU- Tragere de intrare
  • GPIO_Mode_Out_OD- ieșire cu un dren deschis (Eng. Output Open Drain)
  • GPIO_Mode_Out_PP- ieșire în două stări (ieșire engleză Push-Pull - înainte și înapoi)
  • GPIO_Mode_AF_OD- Deschideți ieșirea de scurgere pentru funcție alternativă. Folosit în cazurile în care ieșirea trebuie controlată de periferice atașate la acest pin de port (de exemplu, pin USART1 Tx etc.)
  • GPIO_Mode_AF_PP- la fel, dar cu două stări

În mod similar, puteți vizualiza structura variabilelor altor structuri necesare pentru a lucra cu funcțiile bibliotecii.

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

//Declară structura

/*
Înainte de a începe să completați câmpurile structurii, este recomandat să inițializați conținutul structurii cu datele implicite, acest lucru se face pentru a preveni scrierea datelor incorecte dacă din anumite motive nu au fost completate toate câmpurile structurii. .

Pentru a transmite valorile unei structuri unei funcții, precedați numele structurii cu simbolul &. Acest simbol îi spune compilatorului că este necesar să treacă la funcție nu valorile conținute în structură, ci adresa de memorie în care se află aceste valori. Acest lucru se face pentru a reduce numărul de acțiuni necesare ale procesorului pentru a copia conținutul structurii și, de asemenea, salvează 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 numărul pinului portului în câmpul GPIO_Pin al structurii GPIO_Init_struct, pe care o 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 obligatorii ale structurii, această structură trebuie trecută în funcție, care va face înscrierea 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 la fel, diferentele sunt doar in parametrii si comenzile specifice fiecarui dispozitiv.

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

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

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

//declararea funcției de întârziere a programului

golÎntârziere ( gol);

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

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

//Funcția noastră principală

gol principal( gol)
{

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

//Declară o structură pentru a configura portul
GPIO_InitTypeDef GPIO_Init_struct;

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

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

// Completați câmpurile GPIO_Speed ​​​​și GPIO_Mode într-un mod similar
GPIO_Init_struct.GPIO_Speed= GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode = GPIO_Mode_Out_PP;

//Pasam structura completata pentru a efectua actiuni de configurare a registrelor
GPIO_Init(GPIOC, &GPIO_Init_struct);

//Bucla noastră infinită principală
in timp ce(1)
{
//Setarea pinului 9 al portului C la unul logic (LED "aprins")
GPIO_SetBits(GPIOC, GPIO_Pin_9);

// Adăugați o întârziere software, astfel încât LED-ul să se aprindă pentru o perioadă
întârziere();

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

//Adăugarea întârzierii software din nou
întârziere();
}
}

link .

Din exemplul de mai sus, se poate observa că utilizarea funcțiilor de bibliotecă pentru lucrul cu periferice vă permite să apropiați programele de scriere pentru microcontroler de programarea orientată pe obiecte și, de asemenea, reduce nevoia de acces frecvent la fișa de date pentru a citi descrierea. a registrelor microcontrolerului, dar utilizarea funcțiilor de bibliotecă necesită o cunoaștere mai mare a 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ă foarte mult procesul de scriere a programelor.

Această împrejurare, precum și faptul că ST a avut grijă de un grad înalt de compatibilitate, atât din punct de vedere hardware, cât și din punct de vedere software, a diferitelor sale microcontrolere, contribuie la studiul lor mai ușor, deoarece nu este necesar să se aprofundeze caracteristicile structurale ale diverse controlere.Seria STM32 și vă permite să alegeț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 la 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 ieșirii, fie cele interne - overflow timer, etc. O listă cu posibile surse de întrerupere pentru microcontrolerele din seria STM32 este dat în fișa tehnică" RM0041 Manual de referință" in sectiune " 8 Întreruperi și evenimente».

Deoarece handlerul 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 să fie alese nume predefinite, către care vectorul de întrerupere redirecționează. sunt specificate. Lista de nume ale acestor funcții cu o scurtă descriere se află în fișierul de asamblare " startup_stm32f10x_md_vl.s". Pot exista mai multe surse de întreruperi pentru un singur handler de întreruperi, de exemplu, funcția de gestionare a întreruperilor " USART1_IRQHandler» poate fi apelat în caz de sfârșit de recepție și sfârșit de transmisie a unui octet etc.

Pentru a începe cu întreruperi, trebuie să configurați și să inițializați controlerul de întrerupere NVIC. În arhitectura Cortex M3, fiecare întrerupere poate fi setată la propriul grup de prioritate pentru cazurile în care apar mai multe întreruperi în același timp. Apoi ar trebui să configurați sursa de întrerupere.

Câmpul NVIC_IRQChannel indică ce întrerupere dorim să configuram. Constanta USART1_IRQn desemnează canalul responsabil pentru întreruperile asociate cu USART1. Este definit în dosar stm32f10x.h”, alte constante similare sunt definite acolo.

Următoarele două câmpuri indică prioritatea întreruperilor (valorile maxime ale acestor doi parametri sunt determinate de grupul de prioritate selectat). Ultimul câmp, de fapt, include utilizarea întreruperilor.

În funcțiune NVIC_Init, precum și la configurarea porturilor, se transmite 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 al perifericului.

// Activați întreruperile la sfârșitul transferului de octeți prin USART1
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);

// Activați întreruperile la sfârșitul primirii unui octet de către 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 la diferite 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 ce eveniment a cauzat întreruperea și apoi să resetam setul de indicatori, altfel, la ieșirea din întrerupere, microcontrolerul va decide că nu am procesat întreruperea, deoarece steagul de întrerupere este încă setat.

Pentru a efectua acțiuni diverse, mici, repetitive cu o perioadă precisă, microcontrolerele cu nucleu Cortex-M3 au un cronometru de sistem special conceput pentru aceasta. Funcția acestui cronometru include doar apelul de întrerupere la intervale de timp strict specificate. De regulă, în întreruperea apelată de acest temporizator, codul este plasat 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 magistrală de sistem dintre intervalele de apel ale operatorului de întrerupere a temporizatorului de sistem.

SysTick_Config(clk);

Acum, după ce ne-am ocupat de întreruperi, ne vom rescrie programul, folosind cronometrul sistemului ca element de setare a timpului. Pentru că cronometrul SysTick”este unul de sistem și poate fi folosit de diverse blocuri funcționale ale programului nostru, atunci ar fi rezonabil să mutați funcția de gestionare a întreruperilor din temporizatorul de sistem într-un fișier separat, din această funcție de apelare funcții pentru fiecare bloc funcțional separat.

Un exemplu de fișier „main.c” al programului pentru a clipi LED-ul folosind o întrerupere:

//Conectează fișierul antet cu descrierea registrelor microcontrolerului

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

nesemnat int LED_timer;

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

gol SysTick_Timer_main( gol)
{
//Dacă variabila LED_timer nu a ajuns încă la 0,
dacă(LED_timer)
{
//Verificați valoarea acestuia, dacă este mai mare de 1500 porniț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
altfel GPIOC->BSRR= GPIO_BSRR_BR9;

//Să reducem variabila LED_timer
LED_timer--;
}

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

//Funcția noastră principală

gol principal( gol)
{

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

//Declară o structură pentru a configura portul
GPIO_InitTypeDef GPIO_Init_struct;

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

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

// Completați câmpurile GPIO_Speed ​​​​și GPIO_Mode într-un mod similar
GPIO_Init_struct.GPIO_Speed= GPIO_Speed_2MHz;
GPIO_Init_struct.GPIO_Mode = GPIO_Mode_Out_PP;

//Pasam structura completata pentru a efectua actiuni de configurare a registrelor
GPIO_Init(GPIOC, &GPIO_Init_struct);

// selectează grupul prioritar pentru întreruperi
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

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

//Bucla noastră infinită principală
in timp ce(1)
{
// De data aceasta este gol, toate comenzile cu LED-uri 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.
* @paramNone
* @retvalNone
*/

gol SysTick_Handler( gol)
{
SysTick_Timer_main();
}

Un exemplu de schiță de lucru a programului pentru a clipi un LED folosind o întrerupere poate fi descărcat de pe link.

În acest sens, povestea mea despre elementele de bază ale dezvoltării programelor pentru microcontrolerul STM32 poate fi considerată completă. Am furnizat toate informațiile necesare pentru posibilitatea de auto-studiu în continuare a microcontrolerelor 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 asta 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 de fiecare dată dispozitive din ce în ce mai complexe și mai bune. ș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 munca mea vă va fi utilă și interesantă.

La crearea primei aplicații pe microcontrolerul STM32, există mai multe moduri de parcurs. Primul, clasic, luăm descrierea exactă a controlerului de pe site-ul www.st.com, care apare sub denumirea „Manual de referință” și citim descrierea registrelor periferice. Apoi încercăm să le notăm și să vedem cum funcționează perifericele. Citirea acestui document este foarte utilă, dar la prima etapă de stăpânire a microcontrolerului, acest lucru poate fi abandonat, destul de ciudat. Inginerii STMicroelectronics au scris o bibliotecă standard de drivere pentru periferice. Mai mult, au scris multe exemple de utilizare a acestor drivere, care pot reduce programarea aplicației dvs. la apăsarea tastelor Ctrl + C și Ctrl + V, urmată de o mică modificare a exemplului de driver pentru a se potrivi nevoilor dumneavoastră. Astfel, conectarea bibliotecii de drivere periferice la proiectul dumneavoastră este a doua metodă de construire a unei aplicații. Pe lângă viteza de scriere, există și alte avantaje ale acestei metode: universalitatea codului și utilizarea altor biblioteci proprietare, cum ar fi USB, Ethernet, controlul unității etc., care sunt furnizate în surse și utilizează un driver periferic standard. Există, de asemenea, dezavantaje ale acestei metode: Unde vă puteți descurca cu o singură linie de cod, driverul periferic standard STM32 va scrie 10. Biblioteca periferică în sine este, de asemenea, furnizată ca fișiere sursă, astfel încât să puteți urmări ce bit din care registru acesta sau acea funcție se schimbă. Dacă se dorește, va fi posibilă trecerea de la a doua metodă de scriere a unui program la prima, comentând o parte a codului care utilizează singur biblioteca standard, care controlează direct registrul periferic. Ca urmare a unei astfel de acțiuni, veți câștiga în viteza de control, cantitatea de RAM și ROM și veți pierde în universalitatea codului. În orice caz, inginerii Promelectronica recomandă utilizarea bibliotecii de periferice standard cel puțin în prima etapă.

Cele mai mari dificultăți îl așteaptă pe dezvoltator atunci când conectează biblioteca la proiectul său. Dacă nu știți cum să faceți acest lucru, puteți petrece mult timp pe acest eveniment, care contrazice însăși ideea de a utiliza un șofer gata făcut. Materialul este dedicat conectării bibliotecii standard la orice familie STM32.

Fiecare familie STM32 are propria sa bibliotecă de periferice standard. Acest lucru se datorează faptului că periferia în sine este diferită. De exemplu, perifericele controlerelor STM32L au o funcție de economisire a energiei ca una dintre sarcini, care presupune adăugarea de funcții de control. Un exemplu clasic este ADC, care în STM32L are capacitatea de a opri hardware-ul, în absența unei comenzi de conversie pentru o lungă perioadă de timp - una dintre consecințele sarcinii de economisire a energiei. Controlerele ADC din familiile STM32F nu au o astfel de funcție. De fapt, din cauza prezenței unei diferențe hardware la periferie, avem biblioteci de drivere diferite. Pe lângă diferența evidentă în funcțiile controlerului, există o îmbunătățire a perifericelor. Deci, perifericele controlerelor familiilor care au fost lansate ulterior pot fi mai atent și mai convenabile. De exemplu, perifericele controlerelor STM32F1 și STM32F2 au diferențe de control. În opinia autorului, gestionarea perifericelor STM32F2 este mai convenabilă. Și acesta este de înțeles de ce: familia STM32F2 a fost lansată mai târziu și acest lucru a permis dezvoltatorilor să ia în considerare unele dintre nuanțe. În consecință, pentru aceste familii - biblioteci de control periferice individuale. Ideea celor de mai sus este simplă: pe pagina microcontrolerului pe care urmează să-l utilizați, există o bibliotecă de periferice potrivită pentru acesta.

În ciuda diferenței de periferice în familii, șoferii ascund 90% din diferențe în interiorul lor. De exemplu, funcția de reglare a ADC menționată mai sus arată la fel pentru toate familiile:

void ADC_Init(ADC_Nom, ADC_Param),

unde ADC_Nom este numărul ADC sub formă de ADC1, ADC2, ADC3 etc.

ADC_Param - indicatorul structurii de date, cum să configurați ADC (de la ce să începeți, câte canale să digitalizați, dacă să o faceți ciclic etc.)

10% din diferențele de familie, în acest exemplu, care vor trebui corectate la trecerea de la o familie STM32 la alta, sunt ascunse în structura ADC_Param. În funcție de familie, numărul de câmpuri din această structură poate fi diferit. Partea generală are aceeași sintaxă. Astfel, transferul unei aplicații pentru o familie STM32, scrisă pe baza unor biblioteci periferice standard, la alta este foarte simplu. În ceea ce privește universalizarea soluțiilor bazate pe microcontrolere, STMicroelectronics este irezistibil!

Deci, am descărcat biblioteca pentru STM32 folosit. Ce urmeaza? Apoi, trebuie să creăm un proiect și să conectăm fișierele necesare la acesta. Luați în considerare crearea unui proiect folosind mediul de dezvoltare IAR Embedded Workbench ca exemplu. Începem mediul de dezvoltare și mergem la fila „Proiect”, selectăm elementul „Creare proiect” pentru crearea proiectului:

În noul proiect care apare, introduceți setările trecând cu mouse-ul peste numele proiectului, apăsând butonul din dreapta al mouse-ului și selectând „Opțiuni” din meniul derulant:

Zone de memorie RAM și ROM:

Când faceți clic pe butonul „Salvare”, mediul vă va oferi să scrieți un nou fișier de descriere a controlerului în folderul proiectului. Autorul recomandă crearea unui fișier *.icp individual pentru fiecare proiect și stocarea acestuia în folderul proiectului.

Dacă intenționați să vă depanați proiectul în circuit, ceea ce este recomandat, introduceți tipul de depanator de utilizat:

Pe fila depanatorului selectat, specificați interfața pentru conectarea depanatorului (în cazul nostru, ST-Link este selectat) la controler:



De acum înainte, proiectul nostru fără biblioteci este gata să fie compilat și încărcat în controler. Alte medii precum Keil uVision4, Resonance Ride7 etc. vor trebui să urmeze aceiași pași.

Dacă scrieți linia în fișierul main.c:

#include „stm32f10x.h” sau

#include „stm32f2xx.h” sau

#include „stm32f4xx.h” sau

#include „stm32l15x.h” sau

#include „stm32l10x.h” sau

#include „stm32f05x.h”

indicând locația acestui fișier, sau prin copierea acestui fișier în folderul proiectului, atunci unele zone de memorie vor fi asociate cu registrele periferice ale familiei corespunzătoare. Fișierul în sine se află în folderul bibliotecii de periferice standard în secțiunea: \CMSIS\CM3\DeviceSupport\ST\STM32F10x (sau nume similar pentru alte familii). De acum înainte, înlocuiți adresa de registru periferică ca număr cu numele acesteia. Chiar dacă nu veți folosi funcțiile bibliotecii standard, este recomandat să faceți o astfel de conexiune.

Dacă intenționați să utilizați întreruperi în proiectul dvs., este recomandat să includeți un fișier de pornire cu extensia *.s, care se află de-a lungul căii \CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar sau similar, pentru alte familii. Este important de reținut că fiecare mediu are propriul său fișier. În consecință, dacă folosim IAR EWB, trebuie să luăm fișierul din folderul IAR. Acest lucru se datorează unei diferențe ușoare în sintaxa mediului. Prin urmare, pentru ca proiectul să înceapă imediat, inginerii STMicroelectronics au scris mai multe variante de fișiere de pornire pentru câteva dintre cele mai populare medii de dezvoltare. Majoritatea familiilor STM32 au un singur fișier. Familia STM32F1 are mai multe fișiere de pornire:

  • startup_stm32f10x_cl.s - pentru microcontrolere STM32F105/107
  • startup_stm32f10x_xl.s - pentru microcontrolere STM32F101/STM32F103 768 kb sau mai mult
  • startup_stm32f10x_hd.s - pentru microcontrolere STM32F101/STM32F103 cu memorie Flash 256-512 kb
  • startup_stm32f10x_md.s - pentru microcontrolere STM32F101/ STM32F102/STM32F103 cu memorie Flash 64-128 kb
  • startup_stm32f10x_ld.s - pentru microcontrolere STM32F101/ STM32F102/STM32F103 cu memorie flash mai mică de 64 kb
  • startup_stm32f10x_hd_vl.s pentru microcontrolere STM32F100 cu memorie flash 256-512 kb
  • startup_stm32f10x_md_vl.s pentru microcontrolere STM32F100 cu memorie Flash 64-128 kb
  • startup_stm32f10x_ld_vl.s pentru microcontrolere STM32F100 cu memorie Flash de 32 kb sau mai puțin

Deci, în funcție de familie, subfamilie și mediul de dezvoltare, adăugați fișierul de lansare la proiect:

Aici ajunge microcontrolerul când începe programul. Întreruperea apelează secvenţial funcţia SystemInit() şi apoi __iar_program_start. A doua funcție resetează sau scrie valorile predefinite ale variabilelor globale, după care trece la programul de utilizator main(). Funcția SystemInit() setează ceasul microcontrolerului. Ea este cea care răspunde la întrebări:

  • Trebuie să trec la cristal extern (HSE)?
  • Cum se înmulțește frecvența de la HSI/HSE?
  • Este necesar să conectați coada de descărcare a comenzilor?
  • Ce întârziere este necesară la încărcarea unei comenzi (datorită vitezei reduse a memoriei Flash)
  • Cum să împărțim ceasul autobuzelor periferice?
  • Codul trebuie plasat în RAM externă?

Funcția SystemInit() poate fi scrisă manual în proiectul dumneavoastră. Dacă emiteți această funcție ca goală, atunci controlerul va funcționa pe un generator RC intern cu o frecvență de aproximativ 8 MHz (în funcție de tipul familiei). Opțiunea 2 - conectați fișierul system_stm32f10x.c la proiect (sau similar ca nume în funcție de tipul de familie utilizat), care se află în bibliotecă de-a lungul căii: Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x. Acest fișier conține funcția SystemInit(). Acordați atenție frecvenței cristalului extern HSE_VALUE. Acest parametru este setat în fișierul antet stm32f10x.h. Valoarea standard este de 8 și 25MHz, în funcție de familia STM32. Sarcina principală a funcției SystemInit () este de a comuta ceasul la un cuarț extern și de a multiplica această frecvență într-un anumit mod. Ce se întâmplă dacă valoarea HSE_VALUE este de 8MHz, nucleul ar trebui să fie tactat la 72MHz și, de fapt, placa are un cuarț de 16MHz? Ca urmare a unor astfel de acțiuni incorecte, nucleul va primi o frecvență de 144 MHz, ceea ce poate depăși funcționarea garantată a sistemului pe STM32. Acestea. când includeți fișierul system_stm32f10x.c, va trebui să specificați valoarea HSE_VALUE. Toate acestea înseamnă că fișierele system_stm32f10x.c, system_stm32f10x.h și stm32f10x.h (sau similare ca nume pentru alte familii) trebuie să fie individuale pentru fiecare proiect. ȘI

Inginerii STMicroelectronics au creat instrumentul de configurare a ceasului, care vă permite să configurați corect ceasul sistemului. Acesta este un fișier Excel care generează un fișier system_stm32xxx.c (similar ca nume cu o anumită familie de familii) după setarea parametrilor de intrare și de ieșire ale sistemului. Luați în considerare munca sa pe exemplul familiei STM32F4.

Opțiuni: oscilator RC intern, oscilator RC intern cu multiplicare a frecvenței sau cristal extern cu multiplicare a frecvenței. După selectarea sursei de ceas, introducem parametrii configurației dorite a sistemului, cum ar fi frecvența de intrare (când se utilizează cuarț extern), frecvența ceasului de bază, divizorii frecvenței de ceas ai magistralelor periferice, funcționarea bufferului de preluare a instrucțiunilor, si altii. Făcând clic pe butonul „Generează”, obținem o fereastră


Conectarea fișierului system_stm32f4xx.c și a analogilor acestuia va necesita conectarea unui alt fișier din biblioteca standard periferică. Pentru a controla ceasul, există un întreg set de funcții care sunt apelate din fișierul system_stm32xxxxxx.c. Aceste funcții sunt localizate în fișierul stm32f10x_rcc.c și antetul acestuia. În consecință, la conectarea fișierului system_stm32xxxxxx.c la proiect, este necesară conectarea stm32f10x_rcc.c, altfel linkerul de mediu va raporta absența unei descrieri a funcției cu numele RCC_xxxxxxx. Fișierul specificat se află în biblioteca periferică de-a lungul căii: Libraries\STM32F10x_StdPeriph_Driver\src și antetul său \Libraries\STM32F10x_StdPeriph_Driver\inc.

Fișierele antet ale driverului periferic sunt conectate în fișierul stm32f10x_conf.h, care este referit de stm32f10x.h. Fișierul stm32f10x_conf.h este pur și simplu un set de fișiere antet driver pentru anumite periferice controler care urmează să fie incluse în proiect. Inițial, toate anteturile „#include” sunt marcate ca comentarii. Conectarea fișierului antet al periferiei constă în eliminarea comentariului din numele fișierului corespunzător. În cazul nostru, aceasta este linia #include „stm32f10x_rcc.h”. Evident, fișierul stm32f10x_conf.h este individual pentru fiecare proiect, deoarece proiecte diferite folosesc periferice diferite.

Și ultimul. Trebuie să specificați mai multe directive pentru preprocesorul compilatorului și căi către fișierele antet.



Căile către fișierele antet pot fi diferite, în funcție de locația bibliotecii periferice în raport cu folderul proiectului, dar prezența „USE_STDPERIPH_DRIVER” este obligatorie la conectarea driverelor periferice ale bibliotecii standard.

Deci, am conectat biblioteca standard la proiect. Mai mult, am conectat unul dintre driverele periferice standard la proiectul care controlează ceasul sistemului.

Am aflat cum arată dispozitivul de bibliotecă din interior, acum câteva cuvinte despre cum arată din exterior.



Astfel, includerea fișierului antet stm32f10x.h într-o aplicație presupune includerea altor fișiere antet și fișiere de cod. Unele dintre cele prezentate în figură sunt descrise mai sus. Câteva cuvinte despre restul. Fișierele STM32F10x_PPP.x sunt fișiere driver periferice. Un exemplu de conectare a unui astfel de fișier este prezentat mai sus, acesta este RCC - periferia controlului ceasului sistemului. Dacă dorim să conectăm drivere ale altor periferice, atunci numele fișierelor conectate se obține prin înlocuirea „PPP” cu numele perifericului, de exemplu, ADC - STM32F10x_ADC.s sau porturile I/O STM32F10x_GPIO.s, sau DAC - STM32F10x_DAC.s. În general, este clar intuitiv ce fișier trebuie conectat la conectarea unui anumit periferic. Fișierele „misc.c”, „misc.h” sunt în mare parte aceleași STM32F10x_PPP.x, ele controlează doar nucleul. De exemplu, configurarea vectorilor de întrerupere, care este încorporat în nucleu, sau gestionarea temporizatorului SysTick, care face parte din nucleu. Fișierele xxxxxxx_it.c descriu vectorii NMI ai controlerului. Ele pot fi completate cu vectori de întrerupere periferici. Fișierul core_m3.h descrie nucleul CortexM3. Acest nucleu este standardizat și poate fi găsit în microcontrolere de la alți producători. Pentru universalizarea pe mai multe platforme, STMicroelectronics a lucrat pentru a crea o bibliotecă de bază separată CortexM, după care ARM a standardizat-o și a distribuit-o altor producători de microcontrolere. Deci trecerea la STM32 de la controlere de la alți producători cu nucleu CortexM va fi puțin mai ușoară.

Deci, putem conecta biblioteca de periferice standard la orice familie STM32. Cel care a învățat să facă asta așteaptă un premiu: o programare foarte simplă a microcontrolerelor. Biblioteca, pe lângă drivere sub formă de fișiere sursă, conține multe exemple de utilizare a perifericelor. De exemplu, luați în considerare crearea unui proiect care implică ieșiri de comparare a temporizatorului. Cu abordarea tradițională, vom studia cu atenție descrierea registrelor acestui periferic. Dar acum putem studia textul programului care rulează. Intrăm în folderul cu exemple de periferice standard, care se află de-a lungul căii ProjectSTM32F10x_StdPeriph_Examples. Iată foldere cu exemple cu numele perifericelor utilizate. Mergem la folderul „TIM”. Temporizatoarele din STM32 au multe funcții și setări, așa că este imposibil să demonstrezi capacitățile controlerului cu un singur exemplu. Prin urmare, în directorul specificat, există multe exemple de utilizare a temporizatoarelor. Suntem interesați de generarea unui semnal PWM de către un temporizator. Mergem la folderul „7PWM_Output”. În interior există o descriere a programului în limba engleză și un set de fișiere:

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

Dacă proiectul nu are întreruperi, atunci conținutul se află în întregime în fișierul main.c. Copiați aceste fișiere în directorul proiectului. După compilarea proiectului, vom obține un program pentru STM32, care va configura temporizatorul și porturile I/O pentru a genera 7 semnale PWM de la temporizatorul 1. În continuare, putem adapta codul deja scris la sarcina noastră. De exemplu, reduceți numărul de semnale PWM, schimbați ciclul de lucru, direcția de numărare etc. Funcțiile și parametrii lor sunt bine descriși în fișierul stm32f10x_stdperiph_lib_um.chm. Numele funcțiilor și parametrii acestora sunt ușor de asociat cu scopul lor pentru cei care știu puțin engleză. Pentru claritate, iată o parte din codul exemplului luat:

/* Configurarea bazei de timp */ TIM_TimeBaseStructure.TIM_Prescaler = 0; // fără prescaler de impuls de contor (registru de 16 biți) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // direcția de numărare în sus TIM_TimeBaseStructure.TIM_Period = TimerPeriod; // numără până la valoarea TimerPeriod (constantă în program) TIM_TimeBaseStructure.TIM_ClockDivision = 0; // nicio pre-diviziune a contoarelor TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // contor de overflow pentru generarea de evenimente (nu este folosit în program) TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // introducerea valorilor TimeBaseStructure în registrele temporizatorului 1 (intrarea datelor la această // variabilă este mai sus) /* Configurarea canalului 1, 2,3 și 4 în modul PWM */ // configurarea ieșirilor PWM TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2 ; // Mod de operare PWM2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // activează ieșirea semnalelor temporizatorului PWM TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; // activați ieșirea temporizatorului PWM complementar TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; // lățimea impulsului Channel1Pulse este o constantă în programul TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // setarea polarității de ieșire TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; // setarea polarității ieșirii complementare TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; // setează starea sigură de ieșire PWM TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; // setează starea de siguranță a ieșirii complementare PWM TIM_OC1Init(TIM1, &TIM_OCInitStructure); // introduceți valorile variabilei TIM_OCInitStructure în registrele PWM ale canalului 1 // ale timer-ului 1 TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; // schimba lățimea impulsului în variabila OCInitStructure și introduceți-o în TIM_OC2Init(TIM1, &TIM_OCInitStructure); // timer1 canalul 2 PWM înregistrează TIM_OCInitStructure.TIM_Pulse = Channel3Pulse; // schimba lățimea impulsului în variabila OCInitStructure și introduceți-o în TIM_OC3Init(TIM1, &TIM_OCInitStructure); // timer1 canalul 3 PWM înregistrează TIM_OCInitStructure.TIM_Pulse = Channel4Pulse; // schimba lățimea impulsului în variabila OCInitStructure și introduceți-o în TIM_OC4Init(TIM1, &TIM_OCInitStructure); // timer1 canal 4 registre PWM /* TIM1 counter enable */ TIM_Cmd(TIM1, ENABLE); // start timer1 /* TIM1 Main Output Enable */ TIM_CtrlPWMOutputs(TIM1, ENABLE); // activați temporizatorul 1 compara ieșirile

În partea dreaptă, autorul a lăsat un comentariu în limba rusă pentru fiecare rând a programului. Dacă deschidem același exemplu în descrierea funcțiilor bibliotecilor stm32f10x_stdperiph_lib_um.chm, vom vedea că toți parametrii de funcție utilizați au un link către propria lor descriere, unde vor fi indicate valorile lor posibile. Funcțiile în sine au, de asemenea, un link către propria lor descriere și cod sursă. Acest lucru este foarte util pentru că știind ce face funcția, putem urmări cum o face, ce biți din registrele periferice și cum afectează aceasta. Aceasta este, în primul rând, o altă sursă de informații pentru stăpânirea controlerului, bazată pe utilizarea practică a controlerului. Acestea. mai întâi rezolvi problema tehnică, apoi studiezi soluția în sine. În al doilea rând, acesta este un domeniu de optimizare a programului pentru cei care nu sunt mulțumiți de bibliotecă în ceea ce privește viteza și dimensiunea codului.



Am subliniat că biblioteca standard este conectată la sistem. De fapt, CMSIS este conectat - un sistem generalizat de reprezentare structurală pentru MK, precum și SPL - o bibliotecă periferică standard. Să luăm în considerare fiecare dintre ele:

CMSIS
Este un set de fișiere antet și un mic set de cod pentru unificarea și structurarea muncii cu nucleul și perifericele MK. De fapt, fără aceste fișiere este imposibil să lucrezi normal cu MK. Puteți obține biblioteca pe pagina pentru MK.
Această bibliotecă, conform descrierii, a fost creată pentru a unifica interfețele atunci când lucrați cu orice MK din familia Cortex. Cu toate acestea, în realitate, se dovedește că acest lucru este valabil doar pentru un producător, adică. trecând la microcontrolerul unei alte companii, ești forțat să-i studiezi periferia aproape de la zero.
Deși acele fișiere care se referă la nucleul procesorului MK sunt identice pentru toți producătorii (fie și doar pentru că au un model de nucleu de procesor - furnizate sub formă de ip-blocuri de ARM).
Prin urmare, lucrul cu părți ale nucleului precum registre, instrucțiuni, întreruperi și blocuri de coprocesor este standard pentru toată lumea.
În ceea ce privește perifericele, STM32 și STM8 (brut) sunt aproape similare, iar acest lucru este parțial adevărat și pentru alte MK-uri lansate de ST. În partea practică, voi arăta cât de ușor este să utilizați CMSIS. Cu toate acestea, dificultățile în utilizarea acestuia sunt asociate cu reticența oamenilor de a citi documentația și de a înțelege dispozitivul MK.

SPL
Biblioteca periferică standard - bibliotecă periferică standard. După cum sugerează și numele, scopul acestei biblioteci este de a crea o abstractizare pentru periferia MK. Biblioteca constă din fișiere antet în care constantele ușor de înțeles de om sunt declarate pentru configurarea și lucrul cu periferice MK, precum și fișiere de cod sursă asamblate în biblioteca însăși pentru operațiuni cu periferice.
SPL este o abstractizare peste CMSIS, prezentând utilizatorului o interfață comună pentru toate MK-urile, nu numai de la un producător, ci în general pentru toate MK-urile cu un nucleu de procesor Cortex-Mxx.
Se crede că este mai convenabil pentru începători, deoarece. vă permite să nu vă gândiți la modul în care funcționează perifericele, cu toate acestea, calitatea codului, universalitatea abordării și rigiditatea interfețelor impun anumite restricții dezvoltatorului.
De asemenea, funcționalitatea bibliotecii nu vă permite întotdeauna să implementați cu acuratețe configurația unor componente precum USART (Universal Synchronous-Asynchronous Serial Port) în anumite condiții. În partea practică, voi descrie, de asemenea, cum să lucrați cu această parte a bibliotecii.

Buna ziua. După cum vă amintiți, în ultimul articol am configurat pachetul software pentru a funcționa cu microcontrolere STM32 și am compilat primul program. În această postare, ne vom familiariza cu arhitectura acestei plăci, microcontrolerul și bibliotecile disponibile pentru lucru.

Mai jos este un desen al tablei Descoperirea STM32F3 , unde: 1 — Senzor MEMS. Giroscop digital cu 3 axe L3GD20. 2 - Sistem MEMS într-o carcasă, care conține un accelerometru liniar digital cu 3 axe și un senzor geomagnetic digital cu 3 axe LSM303DLHC. 4 - LD1 (PWR) - alimentare 3.3V. 5 - LD2 - LED roșu/verde. Valoarea implicită este roșu. Verde înseamnă comunicare între ST-LINK/v2 (sau V2-B) și PC. Am ST-LINK/v2-B, precum și indicație de port personalizat USB. 6. -LD3/10 (roșu), LD4/9 (albastru), LD5/8 (portocaliu) și LD6/7 (verde). În ultima intrare, tu și cu mine am clipit LED-ul LD4. 7. - Două butoane: USER și RESET. 8. - UTILIZATOR USB cu conector Mini-B.

9 - Depanator / programator USB ST-LINK/V2. unu 0. - Microcontroler STM32F303VCT6. 11. - Generator extern de înaltă frecvență 8 MHz. 12. - Aici ar trebui să existe un generator de joasă frecvență, din păcate nelipit. 13. - SWD - interfață. 14. - Jumperele pentru selectarea programarii controlerelor externe sau interne, in primul caz, trebuie scoase. 15 - Jumper JP3 - jumper, conceput pentru a conecta un ampermetru pentru măsurarea consumului controlerului. Este clar că dacă este îndepărtat, atunci pietricela noastră nu va începe. 16. - STM32F103C8T6 are placa de depanare. 17. - Regulator LD3985M33R cu cădere de tensiune și nivel de zgomot scăzut, 150mA, 3.3V.

Acum să aruncăm o privire mai atentă asupra arhitecturii microcontrolerului STM32F303VCT6. Caracteristicile sale tehnice: pachet LQFP-100, miez ARM Cortex-M4, frecvență maximă de nucleu 72 MHz, dimensiunea memoriei program 256 KB, memorie program tip FLASH, SRAM RAM 40 KB, RAM 8 KB, număr de intrări/ieșiri 87, interfețe ( CAN, I²C, IrDA, LIN, SPI, UART/USART, USB), periferice (DMA, I2S, POR, PWM, WDT), ADC/DAC 4*12 bit/2*12bit, tensiune de alimentare 2...3,6 V , temperatura de lucru -40 ...... +85 C. În figura de mai jos, pinout, unde vedem 87 porturi I / O, 45 dintre ele I / Os normale (TC, TTa), 42 tolerante la 5 volți I / Os (FT, FTf) - compatibil cu 5 V. (pe placa din dreapta pini 5V, pe stânga 3.3V). Fiecare linie digitală I/O poate funcționa ca o linie I/O comună.
destinație sau funcție alternativă. Pe măsură ce proiectele progresează, ne vom familiariza constant cu periferia.

Luați în considerare diagrama bloc de mai jos. Inima este un nucleu ARM Cortex-M4 pe 32 de biți care rulează până la 72 MHz. Are o unitate FPU în virgulă mobilă încorporată și o unitate de protecție a memoriei MPU, celule macro de urmărire încorporate - Embedded Trace Macrocell (ETM), care poate fi folosită pentru a monitoriza procesul de execuție a programului principal în interiorul microcontrolerului. Ei sunt capabili să transmită aceste observații în mod continuu prin contactele ETM atâta timp cât dispozitivul este în funcțiune. NVIC (controler de întrerupere cu vector imbricat) - modul de control al întreruperii. TPIU (Trace Port Interface Unit). Conține memorie FLASH – 256 KB, SRAM 40 KB, RAM 8 KB. Între nucleu și memorie se află o matrice Bus (Bus matrix), care vă permite să conectați dispozitive direct. Aici vedem și două tipuri de matrice de magistrală AHB și APB, unde prima este mai productivă și este folosită pentru a conecta componente interne de mare viteză, iar ultima pentru periferice (dispozitive I/O). Controlerul are 4 ADC-uri pe 12 biți (ADC) (5 Mbps) și un senzor de temperatură, 7 comparatoare (GP Comparator1 ... 7), 4 amplificatoare operaționale programabile (OpAmp1 ... 4) (PGA (Programmable Gain Array)) , 2 canale pe 12 biți DAC (DAC), RTC (ceas în timp real), două temporizatoare watchdog - independente și ferestre (WinWatchdog și Ind. WDG32K), 17 temporizatoare de uz general și multifuncționale.

În termeni generali, am luat în considerare arhitectura controlerului. Acum luați în considerare bibliotecile de software disponibile. După revizuire, se pot distinge următoarele: CMSIS, SPL și HAL. Luați în considerare fiecare aplicare într-un exemplu simplu de clipire a unui LED.

1). CMSIS(Cortex Microcontroller Software Interface Standard) este biblioteca standard pentru Cortex®-M. Oferă suport pentru dispozitive și simplifică interfețele de programare. CMSIS oferă interfețe consistente și simple pentru kernel, periferice și sisteme de operare în timp real. Utilizarea sa este un mod profesional de a scrie programe, deoarece presupune scrierea directă în registre și, în consecință, este necesară citirea și studiul constant al fișelor de date. Producător de hardware independent.
CMSIS include următoarele componente:
- CMSIS-CORE: Pornire constantă a sistemului și acces periferic (Constant system startup and peripheral access);
- CMSIS-RTOS: Deterministic Real-Time Software Execution (Deterministic Real-Time Software Execution);
- CMSIS-DSP: Implementarea rapidă a procesării semnalului digital (Fast implementation of digital signal processing);
- CMSIS-Driver: Interfețe periferice generice pentru middleware și cod de aplicație (Interfețe periferice comune pentru middleware și cod de aplicație);
- CMSIS-Pack: Acces facil la componente software reutilizabile (Acces facil la componente software reutilizabile);
- CMSIS-SVD: Vizualizare consecventă a dispozitivului și perifericelor (Vizualizare consecventă a dispozitivului și perifericelor);
- CMSIS-DAP: Conectivitate la hardware de evaluare cu costuri reduse. Software de depanare.

De exemplu, să scriem un program - clipește un LED. Pentru a face acest lucru, avem nevoie de documentație care să descrie registrele. În cazul meu RM0316 Manual de referință STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8, STM32F358xC, STM32F398xE MCU avansate bazate pe ARM ®, precum și o descriere a segmentului specific pentru care este responsabil DS9118: MCU+FPU Cortex®-M4 32b bazat pe ARM®, până la 256KB Flash+ 48KB SRAM, 4 ADC-uri, 2 canale DAC, 7 comp, 4 PGA, temporizatoare, 2,0-3,6 V. Pentru început, vom cronometra portul în program, deoarece. În mod implicit, totul este dezactivat, ceea ce duce la un consum redus de energie. Deschideți manualul de referință și uitați-vă la secțiunea Resetare și control ceas, apoi harta registrului RCC și vedeți ce registru este responsabil pentru activarea IOPEEN

Să trecem la descrierea sincronizării periferiei acestui registru Registrul de activare a ceasului periferic AHB (RCC_AHBENR), unde vedem că acest port este sub al 21-lea bit. Activează-l RCC->AHBENR|=(1<<21) . Далее сконфигурируем регистры GPIO. Нас интересует три: GPIOE_MODER и GPIOx_ODR . C помощью них повторим программу с предыдущей статьи, затактируем PE8. Первый отвечает за конфигурацию входа выхода, выбираем 01: General purpose output mode. GPIOE->MODER|=0×10000 . Al doilea pentru includerea unui nivel scăzut/înalt pe picior. Mai jos este programul:

#include „stm32f3xx.h " // Fișier antet microcontroler
nesemnat int i;
anulare întârziere()(
pentru (i=0;i<500000;i++);
}
int main(void)(
RCC->AHBENR|=(1<<21);
GPIOE->MODER|=0×10000;
în timp ce (1)(
întârziere();
GPIOE->ODR|=0×100;
întârziere();
GPIOE->ODR&=~(0×100);
} }

2). SPL(Bibliotecă de periferice standard)- această bibliotecă este concepută pentru a combina toate procesoarele de la ST Electronics. Conceput pentru a face codul mai portabil și destinat în primul rând dezvoltatorului începător. ST a lucrat la un înlocuitor SPL numit „low layer”, care este compatibil cu HAL. Driverele Low Layer (LL) sunt proiectate pentru a oferi un strat aproape ușor, orientat spre experți, care este mai aproape de hardware decât HAL. Pe lângă HAL, sunt disponibile și API-uri LL. Un exemplu de același program în SPL.

#include
#include
#include
#define LED GPIO_Pin_8
int main() (
i lung;
GPIO_InitTypeDef gpio;
// LED-ul albastru este conectat la portul E, pin 8 (autobuz AHB)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
// Configurați portul E (LED)
GPIO_StructInit(&gpio); //declararea și inițializarea variabilei structurii de date
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_Pin = LED;
GPIO_Init(GPIOE, &gpio);
// LED-uri care clipesc
în timp ce (1) (
// Pe
GPIO_SetBits(GPIOE, LED);
pentru (i = 0; i< 500000; i++);
// Toate oprite
GPIO_ResetBits(GPIOE, LED);
pentru (i = 0; i< 500000; i++);
} }

Fiecare funcție este descrisă în documentația tehnică UM1581 Manual de utilizare Descrierea bibliotecii de periferice standard STM32F30xx/31xx. Aici includem trei fișiere de antet care conțin datele necesare, structurile, funcțiile de control de resetare și sincronizare, precum și pentru configurarea porturilor I/O.

3). HAL- (Nivel de acces hardware, strat de abstracție hardware)- O altă bibliotecă de dezvoltare partajată. Cu care a iesit programul CubeMX pentru configuratia pe care am folosit-o in ultimul articol. În același loc, am scris un program pentru a clipi un LED folosind această bibliotecă. După cum puteți vedea în figura de mai jos, cubul generează drivere HAL și CMSIS. Ei bine, să descriem principalele fișiere utilizate:
- system_stm32f3x.c și system_stm32f3x.h- asigurați seturi minime de funcții pentru configurarea sistemului de ceas;
- core_cm4.h - oferă acces la registrele nucleului și periferiei acestuia;
- stm32f3x.h - fișier antet microcontroler;
- startup_system32f3x.s - codul de pornire, conține un tabel de vectori de întrerupere etc.

#include „principal.h”
#include „stm32f3xx_hal.h”
void SystemClock_Config(void); /*Declară funcțiile de configurare a ceasului*/
static void MX_GPIO_Init(void); /*Inițializare I/O*/
int main(void)(
/*Resetarea tuturor perifericelor, Inițializează interfața Flash și Systick-ul.*/
HAL_Init();
/* Configurați ceasul sistemului */
SystemClock_Config();
/* Inițializați toate perifericele configurate */
MX_GPIO_Init();
în timp ce (1) (
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_8); //Schimbați starea piciorului
HAL_Delay(100); )
}
void SystemClock_Config(void){
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
dacă (HAL_RCC_OscConfig (&RCC_OscInitStruct) != HAL_OK){

}
/**Inițializează ceasurile magistralelor CPU, AHB și APB */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
dacă (HAL_RCC_ClockConfig (&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK){
_Error_Handler(__FILE__, __LINE__);
}
/**Configurați timpul de întrerupere Systick*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configurați Systick-ul */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* Configurarea întreruperii SysTick_IRQn */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/** Configurați pinii ca intrare analogică ieșire EVENT_OUT EXTI */
static void MX_GPIO_Init(void){
GPIO_InitTypeDef GPIO_InitStruct;
/* Activare ceas porturi GPIO */
__HAL_RCC_GPIOE_CLK_ENABLE();
/*Configurați nivelul de ieșire a pinului GPIO */
HAL_GPIO_WritePin (GPIOE, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configurați pinii GPIO: PE8 PE9 PE10 PE11 PE12 PE13 PE14 PE15 */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed ​​​​= GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
}
void _Error_Handler (car * fișier, linie int){
în timp ce (1) (
} }
#ifdef USE_FULL_ASSERT

Void assert_failed (fișier uint8_t*, linie uint32_t){
}
#endif
Aici, la fel ca și în exemplul anterior, putem vizualiza, de exemplu, descrierea fiecărei funcții din documentație Manual de utilizare UM1786 Descrierea driverelor STM32F3 HAL și de nivel inferior.

Putem rezuma că prima opțiune, folosind CMSIS, este mai puțin greoaie. Fiecare bibliotecă are documentație. În proiectele ulterioare, vom folosi HAL și CMSIS, folosind programul de configurare STCube și, dacă este posibil, vom folosi registre direct, fără wrapper software. Aici ne vom opri astăzi. În articolul următor, vom lua în considerare principiile de bază ale construirii unei case inteligente. Toate pentru moment.

Top articole similare