čítač/časovač 1 - použití pro zpoždění

Ahoj

ATmega32A, XTAL 9,216MHz zdroj externího přerušení na EXT_INT0

Mám k ATmega připojený radiový modul RFM12BP-433 a právě se potím s funkcí “ping”. Odešlu radiový paket s definovanou zprávou a pokud do předem stanoveného času (0,5s) nepříjde odpověď od protistanice, tak napíše “timeout…” a poku odpověď dojde, tak napíše “PING OK”.

Problém u mě nastal v momentě, kdy mám čekat těch 0,5s na odpověď (ta může příjt v různém čase - maximum je ale pevně stanoveno na 500ms). Pokud radiovému modulu RFM12 příjdou data, tak shodí stav linky nIRQ do log.0, čímž aktivuje přerušení EXT_INT0.

Nevím jestli jsem to pochopil správně, že čítač/časovač jsou vlastně takové hodiny “na pozadí”. Tj. čekám těch 0,5s a pokud kdykoliv v tomto čase příjde odpověď a mám povoleno globální přerušení, tak se toto spustí a provede a pak zase program poběží dál.
Udělal jsem si tedy funkci:

void timer1_delay(unsigned int time_ms)                    // zpouzdeni pomoci timer/counter 1
{
    unsigned int pocet_taktu = 0;
    unsigned int counter1 = 0;
    pocet_taktu = time_ms * 9;
    TCCR1B = 0x05;                                          // NORMAL mode, prescaler 1024
    TCNT1L = 0x00;                                           // vymazani citaciho registru 
    TCNT1H = 0x00;
    while(1)
    {
    counter1 = TCNT1L | (TCNT1H<<8);
    if (counter1 >= pocet_taktu)                            // cekej dokud TCNT1 nedosahne pozadovane hodnoty
    {
        TCNT1L = 0x00;
        TCNT1H = 0x00;
        TCCR1B = 0x00;                                          // NORMAL mode, timer/counter1 off
        break;
    }
    }
    return;                                      
}

Kdy jako parametr předávám požadovanou dobu čekání. Příklad:
Chci čekat 500ms, tak:
pocet_taktu = 500 * 9 = 4500
prescaler je nastaven na 1024, to budeme mít celkem 4500*1024=4608000 taktů (=0,5s při 9,216MHz) než se funkce opustí (pokud nedojde mezitím přerušení - v přerušení mám na 1. řádku TCCR1B = 0x00;)

V simulaci to funguje jak má - pokud předám např. jako parametr “1”, tak skutečně čeká 1ms, v reálu (parametr “500”) se ale běh programu “sekne” a vypadá to, že to stojí právě v té čekací funkci. Ještě před tím, než ji volám, tak povoluji globální přerušení (kvůli EXT_INT0) a jinak EXT_INT0 se mi určitě zatím nespouští (ještě nemám protistanici) a i jsem to zkoumal logickým analyzátorem, že linka nIRQ nespadnedo 0 a tím pádem to nemůže být tím ovlivněno.

Může mi prosím někdo osvětlit, jak tedy použít timer/counter jako obyčejnou zpožďovací smyčku ?

Děkuji

V kódu nevidím žádný problém, proč by to mělo zůstávat právě zde viset. Zkusil bych jako první rozsvícení a zhasnutí LEDky na nějakém pinu (pokud nemáš připojenou žádnou LEDku, použij nějaký volný pin a připoj ho k analyzátoru) :

[code]void timer1_delay(unsigned int time_ms) // zpouzdeni pomoci timer/counter 1
{
unsigned int pocet_taktu = 0;
unsigned int counter1 = 0;

//ROZSVÍTIT LEDKU - po odladění to zase odstraníš

pocet_taktu = time_ms * 9;
TCCR1B = 0x05;                                          // NORMAL mode, prescaler 1024
TCNT1L = 0x00;                                           // vymazani citaciho registru
TCNT1H = 0x00;
while(1)
{
    counter1 = TCNT1L | (TCNT1H<<8);
    if (counter1 >= pocet_taktu)                            // cekej dokud TCNT1 nedosahne pozadovane hodnoty
    {
        TCNT1L = 0x00;
        TCNT1H = 0x00;
        TCCR1B = 0x00;                                          // NORMAL mode, timer/counter1 off
        break;
    }
}

//ZHASNOUT LEDKU - po odladění to zase odstraníš

return;                                     

}
[/code]

Jinak bych kód možná navíc trochu zpřehlednil :

[code]void timer1_delay(unsigned int time_ms) // zpouzdeni pomoci timer/counter 1
{
unsigned int pocet_taktu;

//ROZSVÍTIT LEDKU - po odladění to zase odstraníš

pocet_taktu = time_ms * 9;
TCCR1B = 0x05;                                          // NORMAL mode, prescaler 1024
TCNT1 = 0x0000;                                           // vymazani citaciho registru

while ((TCNT1 < pocet_taktu) && (PaketPrijat == false));  // cekej dokud TCNT1 nedosahne pozadovane hodnoty a není přijat paket

TCNT1 = 0x0000;
TCCR1B = 0x00;                                          // NORMAL mode, timer/counter1 off

//ZHASNOUT LEDKU - po odladění to zase odstraníš

return;

}
[/code]

Jinak vytvořit delay tím, že zůstanu na 500ms viset v nějaké funkci (a je úplně jedno, jestli ta funkce počítá od 0 do milionu nebo od milionu do 0 nebo k tomu použije timer) je prostě fuj. A přestože jsi timer použil správně, tak jeho použitím jenom klesá nepřesnost času vlivem přerušení, která za tu dobu mohou přijít. Procesor má tak jako tak plné ruce práce s delayem a jinou práci v tu chvíli prostě nedělá.

Výhodnější je netestovat přímo čítač. Nastavíš podmínky čítání (předdělič, zdroj hodin, atd), nastavíš která událost má vyvolat přerušení (přetečení, komparátor, atd.) , povolíš přerušení od časovače, povolíš globální přerušení. Hotovo. Na patřičném vektoru přerušení umístíš kód který se bude volat tak často jak často nastane uvažovaná událost (třeba 1x za 10ms) a ten může například snižovat stav nějaké proměnné až k nule. Může současně dělat i další užitečné věci. Pokud chceš tedy čítač použít jako deterministický delay(), stačí nastavit výše zmíněnou proměnou a pak jen testovat kdy dosáhne nulové hodnoty. Popisuju to obecně - konkrétní nastavení čítače Ti tu poradí jiní. A taky nezapomeň, že v C nejsou některé (většina) operací na úrovni ASM atomická.

Radius Ti tady popsal (jedno z mnoha možných) řešení delaye tak, aby procesor mohl dělat i jinou užitečnou práci během čekání. V jediném bodě bych to trošku poopravil - a to v tom

Spíš bych napsal jak často nastane nejčastější událost (můžeš jich “časovat” víc). Ještě lépe pak (kvůli přesnosti časování) - nastavit přerušení na alespoň 5-ti násobek rychlosti nejčastější události (odchylka v časování max. 20% u nejčastější události).

To volání (událost) jsem myslel ve vztahu k časovači, né ne vztahu k tomu co se procesorem ovládá/reguluje/snímá. Granularitu si musí odhadnout programátor sám :wink: