Projekt: Stopky na čtyřmístném 7-segmentovém displeji

Panda38: Umí to nejenom ATmega8, ale i ATmega48(88,168,328).
ATmega128, ATmega16(32), ATmega644 umí dokonce i jet na krystal + druhý krystal pro oscilátor.

pro mě jako začátečnici, je to docela složité… ale děkuji. Při dlouhých zimních večerech se na to zkusím podívat :slight_smile:

Tak to už letos nestihneš, když je už skoro jaro. :slight_smile:

Když se s časovačema a přerušením naučíš pracovat, tak Ti to ušetří do budoucna obrovské množství práce. Například právě zobrazení na multiplexovaném 7-segmentovém displeji většinou řeším právě přes časovač a přerušení. Ve vlastním programu pak jenom zapisuju hodnoty do RAM a nestarám se o to, kdy se má která část zobrazit. Když to pak zkombinuju s OCR registrem a AD převodníkem na kterém je fotoodpor, tak mám k dispozici dokonce i třeba řízení jasu těch 7-segmentovek.

Principiálně to udělám takto :

Hlavní program

Inicializace -

    Spustit AD převodník do freerunning režimu (tzn., že provádí převod pořád dokola) se zarovnáním doleva a pak číst jen horních 8 bitů - ten se použije ke snímání okolního světla a následné regulaci jasu displeje. Ve dne pak displej svítí více a je tak dobře vidět, za tmy pak displej sníží jas a nezáří do okolí jako reflektor. Používám to ve většině zařízení, kde používám LED 7-segmentové zobrazovače, podsvícení displejů, LED jako kontrolky apod.
    Do RAM nastavit "výchozí hodnoty" pro displej
    Pokud už je alespoň jeden AD převod hotový, načíst hodnotu AD převodníku
    Časovač :
              Povolit přeručení TOV a OCR
              Nastavit OCR = do OCR uložit hodnotu z AD převodníku
              Nastavit časovač = spustit časovač s takovým prescalerem, aby vycházel refresh celého displeje alespoň na 80Hz (kvůli blikání).
Příklad : MCU běží na IntRC 8 MHz, mám 4-místný 7-segmentový displej.
8000000(rychlost mcu)/256(rozlišení časovače)/4(počet míst displeje)/prescaler<80 =>
8000000(rychlost mcu)/256(rozlišení časovače)/4(počet míst displeje)/80(požadovaný refresh)>prescaler =>
8000000/256/4/80=97,65625 =>
Prescaler časovače vyberu tak, aby byl menší, než 97,65625 - tedy 64.
Refresh pak mám 8000000/256/4/64=122 Hz

Plus samozřejmě další inicializace, kterou bude vyžadovat program

    Povolit přerušení

Tady už bude hlavní smyčka programu

V hlavní smyčce programu pak jenom vypočítám, co na který pozici má být - v případě 7-segmentového displeje dělám rovnou “překlad” na hodnotu posílanou na port - a uložím na svá místa v RAM. O vlastní zobrazení se starají přerušení od časovače.

Podprogramy pro přerušení

Přerušení TOV :
    Vyberu hodnotu pro další znak
    Na port pošlu hodnotu
    Vyberu a zapnu správnou anodu/katodu
    Přečtu hodnotu z AD převodníku
    Uložím ji do OCR
    Konec

Přerušení OCR :
    Vypnu všechny anody/katody
    Konec

Pokud nebudu komplikovat situaci s AD převodníkem a řízením jasu displeje, pak z inicializace vynecháš nastavení AD převodníku, OCR a povolení přerušení pro OCR.

Podprogram pro přerušení vypadal nějak takto

Přerušení TOV :
    Vypnu všechny anody/katody
    Vyberu hodnotu pro další znak
    Na port pošlu hodnotu
    Vyberu a zapnu správnou anodu/katodu
    Konec

Ve Tvém případě pro stopky bych nastavil časovač do asynchronního režimu s krystalem 32768 Hz a přerušení by vypadalo nějak takhle :

Přerušení TOV:
    Pokud jsou spuštěny stopky, přičíst 1/128 sekundy
    konec

Vlastní program by vypadal takto :

Inicializace -
    Nastavit časovač do asynchronního režimu s prescalerem 1 - perioda pak vychází 32768(frekvence krystalu)/256(rozlišení časovače)/1(prescaler)=128 Hz (počet přerušení za sekundu)

Hlavní smyčka -
    Zpracování tlačítek a spuštění/zastavení/nulování stopek.

Na první pohled to možná vypadá složitě, ale jakmile se s přerušeníma a časovačema naučíš pracovat, tak Ti to následné ušetří spoustu práce a výrazně to zjednoduší programování. Hlavně odpadne “starost” s tím, co se volá pravidelně a co může v podstatě běžet autonomně “na pozadí” vlastního programu…

Neber to, prosím, jako nějaké vytahování se apod. Každý začátek je těžký a všichni jsme si tím prošli. Zkus to vzít jako inspiraci a náznak možností, které Ti použití časovačů, přerušení a vůbec splupráce s integrovanými periferiemi přímo v mcu přináší. Pro začátek si zkus třeba jenom blikání LEDkou pomocí přerušení, vyzkoušej si co a jak.

K multiplexovanému 7-segmentovému LED displeji : Spousta začátečníků dělá tu chybu, že při přepínání znaků přepne segmentovku a pak změní data pro segmenty nebo opačně nejdřív změní data pro segmenty a pak přepne segmentovku. Pak do segmentovky “prosvítá” znak z vedlejší číslice. Vtip je právě v tom, že musíš nejdřív segmentovku zhasnout (vypnout společnou anodu/katodu), pak změnit data pro segmenty a pak teprve segmentovku rozsvítit (zapnout společnou anodu/katodu) - prostě přepnutí společnou anody/katody udělat ve dvou krocích. Druhá varianta je zhasnout segmenty (poslat “zhasnuté” segmenty), přepnout společnou anodu/katodu a pak teprve vyslat vlastní znak.

Ale to už jsem se nějak moc rozepsal, tak držím palce a přeju spoustu objevů při bádání.

Ti, kteří si nestihli dobastlit venkovní teploměry, mají stále ještě kalendářní zimu :wink:

To ma mrzi, ze to vyzera zlozito. Preto posielam odskusany kod obsahujuci iba tu cast s vyuzitim casovaca.

// projekt pre demonstrovanie pouzitia citaca ako casovej zakladne vsektych dejov v mcu
// ATmega8, CLK 8MHz, prekladac GCC, AVRstudio 4.18, -Os

#include <avr/io.h>
#include <stdint.h>

#define SET(BAJT,BIT) ((BAJT) |= (1<<(BIT)))
#define TST(BAJT,BIT) ((BAJT) & (1<<(BIT)))

// parametre casovaca vytvarajuceho casovu zakladnu
// 124 = 125 - 1, do registra je potrebne nastavit hodnotu o jednu mensiu ako je skutocna hodnota. 
// je to preto, lebo aj 0 je pre casovac stav cez ktory musi prejst
#define PREDVOLBA_CZ 124 


int main(void)
{
   // pre generovanie casovej zakladne bude pouzity casovac 2, lebo ma mod PWM. 
	// To znamena, ze vie pocitat do hodnoty nastavenej predvolby, 
	// po jej dosiahnuti nastavi prislusny bit ktory sa da testovat
	// hodnota citaca sa po diosiahnuti predvolby automaticky vynuluje a pocita od zaciatku. 
	// To znamena, ze cas medzi dvoma nastaveniami bitu nezavisi od casu, 
	// kedy sa program v hlavnej slucke dostane k jeho vynulovaniu

	// nastavenie citaca do modu pocitania len do hodnoty v predvolbe. 
	// Predvolba je v registri

   OCR2 = PREDVOLBA_CZ;
   SET(TCCR2,WGM21);

	// predvolba pre spustenie pocitadla a pre delenie vstupnej frekvencie 64. 
	// to znamena, ze pocitadlo sa zmenni o jednotku 1x za 1/125000 sekundy.
	// Zdroj je frekvencia mcu (8MHz, tak som to nastavil cez fuse a uviedol v projekte)

   SET(TCCR2,CS22);

   //...  // tu si daj vsetky potrebne inicializacie

	// nekonecna slucka
   while(1) {
	  // test, ci uz casovac dosiahol hodnotu v OCR2
     if (TST(TIFR,OCF2)) { 
        // priznak treba rucne vynulovat, kedze sa automaticky nenuluje.
        // To robi iba v pripade pouzitia prerusenie, ale to teraz neriesime.
        // Vsimni si, ze sa bit nuluje zapisom jednicky na jeho poziciu. Je to v manuali.
        SET(TIFR,OCF2); 

		  // ... tu si daj kod, ktory sa ma vykonat 1x za 1ms
	     
     }
   }

	// sem sa program nikdy nedostane
	return;
}

Ak by to vyzeralo stale rozsiahle, tak s vymazanymi edukacnymi komentarmi je zdrojak nasledovny:

// projekt pre demonstrovanie pouzitia citaca ako casovej zakladne vsektych dejov v mcu
// ATmega8, CLK 8MHz, prekladac GCC, AVRstudio 4.18, -Os

#include <avr/io.h>
#include <stdint.h>

// Definice makier s parametrami
#define SET(BAJT,BIT) ((BAJT) |= (1<<(BIT)))
#define TST(BAJT,BIT) ((BAJT) & (1<<(BIT)))

#define PREDVOLBA_CZ 124 

int main(void)
{
   OCR2 = PREDVOLBA_CZ;
   SET(TCCR2,WGM21);
   SET(TCCR2,CS22);

   //...  // tu si daj vsetky potrebne inicializacie

	// nekonecna slucka
   while(1) {
     if (TST(TIFR,OCF2)) { 
        SET(TIFR,OCF2); 

		  // ... tu si daj kod, ktory sa ma vykonat 1x za 1ms
	     
     }
   }
	return;
}

Dufam, ze toto uz snad nevyzera na dlhe zimne vecery. :slight_smile:

Ked si kod vlozis do AVRstudia a prelozis v simulatore, daj si break point na riadok

        SET(TIFR,OCF2); 

Pri nastaveni frekvencie v simulatore na 8MHz zistis, ze sem program pride priemerne vzdy za 1000us. dalsia cinnost moze trvat lubovolny cas medzi od 0us do 990us, ale vzdy sa spusti az zaciatkom dalsej milisekundy.

za tento riadok vloz svoj kod, ale prehladnejsie je vlozit volanie funkcie v ktorej sa az bude ten kod nachadzat. To ti umozni casom vyrobit si funkcie a pouzivat ich v roznych projektoch bez potreby zasahovat do nich (teda ak su v poriadku :slight_smile: ) a tym sa vyhnut hladanim chyb sposobenych kopirovanim neuplneho alebo inak zavisleho kodu. Kazda funkcia ma byt napisana tak, aby mala jasne definovany vstup a vystup. Najlepsie, ked pracuje v maximalne moznej miere s lokalnymi premennymi a interakciu s okolim budes mat slusne popisanu.

Snaz sa na kazdu logicku cinnost pouzit zvlast funkciu. Vedie to prehladnosti, lahsie sa hladaju chyby a jednoduchsie vyuzijes kusy kodu v buducnosti.
Takze potom by to vyzeralo nejako takto.

// projekt pre demonstrovanie pouzitia citaca ako casovej zakladne vsektych dejov v mcu
// ATmega8, CLK 8MHz, prekladac GCC, AVRstudio 4.18, -Os

#include <avr/io.h>
#include <stdint.h>

// Definice makier s parametrami
#define SET(BAJT,BIT) ((BAJT) |= (1<<(BIT)))
#define TST(BAJT,BIT) ((BAJT) & (1<<(BIT)))

#define PREDVOLBA_CZ 124 


// deklaracie funkcii
   void fn_inicializuj_casovu_zakladnu(void);
   void meraj_cas(void);
   void fn_testuj_tlacitko(void);
   void fn_zobraz_dalsiu_sedemsegmentovu(void);
   void fn_inic_sedemsegment(void);
// ... tu si vloz premenne spolocne pre viac funkcii

int main(void)
{
   fn_inicializuj_casovu_zakladnu();
   fn_inic_sedemsegment();
   //...  // tu si daj vsetky potrebne inicializacie

	// nekonecna slucka
   while(1) {
      if (TST(TIFR,OCF2)) { 
         SET(TIFR,OCF2); 

         fn_meraj_cas();
         fn_testuj_tlacitko();
         fn_zobraz_dalsiu_sedemsegmentovu();
      }
   }


   return;
}

void fn_inicializuj_casovu_zakladnu(void)
{
      OCR2 = PREDVOLBA_CZ;
      SET(TCCR2,WGM21);
      SET(TCCR2,CS22);
      return;
}

void meraj_cas(void)
{
   //...
   return;
}

void fn_testuj_tlacitko(void)
{
   //...
   return;
}

void fn_zobraz_dalsiu_sedemsegmentovu(void)
{
   //...
   return;
}

void fn_inic_sedemsegment(void)
{
   //...
   return;
}

Este Ti chcem poradit, aby si si zvykla pouzivat #define

napriklad

//...

// definovanie pinu pre ten ktory segment v sedem segmentovke
// piny som vybral nahodne
#define SEG_A 0
#define SEG_B 5
#define SEG_C 3
#define SEG_D 2
#define SEG_E 1
#define SEG_F 4
#define SEG_G 6
#define SEG_P 7

// definovanie pinu pre ten ktory sedem segmentovky
// piny som vybral nahodne
#define CISL_1 0
#define CISL_2 2
#define CISL_3 3
#define CISL_4 1

//...


uint8_t segs[10];

//...

segs[0] = (1<<SEG_A) | (1<<SEG_B) | (1<<SEG_C) | (1<<SEG_D) | (1<<SEG_E) | (1<<SEG_F);
segs[1] = (1<<SEG_B) | (1<<SEG_C);
segs[2] = (1<<SEG_A) | (1<<SEG_B) | (1<<SEG_G) | (1<<SEG_D) | (1<<SEG_E);

// ak to potrebujes invertovane tak potom 
segs[0] = ~((1<<SEG_A) | (1<<SEG_B) | (1<<SEG_C) | (1<<SEG_D) | (1<<SEG_E) | (1<<SEG_F));

Kod ma prakticky rovnaku dlzku .
Vedie to k daleko prehladnejsiemu kodu a to aj v pripade ked si pytas radu od niekoho ineho. Zaroven ak zmenis hw a segment A bude na inom pine, upravu v cislach pinov robis iba na jedinom mieste a nemoze sa stat ze ju v nejakej casti zabudnes.

P.S. Este Ta chcem upozornit, ze ak budes experimentovat s nejakymi kusmi pokusneho kodu, moze sa lahko stat, ze pri nastaveni oprimalizacie vyssej ako -O0 ti prekladac kde co vyhodi, lebo to na celkovu funkciu nema vplyv. On je mudry a rozozna to. Tak pred take pokusne premenne vloz “volatile”. To je info pre neho, aby tuto premennu neoptimalizoval. Ano, optimalizovat sa da az na ultimum, t.j. dana premenna sa z programu uplne vyhodi :slight_smile:. Ale on to robi spravne. To na zaciatku moej programovacej kariery som si cas od casu namyslal, ze som v prekladaci objavil chybu. Vzdy to vsak bola chyba medzi klavesnicou a stolickou. Tak drzim palce. :slight_smile:

Martine myslím že jsi tu chybu udělal znovu. :slight_smile: Tvé texty jsou moc dlouhé (přestože hodnotné a kvalitní). Lidé neumí číst rozsáhlé návody, raději mají co nejjednodušší příklad na pár řádků. Obzvláště začátečníkům by více pomohlo zkrátit Tvůj text asi tak na 1/10.

Kdyby dokázali přečíst takový delší text, to by pak četli i datasheety a nechodili by se ptát na fóra.

OK :slight_smile:

hlavny je ten kod v strede (spolu 25 riadkov).

// projekt pre demonstrovanie pouzitia citaca ako casovej zakladne vsektych dejov v mcu
// ATmega8, CLK 8MHz, prekladac GCC, AVRstudio 4.18, -Os

#include <avr/io.h>
#include <stdint.h>

// Definice makier s parametrami
#define SET(BAJT,BIT) ((BAJT) |= (1<<(BIT)))
#define TST(BAJT,BIT) ((BAJT) & (1<<(BIT)))

#define PREDVOLBA_CZ 124

int main(void)
{
   OCR2 = PREDVOLBA_CZ;
   SET(TCCR2,WGM21);
   SET(TCCR2,CS22);
   while(1) {
     if (TST(TIFR,OCF2)) {
        SET(TIFR,OCF2);
        // ... tu si daj kod, ktory sa ma vykonat 1x za 1ms
     }
   }
   return;
}

Inak to uz vzdavam. :slight_smile:

ted už je to super, mnoho si mi toho vysvětlil. I když je to dlouhé, ale zajímavé. Ale ještě se chci zeptat kolik bude předvolba, když mám oscilátor 16 MHz?

16000000/64=250000

Ak chces nastavenie 1x za 1ms, tak predvolba musi byt 250-1=249.
Vacsie cislo ako 255 tam nevopchas, potom musis zvysit deliaci pomer zo 64 na nejaky vyssi.

díky :wink:

Stopky:
Sopky_v2.c (2.83 KB)

velké diky, a ještě se zeptám. Jak se jmenuje ten simulátor? Ulehčil by mi dost práce :slight_smile:

Teraz presne neviem koho sa pytas, ale simulator GCC je normalnou sucastou AVRstudia 4.xx. Treba stlacit taku zelenu sipku medzi ikonkami. Len pozor na to, ze v menu “debug” treba nastavit parametre v dolnej polozke ako frekvencia a tak podobne. Tak isto tu je mozne simulovat signaly na pinoch mcu pomocou nahraneho suboru *.sti.
Program na ich generovanie stiahnes napriklad tu

avrfreaks.net/index.php?func … ks%20Tools

nie je to nejaky zazrak, ale na zakladne overenie binarnych vstupov sa to pouzit da.

Az na jeden pripad nepoznam nikoho, kto by bol spokojny s AVRstudiom 5/6. Preto odporucam ostat na AVRstudiu 4.xx. Na novsich sa napriklad normalne neda pracovat s AVRdragonom a to je priamo programator priamo od Atmelu. Nejako to ide, ale strasne zdrzovacne, takze prakticky nepouzitelne. Tak nech si vytlacia oko :slight_smile:

ten soft co pouzil Standa33 sa vola Proteus.

ahoj, nějak mi to nefunguje. Stopky se rozběhnou, hned jak to připojím na napájení. Tlačítka mám po stisknutí připojená na zem…

Zkusil jsem to na vývojovém kitu EvB(musel jsem jen změnit porty: PORTA pro a-g,DP;PORTC pro AN1-AN4 a tlačítka na PC0,PC1), ale funguje to bez problémů. Po zapnutí ukazuje 00.00 prvním stiskem tlačítka se rozběhnou stopky/druhým zastaví. Stisk druhého tlačítka resetuje stav LED.

ještě se zeptám. Co je to v té podmínce if(****==48) proč zrovna 48? a ještě jsem se chtěl zetat, jestli nemůže být problém v to, že to do toho mcu programuju přes bootloader

Bootloader může udělat problém, protože nepředává řízení MCU v implicitním stavu, např. ponechává nastavené Rx a Tx porty. Takže se musí udělat důslednější inicializace a nepředpokládat implicitní nastavení.

Pokud je na PC4 úroveň log. 0 při připojení napájení, tak se rozběhou stopky. Při odpojených tlačítkách musí být na PC4 a PC5 log. 1(>3,5V).