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

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: