Spíše sporadicky než pravidelně pracuji na finální podobě časomíry, jejíž srdcem bude TI MSP430G2553. V posledních dnech jsem pracoval na přerušeních od vstupů (startovací tlačítko, koncové spínače) a od časovače.
První dílčí úspěch je napájení. Nevěděl jsem, zda stabilizátor LE33 bude dodávat dostatečný proud. Problém byl jediný, v dokumentaci byly přehozené piny Vin a Vout.
Další problém se objevil na vstupech, které způsobovaly přerušení zcela náhodně. Toto více méně vyřešilo připojení kondenzátorů 100 nF mezi pin a zem.
Větší úskalí nastalo při programování. Přístup s přerušeními se diametrálně liší od toho, na co jsem byl zvyklý a vyžaduje si naprosto jiné smýšlení o problému. Nakonec jsem vypotil kód, který při sepnutí startovacího tlačítka spustí oba časovače a koncový spínač zase jeden z časovačů vypne. Mělo by tak dojít k docela přesnému měření času.
Problém první: jak se ovládá druhý časovač. Původně jsem měl za to, že je v MCU časovač A i časovač B. To byl omyl, v zařízení jsou dva časovače, ale jsou to A0 a A1. Časovač A0 jsem ovládal příkazy, které se běžně dají najít, ale jak na časovač A1?
TA0CCTL0 = CCIE; // CCR0 interrupt enabled
TA0CCR0 = 2048-1; // 2048 -> 1 sec for ACLK/8
TA0CTL = TASSEL_1 + ID_3 + MC_0; // ACLK, /8, stopped
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
TA1CCR0 = 2048-1; // A1 CCR0 level
TA1CTL = TASSEL_1 + ID_3 + MC_0; // ACLK, /8, stopped
TA0R = 0; // clear counter
TA1R = 0; // clear counter
Za těmito řádkami stojí spousta hledání, pokusů a omylů 🙂 Máme tedy nastavené časovače, tak je spustíme
TA1CTL |= (MC_1 + TACLR); //timer A1 starts counting
TA0CTL |= (MC_1 + TACLR); //timer A0 starts counting
a zase vypneme
TA0CTL &= ~MC_1; //stop timer A0
TA1CTL &= ~MC_1; //stop timer A1
Pokud časovač dosáhne hladiny CCR0 spustí přerušení. Pro tyto časovače mají vektory přerušení poněkud matoucí názvy
#pragma vector=TIMER0_A0_VECTOR
#pragma vector=TIMER1_A0_VECTOR
Zbytek ISR se nijak neliší od běžně dostupných návodů.
Další problém bylo zmenšení ISR pro stisknutá tlačítka na minimum. Pokud pošlete procesor spát a budete naslouchat přerušením. Po provedení ISR jde zase spát. My však potřebujeme aby vykonal ještě nějaké instrukce. Základní myšlenka je taková: pokud se nám spustí několik přerušení najednou, vykonávají se podle předem dané priority, pokud se vykonává ISR k jednomu přerušení, nemůže se spustit ISR jiného a proto potřebujeme v kritických chvílích, jako je měření času, udržet ISR na co nejmenší délce, třeba jen nastavit vlajku a podle ní vykonat zbytek instrukcí jinde, tam, kde se může program přerušit a procesor obsloužit nově příchozí přerušení. Jak na to? Na konci
main()
většinou máme něco jako
_BIS_SR(LPM3_bits + GIE); // Enter LowPowerMode 3 w/ interrupt
. To jsem obalil nejdříve cyklem
while(1)
, ve kterém je strom
if
, který vykonává zbývající práci po přerušeních, na základě nastavené vlajky.
_BIS_SR(LPM3_bits + GIE); // Enter LowPowerMode 3 w/ interrupt
přesunu do jeho
else
, takže se provede tolik cyklů while(), kolik máme nastavených vlajek, jsou-li všechny vlajky vyčištěny, jde procesor spát a čeká se na další přerušení.
To ale nestačí, protože normálně po vykonání ISR jde procesor zase spát. V tom mu dočasně zabráníme tím, že na konec ISR, které potřebují ještě vykonat něco mimo, a tedy nastaví příslušnou vlajku, umístíme ještě řádek
__bic_SR_register_on_exit(LPM3_bits + GIE); // Don't Enter LowPowerMode 3 immidiately
Tak se vrátíme do cyklu while() v main(). V té době už můžeme obsluhovat další přerušení.
A to je vše podstatné, ideu ze které jsem čerpal najdete na obrázku v souboru www.ti.com/lit/an/slaa294a/slaa294a.pdf