ARM: sisteminis laikmatis

Posted: 2014-12-20 in Darbeliai
Žymos:,

ARM architektūroje numatytas sisteminis laikmatis, vadinamas SysTick. Jis yra bendras visiems ARM procesoriams nepriklausomai nuo gamintojo, o jo visi registrai ir kita chernia yra CMSIS bibliotekoje. CMSIS, kaip kadaise rašiau, yra bendrinė ir esminė ARM konsorciumo biblioteka. Taigi, kas joje yr, yra visuose ARM procesoriuose — ar tai būtų LPC, ar STM32, ar Atmel ARM.

SysTick’as yra visiškai „tūpas“ laikmatukas. Jis tik tiek ir moka: „įsižiebti“ kas nurodytą laiko tarpą. Vidinis jo skaitliukas yra 24 bitų. Laikmatuką gali valdyti tiek procesoriaus taktinis dažnis, tiek specialus dažnio etalonas (na, gal koks nors labai tikslus kvarcas kartais įsiūtas gamintojo, jei to reikia).

Sisteminiai laikmačiai paprastai naudojami operacinėse sistemose įjunginėti pagrindinį visų užduočių dispečerį. Primityvių OS yra nemažai sukurta ir šiems mažiems procesoriukams ir perjunginėjimą tarp įvairių „gijų“ atlieka būtent dispečeris pagal sisteminį laikmatį. Man, tuo tarpu, norisi paprasčiausio dalyko: milisekundžių skaitliuko ir „busy-wait“ pauzės funkcijos, kuri pagal milisekundžių skaitliuką užlaikytų kodo vykdymą. Be abejo, „busy-wait“ yra labai negeras dalykas, bet primityviems darbeliams jis yra parankus ir patogus. Dar man norisi turėti patogų programinį laikmatį, pagrįstą JeeLabs MilliTimer. O štai ką aš toliau iš to sisteminio laikmačio išvystysiu — bus matyt. Šiandienai — primityvus skaitliukas, pora funkcijikių ir funkcinė C MilliTimer adaptacija.

Pradžiai kiek teorijos. SysTick laikmatis valdomas keturiais registrais:

  • SYST_CSR — SysTick Control and Status Register
  • SYST_RVR — SysTick Reload Value Register
  • SYST_CVR — SysTick Current Value Register
  • SYST_CALIB — SysTick Calibration Value Register

Įdomu, kad šie registrai, kaip ir priklauso ARM, yra 32 bitų, bet tik dalis tų bitų naudojama…

SYST_CSR registrą sudaro šie esminiai bitai:

  • 0 — ENABLE. Na, viskas aišku. Vienetukas laikmatį įjungia, nuliukas — išjungia.
  • 1 — TICKINT. Vienetukas įjungia pertraukimą, įvykstantį laikmačiui „persivertus“.
  • 2 — CLKSOURCE. Nurodo, iš kur imti laikmačio darbo impulsus. 0 — iš procesoriaus, 1 — iš etaloninio taktų šaltinio.
  • 16 — COUNTFLAG. Laikmačio darbo „flegas“. Po pirmojo laikmačio „persivertimo“ nustatomas į vienetuką. Įdomus bestija, jį nuskaičius automatiškai įgyja nulinę reikšmę. Taigi labai patogu, galima sekti, ar laikmatis persivertė, ar ne. Gali būti naudingas programiniams laikmačiams ar tiesiog įvykusių pertraukimų sekimui.
  • Kiti bitukai „rezervuoti“.

SYST_RVR registre nustatoma laikmačio „persivertimo“ reikšmė. T.y. laikmatis suskaičiavęs iki šios reikšmės įvykdo pertraukimą (jei toks užduotas) ir pradeda skaičiuoti iš naujo. Čia ir reikia nurodyti reikšmę, kaip dažnai laikmatis turi „šaudyti“ pertraukimus. Nors registras 32 bitų, bet naudojami tik 24. Taigi iš čia eina apribojimas: laikmatis gali suskaičiuoti maksimum iki 16777215, t.y. iš viso 16777216 taktų. Daugiklio (prescaler) jis neturi. Jei ant sisteminio laikmačio norėsite pakabinti kažką trunkančio dar ilgiau, reikės programinio sprendimo. Bet šiaip tai būtų nesąmonė, nes SysTick yra skirtas generuoti dažniems pertraukimams. Jei norite minutes trunkančių laikmačių, tai tik programiniai ir lieka.

SYST_CVR — čia viskas aišku, tai yra „tikrasis“ sisteminio laikmačio skaitliuko registras. Va jame ir skaičiuojami taktai ir lyginami su SYST_RVR. Šitą registrą galima nuskaityti, pirmuose trijuose jo baituose sukasi skaitliuko reikšmė (iš to ir yra 24 bitų skaitliukas). Beje, į SYST_CVR galima ir įrašyti… bet ką. Visiškai nesvarbu, ką įrašysite, jis bus „nunulintas“. Tad jei reikia staigiai nunulinti SysTick’ą, rašykite bile ką į šitą registrą. Va taip va.

SYST_CALIB — man nevisai aiškus reikalas. Jis skirtas išimtinai tik skaitymui ir ten galima rasti va tokią informaciją:

  • 0–23 bitai (trys baitukai), TENMS: čia įrašyta 10 ms užlaikymo reikšmė. Pagal kažkokį etaloninį generatorių. Bbž, aš šito reikalo nesuprantu ir, tikiuosi, niekada neprireiks.
  • 30 — SKEW. Nurodo, kas yra įrašyta TENMS baituose. Jei nulis, tai TENMS yra kažkokia reali reikšmė. Jei vienetukas, tai TENMS reikšmė yra šiukšlinė…
  • 31 — NOREF. Nurodo, ar įrenginys turi etaloninį taktų generatorių. 0 — turi, 1 — neturi. Va šitas reikalas įdomus ir priklauso nuo gamintojo. T.y. čia nėra kažkas tokio, ką mes, procesoriaus savininkai, galėtume keisti. Jei tą etaloninį generatorių gamintojas įlipdė, tai jis ir bus. Jei neįlipdė — nebus. Va ir viskas. Jei šitame bituke yra 1, t.y. nėr etaloninio generatoriaus, tai CLKSOURCE bitukas irgi bus 1 ir jo pakeisti nebus galima.

Kaip ir tiek apie SysTick registrus. O dabar žiūrom, ką čia galim realaus užkurti. Man reikia buko laikmačio, kuris įvykdytų paprogramę kas vieną milisekundę. Ir nieko daugiau. Procesoriaus dažnis yra 72 MHz, tai šitą reikalą reikia padalinti iš tūkstančio (tiek milisekundžių yra sekundėje) ir gautą reikšmę nurodyti, kaip skaitliuko „persivertimą“. Vienetuką tik reikia atimti, nes skaičiuojama tai nuo nulio, draugai programuotojai 🙂 Toliau reikia laikmatį įjungti, įgalinti pertraukimą ir nurodyti taktų šaltinį (procesorių).

Dažnį ir taktų skaičių apsirašome šitaip:

/* Procesoriaus dažnis ir laikmačio intervalas */
#define F_CPU 		72000000UL
#define TIMER_TICK	F_CPU/1000-1

Laikmačio konfigūravimui ir visokiems bitukams CMSIS bibliotekoje yra sukurtos gražios struktūros ir pavadinimai. SysTick konfigūravimo struktūra atrodo va šitaip:

typedef struct
{
  __IO uint32_t CTRL;  /*!< Offset: 0x00  SysTick Control and Status Register */
  __IO uint32_t LOAD;  /*!< Offset: 0x04  SysTick Reload Value Register       */
  __IO uint32_t VAL;   /*!< Offset: 0x08  SysTick Current Value Register      */
  __I  uint32_t CALIB; /*!< Offset: 0x0C  SysTick Calibration Register        */
} SysTick_Type;

Čia iškarpa iš CMSIS bibliotekos failo core_cm3.h. Toliau ten yra registrų bitų pavadinimai ir aprašymai. Aš ten labai nesigilinau, bet mano poreikiams laikmačio inicializacija gavosi va maždaug tokia:

SysTick->LOAD=TIMER_TICK; // Viskas aišku, čia nurodom, kas kiek taktų laikmačiui „persiversti“.
SysTick->VAL=TIMER_TICK;  // Į SYST_CVR registrą įrašom „bile ką“, nunulinam skaitliuką.
SysTick->CTRL=	SysTick_CTRL_CLKSOURCE_Msk | // Nurodom taktų šaltinį — procesorių.
                SysTick_CTRL_TICKINT_Msk   | // Nurodom, kad laikmatis vykdytų pertraukimus.
                SysTick_CTRL_ENABLE_Msk;     // Įgalinam laikmatį.

Na va, milisekundinis skaitliukas padarytas. Kas toliau? O toliau — pertraukimo apdorojimo paprogramė. Ji vadinasi va šitaip:

void SysTick_Handler(void) {
	...
	...
}

Su ja yra viena subtilybė. Pirmą kartą kompiliuodamas gavau pranešimą: multiple definition of `SysTick_Handler’. Iš pradžių nesupratau. Paskui pradėjau naršyti failus. Tada projekte pastebėjau kartu sukurtą failą stm32f10x_it.c. Pasirodė, kad jame jau yra nieko nedaranti SysTick_Handler funkcija. Aš ją užkomentavau, palikau saviškę ir viskas pasidarė ok. Turėkit šitą reikalą omeny, tame faile yra dar visa krūva kitų pertraukimų apdorojimo funkcijų.

Susikūriau mažą bibliotekėlę, kuri, visai kaip Arduino, palaiko dvi funkcijas: millis() ir delay(…). Taip pat joje yra ir inicializavimo funkcija, paleidžianti laikmatį ir laikmačio pertraukimo apdorojimas. Pertraukimų stabdyti nereikia, nes paprastos operacijos su 32 bitų (ir/arba mažesniais) skaičiukais ARM’uose yra atominės, t.y. galima juos sudėti, atimti bei priskirti per vieną procesoriaus taktą ir galima drąsiai tikėtis, kad reikšmė nuskaitymo metu nepakis. Priešingai, nei AVR, kur atominės yra tik 8 bitų operacijos, o jau su uint32_t reikia sustabdyti pertraukimus, kad būtų galima saugiai tokį skaičiuką nuskaityti-modifikuoti.

Beje, su ARM pertraukimais nėra taip paprasta, kaip AVR. AVR juos arba visus galima sustabdyti, arba vėl įgalinti. O štai ARM tokio smagumo nėra. Buvau kažką radęs, bet taip ir nežinau, ar toks dalykas veikia, smarkiai abejoju. ARM’uose galima dalimis stabdyti pertraukimus. Tad jei SysTick’as darytų kažką daugiau ir išorinės paprogramės norėtų kažką iš to „daugiau“ pasiimti, reikėtų stabdyti tik SysTick pertraukimą — nėra prasmės stabdyti daugiau jų. O mano mažiems poreikiams dabar nė to nereikia. Kol kas, be abejo.

Taigi prikabinu jums projektėlio išeities kodą, kuriame yra vienas mirksiukas iš praeito įrašo apie pertraukimus, o antras mirksiukas — pagrindiniame cikle su ką tik iškepta delay funkcija. 200 ms mirksiukas išjungtas ir 1000 ms (1 s) — įjungtas. Va ir filmukas, kuriame raudonas mirksiukas mirksi nuo Timer2 pertraukimo, o geltonas — pagrindiniame cikle su delay:

Dabar keliaujam prie programinių laikmačių. Jie kur kas geresni už delay, nes minimaliai užima procesorių. Tiesiog pagrindiniame programos cikle nuolat tikrinama, ar konkretus laikmatis „iškapsėjo“ su savo individualia reikšme. Jei norite, pasiskaitykite apie JeeLabs MilliTimer naudojimą, nes aš padariau neobjektinį šios klasės kloną.

Štai dar vienas mirksiukas. Dabar geltonas diodas keičia savo būseną kas sekundę, o raudonas — kas pusę sekundės. Naudojami tik programiniai laikmačiai ir, be abejo, SysTick’as:

Išeities kodą irgi galite parsisiųsti ir naudoti savo reikmėms.

Reklama

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti / Keisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti / Keisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti / Keisti )

Google+ photo

Jūs komentuojate naudodamiesi savo Google+ paskyra. Atsijungti / Keisti )

Connecting to %s