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

ahoj, chtěla jsem udělat stopky s 4 místnou sedmisegmentovkou, aby mi při stisku tlačítka začali počítat. Při opakovaném stisknutí se zastavili. Vynulování se mi snad podařilo. Dělám to s delay_ms, což nevím jestli je správně…
Našel by se někdo, kdo by mě navedl na nějakou možnost? napadlo mě to dělat přes neznámou a podmínku if. Ale asi je to hloupost
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>

const unsigned char segs] =
{0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010,
0b11111000, 0b10000000, 0b10011000, 0b01111111};

#define B1() (bit_is_clear(PINA,4))
#define B2() (bit_is_clear(PINA,5))

int main(void)
{
unsigned char set_1=0;
unsigned char set_10=0;
unsigned char sek_1=0;
unsigned char sek_10=0;
unsigned char show_t=0;

DDRD = 0xFF;
DDRA = 0x1F;
DDRB = 0xFF;
PORTB = 0xFF;


while(1)   

{
if(B1())
{

_delay_ms(1);
set_1++;

if(set_1>9){set_1=0;set_10++;};
if(set_10>9){set_10=0;sek_1++;};
if(sek_1>9){sek_1=0;sek_10++;};


if(++show_t==4) show_t=0;
switch(show_t)
{
    case 0:    //show minutes
        PORTA = 0b11111110;
        PORTB = (segs[set_1]);
        break;
    case 1:    //show 10 minutes
        PORTA = 0b11111101;
        PORTB = (segs[set_10]);
        break;
    case 2:    //show hours
        PORTA = 0b11111011;
        PORTB = (segs[sek_1]) & (segs[10]);
        break;
    case 3:    //show 10hours
        PORTA = 0b11110111;
        PORTB = (segs[sek_10]);
        break;
    default:
        show_t = 0;
        break;
        
    }

}
if(B2())
{
set_1=0;
set_10=0;
sek_1=0;
sek_10=0;
}
}
}

:arrow_right: administrator: přejmenováno z "stopky"

Teoreticky to přes delay udělat můžeš, ale :

1 - nejsem si jistý, že delay_ms(1) bude trvat přesně 1 ms.
2 - ne každý průběh smyčky bude trvat stejně dlouho v závislosti na splněných/nesplněných podmínkách rozhodování

Načítání impulzů (tedy času) se v těchto případech vždy dělá pomocí čítače a buď procesor běží na krystalu nebo alespoň čítač běží v asynchronním režimu na externí krystal nebo jiný přesný zdroj hodin. Řešil jsem podobnou věc (i když ne stopky) s mcu ATmega8A. Procesor jsem nechal běžet na IntRC 8 MHz, naprogramoval jsem pojistku CKOPT a na piny TOSC1/TOSC2 jsem připojil krystal 32768 Hz jako zdroj hodin pro Timer/Counter2, který jsem přepnul do asynchronního režimu a který zajišťuje co možná nejpřesnější čítání.

Misa: Sice je prodleva 1ms celkem přesná, ale ty ostatní obsluhy ve smyčce udělají přídavné zpoždění, které může udělat chybu (podle rychlosti MCU) i např. 10% (u rychlosti několik MHz). Takže na první pokusy ok, ale pro praktické využití by to chtělo pak přejít na hw čítač (nebo alespoň prodloužit tu prodlevu). I kdyby se měl používat jen interní RC oscilátor a poolování hodnoty čítače, tak by to mohlo být už docela vyhovující pro nenáročná měření.

Podobný program se řešil tady (obsluhy tlačítek): viewtopic.php?t=3140&start=0&postdays=0&postorder=asc&highlight= . Autor tam používá prodlevu 10 ms, což je trochu lepší v tom, že se méně projeví ostatní kód ve smyčce a je to proto přesnější (i bez použití přerušení).

Balů: Nevěděl jsem že ATmega8 umí používat externí krystal a přitom jet z interního RC. Teď na to koukám do datasheetu, to je docela zajímavá možnost.

Ja by som na to isiel takto. Urcite pouzivaj casovac na urcenie kvanta casu. Netreba hned pouzivat prerusenie. Jednoducho testuj, ci uz dany cas ubehol a ak ano spusti vsetky funkcie ktore potrebujes.
Samozrejme tieto funkcie sa musia stihnut do nastaveneho casu, inak nastane sklz. V dalsom uvadzam priklad z jedneho skolenia, kde je vysvetleny princip prace s casovacom ako s casovou zakladnou pre riadenie celej cinnosti v mcu. V prikade je nastaveny cas na 100us. Co nie je nejaky extra extrem. Pri 8 MHz za ten cas stihne mcu vykonat 400-800 instrukcii. Myslim, ze nebude problem delice a predvolbu nastavit tak, aby vola casova zakladna 1ms, alebo trebars aj 10ms. Vsetko je na prekladac GCC. Header obsahuje nejake nastavenia tykajuce sa vyvojovej dosticky s ATtiny25, viac menej pre to co som chcel ukazat nepodstatne.
Ty si nahrat funkciu “void fn_matematika_1(void)” nejakou, ktora sa stara o o tie tlacitka a stopky. Vyhoda tohto pristuju je, ze mas vzdy v ramci presnosti hodin presne determinonvane vykonavanie cinnosti v mcu. Na tuto cinnost si zvykni, je to uzitocne. Ziadne posahane _delay_ms(). Staci potom pridat par riadkov programu a uz nevies kolko moze v skutocnosti slucka bezat.
V priklade su premenne typu volatile preto, aby ich prekladac nevyhodil z kodu a dalo sa v simulatore prezentovat, ako program funguje. Treba to brat ako edukacny priklad.

subor

program_c02_timebase_100us.c


// bloky v *.C subore

// dokumentacna cast

/*
	Druhy program.
	Program beží v nekonečnej sľučke.
	Program testuje casovac na dosiahnutie casu 100us.
	Po jeho dosiahnuti vykona dalsiu cinnost.
	V dalsej cinnosti zavola funkciu, ktora spravi prislusne matematicke operacie
	s premennymi typu uint8_t, uint16_t a uint32_t
*/

// vsetky potrebne #include
#include "tiny_kit_zaklad.h"

// deklaracia IMPORTovanych objektov,ale iba ak nieje prislusny *.H subor, takze tu by vlastne nic nemalo byt

// Definicia GLOBALnych premennych

// Definicia lokalnych typov

// Uplne funkcne prototypy LOCALnych funkcii
void fn_matematika_1(void);
void fn_init_cz(void);
uint8_t fn_testuj_cz(void);

// Definicia GLOBALnych funkcii

// Lokalne #define
#define KONST1 7
#define KONST2 547
#define KONST3 856971L

// Definicia LOCALnych premennych
volatile static uint8_t  a;
volatile static uint16_t b;
volatile static uint32_t c;

// main()
int main(void)
{
	
	fn_init_cz();
	// nekonecna slucka
	while(1) {

		if (fn_testuj_cz() == FALSE) continue;

		fn_matematika_1();
	}
}


// Definicia LOCALnych funkcii
void fn_matematika_1(void)
{	
static uint8_t prvy_prechod = 0;

	if (prvy_prechod == 0) {
		prvy_prechod = prvy_prechod + 1;
		a = 0;
		b = 0;
		c = 0;
	}

	a = a + KONST1;
	b = b + KONST2;
	c = c + KONST3;

	return;
}


void fn_init_cz(void)
{

	SET(TCCR0A,WGM01);
	SET(TCCR0B,CS01);
	OCR0A = PREDVOLBA_CZ;

	return;
}

uint8_t fn_testuj_cz(void)
{
	if (TST(TIFR,OCF0A)) {
		// nastala zhoda TCNT0 s obsahom registra OCR0A
		// a casovac pocita od zaciatku
		SET(TIFR,OCF0A);
		return(TRUE);
	}

	return(FALSE);
}

a k tomu este header pre vseobecne nastavenia definicie a pre nastavenia pre konkretny hw obsahujuci nejake tlacitka a nejake ledky.
Subor

tiny_kit_zaklad.h

#include <stdint.h>

// zakaldne binarne stavy
#define FALSE 0
#define TRUE 255

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


// parametre casovaca vytvarajuceho casovu zakladnu
#define PREDVOLBA_CZ 99

// definicie vstupov a vystupov z hladiska funkcnosti
#define LED_BLUE  0 			// 0, lebo je na pine PB0
#define LED_GREEN 1 			// 1, lebo je na pine PB1
#define LED_WHITE 2 			// 2, lebo je na pine PB2

#define TL_2 0 				// 0, lebo je na pine PB0
#define TL_3 1 				// 1, lebo je na pine PB1

#define TRANZISTOR_OC1 2 	// 2, lebo je na pine PB2
#define TRANZISTOR_OC2 1 	// 1, lebo je na pine PB1
#define REPRO 4 			 	// 4, lebo je na pine PB4

#define IN_OP1 2 				// 2, lebo je na pine PB2
#define IN_OP2 1 				// 1, lebo je na pine PB1
#define INFRA_PRIJIMAC 0 	// 0, lebo je na pine PB0

#define AD1 3 					// 3, lebo je na pine PB3
#define AD2 4 					// 4, lebo je na pine PB4

skusim napisat funkciu ( z hlavy, nemam cas to skusat tak dufam, ze tam nebudu nejake zasadne chyby :slight_smile: ) pre funkciu testujucu tlacitka a pocitanie casu, tu potom nahrad za funkciu “void fn_matematika_1(void)”. Ako tlacitko pouzijem TL_2 z demodosky.
Funkciu fn_stopky_ini(void) vloz napr podla vzoru. V nej sa inicializuju stavy a premenne.

 // ....
      fn_init_cz();
      fn_stopky_ini();
 // ....

// toto az po ten posledny static vloz na zaciatok programu k ostatnym premenym 
#define MAX_TEST_ZAKMITOV_TL 100
#define NULOVANIE_CASU 0
#define ZASTAVENE_CASU 1
#define SPUSTENIE_CASU 2
#define MAX_NAMERANY_CAS 999999 // maximalna hodnota je do 99.9999 sekundy

static uint8_t stav_tlacitka_new, stav_tlacitka_old;
static uint8_t cnt_zakmitov_tl, stav_stopiek = NULOVANIE_CASU;
static uint32_t namerany_cas = 0;
static uint16_t namerany_cas_zobraz, pom16;
static uint8_t znak[3];

void fn_stopky_ini(void)
{
    RES(DDRB,TL_2); // nastavenie pinu na vstup. Po inicializacii sice automaticky je, ale takto je to zrozumitelne a odolne voci EMI
    if (TST(PINB, TL_2))  stav_tlacitka_old = TRUE; 
    else  stav_tlacitka_old = FALSE; 
    cnt_zakmitov_tl  = MAX_TEST_ZAKMITOV_TL;
    return;
}

void fn_stopky(void)
{

    RES(DDRB,TL_2); // nastavenie pinu na vstup. Po inicializacii sice automaticky je, ale takto je to zrozumitelne a odolne voci EMI
     // test, ci je odpamatana hodnota stavu tlacitka ina ako znovu nacitana hodnota stavu



    if (TST(PINB, TL_2))  stav_tlacitka_new = TRUE; 
    else  stav_tlacitka_new = FALSE; 
  // na prvy pohlad to vyzera, ze premenna stav_tlacitka_new je zbytocna, ale nie je. Jej pouzitim sa zabrani moznemu chaosu ak tlacitko ma inu hodnotu v case porovnania so starou hodnotou a inu hodnotu v case zmeny starej hodnoty na zaklade novej.

     if (stav_tlacitka_new == TRUE) && (stav_tlacitka == FALSE)) || (stav_tlacitka_new == FALSE) && (stav_tlacitka == TRUE)) ) {
         // ak je odpamatany stav rozny od cerstvo nacitaneho, potom dekrementuj pocitadlo casovo sumovej imunity nacitania tlacitka (pri cz 100us je to pre MAX_TEST_ZAKMITOV_TL = 100 akurat 10ms )
         if (cnt_zakmitov_tl) cnt_zakmitov_tl--;
         // nastavenie novej hodnoty odpamataneho stavu tlacitka
         else {
             // vyhodnotenie stavoveho automatu stopiek.
             stav_tlacitka_old = stav_tlacitka_new;
             // udalost pustenia tlacitka nas nebude zaujimat, budeme vyhodnocovat iba zmenu smerom k "stlaceniu" tlacitka
             if (stav_tlacitka_old == TRUE) {
                 // toto sa samozrejme da spravit aj cez if() a aj cez inkrement premennej. Nie je to vsak tak prehladne ako tento kostrukt a procesoru je to sumafuk, prekladac sa postara o preklad do pouzitelnej rychlej formy. Opat pripominam, ze 100us, nedajboze 1ma je hafo casu.
                 switch (stav_stopiek)  {
                       case NULOVANIE_CASU : {
                            stav_stopiek = SPUSTENIE_CASU;
                            break;
                       }
                       case SPUSTENIE_CASU : {
                            stav_stopiek = ZASTAVENE_CASU;
                            break;
                       }
                       case ZASTAVENE_CASU : {
                            stav_stopiek = NULOVANIE_CASU;
                            break;
                       }
                 }
             }
             // kedze doslo k zmene hodnoty  stav_tlacitka_old, treba znovu prednastavit hodontu cnt_zakmitov_tl, aj ked tato by sa aj tak prednastavila o 100us ked program zisti, ze k zmene stavu nedoslo. Ale takto je to jasnejsie a je vidiet, ze sa na nic nezabudlo :-)
             cnt_zakmitov_tl = MAX_TEST_ZAKMITOV_TL;
 
         }  
    }
    else {
         // ak su hodnoty stav_tlacitka_old a stav_tlacitka_new rovnake, nie je dovod nenastavit premennu cnt_zakmitov_tl. Tym sa eliminuje nespravne vyhodnotenie pri predchadzajucich kratkodobych moznych dekrementoch.
         cnt_zakmitov_tl = MAX_TEST_ZAKMITOV_TL;
    }
        
    // teraz vyhodnotime stavovy automat
    if (stav_stopiek == NULOVANIE_CASU)  namerany_cas = 0;
    if (stav_stopiek == SPUSTENIE_CASU)  {
          // aby nam namerany cas nepretiekol urobime kontrolu
          if(namerany_cas < MAX_NAMERANY_CAS) namerany_cas++;
    }
    // zobrazenie nameraneho casu. NApr, nech nas zaujimaju len desatiny sekundy a cas do hodnoty 99.9s
    namerany_cas_zobraz = uint16_t(namerany_cas/1000);
    // tu sa velmi hodi pouzit funkciu 
    // itoa((int16_t)namerany_cas_zobraz, &znak[0]);
    // tymto by v poli znak] boli ulozne jednotlive asacii znaky cila od 0 do 999
    // ale nepouzijem ju :-)
    // miesto toho zakladny rozklad

    pom16 = namerany_cas_zobraz/10;
    znak[2] = '0' +  uint8_t(namerany_cas_zobraz - pom16*10);
    namerany_cas_zobraz = pom16;
    pom16 = namerany_cas_zobraz/10;
    znak[1] = '0' +  uint8_t(namerany_cas_zobraz - pom16*10);
    namerany_cas_zobraz = pom16;
    // na najnizsom indexe je najvyssia cislica
    znak[0] = '0' +  uint8_t(namerany_cas_zobraz);

    // ak nechces zobrazovat najvyssie nevyznamen nuly, tak potom este test
    if (znak[0] == '0') {
          znak[0] == ' ';
          if (znak[1] == '0') znak[1] == ' ';
          // ak aj znak[2] = '0', ten nechame tak, aby sa na display nieco zobrazovalo.
    }
    // nasledujucu funkciu si napis podla svojich potrieb, nepoznam tvoj hw.
     fn_zobraz_znaky_na_displayi();
   
    return;
}

takze takto nejako by som na to isiel. Urcite si vyber niektory z casovacov pre tvorbu casovej zakladne a dosledne ho pouzivaj. delay pouzi len pre casy tak do 10-20us. Naco zbytocne brzdit cakanim ine casti programu. Drzim palce a daj vediet ako to dopadlo. :slight_smile:

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…