Contacte

Temporizatoarele de bază în STM32. Lucrul cu temporizatoare simple STM32 F4 descoperire întrerupe cronometrul Stm32

Articolul descrie cronometrele microcontrolerelor ARM pe 32 de biți din seria STM32 de la STMicroelectronics. Sunt luate în considerare arhitectura și compoziția registrelor de bază ale temporizatorului și sunt date exemple practice de programe.

Pentru orice microcontroler, cronometrul este una dintre cele mai importante componente, care vă permite să numărați foarte precis intervalele de timp, să numărați impulsurile care sosesc la intrări, să generați întreruperi interne, să generați semnale cu modulație de lățime a impulsurilor (PWM) și să suportați accesul direct la memorie ( procesele DMA).

Microcontrolerul STM32 conține mai multe tipuri de temporizatoare care diferă unele de altele în funcție de funcționalitate. Primul tip de cronometre este cel mai simplu și este Basic Timer. Temporizatoarele TIM6 și TIM7 aparțin acestui tip. Aceste cronometre sunt foarte ușor de configurat și controlat folosind un minim de registre. Ele sunt capabile să numere intervale de timp și să genereze întreruperi atunci când temporizatorul atinge o valoare specificată.
Al doilea tip este temporizatoarele de uz general. Aceasta include temporizatoarele TIM2 la TIM5 și temporizatoarele TIM12 la TIM17. Pot genera PWM, număra impulsurile care ajung la anumiți pini ai microcontrolerului, pot procesa semnale de la encoder etc.

Al treilea tip definește cronometre cu control avansat (Advanced-Control Timer). Acest tip include cronometrul TIM1, care este capabil să efectueze toate operațiunile de mai sus. În plus, pe baza acestui cronometru, puteți construi un dispozitiv capabil să controleze o unitate electrică trifazată.

Dispozitiv cronometru de bază

Să luăm în considerare proiectarea și funcționarea unui temporizator de bază, a cărui diagramă bloc este prezentată în figură. Temporizatorul de bază este construit pe baza registrelor de 16 biți. Baza acestuia este registrul de numărare TIMx_CNT. (În continuare, simbolul „x” înlocuiește numărul 6 sau 7 pentru cronometrele de bază TIM6 și, respectiv, TIM7.) Prescalerul TIMx_PSC vă permite să reglați frecvența ceasului pentru registrul de contor, iar registrul de încărcare automată TIMx_ARR face posibilă setarea intervalul de numărare a temporizatorului. Controlerul de declanșare și sincronizare, împreună cu registrele de control și stare, servesc la organizarea modului de funcționare al temporizatorului și vă permit să controlați funcționarea acestuia.

Datorită organizării sale, contorul cronometrului poate număra înainte și înapoi, precum și până la mijlocul unui interval dat în direcția înainte și apoi în direcția inversă. Intrarea de bază a temporizatorului poate fi furnizată din mai multe surse, inclusiv semnalul de ceas de la magistrala APB1, un semnal extern sau ieșirea altor temporizatoare aplicate pinii de captare și comparare. Temporizatoarele TIM6 și TIM7 sunt tactate din magistrala APB1. Dacă utilizați un cristal de 8 MHz și setări implicite de ceas din fabrică, frecvența de ceas de la magistrala de ceas APB1 va fi de 24 MHz.

Registrele de bază ale temporizatorului

Tabelul prezintă harta registrului pentru cronometrele de bază TIM6 și TIM7. Temporizatoarele de bază includ următoarele 8 registre:

●● TIMx_CNT – Contor (registru de numărare);
●● TIMx_PSC – Prescaler (prescaler);
●● TIMx_ARR – Auto Reload Register;
●● TIMx_CR1 – Registrul de control 1 (registrul de control 1);
●● TIMx_CR2 – Registrul de control 2 (registrul de control 2);
●● TIMx_DIER – Registrul de activare a întreruperii DMA (registru de activare DAP și întrerupere);
●● TIMx_SR – Registrul de stare;
●● TIMx_EGR – Registrul de generare a evenimentelor.

Registrele TIMx_CNT, TIMx_PSC și TIMx_ARR folosesc 16 biți de informații și vă permit să scrieți valori de la 0 la 65535. Frecvența impulsurilor de ceas pentru registrul de contor TIMx_CNT, care trece prin divizorul TIMx_PSC, se calculează folosind formula: Fcnt = Fin /(PSC + 1), unde Fcnt este frecvența pulsului registrului contorului cronometrului; Fin – frecvența ceasului; PSC – conținutul registrului timer TIMx_PSC, care determină coeficientul de divizare. Dacă scrieți valoarea 23999 în registrul TIMx_PSC, atunci registrul contor TIMx_CNT la o frecvență de ceas de 24 MHz își va schimba valoarea de 1000 de ori pe secundă. Registrul de încărcare automată stochează valoarea pentru încărcarea registrului de contor TIMx_CNT. Conținutul registrului TIMx_CNT este actualizat după ce acesta este depășit sau resetat, în funcție de direcția de numărare specificată pentru acesta. Registrul de control TIMх_CR1 are mai mulți biți de control. Bitul ARPE activează și dezactivează tamponarea scrierilor în registrul de încărcare automată TIMx_ARR. Dacă acest bit este zero, atunci când o nouă valoare este scrisă în TIMx_ARR, aceasta va fi încărcată imediat în el. Dacă bitul ARPE este egal cu unu, atunci încărcarea în registru va avea loc după evenimentul în care registrul de numărare atinge valoarea limită. Descărcarea OPM activează modul „impuls unic”. Dacă este setat, după depășirea registrului contor, numărarea se oprește și bitul CEN este resetat. Bitul UDIS activează sau dezactivează generarea unui eveniment cronometru. Daca este sters, evenimentul va fi generat atunci cand apare conditia de generare a evenimentului, adica cand timer-ul depaseste sau cand bitul UG este programat in registrul TIMx_EGR. Bitul CEN pornește și oprește temporizatorul. Dacă resetați acest bit, numărarea va fi oprită, iar când este setată, numărarea va continua. Divizorul de intrare va începe să numere de la zero. Registrul de control TIMx_CR2 are trei biți de control MMS2... MMS0, care determină modul master pentru temporizator. Registrul TIMx_DIER folosește doi biți. Bitul UDE permite sau dezactivează emiterea unei cereri DMA atunci când are loc un eveniment. Bitul UIE activează și dezactivează întreruperile temporizatorului. Registrul TIMx_SR folosește doar un bit UIF ca flag de întrerupere. Este instalat de hardware atunci când are loc un eveniment de cronometru. Trebuie să-l resetați programatic. Registrul TIMx_EGR conține un bit UG, care vă permite să generați în mod programatic evenimentul „depășire a registrului de numărare”. Când acest bit este setat, este generat un eveniment și registrul de numărare și prescaler sunt resetate. Acest bit este resetat de hardware. Datorită acestui bit, puteți genera programatic un eveniment dintr-un cronometru și, prin urmare, puteți apela cu forță funcția de gestionare a întreruperii cronometrului.

Să ne uităm la scopul registrelor de control și al stării temporizatorului folosind exemple de programe specifice.

Exemple de programe

Pentru a porni un cronometru, trebuie efectuate mai multe operații, cum ar fi sincronizarea temporizatorului și inițializarea registrelor acestuia. Să ne uităm la aceste operațiuni bazate pe exemple de programe pentru lucrul cu cronometre. Destul de des în procesul de programare apare sarcina de implementare a întârzierilor. Pentru a rezolva această problemă, este necesară o funcție de generare a întârzierii. Un exemplu de astfel de funcție bazată pe cronometrul de bază TIM7 pentru STM32 este prezentat în Lista 1.

Listarea 1

#define FAPB1 24000000 // Frecvența ceasului magistralei APB1 // Funcția de întârziere în milisecunde și microsecunde void delay(unsigned char t, unsigned int n)( // Încărcați registrul prescaler PSC If(t = = 0) TIM7->PSC = FAPB1 /1000000-1 // pentru numărarea microsecundei If(t = = 1) TIM7->PSC = FAPB1/1000-1 // pentru numărarea milisecundelor TIM7->ARR = n; registrați ARR TIM7 ->EGR |= TIM_EGR_UG // Generați un eveniment de actualizare // pentru a scrie date în registrele PSC și ARR TIM7->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM // Pornește cronometrul // prin scrierea bitului de activare a numărului; CEN // și bitul de mod trece OPM la registrul de control CR1 în timp ce (TIM7->CR1&TIM_CR1_CEN != 0 // Se așteaptă terminarea numărării)

Această funcție poate genera întârzieri în microsecunde sau milisecunde în funcție de parametrul „t”. Durata întârzierii este setată de parametrul „n”. Acest program folosește modul de trecere unică a temporizatorului TIM7, în care registrul contorului CNT numără până la valoarea de depășire înregistrată în registrul ARR. Când aceste valori sunt egale, cronometrul se va opri. Faptul că temporizatorul sa oprit este de așteptat în bucla while prin verificarea bitului CEN al registrului de stare CR1. Temporizatoarele este activată o dată în modulul principal al programului în timpul inițializării acestora. Temporizatoarele de bază sunt conectate la magistrala APB1, astfel încât alimentarea ceasului arată astfel:

RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // Activează ceasul pe TIM6 RCC->APB1ENR |= RCC_APB1ENR_ TIM7EN; // Activează ceasul pe TIM7

Metoda software pentru generarea unei întârzieri descrisă mai sus are un dezavantaj semnificativ din cauza faptului că procesorul este forțat să interogheze steag-ul pe tot timpul de întârziere și, prin urmare, nu este capabil să efectueze alte sarcini în acest timp. Acest dezavantaj poate fi eliminat prin utilizarea modului de întrerupere a temporizatorului. Funcțiile de gestionare a întreruperilor pentru temporizatoarele de bază arată de obicei astfel:

Void TIM7_IRQHandler())( TIM7->SR = ~TIM_SR_UIF; // Ștergeți steagul //Efectuați operațiuni ) void TIM6_DAC_IRQHandler())( //Dacă evenimentul este de la TIM6 if(TIM6->SR & TIM_SR_UIF)( TIM6- >SR =~ TIM_SR_UIF // Șterge steag // Efectuează operațiuni )

Să luăm în considerare un exemplu de program pentru organizarea unei întârzieri pe un cronometru TIM6 de bază, care utilizează întreruperi de la un cronometru. Pentru a controla execuția programului, folosim unul dintre pinii microcontrolerului pentru a controla indicatoarele LED, care vor trebui să comute la intervale determinate de întârzierea programului organizată pe cronometrul TIM6. Un exemplu de astfel de program este prezentat în Lista 2.

Lista 2

// Inclusiv bibliotecile #include #include #include #include #include // Atribuții de pin pentru enumerarea indicatorilor LED ( LED1 = GPIO_Pin_8, LED2 = GPIO_Pin_9 ); // Funcția de inițializare a porturilor de control LED void init_leds() ( RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_Mode = GPIO_PPIO_;GPIO_Mode_; PIOC, &gpio); /TIM6 funcția de inițializare a temporizatorului void init_timer_TIM6() ( // Activați cronometrul RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef base_timer; TIM_TimeBaseStructInit);( //Set3_Timer_base_timer9 r = 24000 - 1 // Setați period to 500 ms base_timer.TIM_Period = 500 TIM_TimeBaseInit(TIM6, &base_timer) // Activare timer overflow interrupt TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE //Activare timer overflow processing(NQ_M_IR); Funcția de gestionare a întreruperilor temporizatorului void TIM6_DAC_IRQHandler())( // Dacă apare o întrerupere din cauza depășirii contorului temporizatorului TIM6 dacă (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( //Resetează bitul întreruperii procesate TIM_BitPending(TPIM6, TIM_IT_Update) , TIM_IT_Update); //Inversează starea indicatorilor LED GPIO_Write(GPIOC, GPIO_ReadOutputData(GPIOC) ^ (LED1 | LED2)); ) ) // Modulul principal al programului int main() ( init_leds(); GPIO_SetBits(GPIOC, LED1); GPIO_ResetBits(GPIOC, LED2); init_timer_TIM6(); while (1) ( // Loc pentru alte comenzi ) )

În acest program, funcția de întârziere este apelată o dată, după care procesorul poate efectua alte operațiuni, iar temporizatorul va genera în mod regulat întreruperi la intervalul de întârziere specificat. Un program similar poate fi scris pentru cronometrul TIM7. Diferența dintre un astfel de program va fi în numele registrelor și numele operatorului de întrerupere. Handler-ul de întrerupere a temporizatorului TIM6 are o caracteristică legată de faptul că vectorul de procesare a întreruperilor pentru acest temporizator este combinat cu o întrerupere de la un convertor digital-analogic (DAC). Prin urmare, funcția de gestionare a întreruperii verifică sursa întreruperii. Puteți afla mai multe despre temporizatoarele microcontrolerului STM32 pe site-ul St.com. Există multe alte sarcini pentru cronometru, descrise mai sus, pe care le poate rezolva cu succes. Prin urmare, utilizarea sa într-un program ușurează semnificativ sarcina procesorului și face programul mai eficient.

Orice controler modern are cronometre. Acest articol va vorbi despre temporizatoare simple (de bază) descoperire stm32f4.
Acestea sunt cronometre obișnuite. Sunt pe 16 biți cu repornire automată. În plus, există un programabil pe 16 biți divizor de frecvență. Este posibil să se genereze întreruperea contorului de preaplinși/sau cerere DMA.

Să începem. Ca și înainte, folosesc Eclipse + st-util în ubuntu linux

În primul rând, conectăm anteturile:

#include #include #include #include #include

Nu este nimic nou în asta. Dacă nu este clar de unde provin, fie citiți articolele anterioare, fie deschideți fișierul și citiți.

Să definim două constante. Una pentru a desemna diode, cealaltă o matrice de aceleași diode:

Const uint16_t LEDS = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; // toate diodele const uint16_t LED = (GPIO_Pin_12, GPIO_Pin_13, GPIO_Pin_14, GPIO_Pin_15); // matrice cu diode

Cel mai probabil, sunteți deja familiarizat cu funcția de inițializare a perifericelor (adică diode):

Void init_leds())( RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // activați tactarea GPIO_InitTypeDef gpio; // structura GPIO_StructInit(&gpio); // umpleți cu valori standard gpio.GPIO_OTy-up-up_oType_; .GPIO_Mode = GPIO_Mode_OUT // funcționează ca ieșire gpio.GPIO_Pin = LED-uri // toți pinii diodei GPIO_Init(GPIOD, &gpio);

Funcția de inițializare a temporizatorului:

Void init_timer())( RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // activați cronometrul /* Alți parametri ai structurii TIM_TimeBaseInitTypeDef * nu au sens pentru temporizatoarele de bază. * Împărțitorul este luat în considerare ca TIM_Pre scaler + 1, deci scade 1 */ base_timer.TIM_Prescaler = 24000 - 1 // divizor 24000 base_timer.TIM_Period = 1000 //perioada de 1000 de impulsuri TIM_TimeBaseInit actualizare (în acest caz - * pentru overflow) TIM6 timer counter */ TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE (TIM6, ENABLE) // Activează procesarea contorului TIM6 EnableIRQ (TIM6_DAC_IRQn);

Am comentat codul, așa că cred că totul este clar.
Parametrii cheie aici sunt divizorul (TIM_Prescaler) și perioada (TIM_Period) a cronometrului. Aceștia sunt parametrii care configurează de fapt funcționarea temporizatorului.

De exemplu, dacă aveți o frecvență de ceas setată la 48 MHz pe STM32F4 DISCOVERY, atunci frecvența cronometrelor de uz general este de 24 MHz. Dacă setați divizorul (TIM_Prescaler) la 24000 (frecvența de numărare = 24MHz/24000 = 1KHz) și perioada (TIM_Period) la 1000, atunci cronometrul va număra intervalul în 1s.

Vă rugăm să rețineți că totul depinde de viteza ceasului. Trebuie să afli exact.

De asemenea, observ că la frecvențe înalte, comutarea LED-ului prin întrerupere distorsionează semnificativ valoarea frecvenței. Cu o valoare de 1 MHz la ieșire am primit aproximativ 250 KHz, adică. diferenta este inacceptabila. Acest rezultat aparent este obținut datorită timpului petrecut cu executarea întreruperii.

Variabilă globală - steag diodă aprinsă:

steagul U16 = 0;

Manager de întreruperi care generează cronometrul. Deoarece Aceeași întrerupere este generată când DAC-ul rulează, mai întâi verificăm dacă a fost declanșat de temporizator:

Void TIM6_DAC_IRQHandler())( /* Deoarece acest handler este apelat și pentru DAC, este necesar să verificați * dacă a avut loc întreruperea de depășire a contorului cronometrului TIM6. */ if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( flag++; if (flag> 3) flag = 0 /* Șterge bitul de întrerupere în curs de procesare */ TIM_ClearITPendingBit(TIM6, TIM_IT_Update(GPIOD, LED);

functie principala:

Int main())( init_leds(); init_timer(); do ( ) while(1); )

Lăsăm ciclul gol. Contorul își desfășoară activitatea în mod asincron, iar întreruperea este o întrerupere, pentru a nu depinde de operația în curs de executare.

Bună ziua. Astăzi voi scrie primul articol despre cronometre în STM32. În general, cronometrele din STM32 sunt atât de mișto, încât chiar și Schwarzenegger fumează nervos din cauza răcoarei))) Și va trebui să le studiezi în mai mult de unul, două sau trei articole. Dar mai întâi, să nu ne deranjam prea mult, ci pur și simplu să studiem primele cronometre simple și să lucrăm cu ele.

În STM32 există în general trei tipuri de temporizatoare
1) cronometre de bază
2) temporizatoare de uz general
3) avansat (temporizatoare cu control avansat)

Temporizatoarele avansate sunt cele mai tari și combină capacitățile celor două grupuri anterioare, plus multe funcții suplimentare, cum ar fi lucrul cu motoare trifazate etc. și așa mai departe. Suntem încă departe de ei, așa că în această parte vom lua în considerare lucrul cu cronometre de bază.
Mai întâi, să vedem ce temporizatoare sunt pe procesorul nostru STM32F407VG (te uiți la procesoarele cu care lucrezi)). Procesorul meu are 14 temporizatoare - 12 - 16 biți și 2 pe 32 de biți

După cum vedem în imagini, cronometrele TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM12 sunt conectate la magistrala ARV1
Și la autobuzul ARV2 - TIM1, TIM8, TIM9, TIM10, TIM11
Acum să ne uităm la imaginea de configurare a tactului nostru în programul CubeMX. Voi descrie, de asemenea, sistemul de ceas separat, deoarece nu pot trăi fără el, dar deocamdată voi arăta doar cum ne putem cronometra cronometrele folosind sursa internă de ceas HSI.
Iată setarea noastră standard de ceas fără multiplicatori de frecvență etc. Acesta este ceea ce vom folosi.

Și iată o opțiune de a grăbi munca)) Dar vă sfătuiesc să nu urci prea mult cu mâinile tale jucăușe, altfel poate pune procesorul pe omoplați)) Vom studia și vom lua în considerare toate acestea mai târziu.

Deci, deschidem manualul de referință pe seria F4 de microcontrolere și începem să fumăm manualul. DA, în STM32 nu totul este atât de simplu, așa că tovarăși, învățați engleza și citiți manualele, pentru că fără aceasta veți petrece mult timp căutând ce este. Mi-a fost foarte greu să citesc documentația (se pare că sarcinile erau simple și aveam destule exemplele obișnuite de pe Internet). Ei bine, acum citim... citeste... citeste...
Hai sa continuăm...
Deci cronometrele 6 și 7 sunt cronometre de bază. Ei stau pe autobuzul ARV1, așa cum vedem în imaginea din manualul de referință.

Temporizatoarele de bază 6 și 7 sunt pe 16 biți, au un prescaler reglabil de la 0 la 65535. Pentru aceste temporizatoare există aceste registre disponibile pentru citire/scriere.
Registrul de contor (TIMx_CNT) - contor
Prescaler Register (TIMx_PSC) - prescaler
Registru de reîncărcare automată (TIMx_ARR) - reîncărcare registru

Nu vom aprofunda prea mult în detaliile lucrării, deoarece sunt disponibile 10 pagini de descrieri ale registrelor etc., cele trei scrise mai sus ne vor fi suficiente.
Deci, ce sunt aceste registre și de ce avem nevoie de ele? Da, de aceea. Am decis să clipim de urgență un LED, să-i surprindem pe colegii noștri AVR-eri, de exemplu, și spunem - oricine poate configura rapid clipirea unui LED cu o perioadă de jumătate de secundă și al doilea cu o perioadă de o secundă, învinge. (apropo, puteți face un experiment similar))))
Pentru a implementa acest lucru avem nevoie de doar 5 pași - 1
1) Lansați CubeMX și creați un proiect pentru controlerul nostru.
2) în CubeMX setați funcționarea cronometrelor
3) generați un proiect și deschideți-l în Keil uVision
4) inițializați temporizatoarele (o linie pe cronometru)
5) scrieți în întreruperea fiecărui temporizator codul pentru schimbarea constantă a stării piciorului la care este conectat LED-ul.
Așa că haideți să ne uităm la asta mai detaliat. În primul rând, să lansăm programul nostru CubeMX
și configurați cei 2 pini ai noștri PD12 și PD13 la ieșire (picioarele unde sunt conectate LED-urile). Am setat modul GPIO_Output pentru ei și modul Output Push_Pull.
În continuare, în stânga, activăm cronometrele noastre de bază 6 și 7.

Acum accesați fila de configurare. După cum ne amintim, nu am schimbat nimic în setările de frecvență pentru procesorul nostru, așa că avem toate magistralele tactate la -16 MHz. Acum, pe baza acestui lucru și pe baza a ceea ce trebuie să obținem, să ne configuram valorile pentru prescalers și registrul de reîncărcare automată.

După cum ne amintim, avem nevoie de un LED care să clipească cu o frecvență de 1 Hz (perioada 1000 ms), iar al doilea cu o frecvență de 2 Hz (perioada de 500 ms). Cum obținem asta este foarte simplu. Deoarece orice prescaler poate fi instalat pe STM32, vom calcula pur și simplu valoarea acestuia
Deci frecvența noastră este de 16.000.000 de tick-uri pe secundă, dar avem nevoie de 1000 de tick-uri pe secundă. Aceasta înseamnă 16.000.000 \ 1.000 = 16.000 Introducem acest număr minus 1 în valoarea prescalerului. Adică, numărul pe care îl obținem este 15999.
Acum cronometrul nostru bifează de 1000 de ori pe secundă. În continuare, trebuie să indicăm când avem nevoie de o întrerupere de overflow. Pentru a face acest lucru, scriem numărul de care avem nevoie în Counter Period (registru de încărcare automată).
Adică, trebuie să primim o întrerupere pe secundă și, după cum ne amintim, cronometrul nostru bifează 1 dată pe milisecundă. Sunt 1000 ms într-o secundă, așa că introducem această valoare în registrul de repornire automată.
Pentru a obține o întrerupere la fiecare jumătate de secundă, scriem în consecință - 500.

Deci, l-am configurat, acum ne putem genera proiectul în siguranță. Generat, bun. A mai rămas puțin timp până când LED-urile clipesc.
Ne-am deschis proiectul. În principiu, totul este setat și gata pentru noi, trebuie doar să ne pornim cronometrele, deoarece, deși CubeMX face totul pentru noi, nu mai face asta. Deci, să inițializam
cronometrele noastre sunt cu aceste linii

HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);

Aici se află gestionatorii noștri de întrerupere pentru cronometrele noastre.
Aici este gestionarea întreruperilor pentru temporizatorul 7

void TIM7_IRQHandler(void)
{
/* COD UTILIZATOR ÎNCEPE TIM7_IRQn 0 */

/* COD UTILIZATOR END TIM7_IRQn 0 */
HAL_TIM_IRQHandler(&htim7);
/* COD UTILIZATOR ÎNCEPE TIM7_IRQn 1 */

/* COD UTILIZATOR END TIM7_IRQn 1 */
}

Intrăm în manipulatorul de întreruperi ce vrem să facem - și cu fiecare întrerupere vrem să schimbăm starea picioarelor noastre la care sunt conectate LED-urile.
Folosim această construcție - HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);

Asta e tot. Apăsăm F7, ne asigurăm că nu există erori - și putem încărca toate aceste lucruri pe procesorul nostru experimental.
Ei bine, deja ne putem bucura de clipirea interesantă a LED-urilor.
Voi adăuga un videoclip puțin mai târziu, dar deocamdată, ca de obicei, imaginea este corectă. Ei bine, nu uita de recunoştinţă))

Temporizatoarele din STM32, ca toate perifericele în principiu, sunt foarte sofisticate. Abundența diferitelor funcții pe care le pot îndeplini cronometrele poate chiar să vă facă capul să se învârtă. Deși, s-ar părea că cronometrul este doar un cronometru, doar pentru a număra. Dar în realitate totul este mult mai cool)

Nu numai că cronometrele au capacități atât de mari, dar fiecare controler are și câteva dintre ele. Și nici măcar două sau trei, ci mai multe! În general, se pot lăuda toate acestea la nesfârșit. Să ne dăm seama ce funcționează și cum. Deci, microcontrolerul STM32F103CB are:

  • 3 temporizatoare de uz general (TIM2, TIM3, TIM4)
  • 1 temporizator mai avansat cu capabilități avansate (TIM1)
  • 2 WDT (Timer WatchDog)
  • 1 cronometru SysTick

De fapt, temporizatoarele de uz general și temporizatorul TIM1 nu sunt foarte diferite unele de altele, așa că ne vom limita la a lua în considerare doar un temporizator. Apropo, am ales TIM4. Fără un motiv anume, pur și simplu mi-a plăcut =). Temporizatoarele au 4 canale independente care pot fi folosite pentru:

  • Captură semnal
  • Comparații
  • Generație PWM
  • Generarea unui singur impuls
  • Revărsare
  • Captură semnal
  • Comparaţie
  • Eveniment declansator

Când are loc oricare dintre aceste evenimente, temporizatoarele pot genera o solicitare către DMA (DMA este acces direct la memorie, ne vom ocupa de asta în curând =)). Acum puțin mai multe despre fiecare dintre modurile de funcționare a temporizatorului.

Modul de captare a semnalului. Este foarte convenabil să măsurați perioada de repetare a pulsului atunci când funcționează temporizatorul în acest mod. Vedeți singur: sosește un impuls, temporizatorul își pune valoarea curentă a contorului în registru TIM_CCR. Luăm rapid această valoare și o ascundem într-o variabilă. Ne așezăm și așteptăm următorul impuls. Hopa! Impulsul a sosit, temporizatorul împinge din nou valoarea contorului TIM_CCR, și tot ce trebuie să facem este să scădem din această valoare pe cea pe care am salvat-o anterior. Aceasta este probabil cea mai simplă utilizare a acestui mod de cronometru, dar foarte utilă. Puteți prinde atât marginea anterioară a unui impuls, cât și marginea posterior, așa că posibilitățile sunt destul de mari.

Modul de comparație. Aici pur și simplu conectăm un canal de temporizator la ieșirea corespunzătoare și, de îndată ce temporizatorul numără până la o anumită valoare (este în TIM_CCR) starea de ieșire se va schimba în funcție de setarea modului (se va fi setată fie la unu, fie la zero, fie se va schimba la opus).

Modul de generare PWM. Ei bine, totul este ascuns în nume) În acest mod, cronometrul generează PWM! Probabil că nu are rost să scrii ceva aici acum. În curând va exista un eșantion doar pentru PWM și îl vom cerceta mai detaliat.

Modul timp mort. Esența modului este că apare o anumită întârziere între semnalele de la pinii principal și complementar al temporizatorului. Există destul de multe informații pe Internet despre unde poate și ar trebui aplicat acest lucru.

Ei bine, în principiu, foarte pe scurt despre principalele moduri de funcționare ale cronometrului. Dacă aveți întrebări despre alte moduri, mai specifice, scrieți în Comentarii 😉

Trebuie să scriem încet un program care să funcționeze cu cronometre. Dar mai întâi, să vedem ce se află în Biblioteca standard de periferice. Deci, fișierele sunt responsabile pentru cronometre - stm32f10x_tim.hȘi stm32f10x_tim.c. Deschidem primul și vedem că structura fișierului repetă structura fișierului pentru lucrul cu GPIO, despre care am discutat în articolul anterior. Acesta descrie structurile și câmpurile structurilor care sunt necesare pentru configurarea temporizatoarelor. Adevărat, nu există doar una, ci mai multe structuri (temporizatoarele au mai multe moduri și, prin urmare, setări, decât porturi I/O). Toate câmpurile de structură sunt furnizate cu comentarii, așa că nu ar trebui să fie probleme aici. Ei bine, de exemplu:

uint16_t TIM_OCMode; // Specifică modul TIM.

Aici vom seta modul de funcționare al cronometrului. Și iată un altul:

uint16_t TIM_Channel; // Specifică canalul TIM.

Aici selectăm canalul temporizatorului, nimic neașteptat) În general, totul este destul de transparent, dacă întrebi ceva =) Primul fișier este clar. Și în dosar stm32f10x_tim.c– funcții gata făcute pentru lucrul cu cronometre. Totul este, de asemenea, în general clar. Am folosit deja biblioteca pentru a lucra cu GPIO-uri, acum lucrăm cu cronometre și este evident că pentru diferite periferice totul este foarte asemănător. Deci haideți să creăm un proiect și să scriem un program.

Deci, să creăm un nou proiect, să adăugăm toate fișierele necesare:

Scriem codul:

Trebuie remarcat faptul că în câmpul TIM_Prescaler trebuie să scrieți o valoare care este cu una mai mică decât cea pe care vrem să o obținem.

/**************************** cronometre.c******************** *************/#include „stm32f10x.h” #include „stm32f10x_rcc.h” #include „stm32f10x_gpio.h” #include „stm32f10x_tim.h” //Cu acest prescaler primesc o bifă de cronometru la 10 µs#define TIMER_PRESCALER 720 /*******************************************************************/ //Variabilă pentru a stoca starea anterioară a pinului PB0 uint16_t previousState; Port GPIO_InitTypeDef; temporizator TIM_TimeBaseInitTypeDef; /*******************************************************************/ void initAll() ( //Activați sincronizarea portului GPIOB și a temporizatorului TIM4 //Timer 4 se blochează pe magistrala APB1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE) ; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE) ; //Aici configuram portul PB0 pentru iesire //Mai multe despre asta în articolul despre GPIO GPIO_StructInit(& port) ; port.GPIO_Mode = GPIO_Mode_Out_PP; port.GPIO_Pin = GPIO_Pin_0; port.GPIO_Speed ​​​​= GPIO_Speed_2MHz; GPIO_Init(GPIOB, & port) ; // Și aici este setarea temporizatorului //Umpleți câmpurile de structură cu valori implicite TIM_TimeBaseStructInit(& timer) ; //Setați prescalerul timer.TIM_Prescaler = TIMER_PRESCALER - 1 ; //Iată valoarea la care temporizatorul va genera o întrerupere //Apropo, vom schimba această valoare în întrerupere în sine timer.TIM_Period = 50 ; //Inițializați TIM4 cu valorile noastre TIM_TimeBaseInit(TIM4, & timer) ; ) /*******************************************************************/ int main() ( __enable_irq() ; initAll() ; //Configurați un cronometru pentru a genera o întrerupere de actualizare (depășire). TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE) ; //Porniți cronometrul TIM_Cmd(TIM4, ENABLE) ; //Activați întreruperea corespunzătoare NVIC_EnableIRQ(TIM4_IRQn) ; în timp ce (1) ( //Suntem nesfârșit de proști) Toată munca utilă este în întrerupere __NOP() ; ) ) /*******************************************************************/ //Dacă rezultatul a fost 0.. timer.TIM_Period = 50 ; TIM_TimeBaseInit(TIM4, & timer) ; //Șterge bitul de întrerupere TIM_ClearITPendingBit(TIM4, TIM_IT_Update) ; ) altfel ( //Setați zero la ieșire timer.TIM_Period = 250 ; TIM_TimeBaseInit(TIM4, & timer) ; TIM_ClearITPendingBit(TIM4, TIM_IT_Update) ; ) )

În acest program ne uităm la ceea ce era la ieșire înainte de generarea întreruperii - dacă este zero, îl setăm la unu pentru 0,5 ms. Dacă a existat unul, setați zero la 2,5 ms. Compilați și începeți depanarea =)

O mică, dar foarte importantă digresiune... Exemplul nostru, desigur, va funcționa și va fi destul de potrivit pentru testare, dar totuși, în programele „de luptă” trebuie să monitorizați optimitatea codului atât în ​​ceea ce privește volumul, cât și în ceea ce priveşte performanţa şi memoria de consum. În acest caz, nu are rost să folosiți structura cronometrului și, de asemenea, să apelați funcția TIM_TimeBaseInit() de fiecare dată când perioada se schimbă. Este mai corect să se schimbe doar o singură valoare într-un registru, și anume în registrul TIMx->ARR (unde x este numărul cronometrului). În acest exemplu, codul este transformat după cum urmează:

/*******************************************************************/ void TIM4_IRQHandler() ( //Dacă rezultatul a fost 0.. dacă (previousState == 0 ) ( //Setați unul la ieșire previousState = 1 ; GPIO_SetBits(GPIOB, GPIO_Pin_0) ; //Perioada este de 50 de ticuri de cronometru, adică 0,5 ms TIM4->ARR = 50; ) altfel ( //Setați zero la ieșire previousState = 0 ; GPIO_ResetBits(GPIOB, GPIO_Pin_0) ; //Și perioada va fi acum 250 de căpușe – 2,5 ms TIM4->ARR = 250; ) TIM_ClearITPendingBit(TIM4, TIM_IT_Update) ; ) /**************************** Sfârșitul fișierului******************** **********/

Deci, să continuăm, avem o altă grebla pe drum) Și anume eroarea:

..\..\..\SPL\src\stm32f10x_tim.c(2870): eroare: #20: identificatorul „TIM_CCER_CC4NP” este nedefinit

Nu atât de înfricoșător pe cât ar părea, accesați fișierul stm32f10x.h, găsiți liniile

Acum totul este asamblat, îl puteți depana. Porniți analizorul logic. Pe linia de comandă scriem: la portb&0x01și vedeți rezultatul:

Am obținut ceea ce ne-am dorit) Cu alte cuvinte, totul funcționează corect. În articolul următor vom pătrunde în modul de generare PWM, rămâneți în legătură 😉

Nu ratați un articol bun despre cronometre în general -.

Modul de captare este un mod special de operare al temporizatorului, a cărui esență este următoarea: atunci când nivelul logic se schimbă la un anumit pin al microcontrolerului, valoarea registrului de numărare este scrisă într-un alt registru, care se numește registru de captură. .

Pentru ce e asta?
Folosind acest mod, puteți măsura durata pulsului sau perioada semnalului.

Modul de captură STM32 are câteva caracteristici:

  • capacitatea de a alege ce front va fi activ
  • capacitatea de a schimba frecvența semnalului de intrare folosind un prescaler (1,2,4,8)
  • fiecare canal de captare este echipat cu un filtru de intrare încorporat
  • sursa semnalului de captare poate fi un alt temporizator
  • Pentru fiecare canal există două steaguri, primul este setat dacă a avut loc o captură, al doilea dacă s-a produs o captură în timp ce primul steag este setat

Registrele sunt folosite pentru a configura modul de captare CCMR1(pentru canalul 1 și 2) și CCMR2(pentru 3 și 4), precum și registre CCER, DIER.

Să aruncăm o privire mai atentă la câmpurile de biți de registru CCMR2, responsabil pentru setarea celui de-al 4-lea canal al temporizatorului, asta este ceea ce vom configura în exemplu. De asemenea, aș dori să remarc faptul că același registru conține câmpuri de biți care sunt utilizate la setarea temporizatorului în modul de comparație.

CC4S- determină direcția de funcționare a celui de-al patrulea canal (intrare sau ieșire). Când setați un canal ca intrare, i se atribuie un semnal de captură

  • 00 - canalul funcționează ca ieșire
  • 01 - canalul funcționează ca intrare, semnal de captare - TI4
  • 10 - canalul funcționează ca intrare, semnal de captare - TI3
  • 11 - canalul funcționează ca intrare, semnal de captare - TRC
IC4PSC– determinați coeficientul de diviziune pentru semnalul de captare
  • 00 - divizorul nu este utilizat, semnalul de captare IC1PS este generat pentru fiecare eveniment
  • 01 - un semnal de captare este generat pentru fiecare al doilea eveniment
  • 10 - un semnal de captare este generat pentru fiecare al patrulea eveniment
  • 11 - un semnal de captare este generat pentru fiecare al optulea eveniment
IC4F- este destinat setarii filtrului de intrare, pe langa numarul de mostre in timpul carora microcontrolerul nu va raspunde la semnalele de intrare, puteti seta si frecventa de esantionare. În esență, ajustăm timpul de întârziere din momentul în care sosește marginea până la eșantionul de „confirmare”.

Acum să ne uităm la registru CCER.

CC4E- pornește/dezactivează modul de captură.
CC4P- determină frontul de-a lungul căruia se va efectua captura, 0 - față, 1 - spate.

Și înregistrează-te DIER.

CC4DE- vă permite să generați o cerere către DMA.
CC4IE- permite întreruperea capturii.

După ce a avut loc o captură, este generat un eveniment de captură care setează steag-ul corespunzător. Acest lucru poate duce la generarea unei întreruperi și la o solicitare DMA, dacă sunt permise în registru DIER. În plus, un eveniment de captură poate fi generat programatic prin setarea unui câmp de biți în registrul de generare a evenimentelor EGR:

Câmpuri de biți CC1G, CC2G, CC3G și CC4G vă permit să generați un eveniment în canalul de captură/comparație corespunzător.

Apropo, CCR1, CCR2, CCR3 și CCR4- registre de captare, în care valoarea temporizatorului este stocată pe baza semnalului de captare.

Pentru a controla generarea semnalului de captare, in registru S.R. Pentru fiecare canal sunt alocate două steaguri.

CC4IF- setat atunci când este generat un semnal de captură, aceste steaguri sunt resetate prin software sau prin citirea registrului de captare/comparare corespunzător.
CC4OF- setați dacă steagul CC4IF nu a fost șters, dar a sosit un alt semnal de captură. Acest steag este șters programatic prin scrierea zero.

Acum să punem în practică aceste cunoștințe de la generatorul de semnal vom furniza o sinusoidă cu o frecvență de 50 Hz de la generatorul de semnal la intrarea lui TIM5_CH4 și vom încerca să îi măsurăm perioada. Pentru a accelera procesul, vă sugerez să utilizați DMA. Ce pin MK corespunde canalului 4 TIM5 poate fi găsit în fișa de date de pe MK în secțiunea Pinouts și descriere pin.

Pentru DMA este necesară adresa de înregistrare CCR4, iată cum să-l găsești. Deschidere RM0008 iar în tabel Înregistrați adresele de limită găsiți adresa de pornire a lui TIM5.


compensare pentru registru CCR4 pot fi găsite în același document în secțiune inregistreaza harta.

#include "stm32f10x.h" #define TIM5_CCR4_Address ((u32)0x40000C00+0x40) #define DMA_BUFF_SIZE 2 uint16_t buff;//Buffer uint16_t volatile T; void DMA2_Channel1_IRQHandler (void) ( T = (buff > buff) ? (buff - buff) : (65535+ buff - buff); DMA2->IFCR |= DMA_IFCR_CGIF1; ) void Init_DMA(void) ( RCC->AHBENR |= RCCMA_AHBENR | ; //Activează tastatura primului modul DMA2_Channel1->CPAR = TIM5_CCR4_Address //Specificați adresa periferică - registrul rezultat al conversiei ADC pentru canalele obișnuite DMA2_Channel1->CMAR = (uint32_t)buff -; adresa de bază a matricei în RAM DMA2_Channel1 ->CCR &= ~DMA_CCR1_DIR //Indicați direcția de transfer de date, de la periferic la memorie DMA2_Channel1->CNDTR = DMA_BUFF_SIZE //Numărul de valori transferate DMA2_Channel1->; CCR &= ~DMA_CCR1_PINC //Adresa periferică nu este incrementată după fiecare transfer DMA2_Channel1 -> CCR = DMCR1_MINC DMA2_ChanNEL1- >CCR |= DMA_CCR1_PL; //Prioritate - foarte mare DMA2_Channel1->CCR |= DMA_CCR1_CIRC; //Activați funcționarea DMA în modul ciclic DMA2_Channel1->CCR |= DMA_CCR1_TCIE;//Activați întreruperea la sfârșitul transmisiei DMA2_Channel1->CCR |= DMA_CCR1_EN; //Activați funcționarea primului canal DMA) int main(void) ( Init_DMA(); //activați sincronizarea portului A, funcții alternative și cronometru RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |= RCC_TIAPB1EN |= RCC_TIAPB5EN ; TIM5 ->PSC = 56000-1;//frecvență nouă 1Khz TIM5->CCMR2 |= TIM_CCMR2_CC4S_0;//selectați TI4 pentru TIM5_CH4 TIM5->CCMR2 &= ~(TIM_CCMR2_IC4F | TIM_CCMR2_CCMR2// nu faceți filtru); utilizați un divizor TIM5- >CCER &= ~TIM_CCER_CC4P;//selectați capturarea pe marginea anterioară TIM5->CCER |= TIM_CCER_CC4E;//activați modul de captură pentru al 4-lea canal TIM5->DIER |= TIM_DIER_CC4DE;//permiteți pentru a genera o solicitare către DMA //TIM5 ->DIER |= TIM_DIER_CC4IE //activare întreruperea capturii TIM5->CR1 |= TIM_CR1_CEN //activarea contorului //NVIC->ISER |= NVIC_ISER_SETENA_18; >ISER |= NVIC_ISER_SETENA_24 Întreruperea în timp ce(1) ( ) )



Ți-a plăcut articolul? Împărtășește-l