Stm32 bendrosios paskirties laikmačiai. STM32 nuo nulio

Laikmačiai yra tokia STM32 valdiklio periferija, kuri leidžia labai tiksliai skaičiuoti laiko intervalus. Tai turbūt viena iš svarbiausių ir dažniausiai naudojamų funkcijų, tačiau yra ir kitų. Pradėti reikėtų nuo to, kad STM32 valdikliuose yra įvairaus vėsumo laipsnio laikmačiai. Paprasčiausi yra Pagrindinis laikmačiai . Jie yra geri, nes juos labai lengva konfigūruoti ir valdyti naudojant minimalų registrų skaičių. Viskas, ką jie gali padaryti, tai skaičiuoti laiko intervalus ir generuoti, kai laikmatis pasiekia nurodytą vertę. Kita grupė ( bendrosios paskirties laikmačiai ) yra daug vėsesni nei pirmasis, gali generuoti PWM, gali skaičiuoti į tam tikras kojeles ateinančius impulsus, galima prijungti koderį ir t.t. Ir šauniausias laikmatis yra išplėstinio valdymo laikmatis , manau, kad labai ilgai nenaudosiu, nes kol kas nereikia valdyti trifazio elektros variklio. Turėtumėte pradėti susipažinti su laikmačiais nuo kažko paprastesnio. Užduotis, kurią išsikėliau sau: Priverskite laikmatį generuoti pertraukimus kas sekundę.

Visų pirma atkreipsiu dėmesį, kad Basic laikmačiai (TIM6 ir TIM7) yra prijungti prie APB1 magistralės, tad pasikeitus joje laikrodžio impulsų dažniui, laikmačiai pradės tiksėti greičiau arba lėčiau. Jei nieko nepakeisite laikrodžio nustatymuose ir paliksite juos numatytuosius, tada dažnis APB1 yra 24 MHz, jei yra prijungtas išorinis kvarcas 8 MHz dažniu. Apskritai STM32 laikrodžio sistema yra labai sudėtinga ir aš pasistengsiu tinkamai parašyti apie tai atskirą įrašą. Kol kas naudosime tik laikrodžio nustatymus, kuriuos nustato CooCox automatiškai sugeneruotas kodas. Verta pradėti nuo svarbiausio registro - TIMx_CNT(toliau x yra pagrindinio laikmačio 6 arba 7 skaičius). Tai 16 bitų skaičiavimo registras, kuriame tiesiogiai skaičiuojamas laikas. Kiekvieną kartą iš autobuso APB1 ateina laikrodžio impulsas, šio registro turinys padidinamas vienu. Kai registras persipildo, viskas prasideda nuo nulio. Numatytuoju APB1 magistralės dažniu laikmatis tiks 24 milijonus kartų per vieną sekundę! Tai yra daug, todėl laikmatis turi išankstinį skirstytuvą, kurį galime valdyti naudodami registrą TIMx_PSC. Įrašę į ją reikšmę 24000-1, priversime skaičiavimo registrą TIMx_CNT padidinkite jo vertę kas milisekundę (dažnis APB1 padalinkite iš skaičiaus išankstinio skalavimo registre ir gaukite, kiek kartų per sekundę skaitiklis didėja). Vienetas turi būti atimtas, nes jei registre yra nulis, tai reiškia, kad daliklis iš vieneto yra įjungtas. Dabar, kai skaičiavimo registras pasiekia 1000, tikrai galime pasakyti, kad praėjo lygiai viena sekundė! Kodėl dabar reikia apklausti skaičiavimo registrą ir laukti, kol ten atsiras 1000? Tai ne mūsų metodas, nes galime naudoti! Tačiau bėda ta, kad turime tik vieną pertraukimą, ir jis įvyksta, kai skaitiklis nukrenta iki nulio. Norint, kad skaitiklis būtų iš naujo nustatytas anksčiau laiko, o ne tada, kai jis pasiekia 0xFFFF, naudojamas registras TIMx_ARR. Į jį įrašome skaičių, iki kurio turėtų būti skaičiuojamas registras TIMx_CNT prieš eidamas į nulį. Jei norime, kad pertraukimas įvyktų vieną kartą per sekundę, tada turime ten įrašyti 1000 Kalbant apie tiesioginį laiką, tai viskas, bet pats laikmatis nepradės tiksėti. Jis turi būti įjungtas nustatant bitą CEN registre TIMx_CR1. Šis bitas leidžia pradėti atgalinį skaičiavimą, taigi, jei jis bus nustatytas iš naujo, atgalinis skaičiavimas sustos (jūsų C.O.). Registre yra ir kitų bitų, bet jie mums nėra itin įdomūs. Bet mus domina dar vienas dalykas, bet jau registre TIMx_DIER. Tai vadinama UIE, Jį nustatydami leidžiame laikmačiui generuoti pertraukimus, kai skaičiavimo registras yra iš naujo nustatytas. Tai viskas, tai nėra dar sudėtingiau nei kai kuriuose AVR. Taigi trumpa santrauka: norint naudoti pagrindinį laikmatį, jums reikia:

  1. Nustatykite išankstinį skalavimo įtaisą, kad laikmatis greitai netikėtų ( TIMx_PSC)
  2. Prieš nustatydami iš naujo nustatykite ribą, kurią laikmatis turi pasiekti ( TIMx_ARR)
  3. Įgalinti bitų skaičiavimą CEN registre TIMx_CR1
  4. Įgalinti bitų perpildymo pertraukimą UIE registre TIMx_DIER

Štai paprasta seka. O dabar laikas jį išimti ir milijoną kartą pabandyti mirksėti šiuos nelemtus šviesos diodus, bet naudojant laikmatį :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int main() ( GPIO_InitTypeDef PORT; //Įgalinti prievadą C ir laikmatį 6 RCC_APB2PeriphClock_GPABCm; RCC_APB1Periph_TIM6,Įgalinti) // Nustatykite kojas su šviesos diodais PORT.GPIO_Pin_8; PORT.GPIO_Mode = GPIO_Mode_Out_PP // Kad pertraukimas įvyktų vieną kartą per sekundę TIM6->DIER (1) ( //Programa nieko nedaro tuščioje kilpoje) ) // TIM6_DAC pertraukimų tvarkytuvas void TIM6_DAC_IRQHandler(void) ( TIM6->SR &= ~TIM_SR_UIF; //Atkurti vėliavėlę UIF GPIOC->ODR^=(GPIO_Pin_9 |. GPIO_Pin_8) //Invertuoti šviesos diodų būseną

Verta pridėti nedidelę pastabą pertraukimų tvarkyklei. Faktas yra tas, kad jį vienu metu naudoja du periferiniai blokai: laikmatis 6 ir DAC. Tai reiškia, kad jei rašote programą, kuri leidžia pertraukti iš abiejų šių išorinių įrenginių, tada tvarkyklės korpuse turite patikrinti, kuris iš jų sukėlė pertraukimą. Mūsų atveju aš to nepadariau, nes negali atsirasti DAC pertrūkių. Jis nesukonfigūruotas, o pertraukimai išjungti pagal numatytuosius nustatymus. Kitą kartą panagrinėsime bendrosios paskirties laikmačius ir jų praktinį pritaikymą.

Mes jau pažvelgėme į SysTick laikmatį, kuris yra Cortex branduolio dalis. Tačiau viskas tuo nesibaigia. Stm32 yra daug laikmačių ir jie skiriasi. Priklausomai nuo tikslo, turėsite pasirinkti vieną ar kitą laikmatį:

  • SysTick;
  • bendrosios paskirties laikmačiai - TIM2, TIM3, TIM4, TIM15, TIM16, TIM17;
  • išplėstinis laikmatis - TIM1;
  • sarginio šuns laikmatis.

Vienintelis dalykas, kurį verta paminėti apie pastarąjį, yra tai, kad jis skirtas valdyti sistemos užšalimą ir yra laikmatis, kurį reikia periodiškai iš naujo nustatyti. Jei laikmatis per tam tikrą laikotarpį nenustatomas iš naujo, laikmatis iš naujo paleis sistemą (mikrovaldiklį).

Laikmačiai yra skirtingų bitų dydžių: pavyzdžiui, SysTick yra 24 bitų, o visi laikmačiai, kuriuos turime akmenyje, yra 16 bitų (tai yra, jie gali skaičiuoti iki 2 16 = 65535), išskyrus WatchDog. Be to, kiekvienas laikmatis turi tam tikrą kanalų skaičių, tai yra, iš tikrųjų jis gali veikti du, tris ir tt Šie laikmačiai gali dirbti su prieauginiais kodavimo įrenginiais, Hall jutikliais ir gali generuoti PWM (impulso pločio moduliacija, angl.). Impulso pločio moduliavimas – apie kurį kalbėsime vėliau) ir daug daugiau. Be to, jie gali generuoti pertraukimus arba pateikti užklausas kitiems moduliams (pavyzdžiui, DMA – tiesioginė prieiga prie atminties), remdamiesi įvairiais įvykiais:

  • perpildymas;
  • signalo gaudymas (angl. input capture);
  • palyginimas (angl. output compe);
  • įvykio paleidiklis.

Jei mums viskas aišku su laikmačio perpildymu (tiksliau, pasiekus „0“) - pažvelgėme į SysTick - tada dar nesame susipažinę su kitais galimais darbo režimais. Pažvelkime į juos atidžiau.

Signalo fiksavimas

Šis režimas puikiai tinka matuoti impulsų pasikartojimo periodą (arba jų skaičių, tarkime, per sekundę). Kai impulsas patenka į MK išvestį, laikmatis sukuria pertraukimą, iš kurio galime pašalinti esamą skaitiklio reikšmę (iš TIM_CCRx registro, kur x yra kanalo numeris) ir išsaugoti jį išoriniame kintamajame. Tada ateis kitas impulsas ir paprasta atėmimo operacija gausime „laiką“ tarp dviejų impulsų. Galite užfiksuoti tiek priekinį, tiek galinį impulso kraštą arba net abu iš karto. Kodėl tai būtina? Tarkime, kad turite magnetą, kurį priklijavote prie rato ratlankio, ir Holo jutiklį prie dviračio šakės. Tada, sukdami ratą, gausite impulsą kiekvieną kartą, kai magnetas ant rato bus toje pačioje plokštumoje kaip Holo jutiklis. Žinodami atstumą, kurį magnetas nukeliauja per apsisukimą, ir laiką, galite apskaičiuoti judėjimo greitį.

Taip pat yra PWM fiksavimo režimas, tačiau tai labiau specialus laikmačio nustatymo būdas, o ne atskiras veikimo režimas: vienas kanalas fiksuoja kylančias, o antrasis – krentančias. Tada pirmasis kanalas aptinka laikotarpį, o antrasis - užpildymą.

Palyginimo režimas

Šiuo režimu pasirinktas laikmačio kanalas yra prijungtas prie atitinkamo kaiščio, o kai laikmatis pasieks tam tikrą reikšmę, kaiščio būsena pasikeis priklausomai nuo režimo nustatymo (gali būti "1" arba "0" arba išvesties būsena yra tiesiog apverstas).

PWM generavimo režimas

Kaip rodo pavadinimas, laikmatis šiame režime generuoja impulsų pločio moduliavimą. Daugiau apie šį režimą, taip pat kur jis gali/turėtų būti naudojamas, pakalbėsime kitoje pamokoje, apžvelgę ​​signalo fiksavimą.

Naudodami pažangų laikmatį galite generuoti trifazį PWM, kuris yra labai naudingas valdant trifazį variklį.

Negyvo laiko režimas

Kai kurie laikmačiai turi šią funkciją; reikia sukurti uždelsimus išėjimuose, kurie reikalingi, pavyzdžiui, norint pašalinti pratekamąsias sroves valdant maitinimo jungiklius.

Kurso metu naudosime tik „signalo fiksavimą“ ir „PWM generavimą“.

Straipsnyje aprašomi STMicroelectronics STM32 serijos 32 bitų ARM mikrovaldiklių laikmačiai. Nagrinėjama pagrindinių laikmačių registrų architektūra ir sudėtis, pateikiami praktiniai programų pavyzdžiai.

Kiekvienam mikrovaldikliui laikmatis yra vienas iš svarbiausių komponentų, leidžiantis labai tiksliai skaičiuoti laiko intervalus, skaičiuoti į įėjimus ateinančius impulsus, generuoti vidinius pertraukimus, generuoti signalus su impulsų pločio moduliacija (PWM) ir palaikyti tiesioginę prieigą prie atminties ( DMA) procesai.

STM32 mikrovaldiklyje yra kelių tipų laikmačiai, kurie skiriasi vienas nuo kito funkcionalumu. Pirmojo tipo laikmačiai yra paprasčiausi ir yra pagrindiniai laikmačiai. Šiam tipui priklauso laikmačiai TIM6 ir TIM7. Šiuos laikmačius labai lengva konfigūruoti ir valdyti naudojant minimalų registrų skaičių. Jie gali skaičiuoti laiko intervalus ir generuoti pertraukimus, kai laikmatis pasiekia nurodytą reikšmę.
Antrasis tipas yra bendrosios paskirties laikmačiai. Tai apima laikmačius TIM2 iki TIM5 ir laikmačius TIM12 iki TIM17. Jie gali generuoti PWM, skaičiuoti impulsus, patenkančius į tam tikrus mikrovaldiklio kontaktus, apdoroti kodavimo įrenginio signalus ir kt.

Trečiasis tipas apibrėžia laikmačius su išplėstiniu valdymu (Advanced-Control Timer). Šis tipas apima TIM1 laikmatį, kuris gali atlikti visas aukščiau nurodytas operacijas. Be to, remiantis šiuo laikmačiu, galite sukurti įrenginį, galintį valdyti trifazę elektros pavarą.

Pagrindinis laikmačio įrenginys

Panagrinėkime pagrindinio laikmačio, kurio blokinė schema parodyta paveikslėlyje, konstrukciją ir veikimą. Pagrindinis laikmatis yra sukurtas remiantis 16 bitų registrais. Jo pagrindas yra skaičiavimo registras TIMx_CNT. (Toliau simbolis „x“ atitinkamai pakeičia skaičių 6 arba 7 pagrindiniams laikmačiams TIM6 ir TIM7.) TIMx_PSC išankstinis skaleris leidžia reguliuoti skaitiklio registro laikrodžio dažnį, o TIMx_ARR automatinio įkėlimo registras leidžia nustatyti laikmačio skaičiavimo diapazonas. Trigerio ir sinchronizavimo valdiklis kartu su valdymo ir būsenos registrais padeda organizuoti laikmačio darbo režimą ir leidžia valdyti jo veikimą.

Dėl savo organizavimo laikmačio skaitiklis gali skaičiuoti pirmyn ir atgal, taip pat iki tam tikro diapazono vidurio pirmyn ir atgal. Bazinio laikmačio įvestis gali būti tiekiama iš kelių šaltinių, įskaitant laikrodžio signalą iš APB1 magistralės, išorinį signalą arba kitų laikmačių, taikomų fiksavimo ir palyginimo kaiščiams, išvestį. Laikmačiai TIM6 ir TIM7 laikomi iš APB1 magistralės. Jei naudojate 8 MHz kristalą ir gamyklinius numatytuosius laikrodžio nustatymus, laikrodžio dažnis iš APB1 laikrodžio magistralės bus 24 MHz.

Pagrindiniai laikmačio registrai

Lentelėje parodytas pagrindinių laikmačių TIM6 ir TIM7 registrų žemėlapis. Pagrindiniai laikmačiai apima šiuos 8 registrus:

●● TIMx_CNT – Skaitiklis (skaičiavimo registras);
●● TIMx_PSC – Prescaler (prescaler);
●● TIMx_ARR – Automatinio perkrovimo registras;
●● TIMx_CR1 – 1 valdymo registras (1 valdymo registras);
●● TIMx_CR2 – 2 valdymo registras (2 valdymo registras);
●● TIMx_DIER – DMA pertraukimo įjungimo registras (DAP ir pertraukimo įjungimo registras);
●● TIMx_SR – Būsenos registras;
●● TIMx_EGR – įvykių generavimo registras.

TIMx_CNT, TIMx_PSC ir TIMx_ARR registrai naudoja 16 informacijos bitų ir leidžia įrašyti reikšmes nuo 0 iki 65535. TIMx_CNT skaitiklio registro laikrodžio impulsų dažnis, einantis per TIMx_PSC daliklį, apskaičiuojamas pagal formulę: Fcnt = Fin /(PSC + 1), kur Fcnt yra laikmačio skaitiklio registro impulsų dažnis; Fin – laikrodžio dažnis; PSC – laikmačio TIMx_PSC registro turinys, kuris nustato padalijimo koeficientą. Jei į TIMx_PSC registrą įrašysite reikšmę 23999, tada TIMx_CNT skaitiklio registras, kurio laikrodžio dažnis yra 24 MHz, pakeis savo reikšmę 1000 kartų per sekundę. Automatinio įkėlimo registre išsaugoma TIMx_CNT skaitiklio registro įkėlimo reikšmė. TIMx_CNT registro turinys atnaujinamas, kai jis perpildytas arba nustatomas iš naujo, atsižvelgiant į jam nurodytą skaičiavimo kryptį. TIMх_CR1 valdymo registre yra keli valdymo bitai. ARPE bitas įgalina ir išjungia įrašų buferį į TIMx_ARR automatinio įkėlimo registrą. Jei šis bitas yra lygus nuliui, tada, kai į TIMx_ARR įrašoma nauja reikšmė, ji bus nedelsiant įkelta. Jei ARPE bitas lygus vienetui, tai įkėlimas į registrą įvyks po to, kai skaičiavimo registras pasieks ribinę vertę. OPM iškrova įgalina „vieno impulso“ režimą. Jei jis nustatytas, skaitiklio registrui persipildžius, skaičiavimas sustoja ir CEN bitas nustatomas iš naujo. UDIS bitas įgalina arba išjungia laikmačio įvykio generavimą. Jei jis išvalytas, įvykis bus sugeneruotas, kai įvyks įvykio generavimo sąlyga, tai yra, kai laikmatis persipildo arba kai UG bitas užprogramuotas TIMx_EGR registre. CEN bitas įjungia ir išjungia laikmatį. Jei iš naujo nustatysite šį bitą, skaičiavimas bus sustabdytas, o kai jis bus nustatytas, skaičiavimas bus tęsiamas. Įvesties daliklis pradės skaičiuoti nuo nulio. TIMx_CR2 valdymo registre yra trys valdymo bitai MMS2... MMS0, kurie nustato pagrindinį laikmačio režimą. TIMx_DIER registre naudojami du bitai. UDE bitas leidžia arba išjungia DMA užklausą, kai įvyksta įvykis. UIE bitas įjungia ir išjungia laikmačio pertraukimus. TIMx_SR registras naudoja tik vieną UIF bitą kaip pertraukimo vėliavėlę. Jį įdiegia aparatinė įranga, kai įvyksta laikmačio įvykis. Turite jį iš naujo nustatyti programiškai. TIMx_EGR registre yra UG bitas, leidžiantis programiškai generuoti „skaičiuojamojo registro perpildymo“ įvykį. Kai šis bitas yra nustatytas, sugeneruojamas įvykis, o skaičiavimo registras ir išankstinis skalės nustatymas iš naujo nustatomi. Šį bitą iš naujo nustato aparatinė įranga. Dėl šio bito galite programiškai generuoti įvykį iš laikmačio ir taip priverstinai iškviesti laikmačio pertraukimo tvarkyklės funkciją.

Pažvelkime į valdymo registrų paskirtį ir laikmačio būseną naudodami konkrečių programų pavyzdžius.

Programų pavyzdžiai

Norint paleisti laikmatį, reikia atlikti keletą operacijų, pavyzdžiui, nustatyti laikmatį ir inicijuoti jo registrus. Pažvelkime į šias operacijas naudodami darbo su laikmačiais programų pavyzdžius. Gana dažnai programavimo procese iškyla užduotis įgyvendinti laiko uždelsimus. Norint išspręsti šią problemą, reikalinga vėlavimo generavimo funkcija. Tokios funkcijos pavyzdys, pagrįstas pagrindiniu STM32 TIM7 laikmačiu, parodytas 1 sąraše.

Sąrašas 1

#define FAPB1 24000000 // APB1 magistralės laikrodžio dažnis // Vėlavimo funkcija milisekundėmis ir mikrosekundėmis void delay(nesigned char t, unsigned int n)( // Įkelti PSC prescaler registrą If(t = = 0) TIM7->PSC = FAPB1 /1000000-1 // mikrosekundėms skaičiuoti If(t = = 1) TIM7->PSC = FAPB1/1000-1 // milisekundėms skaičiuoti TIM7->ARR = n // Įkelti mėginių skaičių; registras ARR TIM7 ->EGR |= TIM_EGR_UG // Sugeneruoti atnaujinimo įvykį // įrašyti duomenis į PSC ir ARR registrus TIM7->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM // Paleiskite laikmatį // įrašydami skaičiavimo įjungimo bitą; CEN // ir režimo bitas perduoda OPM į valdymo registrą CR1 (TIM7->CR1&TIM_CR1_CEN != 0 // Laukiama, kol baigsis skaičiavimas)

Ši funkcija gali generuoti vėlavimą mikrosekundėmis arba milisekundėmis, priklausomai nuo parametro „t“. Uždelsimo trukmė nustatoma „n“ parametru. Ši programa naudoja TIM7 laikmačio vieno praėjimo režimą, kuriame CNT skaitiklio registras skaičiuoja iki perpildymo vertės, įrašytos ARR registre. Kai šios vertės bus lygios, laikmatis sustos. Tai, kad laikmatis sustojo, tikimasi while cikle, patikrinus CR1 būsenos registro CEN bitą. Laikmačių laikmatis įjungiamas vieną kartą pagrindiniame programos modulyje jų inicijavimo metu. Pagrindiniai laikmačiai yra prijungti prie APB1 magistralės, todėl laikrodžio tiekimas atrodo taip:

RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // Įjungti laikrodį TIM6 RCC->APB1ENR |= RCC_APB1ENR_ TIM7EN; // Įjungti laikrodį TIM7

Aukščiau aprašytas programinės įrangos metodas vėlavimui generuoti turi reikšmingą trūkumą dėl to, kad procesorius yra priverstas apklausti vėliavėlę per visą delsos laiką ir todėl negali atlikti kitų užduočių per tą laiką. Šį trūkumą galima pašalinti naudojant laikmačio pertraukimo režimą. Pagrindinių laikmačių pertraukimų tvarkymo funkcijos paprastai atrodo taip:

Void TIM7_IRQHandler())( TIM7->SR = ~TIM_SR_UIF; // Išvalyti vėliavėlę //Atlikti operacijas ) void TIM6_DAC_IRQHandler())( //Jei įvykis yra iš TIM6 if(TIM6->SR & TIM_SR_UIF)( TIM6- >SR =~ TIM_SR_UIF // Iš naujo nustatykite vėliavėlę // Atlikite operacijas ) ;

Panagrinėkime programos, skirtos pagrindinio TIM6 laikmačio uždelsimo organizavimui, kuri naudoja laikmačio pertraukimus, pavyzdį. Programos vykdymui valdyti naudojame vieną iš mikrovaldiklio kaiščių, skirtų valdyti LED indikatorius, kurie turės persijungti intervalais, kuriuos lemia TIM6 laikmatyje organizuotas programos uždelsimas. Tokios programos pavyzdys pateiktas 2 sąraše.

2 sąrašas

// Įskaitant bibliotekas #include #įtraukti #įtraukti #įtraukti #įtraukti // Kaiščių priskyrimas LED indikatoriams enum ( LED1 = GPIO_Pin_8, LED2 = GPIO_Pin_9 ); // Šviesos diodų valdymo prievadų inicijavimo funkcija void init_leds() ( RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_PPMode = GPI_GPIOut Init(GPIOC, &gpio); /TIM6 laikmačio inicijavimo funkcija void init_timer_TIM6() ( // Įgalinti laikmačio laikrodį RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef base_timer; TIM_TimeBase2tti the base.9 to9 /base_tti TIM_Prescale r = 24000 - 1 // Nustatykite periodas iki 500 ms bazinis_laikmatis.TIM_Period = 500 TIM_TimeBaseInit(TIM6, &base_timer) // Įjungti laikmačio perpildymo pertraukimą (TIM6, TIM_IT_Update, Įjungti laikmačio perpildymo skaitiklį) //Įjungti laikmačio perpildymo skaitiklį /TIQM; / Laikmačio pertraukimo tvarkymo funkcija void TIM6_DAC_IRQHandler())( // Jei pertraukimas įvyksta dėl TIM6 laikmačio skaitiklio perpildymo if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( //Atkurti apdoroto pertraukimo bitą TIM6_ClearIT(TIMendingBitarIT) , TIM_IT_Update); //Invertuoti LED indikatorių būseną GPIO_Write(GPIOC, GPIO_ReadOutputData(GPIOC) ^ (LED1 | LED2)); ) ) // Pagrindinis programos modulis int main() ( init_leds(); GPIO_SetBits(GPIOC, LED1); GPIO_ResetBits(GPIOC, LED2); init_timer_TIM6(); while (1) ( // Vieta kitoms komandoms ) )

Šioje programoje uždelsimo funkcija iškviečiama vieną kartą, po to procesorius gali atlikti kitas operacijas, o laikmatis reguliariai generuos pertraukimus nurodytu uždelsimo intervalu. Panašią programą galima parašyti ir TIM7 laikmačiui. Skirtumas tarp tokios programos bus registrų pavadinimuose ir pertraukimo tvarkyklės pavadinime. TIM6 laikmačio pertraukimų tvarkytuvė turi vieną savybę, susijusią su tuo, kad šio laikmačio pertraukų apdorojimo vektorius derinamas su pertraukimu iš skaitmeninio į analoginį keitiklį (DAC). Todėl pertraukimo tvarkyklės funkcija patikrina pertraukimo šaltinį. Daugiau apie STM32 mikrovaldiklio laikmačius galite sužinoti St.com svetainėje. Yra daug kitų aukščiau aprašytų laikmačio užduočių, kurias jis gali sėkmingai išspręsti. Todėl jo naudojimas programoje žymiai sumažina procesoriaus apkrovą ir daro programą efektyvesnę.

Bet koks modernus valdiklis turi laikmačiai. Šiame straipsnyje bus kalbama apie paprasti (pagrindiniai) laikmačiai stm32f4 atradimas.
Tai įprasti laikmačiai. Jie yra 16 bitų su automatiniu perkrovimu. Be to, yra 16 bitų programuojamas dažnio daliklis. Galima generuoti nutrūksta skaitiklio perpildymas ir (arba) DMA užklausa.

Pradėkime. Kaip ir anksčiau naudoju Eclipse + st-util ubuntu linux

Pirmiausia sujungiame antraštes:

#įtraukti #įtraukti #įtraukti #įtraukti #įtraukti

Čia nėra nieko naujo. Jei neaišku, iš kur jie kilę, perskaitykite ankstesnius straipsnius arba atidarykite failą ir perskaitykite.

Apibrėžkime dvi konstantas. Vienas skirtas diodams žymėti, kitas – tų pačių diodų masyvui:

Const uint16_t LEDS = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; // visi diodai const uint16_t LED = (GPIO_Pin_12, GPIO_Pin_13, GPIO_Pin_14, GPIO_Pin_15); // masyvas su diodais

Greičiausiai jau esate susipažinę su periferinių įrenginių (ty diodų) inicijavimo funkcija:

Void init_leds())(RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); // įgalinti GPIO_InitTypeDef gpio laikrodį; // struktūra GPIO_StructInit(&gpio); // užpildykite standartinėmis reikšmėmis gpio_pey pio .GPIO_Mode = GPIO_Mode_OUT; // veikia kaip išvestis gpio.GPIO_Pin = LEDS // visi diodo kontaktai GPIO_Init(GPIOD, &gpio);

Laikmačio inicijavimo funkcija:

Void init_timer())(RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // įgalinti laikmačio laikrodį /* Kiti TIM_TimeBaseInitTypeDef * struktūros parametrai nėra prasmingi baziniams laikmačiams mer); /* Daliklis yra atsižvelgiama kaip TIM_Pre scaler + 1, todėl atimkite 1 */ base_timer.TIM_Prescaler = 24000 - 1 atnaujinti (šiuo atveju - * perpildymo atveju) TIM6 laikmačio skaitiklis (TIM6, TIM_IT_Update, ENABLE) pertraukimas taip pat yra atsakingas už DAC ištuštinimą NVIC_EnableIRQ(TIM6_DAC_IRQn).

Kodą pakomentavau, tad manau viskas aišku.
Pagrindiniai parametrai čia yra laikmačio daliklis (TIM_Prescaler) ir periodas (TIM_Period). Tai yra parametrai, kurie iš tikrųjų konfigūruoja laikmačio veikimą.

Pavyzdžiui, jei STM32F4 DISCOVERY laikrodžio dažnis nustatytas į 48 MHz, tai bendrosios paskirties laikmačių dažnis yra 24 MHz. Jei skirstytuvą (TIM_Prescaler) nustatysite į 24 000 (skaičiavimo dažnis = 24 MHz / 24 000 = 1 KHz), o periodą (TIM_Period) – į 1 000, laikmatis skaičiuos intervalą per 1 sek.

Atminkite, kad viskas priklauso nuo laikrodžio greičio. Turite tiksliai išsiaiškinti.

Taip pat atkreipiu dėmesį į tai, kad esant aukštam dažniui, šviesos diodo perjungimas pertraukimu žymiai iškraipo dažnio reikšmę. Su 1 MHz reikšme išėjime gavau maždaug 250 KHz, t.y. skirtumas nepriimtinas. Šis rezultatas, matyt, gaunamas dėl laiko, praleisto vykdant pertraukimą.

Visuotinis kintamasis – šviečianti diodo vėliavėlė:

U16 vėliavėlė = 0;

Pertraukimų tvarkytuvas, generuojantis laikmatį. Nes Tas pats pertraukimas generuojamas, kai veikia DAC, pirmiausia patikriname, ar jį suaktyvino laikmatis:

Void TIM6_DAC_IRQHandler())( /* Kadangi šis tvarkytuvas taip pat iškviečiamas DAC, reikia patikrinti * ar neįvyko TIM6 laikmačio skaitiklio perpildymo pertraukimas. */ if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( flag++; if (flag> 3) flag = 0 /* Išvalyti apdorojamo pertraukimo bitą (TIM6, TIM_IT_Update (GPIOD, LED));

pagrindinė funkcija:

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

Paliekame ciklą tuščią. Skaitiklis savo darbą atlieka asinchroniškai, o pertraukimas yra pertraukimas, kad nepriklausytų nuo šiuo metu atliekamos operacijos.

STM32 turi daug labai patogių ir lanksčių laikmačių. Netgi jauniausias mikrovaldiklis (STM32F030F4P6) turi 4 tokius laikmačius.

8. Nustatykite projektą – pridėkite reikiamus failus

Norėdami naudoti laikmatį, turėsime įtraukti periferinės bibliotekos failą stm32f10x_tim.c. Lygiai taip pat dešiniuoju pelės mygtuku spustelėkite „Workspace“ (langas kairėje) „StdPeriphLib“ grupėje, Add –> Add files, file LibrariesSTM32F10x_StdPeriph_Driversrcstm32f10x_tim.c.

Taip pat turite įgalinti šio failo antraštės naudojimą. Atidarykite stm32f10x_conf.h (dešiniuoju pelės mygtuku spustelėkite šio failo pavadinimą kode „Atidaryti stm32f10x_conf.h“. Ištraukite eilutės #include „stm32f10x_tim.h“ komentarą).

9. Pridėkite laikmatį

Vėlavimas tuščiu ciklu yra šventvagystė, ypač tokio galingo kristalo, kaip STM32, su daugybe laikmačių. Todėl šį delsą atliksime naudodami laikmatį.

STM32 turi skirtingus laikmačius su skirtingais savybių rinkiniais. Paprasčiausi yra pagrindiniai laikmačiai, sudėtingesni yra bendrosios paskirties laikmačiai, o sudėtingiausi yra išplėstiniai laikmačiai. Paprasti laikmačiai apsiriboja tiesiog laikrodžio ciklų skaičiavimu. Sudėtingesniuose laikmačiuose pasirodo PWM. Pavyzdžiui, sudėtingiausi laikmačiai gali generuoti 3 fazių PWM su tiesioginiais ir atvirkštiniais išėjimais ir galiojimo laiku. Mums užteks paprasto laikmačio, numerio 6.

Šiek tiek teorijos

Viskas, ko mums reikia iš laikmačio, yra suskaičiuoti iki tam tikros vertės ir sugeneruoti pertraukimą (taip, mes taip pat išmoksime naudoti pertraukimus). TIM6 laikmatis veikia iš sistemos magistralės, bet ne tiesiogiai, o per išankstinį skalerį - paprastą programuojamą skaitiklį (tik pagalvokite, SSRS buvo gaminami specialūs mikroschemų skaitikliai, o programuojamų ypač trūko - o dabar aš Apie tokį skaitiklį kalbu tik pro šalį). Preskalerį galima sukonfigūruoti bet kokiai vertei nuo 1 (ty skaitiklis gaus visą magistralės dažnį, 24 MHz) iki 65536 (t. y. 366 Hz).

Laikrodžio signalai savo ruožtu padidina vidinį laikmačio skaitiklį, pradedant nuo nulio. Kai tik skaitiklio reikšmė pasiekia ARR reikšmę, skaitiklis persipildo ir įvyksta atitinkamas įvykis. Kai įvyksta šis įvykis, laikmatis vėl įkelia 0 į skaitiklį ir pradeda skaičiuoti nuo nulio. Tuo pačiu metu tai gali sukelti pertraukimą (jei sukonfigūruota).

Tiesą sakant, procesas yra šiek tiek sudėtingesnis: yra du ARR registrai - išorinis ir vidinis. Skaičiuojant esama reikšmė lyginama konkrečiai su vidiniu registru ir tik jai persipildžius vidinė atnaujinama iš išorinio. Tokiu būdu galite saugiai pakeisti ARR, kol veikia laikmatis – bet kuriuo metu.

Kodas

Kodas bus labai panašus į ankstesnį, nes... Visų periferinių įrenginių inicijavimas vyksta vienodai – su vienintele išimtimi, kad TIM6 laikmatis kabo ant APB1 magistralės. Todėl įjungiant laikmatį: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

Dabar sukuriame TIM_TimeBaseInitTypeDef tipo struktūrą, inicijuojame ją (TIM_TimeBaseStructInit), sukonfigūruojame, perduodame laikmačio inicijavimo funkcijai (TIM_TimeBaseInit) ir galiausiai įjungiame laikmatį (TIM_Cmd).

TIM_TimeBaseInitTypeDef TIM_InitStructure; // Sukurkite struktūrą TIM_TimeBaseStructInit(&TIM_InitStructure); // Inicijuoti struktūrą TIM_InitStructure.TIM_Prescaler = 24000; // Prescaler TIM_InitStructure.TIM_Period = 1000; // Laikmačio laikotarpis TIM_TimeBaseInit(TIM6, &TIM_InitStructure); // Laikmačio nustatymo funkcija TIM_Cmd(TIM6, ENABLE); // Įjunkite laikmatį

Kokie yra stebuklingi skaičiai? Kaip prisimename, magistralės laikrodžio dažnis yra 24 MHz (su mūsų projekto nustatymais). Laikmačio preskalerį nustatę į 24000, šį dažnį padaliname iš 24 tūkstančių ir gauname 1 kHz. Tai dažnis, kuris pateks į laikmačio skaitiklio įvestį.

Skaitiklio reikšmė yra 1000. Tai reiškia, kad skaitiklis persipildys per 1000 laikrodžio ciklų, t.y. lygiai per 1 sekundę.

Po to mes iš tikrųjų turime veikiantį laikmatį. Bet tai dar ne viskas.

10. Spręskime trukdžius

Gerai, trukdžiai. Man kažkada (PIC metu) jie buvo tamsus miškas, ir aš stengiausi jų visai nenaudoti - o iš tikrųjų nežinojau, kaip. Tačiau juose yra galios, kurios visiškai neverta ignoruoti. Tiesa, pertraukimai STM32 yra dar sudėtingesnis dalykas, ypač jų išstūmimo mechanizmas; bet apie tai vėliau.

Kaip jau minėjome anksčiau, laikmatis generuoja pertraukimą, kai skaitiklis persipildo – jei šio įrenginio pertrūkių apdorojimas apskritai įjungtas, įjungiamas šis konkretus pertraukimas, o ankstesnis atstatomas. Analizuodami šią frazę suprantame, ko mums reikia:

  1. Įjungti TIM6 laikmačio pertraukimus apskritai;
  2. Įjungti TIM6 laikmačio pertraukimą skaitiklio perpildymui;
  3. Parašykite pertraukimų tvarkyklės procedūrą;
  4. Apdoroję pertraukimą, nustatykite jį iš naujo.

Pertraukimų įjungimas

Tiesą sakant, čia nėra nieko sudėtingo. Visų pirma, įjunkite TIM6 pertraukimus: NVIC_EnableIRQ(TIM6_DAC_IRQn); Kodėl šis vardas? Nes STM32 branduolyje pertraukimai iš TIM6 ir iš DAC turi tą patį skaičių. Nežinau, kodėl taip buvo padaryta – taupymas, skaičių trūkumas ar tiesiog kažkoks paveldimas dalykas – bet kokiu atveju tai nesukels problemų, nes šiame projekte DAC nenaudojamas. Net jei mūsų projekte būtų naudojamas DAC, įvesdami pertraukimą galėtume sužinoti, kas tiksliai jį pavadino. Beveik visi kiti laikmačiai turi vieną pertraukimą.

Pertraukimo šaltinio įvykio konfigūravimas: TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); - įjungti TIM6 laikmačio pertraukimą pagal TIM_DIER_UIE įvykį, t.y. ARR vertės atnaujinimo įvykis. Kaip prisimename iš paveikslėlio, tai įvyksta kartu su skaitiklio perpildymu – taigi būtent tokio įvykio mums reikia.

Šiuo metu laikmačio atvejų kodas yra toks:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, Įgalinti); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit(&TIM_InitStructure); TIM_InitStructure.TIM_Prescaler = 24000; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM6, &TIM_InitStructure); TIM_Cmd(TIM6, ĮJUNGTI); NVIC_EnableIRQ(TIM6_DAC_IRQn); TIM_ITConfig(TIM6, TIM_DIER_UIE, ĮJUNGTI);

Nutraukti tvarkymą

Dabar negalite paleisti projekto - pats pirmasis laikmačio pertraukimas neras savo tvarkyklės, o valdiklis pakibs (tiksliau, jis pateks į HARD_FAULT tvarkyklę, o tai iš esmės yra tas pats). Turime tai parašyti.

Šiek tiek teorijos

Jis turi turėti labai konkretų pavadinimą, negaliojantį TIM6_DAC_IRQHandler(void). Šis pavadinimas, vadinamasis pertraukimo vektorius, aprašytas paleisties faile (mūsų projekte tai startup_stm32f10x_md_vl.s – galite patys įsitikinti, 126 eilutė). Tiesą sakant, vektorius yra pertraukimų tvarkyklės adresas, o kai įvyksta pertraukimas, ARM branduolys patenka į pradinę sritį (į kurią išverčiamas paleidimo failas - t. y. jo vieta nustatoma visiškai griežtai, pačioje „flash“ pradžioje). atmintyje), ten ieško vektoriaus ir pereina į reikiamą kodo vietą.

Įvykio patikrinimas

Pirmas dalykas, kurį turime padaryti įvesdami tokį tvarkyklę, yra patikrinti, kuris įvykis sukėlė pertraukimą. Dabar turime tik vieną renginį, bet realiame projekte gali būti keli įvykiai vienu laikmačiu. Todėl mes patikriname įvykį ir vykdome atitinkamą kodą.

Mūsų programoje šis patikrinimas atrodys taip: if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) - viskas aišku, funkcija TIM_GetITStatus patikrina, ar laikmatyje yra nurodyto įvykio, ir grąžina 0 arba 1.

UIF vėliavėlės išvalymas

Antrasis žingsnis yra išvalyti pertraukimo vėliavėlę. Grįžkite į paveikslėlį: paskutinis UIF grafikas yra pertraukimo vėliavėlė. Jei jis nebus išvalytas, kitas pertraukimas nebus iškviestas, o valdiklis vėl pateks į HARD_FAULT (kas tai yra!).

Veiksmų vykdymas pertraukimo metu

Tiesiog pakeisime šviesos diodo būseną, kaip ir pirmoje programoje. Skirtumas tas, kad dabar mūsų programa tai apsunkina! Tiesą sakant, taip rašyti yra daug teisingiau.

If(state) GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_SET); else GPIO_WriteBit(GPIOC, GPIO_Pin_8, Bit_RESET); būsena = 1 - būsena;

Naudojame globalų kintamąjį int state=0;

11. Visas projekto kodas su laikmačiu

#include "stm32f10x_conf.h" int state=0; void TIM6_DAC_IRQHandler(void) ( if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( TIM_ClearITPendingBit(TIM6, TIM_IT_Update); if(state) GPIO_Write_Bit(GPIOCin_8); C, GPIO_ Pin_8, Bit_RESET); = 1 - būsena; ) ) void main() ( RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit(&GPIO_InitStructure) IO_InitS tructure.GPIO_Mode = GPIO_Mode_Out_PP GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIOC, &GPIO_WriteBit(GPIO_Pin_8, Bit_TIM6, ENABLE); TIM_InitStructure. NVIC_EnableIRQ(TIM6_DAC_IRQn); TIM_ITConfig(TIM6, TIM_DIER_UIE, ĮJUNGTI );

Archyvas su laikmačio projektu.

Na, beje, laikmatis gali perjungti patį koją, be pertrūkių ar rankinio apdorojimo. Tai bus trečiasis mūsų projektas.

Visas ciklas:

1. I/O prievadai

2. Laikmatis ir pertraukimai

3. Laikmačio išėjimai

4. Išoriniai pertraukimai ir NVIC

5. Įdiekite FreeRTOS

Pranešimo peržiūros: 235

Dalintis