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

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: