Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • Windows Phone
  • Microcontrolere procesoare, proiecte, programare. Protejarea biților de configurare GPIO

Microcontrolere procesoare, proiecte, programare. Protejarea biților de configurare GPIO

Registrele principale ale portului I/O al microcontrolerului STM32

Un port se referă la un anumit set numit de picioare de microcontroler. În microcontrolerele STM se numesc GPIOA, GPIOB, GPIOC etc. Porturile I/O ale microcontrolerelor STM32 au de obicei 16 linii (picioare). O linie se referă la unul sau altul picior al microcontrolerului. Fiecare linie de port poate fi configurată într-un mod specific și poate îndeplini următoarele funcții:

  • Intrare digitală;
  • Ieșire digitală;
  • intrare de întrerupere externă;
  • Funcția I/O a altor module de microcontroler.

Configurați portul la modul dorit munca se poate face folosind registrele microcontrolerului. Aceste registre pot fi accesate direct sau pot fi folosite metode speciale din biblioteca periferică.

Să ne uităm la registrele principale necesare pentru a lucra cu porturile I/O.

Registrele responsabile pentru configurarea porturilor

Înainte de a începe să lucrați cu un port de ieșire, trebuie să îl configurați pentru a se potrivi nevoilor dvs.

Registrele de configurare sunt responsabile pentru setarea sau configurarea unui port. În microcontrolerele din familiile STM32F302xx, STM32F303xx și STM32F313xx, acestea sunt următoarele registre:

  • GPIOx_MODER;
  • GPIOx_OTYPER;
  • GPIOx_OSPEEDR;
  • GPIOx_PUPDR.

Înregistrați GPIOx_MODER (unde x=A...F)

Acesta este pe 32 de biți Registrul este responsabil pentru modul de funcționare al liniei. Necesită 2 biți pentru a configura o anumită linie. Sunt posibile următoarele combinații:

  • 00 - linia este configurată pentru intrare;
  • 01 - ieșire;
  • 10 - mod alternativ de funcționare;
  • 11 - modul analogic.

Înregistrați GPIOx_TYPER (unde x=A...F)


Acest registru este folosit pentru a configura tipul de operare pe linie; folosește 16 biți în funcționare, restul de 16 sunt rezervați. Acceptă următoarele valori:

  • 0 - modul push-pull;
  • 1 - scurgere deschisă

Înregistrați GPIOx_PUPDR (unde x=A...F)


Registrul de date este responsabil pentru strângere. Acceptă următoarele valori:

  • 00 - fără lift
  • 01 - tragere la sursa de alimentare plus
  • 10 - tragere la sol

Înregistrați GPIOx_SPEEDR (unde x=A...F)


Registrul de setare a vitezei liniei.

  • 00 - 2 MHz;
  • 01 - 10 MHz;
  • 10 - 50MHz.

Registrul de ieșire (registrul de ieșire) GPIOx_ODR (unde x=A…F) – registru de date de ieșire


Acest registru este folosit pentru a scoate date către port. Când scrieți anumite valori la acest registru o valoare similară este setată pe linie (picior). Deoarece avem 16 linii și registrul are 32 de biți, sunt folosiți doar primii 16 biți (inferioare).

Registrul de intrare (starea portului sau registrul de date de intrare) GPIOx_IDR (unde x=A…F) – registru de date de intrare


Acest registru este doar pentru citire. Un fel de indicator de stare a portului. Analog de PINx în microcontrolerele din seria AVR.

Set de biți portului de ieșire Registrul GPIOx_BSRR


Acest registru stabilește starea pinului în portul de ieșire bit cu bit.

Mai multe informații despre toate registrele unui anumit microcontroler pot fi găsite în manualul de referință.pdf care poate fi descărcat de pe site-ul oficial www.st.com

Configurarea unui port de microcontroler folosind biblioteca

Portul poate fi configurat și folosind o bibliotecă specială care conține metode diferite pentru lucrul cu registrele I/O, iar variabilele speciale sunt declarate. Această bibliotecă eliberează programatorul de necesitatea de a calcula „manual” ce valoare trebuie scrisă într-un anumit registru.

Să ne uităm la un exemplu de cod care aprinde un LED

#include „stm32f30x.h” // Antetul dispozitivului #include „stm32f30x_rcc.h” #include „stm32f30x_gpio.h” void init_led(void) ( RCC_APB2PeriphClockCmd (GPIOE,ENABLE); GPIOE_Init_Struture_GPIO_Init_Init_Init_Init .GPIO_Speed ​​= GPIO_Speed_2MHz; // Setați viteza maximă GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Modul de ieșire GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //Indicați pin-ul la care este atașat LED-ul GPIO_Init(GPIOE,GPIO_InitStructure/InitStructure) (în structura principală) ; (); GPIO_SetBits( GPIOE,GPIO_Pin_8); //Setați pinul la o stare ridicată în timp ce(1) ( _NOP(); ) )

Pentru a economisi energie, toate perifericele microcontrolerului sunt dezactivate. Pentru a „activa” unul sau altul periferic, trebuie mai întâi să îi aplicați semnale de ceas.

Lucrăm cu portul GPIOE, așa că trebuie să activăm sincronizarea folosind metoda

RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

Care are două valori: prima este portul real pe care trebuie să-l activăm, iar a doua este starea a acestui port, pornit sau oprit.

Ne-am uitat la lucrul cu operațiuni cu biți și numere binare, punând astfel bazele luate în considerare subiect nou. În această lecție vom lua în considerare o altă întrebare: ce sunt registrele și cum se lucrează cu ele?

Memorie și registre

Una dintre cele mai importante abilități necesare atunci când lucrați cu microcontrolere este capacitatea de a interacționa cu registrele. Să ne dăm seama singuri Ce este?

În general, un registru este un tip special de memorie din interiorul unui microcontroler care este folosit pentru a controla procesorul și dispozitiv periferic. Fiecare se înregistrează în Arhitectura ARM este o celulă de memorie și are o lungime de 32 de biți, unde fiecare bit poate fi reprezentat ca un mic comutator cu care se controlează unul sau altul parametru al microcontrolerului.

Fiecare registru are al lui număr de serie- abordare. Adresa de registru este indicată de un număr de 32 de biți reprezentat în sistem hexazecimal Socoteala. Scriind la adresa registrului o anumită combinație Unurile și zerourile, care sunt de obicei prezentate în formă hexazecimală, sunt folosite pentru a configura și controla un anumit nod în MK. Să ne amintim că într-un program de lucru cu operații pe biți, l-am putea reprezenta sub formă număr hexazecimal un set arbitrar de unu și zero. În general, este de remarcat faptul că există două tipuri de registre: registre scop generalși registre speciale. Primele sunt situate în interiorul nucleului MK, iar cele din urmă fac parte din memoria RAM.

De asemenea, este de remarcat faptul că Manual de referință, pe care am descărcat-o, este o carte mare de referință despre registrele conținute în microcontrolerul țintă, iar biblioteca CMSIS ne permite să operam cu nume de registre simbolice în loc de adrese numerice. De exemplu, la registru 0x40011018 putem accesa pur și simplu folosind nume simbolic GPIOC_BSSR. Exemple specifice Ne vom uita la configurație în timpul analizei programului nostru de la prima lecție.

Deci, de obicei, structura registrului este descrisă sub forma unui mic tabel care indică:

  1. Înregistrați numele și descrierile scopului său
  2. Înregistrați adrese sau deplasați față de adresa de bază
  3. Valori implicite după resetare
  4. Tip de acces la celulele de înregistrare (citire, scriere, citire/scriere)
  5. Valori și descrieri ale parametrilor biților care urmează să fie scrisi
Să ne uităm la un exemplu de lucru cu registre într-o situație specifică pentru a obține ideea generala despre principiile instalării unui microcontroler.

Analiza codului de la prima lecție

Deci, să ne amintim problema pe care am rezolvat-o în prima lecție folosind cod gata exemplu: trebuia să scriem un program care să aprindă alternativ două LED-uri pe placa Discovery (poate nu două, dacă ai o versiune diferită a plăcii Discovery) cu un interval de timp.

Să aruncăm o altă privire la codul de program pe care l-am folosit pentru a face să sară MK-ul nostru cu două picioare pe care sunt amplasate LED-urile noastre:

Cod principal.c

/* Fișier antet pentru familia noastră de microcontrolere */ #include "stm32f0xx.h" /* Corpul programului principal */ int main(void) ( /* Activează ceasul pe Port GPIO*/ RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /* Configurați modul de funcționare al porturilor PC8 și PC9 în Output*/ GPIOC ->MODER = 0x50000; /* Setați tipul de ieșire la modul Push-Pull */ GPIOC->OTYPER = 0; /* Setați viteza portului la Scăzut */ GPIOC->OSPEEDR = 0; while(1) ( /* Porniți LED-ul PC8, opriți PC9 */ GPIOC->ODR = 0x100; pentru (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = 0x200; pentru (int i=0; i<500000; i++){} // Искусственная задержка } }


În primul rând, atunci când lucrăm cu STM32, chiar și pentru o sarcină atât de simplă precum pornirea și oprirea unui LED, trebuie să răspundem mai întâi la o serie de întrebări:
  1. Cum să configuram pinii portului GPIO de care avem nevoie, astfel încât să putem aprinde LED-ul?
  2. Cum se aprinde și se stinge LED-ul?
Să le răspundem în ordine.

Unde sunt conectate LED-urile noastre? La ce pin al microcontrolerului?

Pentru a vedea unde este totul pe placa Discovery, și în special, LED-urile de care avem nevoie, trebuie să deschidem fișierul Schematic, fie cel pe care l-am descărcat de pe site-ul ST, fie direct de pe Keil:


Deschiderea Schemei vom vedea o diagramă a tot ceea ce este pe placă - o diagramă ST-Link, cablarea tuturor perifericelor și multe altele. În momentul de față suntem interesați de două LED-uri, căutăm denumirea lor:


După cum putem vedea, LED-urile noastre sunt conectate la portul GPIOC de pe pinii 8 și 9.

Cum se activează ceasul pe portul GPIO dorit?

În general, orice lucru cu periferice în microcontrolerele STM32 se reduce la o secvență standard de acțiuni:
  1. Activați sincronizarea modulului periferic corespunzător. Acest lucru se realizează prin registrul RCC prin furnizarea unui semnal de ceas direct de la magistrala pe care se află acest modul. În mod implicit, tactarea tuturor perifericelor este dezactivată pentru a minimiza consumul de energie.
  2. Configurare prin registre de control, prin modificarea parametrilor specifici unui anumit dispozitiv periferic
  3. Lansarea directă și utilizarea rezultatelor modulelor
Adică, pentru a începe, trebuie să începem să facem tac pe portul GPIOC. Acest lucru se face direct prin accesarea registrului RCC, care este responsabil cu tactarea totul și pornirea semnalului de ceas de la magistrala la care este conectat portul nostru GPIO.

Atenţie! Într-un articol separat vom analiza în detaliu întrebarea referitoare la sistemul de ceas, configurația și utilizarea acestuia.

Aflați la ce magistrală este conectat portul nostru GPIOC, puteți găsi în Fișa de date pentru MK-ul nostru, în secțiunea Maparea memoriei din Tabelul 16. Adresele limitelor registrului periferic STM32F051xx.


După cum probabil ați observat deja, anvelopa de care avem nevoie se numește AHB2. Pentru a ne familiariza mai bine cu registrul în care este activată ceasul pentru portul GPIO de care avem nevoie pe magistrala AHB, trebuie să mergem la secțiunea corespunzătoare din Manualul de referință. După numele registrelor îl putem determina pe cel de care avem nevoie:


Mergem în acest punct și vedem registrul nostru de 32 de biți, adresa lui offset, valoarea implicită, cum să accesăm registrul și o listă cu ceea ce este responsabil fiecare bit din registru.


Ne uităm la tabel și vedem ceva asemănător cu opțiunile pentru activarea tacării pe porturile GPIO. Să mergem la descriere și să găsim opțiunea de care avem nevoie:


În consecință, dacă setăm bitul 19 la „1”, aceasta va permite tactarea pe portul I/O C - adică pe GPIOC-ul nostru. În plus, trebuie să pornim un bit din grup separat, fără a afecta restul deoarece nu trebuie să interferăm sau să modificăm alte setări în mod inutil.

Pe baza materialelor din lecția anterioară, știm că, pentru a seta un anumit bit, trebuie să folosim operația logică „SAU” pentru a adăuga valoarea registrului curent cu o mască care conține biții care trebuie activați. De exemplu, să adăugăm valoarea implicită a registrului RCC->AHBENR, adică. 0x14 și numărul 0x80000 vor activa astfel tactarea GPIOC prin setarea bitului 19:

Cum putem face asta dintr-un program? Este destul de simplu. În acest caz avem două opțiuni:

  1. Scrierea la un registru direct a valorii numerice a unui registru direct prin adresa acestuia.
  2. Configurare folosind biblioteca CMSIS
Nu există probleme speciale cu scrierea unei valori într-un registru direct, dar există câteva dezavantaje semnificative. În primul rând, un astfel de cod devine imposibil de citit și, în al doilea rând, nu putem determina imediat la ce registru se referă o anumită adresă din memorie.

Adică, am putea accesa adresele de registru direct după adresă și să scriem asta:

IO uint32_t * adresă_registru = (uint32_t *) 0x40021014U; // Adresa registrului nostru în memorie *(__IO uint32_t *)adresa_registrului |= 0x80000; // Activați 19 biți cu parametrul nostru
A doua varianta mi se pare cea mai atractiva, pentru ca... Biblioteca CMSIS este organizată în așa fel încât un registru să poată fi accesat folosind doar numele acestuia. Preprocesorul, atunci când procesează textul programului înainte de compilare, va înlocui automat toate valorile digitale ale adresei de registru. Să ne uităm puțin mai detaliat la această întrebare.

Vă sugerez să deschidem proiectul nostru, pe care l-am făcut în prima lecție, sau să descărcați unul pregătit în prealabil de aici și să ștergeți tot conținutul programului, lăsând doar fișierul antet inclus, funcția main() și instrucțiunile de activare a tacării ( vom avea nevoie de el pentru o analiză detaliată a codului).

Codul nostru va arăta astfel:

/* Fișier antet pentru familia noastră de microcontrolere */ #include "stm32f0xx.h" /* Corpul programului principal */ int main(void) ( /* Activați sincronizarea pe portul GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; )
Să pătrundem adânc în biblioteca CMSIS pentru a ne cunoaște.

Pentru a ajunge rapid la locul unde este declarată una sau alta constantă sau variabilă, Keil implementează o funcție convenabilă. Facem clic dreapta pe constanta de care avem nevoie, de exemplu, pe RCC:


Și suntem transportați în adâncurile bibliotecii CMSIS, în care vom vedea că toate registrele disponibile pentru control programatic au forma structurilor TypeDef, inclusiv RCC-ul nostru:


După ce a eșuat în acest fel în RCC_TypeDef, vom vedea o structură care descrie toate câmpurile registrului nostru:


În consecință, putem accesa în siguranță registrul de care avem nevoie cu o intrare similară PERIPH_MODULE->REGISTERși atribuiți-i o anumită valoare.

Pe lângă desemnarea mnemonică a registrelor, există și desemnări pentru biți specifici. Dacă nu reușim să declarăm parametrul RCC_AHBENR_GPIOCEN din programul nostru, vom vedea și declarația tuturor parametrilor:


Astfel, folosind biblioteca CMSIS, obținem o intrare concisă, lizibilă, a parametrului de care avem nevoie într-un registru, prin a cărui setare începem să tacăm pe portul de care avem nevoie:

/* Activează ceasul pe portul GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN;
Ca sarcină: Determinați, folosind capabilitățile lui Keil, modul în care adresa registrului RCC->AHBENR s-a dovedit a fi 0x40021014.

Cum să configuram pinii GPIO de care avem nevoie, astfel încât să putem porni LED-ul?

Deci, știm că LED-urile de care avem nevoie sunt conectate la portul GPIOC la pinii PC8 și PC9. Trebuie să le setăm într-un astfel de mod încât LED-ul să se aprindă. Aș dori să fac imediat o rezervare că ne vom uita la porturile GPIO mai detaliat într-un alt articol și aici ne vom concentra în mod special pe lucrul cu registrele.

În primul rând, trebuie să comutăm modul de funcționare al pinii PC8 și PC9 în modul Ieșire. Restul parametrilor portului pot fi lăsați implicit. Accesați secțiunea Manual de referință 9. I/O de uz general (GPIO) și deschideți elementul responsabil pentru modul de funcționare al pinii portului GPIO și vedeți că registrul MODER este responsabil pentru acest parametru:


Judecând după descriere, pentru a seta pinii PC8 și PC9 în modul Ieșire, trebuie să scriem 01 în câmpurile corespunzătoare ale registrului GPIOC.

Acest lucru se poate face prin instalare directă folosind valori numerice:


Sau folosind definiții din bibliotecă:

/* Activează ceasul pe portul GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0;
După această instrucțiune, pinii noștri PC8 și PC9 vor comuta în modul Ieșire.

Cum se aprinde LED-ul?

Dacă acordăm atenție listei de registre disponibile pentru controlul portului GPIO, putem vedea registrul ODR:


Fiecare dintre biții corespunzători este responsabil pentru unul dintre pinii portului. Puteți vedea mai jos structura acestuia:


Pentru a asigura o schimbare alternativă a stărilor LED-urilor, este necesar să porniți/opriți biții 8 și 9 cu un anumit interval de timp. Adică, atribuiți alternativ registrului valoarea 0x100 și 0x200.

Putem face acest lucru atribuind direct valori registrului:

GPIOC->ODR = 0x100; // Aprindeți PC8, opriți PC9 GPIOC->ODR = 0x200; // Aprindeți PC9, opriți PC8
Putem folosi definiții din bibliotecă:

GPIOC->ODR = GPIO_ODR_8; // Porniți PC8, opriți PC9 GPIOC->ODR = GPIO_ODR_9; // Aprindeți PC9, opriți PC8
Dar, deoarece microcontrolerul funcționează foarte repede, nu vom observa modificări ale stărilor LED-urilor și vizual se va părea că ambele sunt aprinse constant. Pentru ca ei să clipească efectiv alternativ, vom introduce o întârziere artificială sub forma unui ciclu care va ocupa MK-ul cu calcule inutile de ceva timp. Veți obține următorul cod:

/* Porniți LED-ul PC8, opriți PC9 */ GPIOC->ODR = GPIO_ODR_8; pentru (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = GPIO_ODR_9; pentru (int i=0; i<500000; i++){} // Искусственная задержка
Aici putem termina cunoștințele noastre inițiale cu registrele și metodele de lucru cu acestea.

Verificarea rezultatelor codului nostru

O mică adăugare drăguță la sfârșitul articolului: Keil are un instrument excelent de depanare cu care putem parcurge programul nostru și să vedem starea curentă a oricărei unități periferice. Pentru a face acest lucru, după descărcarea firmware-ului după compilare, putem face clic pe butonul Start Debug Session:

STM32 are multe cronometre foarte convenabile și flexibile. Chiar și cel mai tânăr microcontroler (STM32F030F4P6) are 4 astfel de temporizatoare.

Pentru a folosi temporizatorul, va trebui să includem fișierul bibliotecii periferice stm32f10x_tim.c. În același mod, faceți clic dreapta în Workspace (fereastra din stânga) pe grupul StdPeriphLib, Add –> Add files, file LibrariesSTM32F10x_StdPeriph_Driversrcstm32f10x_tim.c.

De asemenea, trebuie să activați utilizarea unui antet pentru acest fișier. Deschideți stm32f10x_conf.h (dați clic dreapta pe numele acestui fișier din cod, „Deschideți stm32f10x_conf.h”. Decomentați linia #include „stm32f10x_tim.h”.

9. Adăugați un cronometru

O întârziere a unui ciclu gol este o blasfemie, mai ales pe un cristal atât de puternic precum STM32, cu o grămadă de temporizatoare. Prin urmare, vom face această întârziere folosind un cronometru.

STM32 are cronometre diferite cu seturi diferite de proprietăți. Cele mai simple sunt cronometrele de bază, cu atât sunt mai complexe cronometrele de uz general, iar cele mai complexe sunt cronometrele avansate. Temporizatoarele simple sunt limitate la simpla numărare a ciclurilor de ceas. În temporizatoarele mai complexe, apare PWM. Cele mai complexe temporizatoare, de exemplu, pot genera PWM trifazat cu ieșiri directe și inverse și timp mort. Un simplu cronometru, numărul 6, ne va fi suficient.

Puțină teorie

Tot ce ne trebuie de la temporizator este să numărăm până la o anumită valoare și să generăm o întrerupere (da, vom învăța și cum să folosim întreruperile). Cronometrul TIM6 este tactat de la magistrala de sistem, dar nu direct, ci printr-un prescaler - un simplu contra-divizor programabil (doar gândiți-vă, contoare speciale cu microcircuite au fost produse în URSS, iar cele programabile erau în special în lipsă - și acum am vorbesc doar despre un astfel de contor în treacăt). Prescalerul poate fi configurat la orice valoare de la 1 (adică contorul va primi frecvența completă a magistralei, 24 MHz) la 65536 (adică 366 Hz).

Semnalele ceasului, la rândul lor, cresc contorul cronometrului intern, începând de la zero. De îndată ce valoarea contorului atinge valoarea ARR, contorul depășește și are loc evenimentul corespunzător. Când are loc acest eveniment, cronometrul încarcă din nou 0 în contor și începe să numere de la zero. În același timp, poate provoca o întrerupere (dacă este configurată).

De fapt, procesul este puțin mai complicat: există două registre ARR - externe și interne. În timpul numărării, valoarea curentă este comparată în mod specific cu registrul intern și numai atunci când acesta depășește este actualizat cel intern față de cel extern. În acest fel, puteți schimba în siguranță ARR în timp ce cronometrul funcționează - în orice moment.

Cod

Codul va fi foarte asemănător cu cel precedent, deoarece... Inițializarea tuturor perifericelor are loc în același mod - cu singura excepție că temporizatorul TIM6 se blochează pe magistrala APB1. Prin urmare, activarea temporizatorului: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

Acum creăm o structură de tip TIM_TimeBaseInitTypeDef, o inițializam (TIM_TimeBaseStructInit), o configuram, o trecem la funcția de inițializare a temporizatorului (TIM_TimeBaseInit) și la final pornim cronometrul (TIM_Cmd).

Care sunt numerele magice? După cum ne amintim, magistrala are o frecvență de ceas de 24 MHz (cu setările proiectului nostru). Setând prescalerul cronometrului la 24000, împărțim această frecvență la 24 de mii și obținem 1 kHz. Aceasta este frecvența care va merge la intrarea contorului cronometrului.

Valoarea din contor este 1000. Aceasta înseamnă că contorul va depăși în 1000 de cicluri de ceas, adică. in exact 1 secunda.

După aceasta, avem de fapt un cronometru care funcționează. Dar asta nu este tot.

10. Să ne ocupăm de întreruperi

Bine, întreruperi. Pentru mine odată (la momentul PIC) erau o pădure întunecată și am încercat să nu le folosesc deloc - și nu știam cum, de fapt. Cu toate acestea, ele conțin putere care este complet nedemn de ignorat. Adevărat, întreruperile în STM32 sunt un lucru și mai complex, în special mecanismul de deplasare a acestora; dar mai multe despre asta mai târziu.

După cum am observat mai devreme, temporizatorul generează o întrerupere atunci când contorul depășește - dacă procesarea întreruperilor pentru acest dispozitiv este activată, această întrerupere specială este activată și cea anterioară este resetată. Analizând această frază, înțelegem de ce avem nevoie:

  1. Activați întreruperile temporizatorului TIM6 în general;
  2. Activați întreruperea temporizatorului TIM6 pentru depășirea contorului;
  3. Scrieți o procedură de gestionare a întreruperilor;
  4. După procesarea întreruperii, resetați-o.

Activarea întreruperilor

Sincer să fiu, nu este nimic complicat aici. În primul rând, activăm întreruperile TIM6: NVIC_EnableIRQ(TIM6_DAC_IRQn); De ce acest nume? Pentru că în nucleul STM32, întreruperile de la TIM6 și de la DAC au același număr. Nu știu de ce s-a făcut acest lucru - economii, lipsă de numere sau doar un fel de chestie moștenită - în orice caz, nu va cauza probleme, deoarece acest proiect nu folosește un DAC. Chiar dacă proiectul nostru a folosit un DAC, am putea afla cine l-a sunat exact la intrarea în întrerupere. Aproape toate celelalte temporizatoare au o singură întrerupere.

Configurarea evenimentului sursă de întrerupere: TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); - activați întreruperea temporizatorului TIM6 pe baza evenimentului TIM_DIER_UIE, de ex. Eveniment de actualizare a valorii ARR. După cum ne amintim din imagine, acest lucru se întâmplă simultan cu depășirea contorului - deci acesta este exact evenimentul de care avem nevoie.

În acest moment, codul pentru cazurile de cronometru este următorul:

Manevrarea întreruperii

Acum nu puteți rula proiectul - prima întrerupere a temporizatorului nu își va găsi handlerul, iar controlerul se va bloca (mai precis, va ajunge în handler-ul HARD_FAULT, care este în esență același lucru). Trebuie să o scriem.

Puțină teorie

Trebuie să aibă un nume foarte specific, void TIM6_DAC_IRQHandler(void). Acest nume, așa-numitul vector de întrerupere, este descris în fișierul de pornire (în proiectul nostru este startup_stm32f10x_md_vl.s - puteți vedea singur, linia 126). De fapt, vectorul este adresa operatorului de întrerupere, iar atunci când are loc o întrerupere, miezul ARM urcă în zona inițială (în care este tradus fișierul de pornire - adică locația sa este setată complet rigid, chiar la începutul flash-ului). memorie), caută vectorul acolo și se mută în locul potrivit în cod.

Verificarea evenimentului

Primul lucru pe care trebuie să-l facem atunci când intrăm într-un astfel de handler este să verificăm ce eveniment a cauzat întreruperea. Acum avem un singur eveniment, dar într-un proiect real pot exista mai multe evenimente pe un singur cronometru. Prin urmare, verificăm evenimentul și executăm codul corespunzător.

În programul nostru, această verificare va arăta astfel: dacă (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) - totul este clar, funcția TIM_GetITStatus verifică prezența evenimentului specificat pe cronometru și returnează 0 sau 1.

Ștergerea steagului UIF

Al doilea pas este să ștergeți indicatorul de întrerupere. Reveniți la imagine: ultimul grafic UIF este indicatorul de întrerupere. Dacă nu este șters, următoarea întrerupere nu va fi apelată, iar controlerul va cădea din nou în HARD_FAULT (ce este asta!).

Executarea acțiunilor într-o întrerupere

Vom comuta pur și simplu starea LED-ului, ca în primul program. Diferența este că acum programul nostru o face mai dificilă! De fapt, este mult mai corect să scrii astfel.

Folosim variabila globala int state=0;

11. Toate codurile de proiect cu un cronometru

Arhivați cu proiectul de cronometru.

Ei bine, apropo, cronometrul poate comuta piciorul în sine, fără întreruperi sau procesare manuală. Acesta va fi al treilea proiect al nostru.

Întregul ciclu:

1. Porturi I/O

2. Timer și întreruperi

3. Ieșiri timer

4. Întreruperi externe și NVIC

5. Instalați FreeRTOS

Recent, un coleg m-a atras de ideea de a crea o casă inteligentă și chiar am reușit să comand zeci de senzori diferiți pentru mine. A apărut întrebarea despre alegere Microcontroler(denumite în continuare MK) sau panouri. După câteva căutări am găsit mai multe opțiuni. Printre ei au fost Arduino(inclusiv clonele lui, dintre care una mi-am comandat-o doar pentru a mă distra) și Platforma de lansare, dar toate acestea sunt redundante și greoaie (deși în ceea ce privește programarea este mult mai simplu, dar nu voi ridica subiectul holivarurilor, fiecare are gusturile lui). În cele din urmă, am decis să nu mă decid asupra unei plăci gata făcute, ci să iau doar MK-ul și să fac totul de la zero. Până la urmă am ales între Atmel ATtiny (2313), Atmel ATmega(am decis să refuz pentru că nu l-am putut găsi pentru bani adecvați), STM32(Cortex pe miez BRAŢ). Mă jucasem deja cu adolescentul, așa că am luat-o pentru mine STM32VL-Descoperire. Aceasta poate fi numită o introducere la o serie de articole despre STM32. Permiteți-mi să fac o rezervare imediat: nu voi fi autorul majorității acestor articole, pentru că... Doar o învăț eu, aici le public în primul rând pentru mine, astfel încât să fie mai ușor să caut dacă uit ceva. Și deci hai să mergem!

Informații generale

Microcontrolere familii STM32 conține până la șapte porturi I/O pe 16 biți cu nume de la PORTA la PORTG. Într-un anumit model microcontroler Fără excepție, sunt disponibili toți pinii portului, al căror număr total depinde de tipul carcasei și este specificat în fișa de date pentru subfamilia corespunzătoare.

Pentru a activa portul x, trebuie mai întâi să îl conectați la magistrala APB2 setând bitul IOPxEN corespunzător în registrul de activare a ceasului periferic RCC_APB2ENR:

Managementul portului STM32 realizat folosind seturi de șapte registre de 32 de biți:

  • GPIOx_CRL, GPIOx_CRH– setați modurile de funcționare ale fiecăruia dintre biții de port ca intrare sau ieșire, determinați configurația etapelor de intrare și ieșire.
  • GPIOx_IDR– registru de date de intrare pentru citirea stării fizice a portului x pini.
  • GPIOx_ODR– registrul de ieșire scrie datele direct pe port.
  • GPIOx_BSRR– resetare atomică și registru de setare a biților de port.
  • GPIOx_BSR– registru de resetare a bitului de port.
  • GPIOx_LCKR– registru de blocare a configurației pin.

Moduri de operare a pinului GPIO

Modurile de funcționare ale pinii individuali sunt determinate de o combinație de biți MODEyȘi CNFy registre GPIOx_CRLȘi GPIOx_CRH(în continuare: x este numele portului, y este numărul de bit al portului).

GPIOx_CRL- registru de configurare pin 0…7 porturi X:

Structura registrului GPIOx_CRH structură similară GPIOx_CRLși este conceput pentru a controla modurile de operare ale pinilor superiori ai portului (biții 8...15).

Biții MODEy ai registrelor specificate determină direcția de ieșire și limita vitezei de comutare în modul de ieșire:

  • MODEy = 00: Mod de intrare (stare după resetare);
  • MODEy = 01: Mod de iesire, viteza maxima – 10 MHz;
  • MODEy = 10: Mod de iesire, viteza maxima – 2 MHz;
  • MODEy = 11: Mod de ieșire, viteza maximă – 50 MHz.

Biții CNF specifică configurația etajelor de ieșire ale pinilor corespunzători:

în modul de conectare:

  • CNFy = 00: Intrare analogică;
  • CNFy = 01: Intrare în stare a treia (stare după resetare);
  • CNFy = 10: Intrare cu rezistență pull-up (dacă PxODR=1) sau pull-down (dacă PxODR=0);
  • CNFy = 11: Rezervat.

în modul de ieșire:

  • CNFy = 00: Ieșire push-pull de uz general;
  • CNFy = 01: Ieșire de scurgere deschisă de uz general;
  • CNFy = 10: Ieșire push-pull cu funcție alternativă;
  • CNFy = 11: Ieșire de scurgere deschisă cu funcție alternativă.

Pentru a crește imunitatea la zgomot, toate tampoanele de intrare conțin declanșatoare Schmidt. O parte din concluzii STM32, echipate cu diode de protecție conectate la magistrala comună și la magistrala de alimentare, sunt marcate în fișa de date ca FT (tolerant 5V) - compatibil cu o tensiune de 5 volți.

Protejarea biților de configurare GPIO

Pentru a proteja biții din registrele de configurare împotriva scrierii neautorizate în STM32 este furnizat un registru de blocare a setărilor GPIOx_LCKR
GPIOx_LCKR- registru de blocare a setărilor de ieșire portului:

Pentru a proteja setările unui pin de port individual, trebuie setat bitul LCKy corespunzător. Apoi efectuați înregistrarea secvențială în categorie LCKK valorile „1” - „0” - „1” și două operații de citire a registrului LCKR, care în caz de blocare reușită va da pentru bit LCKK valorile „0” și „1”.

Protecția biților de setare va rămâne în vigoare până la următoarea repornire a microcontrolerului.

Fișier de definiție periferică microcontrolereSTM32 definește grupuri separate de registre unite printr-un scop funcțional comun (inclusiv GPIO), ca structuri ale limbajului C, iar registrele în sine ca elemente ale acestei structuri.

STM32 - de la zero la RTOS. 2: Timer și întreruperi

De exemplu:

GPIOC->BSRR– Setarea/resetarea registrului portului GPIOC BSRR.
Să folosim definițiile din fișierul stm32f10x.h pentru a ilustra modul de lucru cu registrele I/O ale microcontrolerului STM32F100RB instalat în trusa de pornire STM32VLDESCOPERIRE:

Scrieți și citiți GPIO

Porturile de intrare sunt destinate scrierii și citirii GPIOx_IDRși zi liberă GPIOx_ODR registre de date.

Scrieți în registrul de ieșire ODR port configurat pentru ieșire, setează nivelurile de ieșire ale tuturor biților portului în conformitate cu valoarea înregistrată. Dacă un pin este configurat ca intrare pull-up, starea bitului de registru corespunzător ODR activează tragerea ieșirii către magistrala de alimentare (pull-up, ODR=1) sau autobuz comun microcontroler (pull-down, ODR=0).

Citiți registrul IDR returnează valoarea de stare a pinii microcontrolerului configurați ca intrări:

Resetarea și setarea biților de port

Pentru resetarea atomică și setarea biților GPIOîn microcontrolere STM32 registrul este destinat GPIOx_BSRR. Tradițional pentru arhitectură BRAŢ o metodă de gestionare a biților de registru care nu necesită utilizarea unei operații de tip "citire-modificare-scriere" vă permite să setați și să resetați biții de port prin simpla scriere a unuia în biții setați BS (Set de biți) și resetați BR (BitReset) Inregistreaza-te BSRR. În acest caz, scrierea de biți zero în registru nu afectează starea pinilor corespunzători.

GPIOx_BSRR- Înregistrați-vă pentru resetarea și setarea biților de port:

Funcții alternative GPIOși reatribuirea acestora (remapare)
Aproape toate circuitele externe pentru scopuri speciale STM32(inclusiv cabluri pentru conectarea rezonatoarelor de cuarț, JTAG/SWDși așa mai departe) pot fi activate pe pinii corespunzători ai microcontrolerului sau dezactivate de la aceștia pentru a permite utilizarea lor ca pini de uz general. Selectarea unei funcții alternative de ieșire se realizează folosind registre cu prefix „AFIO”_.
În plus, registrele AFIO _ vă permit să alegeți mai multe opțiuni de aspect funcții speciale pe pinii microcontrolerului. Acest lucru se aplică în special la ieșirile interfețelor de comunicație, temporizatoarelor (registre AFIO_MAPR), pini de întrerupere externi (registri AFIO_EXTICR) etc.

Consultați documentele pentru mai multe detalii "Manual de referință" la subgrupul corespunzător de microcontrolere.

Proiecte pentru acest articol:

  1. µVision 4.13a-> STM32GPIO_emcu_uV
  2. IAR ARM 6.0-> STM32GPIO_emcu_iar
  3. IAR ARM 6.21-> STM32GPIO_emcu_iar_V6.21

Pentru condus GPIOSTM32 Puteți folosi macrocomenzi scrise ca alternativă la bibliotecile ST care sunt departe de a fi optime, după mulți: gpio_emcu.h

Material suplimentar:

  1. STM32F10xxx Manual de referință. Ghid de ajutor dezvoltator
  2. STM32F100xx Manual de referință. Ghid de ajutor pentru dezvoltatori
  3. STM32F105xx, STM32F107xx Fișă de date
  4. STM32F100x4, STM32F100x6, STM32F100x8, STM32F100xB Fișă de date
  5. Ghid pentru crearea de proiecte pentru STM32DISCOVERY în IAR
  6. Ghid pentru crearea proiectelor pentru STM32DISCOVERY în MDK-ARM, uVision

Alte părți

  1. (această parte) Programare STM32. Partea 1. Porturi I/O GPIO, STM32
  2. Programare STM32. Partea 2. Sistem de ceas STM32
  3. Programare STM32. Partea 3. Sistem de întrerupere
  4. Programare STM32. Partea 4. Întreruperi externe EXTI

Temporizatoarele de bază în STM32

Temporizatoarele sunt o astfel de periferie a controlerului STM32 care ne permit să numărăm foarte precis intervalele de timp. Aceasta este poate una dintre cele mai importante și mai utilizate funcții, dar există și altele. Ar trebui să începem cu faptul că în controlerele STM32 există cronometre cu diferite grade de răcoare. Cele mai simple sunt De bazăcronometre . Sunt bune pentru că sunt foarte ușor de configurat și controlat folosind un minim de registre. Tot ce pot face este să numere intervale de timp și să genereze întreruperi atunci când cronometrul ajunge valoarea stabilită. Următorul grup ( temporizatoare de uz general ) sunt mult mai misto decat primul, pot genera PWM, pot numara impulsurile care ajung la anumite picioare, poti conecta un encoder etc. Și cel mai tare cronometru este temporizator cu control avansat , cred că nu îl voi folosi mult timp, deoarece nu am nevoie să îl gestionez încă motor electric trifazat. Ar trebui să începeți să vă familiarizați cu cronometrele cu ceva mai simplu; am decis să iau cronometrele de bază. Sarcina pe care mi-am propus-o: faceți ca temporizatorul să genereze întreruperi în fiecare secundă.

În primul rând, voi reține că temporizatoarele de bază (TIM6 și TIM7) sunt atașate magistralei APB1, așa că dacă frecvența impulsurilor ceasului de pe aceasta se schimbă, temporizatoarele vor începe să ticăie mai repede sau mai lent. Dacă nu modificați nimic în setările ceasului și le lăsați implicite, atunci frecvența APB1 este de 24 MHz, cu condiția ca un cuarț extern să fie conectat la o frecvență de 8 MHz. În general, sistemul de ceas al STM32 este foarte complicat și voi încerca să scriu un post separat despre el în mod corespunzător. Pentru moment, vom folosi doar setările de ceas care sunt setate de codul generat automat de CooCox. Merită să începeți cu cel mai important registru - TIMx_CNT(în continuare x este numărul cronometrului de bază 6 sau 7). Acesta este un registru de numărare pe 16 biți care se ocupă direct de numărarea timpului. De fiecare dată din autobuz APB1 sosește un impuls de ceas, conținutul acestui registru este mărit cu unu. Când registrul se revarsă, totul începe de la zero. La frecvența implicită a magistralei APB1, temporizatorul bifează de 24 de milioane de ori într-o secundă! Este mult și, prin urmare, cronometrul are un prescaler, pe care îl putem controla folosind un registru TIMx_PSC. Scriind valoarea 24000-1 în el, vom forța registrul de numărare TIMx_CNT crește valoarea acesteia la fiecare milisecundă (Frecvență APB1împărțiți cu numărul din registrul prescaler și obțineți de câte ori pe secundă crește contorul). Unitatea trebuie scăzută pentru că dacă în registru este zero, înseamnă că divizorul cu unu este pornit. Acum, când registrul de numărare ajunge la 1000, putem spune cu siguranță că a trecut exact o secundă! De ce acum sondați registrul de numărare și așteptați până când apare 1000 acolo? Aceasta nu este metoda noastră, pentru că putem folosi întreruperi! Dar problema este că avem o singură întrerupere și apare atunci când contorul ajunge la zero. Pentru ca contorul să fie resetat înainte de program și nu când ajunge la 0xFFFF, se folosește registrul TIMx_ARR. Scriem în el numărul la care ar trebui să se numere registrul TIMx_CNTînainte de a merge la zero. Dacă dorim ca întreruperea să apară o dată pe secundă, atunci trebuie să scriem acolo 1000. În ceea ce privește sincronizarea directă, asta este tot, dar cronometrul în sine nu va începe să țină. Trebuie activat prin setarea bitului CENîn registru TIMx_CR1. Acest bit permite să înceapă numărătoarea inversă, așa că, dacă este resetată, numărătoarea inversă se va opri (C.O.).

Temporizatoare pe STM32. Setarea temporizatorului de bază

Există și alte biți în registru, dar nu sunt deosebit de interesante pentru noi. Dar ne interesează încă un pic, dar deja în registru TIMx_DIER. Se numeste UIE, Prin setarea acestuia, permitem cronometrului să genereze întreruperi atunci când registrul de numărare este resetat. Atât, nu este nici măcar mai complicat decât în ​​unele AVR-uri. Așadar, un mic rezumat: Pentru a utiliza temporizatorul de bază, aveți nevoie de:

  1. Setați un prescaler, astfel încât cronometrul să nu bifeze rapid ( TIMx_PSC)
  2. Setați limita la care trebuie să ajungă cronometrul înainte de resetare ( TIMx_ARR)
  3. Activați numărarea biților CENîn registru TIMx_CR1
  4. Activați întreruperea de depășire a biților UIEîn registru TIMx_DIER

Iată o succesiune simplă. Și acum este timpul să scoți știi ce și să încerci să clipești aceste LED-uri nefericite pentru a miliona oară, dar cu ajutorul unui cronometru :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int main() ( GPIO_InitTypeDef PORT; //Activează portul C și temporizatorul 6 RCC_APB2PeriphClockCmdCmd); phClockCmd(RCC_APB1Periph_TIM6,ENABLE ) ; // Configurați picioarele cu LED-uri pentru ieșire PORT.GPIO_Pin = (GPIO_Pin_9 | GPIO_Pin_8); PORT.GPIO_Mode = GPIO_Mode_Out_PP; PORT.GPIO_Speed ​​​​= GPIO_Speed_2MHz; GPIO_Speed_2MHz; GPIO_Init_2MHz; GPIO_Init_0; GPIO_Pin_0; 1; // Configurați divizorul pe care cronometrul a bifat de 1000 de ori pe secundă TIM6->ARR = 1000; // astfel încât întreruperea să apară o dată pe secundă TIM6->DIER |= TIM_DIER_UIE; //activează întreruperea de la temporizator TIM6-> CR1 |= TIM_CR1_CEN; // Începeți numărarea! NVIC_EnableIRQ(TIM6_DAC_IRQn); //Activați întreruperea TIM6_DAC_IRQn while(1) ( //Programul nu face nimic într-o buclă goală) ) // TIM6_DAC întreruperea handler void TIM6_DAC_ID) (și TIM6_VOid) >SR &= ~TIM_SR_UIF; //Resetați pavilionul UIF GPIOC->ODR^=(GPIO_Pin_9 | GPIO_Pin_8); //Inversați starea LED-urilor)

Merită să adăugați o notă mică la manipulatorul de întreruperi. Faptul este că este folosit de două blocuri periferice simultan: timer 6 și DAC. Aceasta înseamnă că, dacă scrieți un program care permite întreruperi de la ambele dispozitive periferice, atunci în corpul handler-ului trebuie să verificați care dintre ele a cauzat întreruperea. În cazul nostru, nu am făcut acest lucru, deoarece nu pot apărea întreruperi de la DAC. Nu este configurat, iar întreruperile sunt dezactivate implicit. Data viitoare ne vom uita la temporizatoarele de uz general și la aplicațiile lor practice.

Generarea PWM în STM32

În articolul anterior despre temporizatoarele de bază, am intermitent din nou LED-urile, dar de data aceasta vom merge mult mai departe și vom încerca să ne dăm seama cum să facem ca controlerul STM32 să genereze PWM. Pentru a face acest lucru, va trebui să folosim unul dintre cronometrele de uz general, deoarece au tot ce ne trebuie pentru asta. Toate celelalte funcționalități ale acestor temporizatoare sunt cu siguranță impresionante, dar în practica mea nu a fost încă utilă. Deși este posibil ca în viitor să am nevoie de funcții atât de utile, cum ar fi funcția de numărare a impulsurilor externe și capacitatea de a procesa rotațiile codificatorului în hardware. Dar deocamdată să ne ocupăm de PWM. Există un astfel de circuit format dintr-un controler, trei rezistențe și LED RGB pe care o vom gestiona. Controlul este de a lumina și de a stinge fără probleme fiecare culoare. Desigur, puteți lua trei LED-uri diferite dacă nu există RGB.

Am conectat LED-ul la acești pini dintr-un motiv. Temporizatoarele de uz general pot genera PWM numai pe anumiți pini. Deoarece vom folosi cronometrul 2, avem 4 picioare (PA0-PA3) la dispoziție. Pentru ca temporizatorul să le folosească, trebuie să activați acest lucru în două locuri: Configurați trei picioare (PA1-PA3) ca ieșire cu o funcție alternativă și permiteți în setările temporizatorului să tragă aceste picioare pentru a genera PWM. Pentru asta avem nevoie de un registru CCER

Dacă setați unul dintre biții evidențiați cu albastru la unu, cronometrul va avea voie să folosească piciorul corespunzător pentru PWM. Diagrama arată că trebuie să setăm biții CC2E, CC3EȘi CC4E. Acum trebuie să configuram modul PWM: Direct sau invers (nu pretind că am terminologia corectă). Diferența este destul de evidentă - cu PWM direct decât număr mai mareîn registrul de comparație, cu atât ciclul de lucru PWM este mai mare. În cazul PWM invers, este adevărat opusul. Am scris zero în registrul de comparație - factorul de umplere este 100%.

Lucrăm cu cronometre simple STM32 F4 discovery

Pentru a selecta modul, sunt utilizate două registre CCMR1 și CCMR2:

Sunt alocați până la 8 biți pentru setarea fiecărui canal! Dar, din fericire, ne interesează doar trei biți OCxM, pe care i-am marcat cu albastru. Ceea ce este marcat cu gri sunt aceiași biți, dar cu un nume diferit, aceștia sunt utilizați dacă canalul temporizatorului este în modul de captură. Nu voi lua în considerare toate combinațiile de biți, deoarece majoritatea nu au nicio legătură cu PWM. Avem nevoie doar de două combinații de biți:

PWM direct

PWM invers

Am un LED RGB cu un catod comun și, prin urmare, folosesc PWM invers. Astfel, ar trebui să setăm toți cei trei biți OCxM pentru cele trei canale de care sunt suspendate LED-urile. Și este tot! Configurarea PWM este completă, acum trebuie doar să porniți temporizatorul setând bitul CEN în registrul CR1. Pentru a controla ciclul de lucru, scrieți pur și simplu un număr de la 0x0000 la 0xFFFF în registrele CCR X, Unde X numărul canalului. De fapt, următorul cod implementează ceea ce era planificat la începutul acestui articol: Mărește luminozitatea LED-ului și apoi o reduce la zero și trece la următorul.

Procesul se repetă la nesfârșit, arată frumos :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" void delay(void) ( volatile uint32_t i; for (i=1; i != 0xF000; i++); ) int main() ( //Activează portul A RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //Activează Timer 2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); GPIO_InitTypeDef PORT; _1 | GPIO_Pin_2 | GPIO_Pin_3); // Noi vom folosi mod alternativ nu GPIO PORT.GPIO_Mode = GPIO_Mode_AF_PP; PORT.GPIO_Speed ​​​​= GPIO_Speed_2MHz; GPIO_Init(GPIOA, &PORT); //Permite cronometrului să folosească picioarele PA1,PA2,PA3 pentru PWM TIM2->CCER |= (TIM_CCER_CC2E|TIM_CCER_CC3E|TIM_CCER_CC4E); // Setați PWM invers pentru toate cele trei canale. TIM2->CCMR1|=(TIM_CCMR1_OC2M_0| TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2); TIM2->CCMR2|=(TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC4M_0 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2); //Porniți cronometrul! TIM2->CR1 |= TIM_CR1_CEN; //După aceasta scriem datele în TIM2->CCRx - iar luminozitatea LED-urilor se modifică uint32_t pwm_arr=(0,0,6553,13107,19660,26214,32768, 39321,45875,524828,5859328,585,524828); uint8_t i; în timp ce(1) ( pentru (i=1;i<=11;i++) { TIM2->CCR3=pwm_arr[i]; întârziere(); ) pentru (i=11;i>=1;i—) ( TIM2->CCR3=pwm_arr[i]; întârziere(); ) pentru (i=1;i<=10;i++) { TIM2->CCR2=pwm_arr[i]; întârziere(); ) pentru (i=11;i>=1;i—) ( TIM2->CCR2=pwm_arr[i]; întârziere(); ) pentru (i=1;i<=10;i++) { TIM2->CCR4=pwm_arr[i]; întârziere(); ) pentru (i=11;i>=1;i—) ( TIM2->CCR4=pwm_arr[i]; întârziere(); ) ) )

Sper că acest cod vă va ajuta să rulați PWM pe STM32 și să mă ajute să nu uit ce am fumat aproape jumătate de zi. Întrebările care nu sunt dificile pot fi scrise mai jos.

Osciloscop de buzunar „Lori” pe microcontrolerul STM32F103

Maxim Kerimov
decembrie 2016

Formularea problemei

Faceți un osciloscop de buzunar simplu cu costuri minime timp si bani.

Lista componentelor

  • Clonă chineză a plăcii Maple Mini cu un microcontroler STM32F103C8T6.
  • Display 1.8 TFT 128×160 SPI cu driver ST7735.
  • Cinci rezistențe și doi condensatori (Fig. 3).
  • Regulator liniar cu abandon scăzut AMS1117-3.3 (opțional).
  • „Pensetă” cu clemă pentru sondă - 2 buc.
  • Butonul este miniatural, de obicei deschis fără blocare, cu un clic.

Orez. 1. Test de funcționare a osciloscopului. Unda sinusoidală a fost generată de un sunet blaster, motiv pentru care este treptă.

Caracteristici

7 intervale cu valori de diviziune (celule): 7 µS, 28 µS, 113 µS, 559 µS, 2 mS, 10 mS, 20 mS.
Sensibilitate: 0,25 și 1,0 V/div.
Amplitudinea maximă a semnalului de intrare: 6 V.
Impedanta de intrare: 20 kΩ.
Putere: 4 baterii AA.
Consum de curent: 80 mA.

Ce semnal de frecvență puteți vedea?

Teoretic, puteți vedea 477 kHz. Teoretic, este posibil să distingem un meadru de un ferăstrău la frecvențe de 350 kHz și mai jos. În practică, puteți observa mai mult sau mai puțin confortabil semnale de până la 200 kHz. Dimensiunea celulei: 20 x 20 px.

„Frecvența de baleiaj” a osciloscopului nostru depinde de viteza ADC. În STM32F103, lățimea biților ADC este fixă ​​și egală cu 12. Aceasta este de o ori și jumătate mai mult decât avem nevoie. În STM32F407, de exemplu, adâncimea de biți poate fi redusă, ceea ce va reduce timpul de măsurare. Dar asta e o altă poveste cu un buget diferit.

Orez. 2. Afișare conexiune.

Orez. 3. Alimentare și circuit de intrare.

Divizorul de tensiune R1-R2 este utilizat pentru a controla nivelul de încărcare a bateriei. Pe dreapta colțul de sus ecran - pictograma bateriei, ca pe telefon mobil(nu este imagine).

Un regulator de tensiune extern nu este întotdeauna necesar. Placa de microcontroler are propriul regulator de 3,3 V 100 mA. Dacă alimentați afișajul de la acesta, acesta se va încălzi. Pe alte tipuri de plăci (cu un conector JTAG mare) există doar AMS1117, pentru ele nu este nevoie de unul extern. Unele afișaje au și AMS1117 (și jumper). Decide pentru tine.

Este logic să instalați un întrerupător de alimentare PD9-1 sau similar în serie cu bateriile.

Dacă doriți să creșteți dimensiunea impedanței dvs., puteți adăuga la intrare un adept de amplificator operațional care nu inversează, ceea ce vă va permite să obțineți o valoare de 1 MΩ sau mai mult. Op-amp-ul trebuie alimentat direct de la baterii cu o tensiune de 4,8 - 5,4 V.

Principiul de funcționare

Jumătate din textul programului este tot felul de inițializări. Principiul de funcționare al unui osciloscop digital este simplu și evident.

ADC efectuează o serie de măsurători secvențiale continue ale nivelului de semnal. Valorile rezultate sunt adăugate în memorie folosind DMA. De fiecare dată înregistrăm timpul și stabilim durata seriei de măsurători. Așa aflăm prețul împărțirii axei timpului.

Analizând valorile nivelului semnalului înregistrate, căutăm primul extremum, după care tragem semnalul pe ecran. Deci încercăm să facem un fel de sincronizare. Funcționează bine pe semnale netede și este practic inutil pe cele de bandă largă.

Lăsăm utilizatorul să se bucure de imagine timp de o secundă, în timp ce sondam noi înșine butonul. O apăsare scurtă a butonului comută intervalele într-un cerc. Apăsarea lungă modifică sensibilitatea.

STM32 pentru începători. Lecția 3. Cronometre STM32.

Apoi totul se repetă.

Folosesc CooCox CoIDE pentru compilare. Nu am postat aici proiectul Cox pentru că conține căi absolute la dosare. Este mai ușor să creați unul nou decât să editați toate căile. După crearea proiectului, nu uitați să conectați bibliotecile: RCC, GPIO, DMA, SPI, TIM, ADC.

L-am cusut folosind programatorul-depanator ST-Link V2. O poți face fără el, printr-un adaptor USB-Serial.

Materiale folosite:
Proiect al maestrului scoțian Pingumacpenguin
Adafruit Display
STM32 Stocarea datelor ADC folosind DMA

Textul programului

  • principal.c
  • lcd7735.c - Afișare și SPI. Inițializare și funcții.
  • delay.c - Contor: functii de initializare, pauza.
  • ADC.c - ADC și DMA.
  • font7x15.h - Font.

STM32 de la zero. Cronometre.

Microcontrolerele STM32 au mai multe temporizatoare care pot funcționa modularea lățimii impulsului. Toate cronometrele cu excepția cronometrelor de bază (TIM6 și TIM7) au această funcționalitate.

Asta e tot, acest lucru este suficient pentru [Microcontrolerele STM32 au mai multe temporizatoare care pot funcționa în modul de modulare a lățimii impulsului. Toate cronometrele cu excepția cronometrelor de bază (TIM6 și TIM7) au această funcționalitate.

Voi da un exemplu de utilizare a temporizatorului TIM3 în acest mod.

Gata, este suficient pentru](http://catethysis.ru/stm32-%e2%86%92-%d0%bf%d0%be%d1%80%d1%82%d1%8b-gpio / “ STM32 → porturi GPIO”) a apărut un semnal PWM. Cum se face asta?

Temporizatorul primește impulsuri de ceas de la magistrala APB, a cărei frecvență este jumătate din frecvența de bază (24 MHz în cazul nostru) (link la RCC) și trec prin prescaler, pe care îl setăm la 10 - adică. se dovedește a fi 1,2 MHz. Cronometrul este configurat pentru a număra 1000 de cicluri de ceas, după care ia o nouă valoare din registrul ARR, pe care nu o modificăm - adică. același 1000, aceasta este perioada semnalului PWM. La începutul ciclului, temporizatorul emite „1” la ieșire, iar după 200 de cicluri se resetează la „0” - acesta este ciclul de lucru PWM.

Ne-am uitat la lucrul cu operații pe biți și numere binare, punând astfel bazele pentru luarea în considerare a unui nou subiect. În această lecție vom lua în considerare o altă întrebare: ce sunt registrele și cum se lucrează cu ele?

Memorie și registre

Una dintre cele mai importante abilități necesare atunci când lucrați cu microcontrolere este capacitatea de a interacționa cu registrele. Să ne dăm seama singuri Ce este?

În general, un registru este un tip special de memorie în interiorul unui microcontroler care este folosit pentru a controla procesorul și dispozitivele periferice. Fiecare registru din arhitectura ARM este o celulă de memorie și are o lungime de 32 de biți, unde fiecare bit poate fi reprezentat ca un mic comutator care controlează unul sau altul parametru al microcontrolerului.

Fiecare dintre registre are propriul număr de serie - adresa. Adresa de registru este indicată printr-un număr de 32 de biți reprezentat în notație hexazecimală. Scriind la adresa de registru o anumită combinație de unu și zero, care sunt de obicei prezentate în formă hexazecimală, unul sau altul nod din MK este configurat și controlat. Amintiți-vă că într-un program pentru lucrul cu operații pe biți, am putea reprezenta o mulțime arbitrară de unu și zero ca număr hexazecimal. În general, este de remarcat faptul că există două tipuri de registre: registre de uz general și registre speciale. Primele sunt situate în interiorul nucleului MK, iar cele din urmă fac parte din memoria RAM.

De asemenea, este de remarcat faptul că Manual de referință, pe care am descărcat-o în prima lecție, este o carte mare de referință despre registrele conținute în microcontrolerul țintă, iar biblioteca CMSIS ne permite să operam cu nume de registre simbolice în loc de adrese numerice. De exemplu, la registru 0x40011018 ne putem referi pur și simplu folosind un nume simbolic GPIOC_BSSR. Ne vom uita la exemple specifice de configurare în timpul analizei programului nostru din .

Deci, de obicei, structura registrului este descrisă sub forma unui mic tabel care indică:

  1. Înregistrați numele și descrierile scopului său
  2. Înregistrați adrese sau deplasați față de adresa de bază
  3. Valori implicite după resetare
  4. Tip de acces la celulele de înregistrare (citire, scriere, citire/scriere)
  5. Valori și descrieri ale parametrilor biților care urmează să fie scrisi
Să ne uităm la un exemplu de lucru cu registre într-o situație specifică pentru a obține o înțelegere generală a principiilor de configurare a unui microcontroler.

Analiza codului de la prima lecție

Deci, să ne amintim problema pe care am rezolvat-o folosind un exemplu de cod gata făcut: trebuia să scriem un program care să asigure că două LED-uri de pe placa Discovery sunt aprinse alternativ (poate nu două, dacă aveți o versiune diferită a Discovery). bord) cu un interval de timp.

Să aruncăm o altă privire la codul de program pe care l-am folosit pentru a face să sară MK-ul nostru cu două picioare pe care sunt amplasate LED-urile noastre:

Cod principal.c

/* Fișier antet pentru familia noastră de microcontrolere */ #include "stm32f0xx.h" /* Corpul programului principal */ int main(void) ( /* Activați sincronizarea pe portul GPIO */ RCC->AHBENR |= RCC_AHBENR_GPIOCEN ; /* Configurați modul de operare al porturilor PC8 și PC9 în Output*/ GPIOC ->MODER = 0x50000; /* Setați tipul de ieșire la modul Push-Pull */ GPIOC->OTYPER = 0; /* Setați viteza portului la Low */ GPIOC->OSPEEDR = 0; while(1) ( /* Porniți LED-ul PC8, opriți PC9 */ GPIOC->ODR = 0x100; pentru (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = 0x200; pentru (int i=0; i<500000; i++){} // Искусственная задержка } }


În primul rând, atunci când lucrăm cu STM32, chiar și pentru o sarcină atât de simplă precum pornirea și oprirea unui LED, trebuie să răspundem mai întâi la o serie de întrebări:
  1. Cum să configuram pinii portului GPIO de care avem nevoie, astfel încât să putem aprinde LED-ul?
  2. Cum se aprinde și se stinge LED-ul?
Să le răspundem în ordine.

Unde sunt conectate LED-urile noastre? La ce pin al microcontrolerului?

Pentru a vedea unde este totul pe placa Discovery, și în special, LED-urile de care avem nevoie, trebuie să deschidem fișierul Schematic, fie cel pe care l-am descărcat de pe site-ul ST, fie direct de pe Keil:


Deschiderea Schemei vom vedea o diagramă a tot ceea ce este pe placă - o diagramă ST-Link, cablarea tuturor perifericelor și multe altele. În momentul de față suntem interesați de două LED-uri, căutăm denumirea lor:


După cum putem vedea, LED-urile noastre sunt conectate la portul GPIOC de pe pinii 8 și 9.

Cum se activează ceasul pe portul GPIO dorit?

În general, orice lucru cu periferice în microcontrolerele STM32 se reduce la o secvență standard de acțiuni:
  1. Activați sincronizarea modulului periferic corespunzător. Acest lucru se realizează prin registrul RCC prin furnizarea unui semnal de ceas direct de la magistrala pe care se află acest modul. În mod implicit, tactarea tuturor perifericelor este dezactivată pentru a minimiza consumul de energie.
  2. Configurare prin registre de control, prin modificarea parametrilor specifici unui anumit dispozitiv periferic
  3. Lansarea directă și utilizarea rezultatelor modulelor
Adică, pentru a începe, trebuie să începem să facem tac pe portul GPIOC. Acest lucru se face direct prin accesarea registrului RCC, care este responsabil cu tactarea totul și pornirea semnalului de ceas de la magistrala la care este conectat portul nostru GPIO.

Atenţie! Într-un articol separat vom analiza în detaliu întrebarea referitoare la sistemul de ceas, configurația și utilizarea acestuia.

Aflați la ce magistrală este conectat portul nostru GPIOC, puteți găsi în Fișa de date pentru MK-ul nostru, în secțiunea Maparea memoriei din Tabelul 16. Adresele limitelor registrului periferic STM32F051xx.


După cum probabil ați observat deja, anvelopa de care avem nevoie se numește AHB2. Pentru a ne familiariza mai bine cu registrul în care este activată ceasul pentru portul GPIO de care avem nevoie pe magistrala AHB, trebuie să mergem la secțiunea corespunzătoare din Manualul de referință. După numele registrelor îl putem determina pe cel de care avem nevoie:


Mergem în acest punct și vedem registrul nostru de 32 de biți, adresa lui offset, valoarea implicită, cum să accesăm registrul și o listă cu ceea ce este responsabil fiecare bit din registru.


Ne uităm la tabel și vedem ceva asemănător cu opțiunile pentru activarea tacării pe porturile GPIO. Să mergem la descriere și să găsim opțiunea de care avem nevoie:


În consecință, dacă setăm bitul 19 la „1”, aceasta va permite tactarea pe portul I/O C - adică pe GPIOC-ul nostru. În plus, trebuie să pornim un bit din grup separat, fără a afecta restul deoarece nu trebuie să interferăm sau să modificăm alte setări în mod inutil.

Pe baza materialelor din lecția anterioară, știm că, pentru a seta un anumit bit, trebuie să folosim operația logică „SAU” pentru a adăuga valoarea registrului curent cu o mască care conține biții care trebuie activați. De exemplu, să adăugăm valoarea implicită a registrului RCC->AHBENR, adică. 0x14 și numărul 0x80000 vor activa astfel tactarea GPIOC prin setarea bitului 19:

Cum putem face asta dintr-un program? Este destul de simplu. În acest caz avem două opțiuni:

  1. Scrierea la un registru direct a valorii numerice a unui registru direct prin adresa acestuia.
  2. Configurare folosind biblioteca CMSIS
Nu există probleme speciale cu scrierea unei valori într-un registru direct, dar există câteva dezavantaje semnificative. În primul rând, un astfel de cod devine imposibil de citit și, în al doilea rând, nu putem determina imediat la ce registru se referă o anumită adresă din memorie.

Adică, am putea accesa adresele de registru direct după adresă și să scriem asta:

IO uint32_t * adresă_registru = (uint32_t *) 0x40021014U; // Adresa registrului nostru în memorie *(__IO uint32_t *)adresa_registrului |= 0x80000; // Activați 19 biți cu parametrul nostru
A doua varianta mi se pare cea mai atractiva, pentru ca... Biblioteca CMSIS este organizată în așa fel încât un registru să poată fi accesat folosind doar numele acestuia. Preprocesorul, atunci când procesează textul programului înainte de compilare, va înlocui automat toate valorile digitale ale adresei de registru. Să ne uităm puțin mai detaliat la această întrebare.

Vă sugerez să deschidem proiectul nostru, pe care l-am făcut în prima lecție, sau să descărcați unul pregătit în prealabil de aici și să ștergeți tot conținutul programului, lăsând doar fișierul antet inclus, funcția main() și instrucțiunile de activare a tacării ( vom avea nevoie de el pentru o analiză detaliată a codului).

Codul nostru va arăta astfel:

/* Fișier antet pentru familia noastră de microcontrolere */ #include "stm32f0xx.h" /* Corpul programului principal */ int main(void) ( /* Activați sincronizarea pe portul GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; )
Să pătrundem adânc în biblioteca CMSIS pentru a ne cunoaște.

Pentru a ajunge rapid la locul unde este declarată una sau alta constantă sau variabilă, Keil implementează o funcție convenabilă. Facem clic dreapta pe constanta de care avem nevoie, de exemplu, pe RCC:


Și suntem transportați în adâncurile bibliotecii CMSIS, în care vom vedea că toate registrele disponibile pentru control programatic au forma structurilor TypeDef, inclusiv RCC-ul nostru:


După ce a eșuat în acest fel în RCC_TypeDef, vom vedea o structură care descrie toate câmpurile registrului nostru:


În consecință, putem accesa în siguranță registrul de care avem nevoie cu o intrare similară PERIPH_MODULE->REGISTERși atribuiți-i o anumită valoare.

Pe lângă desemnarea mnemonică a registrelor, există și desemnări pentru biți specifici. Dacă nu reușim să declarăm parametrul RCC_AHBENR_GPIOCEN din programul nostru, vom vedea și declarația tuturor parametrilor:


Astfel, folosind biblioteca CMSIS, obținem o intrare concisă, lizibilă, a parametrului de care avem nevoie într-un registru, prin a cărui setare începem să tacăm pe portul de care avem nevoie:

/* Activează ceasul pe portul GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN;
Ca sarcină: Determinați, folosind capabilitățile lui Keil, modul în care adresa registrului RCC->AHBENR s-a dovedit a fi 0x40021014.

Cum să configuram pinii GPIO de care avem nevoie, astfel încât să putem porni LED-ul?

Deci, știm că LED-urile de care avem nevoie sunt conectate la portul GPIOC la pinii PC8 și PC9. Trebuie să le setăm într-un astfel de mod încât LED-ul să se aprindă. Aș dori să fac imediat o rezervare că ne vom uita la porturile GPIO mai detaliat într-un alt articol și aici ne vom concentra în mod special pe lucrul cu registrele.

În primul rând, trebuie să comutăm modul de funcționare al pinii PC8 și PC9 în modul Ieșire. Restul parametrilor portului pot fi lăsați implicit. Accesați secțiunea Manual de referință 9. I/O de uz general (GPIO) și deschideți elementul responsabil pentru modul de funcționare al pinii portului GPIO și vedeți că registrul MODER este responsabil pentru acest parametru:


Judecând după descriere, pentru a seta pinii PC8 și PC9 în modul Ieșire, trebuie să scriem 01 în câmpurile corespunzătoare ale registrului GPIOC.

Acest lucru se poate face prin instalare directă folosind valori numerice:


Sau folosind definiții din bibliotecă:

/* Activează ceasul pe portul GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0;
După această instrucțiune, pinii noștri PC8 și PC9 vor comuta în modul Ieșire.

Cum se aprinde LED-ul?

Dacă acordăm atenție listei de registre disponibile pentru controlul portului GPIO, putem vedea registrul ODR:


Fiecare dintre biții corespunzători este responsabil pentru unul dintre pinii portului. Puteți vedea mai jos structura acestuia:


Pentru a asigura o schimbare alternativă a stărilor LED-urilor, este necesar să porniți/opriți biții 8 și 9 cu un anumit interval de timp. Adică, atribuiți alternativ registrului valoarea 0x100 și 0x200.

Putem face acest lucru atribuind direct valori registrului:

GPIOC->ODR = 0x100; // Aprindeți PC8, opriți PC9 GPIOC->ODR = 0x200; // Aprindeți PC9, opriți PC8
Putem folosi definiții din bibliotecă:

GPIOC->ODR = GPIO_ODR_8; // Porniți PC8, opriți PC9 GPIOC->ODR = GPIO_ODR_9; // Aprindeți PC9, opriți PC8
Dar, deoarece microcontrolerul funcționează foarte repede, nu vom observa modificări ale stărilor LED-urilor și vizual se va părea că ambele sunt aprinse constant. Pentru ca ei să clipească efectiv alternativ, vom introduce o întârziere artificială sub forma unui ciclu care va ocupa MK-ul cu calcule inutile de ceva timp. Veți obține următorul cod:

/* Porniți LED-ul PC8, opriți PC9 */ GPIOC->ODR = GPIO_ODR_8; pentru (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = GPIO_ODR_9; pentru (int i=0; i<500000; i++){} // Искусственная задержка
Aici putem termina cunoștințele noastre inițiale cu registrele și metodele de lucru cu acestea.

Verificarea rezultatelor codului nostru

O mică adăugare drăguță la sfârșitul articolului: Keil are un instrument excelent de depanare cu care putem parcurge programul nostru și să vedem starea curentă a oricărei unități periferice. Pentru a face acest lucru, după descărcarea firmware-ului după compilare, putem face clic pe butonul Start Debug Session:

Porturile de intrare/ieșire GPIO din STM32 au 16 linii, fiecare dintre acestea putând fi configurată după cum este necesar. Suportă intrare digitală, ieșire digitală, intrare externă de întrerupere și funcții I/O ale altor module de microcontroler. Programarea STM32 pentru a funcționa cu GPIO se bazează pe utilizarea registrelor de configurare, citire, scriere, protecție de configurare și acces la biți.

Registre de configurare porturi.

Registrul de configurare porturi scăzut (GPIOx_CRL) (x=A..G)

Registrul de configurare a porturilor este ridicat (GPIOx_CRH) (x=A..G)

Pentru a programa modurile de operare ale porturilor I/O STM32, se folosesc două registre de 32 de biți pentru fiecare GPIO. Ele vă permit să configurați în mod arbitrar modul de operare al oricărei linii individuale. Registrul GPIOx_CRL este responsabil pentru liniile cu numere de la 0 la 7, GPIOx_CRH - pentru liniile 8-15. Pentru fiecare dintre ele, registrul are două câmpuri de doi biți CNFy și MODEy. Primul determină tipul de operare a liniei, al doilea - direcția de schimb de-a lungul liniei. toți biții sunt de citire/scriere.

Înregistrați GPIOx_CRL

PicInregistreaza-te

Camp

linie I/O

Bit de înregistrare

Camp

linie de intrare/ieșire

Registrul GPIOX_CRH

Bit de înregistrare

Camp

linie I/O

Bit de înregistrare

Camp

linie I/O

Câmpul MODEy poate lua următoarele valori:

  • 00 – linia este folosită pentru intrare. Această stare este setată după resetare.
  • 01 – linia funcționează ca ieșire, cu o frecvență de comutare maximă de 10 MHz
  • 10 – linia funcționează ca ieșire, cu o frecvență de comutare maximă de 20 MHz
  • 11 – linia funcționează ca ieșire, cu o frecvență de comutare maximă de 50 MHz

Câmpul CNFy depinde de direcția de transmisie. Când funcționează ca intrare (MODEy=0), sunt disponibile următoarele stări:

  • 00 – intrare analogică.
  • 01 – intrare în stare a treia. (Setat după resetare).
  • 10 – intrare cu rezistență de tragere
  • 11 – rezervat pentru utilizare ulterioară.

Când funcționează ca ieșire (MODEy>0), câmpul CNFy poate avea următoarele stări:

  • 00 – ieșire digitală
  • 01 – ieșire digitală cu scurgere deschisă
  • 10 – ieșire digitală conectată la unități specializate
  • 11 – ieșire digitală conectată la unități specializate de scurgere deschisă

Registrul de protecție a setărilor

Registrul de blocare a configurației portului (GPIOx_LCKR) (x=A..G)

Camp

Camp

Setați bitul blocabil în GPIOx_LCKR Pentru a face imposibilă modificarea setărilor portului în microcontrolerele STM32, este utilizat registrul GPIOx_LCKR. Cei 15 biți scăzuti sunt responsabili pentru liniile corespunzătoare ale portului I/O. Bit 16 setat la 1 permite dezactivarea modificărilor setărilor. toți biții sunt de citire/scriere. Pentru a face viața mai dificilă utilizatorilor ;-) , se folosește un algoritm special pentru instalarea protecției. Dacă se aplică, următoarea modificare a configurației este disponibilă numai după o resetare. Algoritmul pentru instalarea protecției este următorul:

  1. Setați bitul 16 GPIOx_LCKR.
  2. Resetați bitul 16 GPIOx_LCKR.
  3. Setați bitul 16 GPIOx_LCKR.
  4. Citiți GPIOx_LCKR
  5. Recitiți GPIOx_LCKR

Registre de stare de linie

Spre deosebire de modelele obișnuite pe 8 biți, STM32 are mai multe registre responsabile pentru starea liniilor portului I/O. În mod convențional, acestea sunt împărțite în două grupuri - registre de porturi și registre pentru setarea biților individuali.

Registrul de ieșire a portului I/O

Registrul de date de ieșire a portului (GPIOx_ODR) (x=A..G)

Camp

Camp

Acest registru are o lățime de 32, dar sunt utilizați doar cei 16 biți inferiori. Biții de la 16 la 31 nu sunt utilizați. Când o valoare este scrisă în GPIOx_ODR, această valoare este setată pe liniile de ieșire ale portului corespunzător. Biții de registru sunt doar pentru citire/scriere.

Registrul de intrare

Registrul de date de intrare de port (GPIOx_IDR) (x=A..G)

Pic 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
Camp rezervă
Pic 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1
Camp IDR15 IDR14 IDR13 IDR12 IDR11 IDR10 IDR9 IDR8 IDR7 IDR6 IDR5 IDR4 IDR3 IDR2 IDR1 IDR0

Similar cu registrul de ieșire, registrul de intrare are doar 16 biți efectivi scăzuti din 32. Citirea GPIOx_IDR returnează valoarea de stare a tuturor liniilor de pe port. Biții de înregistrare sunt doar pentru citire.

Registrul de operațiuni cu biți

Setarea/resetarea registrului de biți de port (GPIOx_BSRR) (x=A..G)

Pic 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
Camp BR15 BR14 BR13 BR12 BR11 BR10 BR9 BR8 BR7 BR6 BR5 BR4 BR3 BR2 BR1 BR0
Pic 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1
Camp BS15 BS14 BS13 BS12 BS11 BS10 BS9 BS8 BS7 BS6 BS5 BS4 BS3 BS2 BS1 BS0

Acest registru vă permite să accesați o linie specifică de intrare/ieșire a microcontrolerului STM32. Scrierea unuia la unul dintre biții de ordin înalt resetează ieșirea liniei, iar scrierea unuia la biții de ordin inferior setează nivelul semnalului la înalt pe linia corespunzătoare. Registrul este scris în format word și zero biți nu au niciun efect. Biții de registru sunt doar pentru scriere.

Resetați registrul

Registrul de resetare a bitului de port (GPIOx_BRR) (x=A..G)

Pic 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
Camp rezervă
Pic 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1
Camp BR15 BR14 BR13 BR12 BR11 BR10 BR9 BR8 BR7 BR6 BR5 BR4 BR3 BR2 BR1 BR0

Acest registru resetează nivelul înalt al liniei setate în registrul GPIOx_ODR. Sunt utilizați doar cei 16 biți inferiori, care sunt doar pentru scriere.

Cele mai bune articole pe această temă