Přerušení od libovolného tlačítka

Ahojte.
Prosím mám dotaz. Jde nějak udělat u ATmegy8, aby došlo k přerušení programu od tlačítka třeba na portě PC2?? Nebo to musí být výhradně na pinu PD2 nebo 3?? Dík moc za jakékoli info

Na m8 to nejde narozdíl od m88 (ikdyž tam je 1 přerušení společné pro několik pinů).

Otazka je, preco potrebujes prave prerusenie pre zistovanie stlacenia tlacitok.

Ak chces obsluhovat X tlacitok, nema zmysel prislusne porty scanovat castejsie ako raz za 5ms kvoli zakmitom, a to este aj tak treba robit sw osetrenie tychto zakmitov.

Spravit sw, ktory vygeneruje prerusenie 1x za 1/2/5/10ms od nejakeho casovaca v m8, v preruseni otestuje pin za pinom na ktorom mas tlacitka a po vyhodnoteni sa prerusenie ukonci nezaberie viac ako nejakych par desiatok mikrosekund casu. Zaroven mas garantovane, ze vyhodnotenie tlacitka zaberie vzdy definovane mnozstvo casu.

O rychlost sa absolutne bat nemusis. M8 ma vykonu az az. Len treba progam rozumne napisat bez paskvilov typu “pause()”. To je ista cesta do pekla. Tazko je potom sa stazovat na nizky vykon mcu, ak ma v programe kazdu chvilu cakat Xms, ci dokonca Xsekund. :slight_smile:

No protože si stavím zažízení s MCU, kde mám pár tlačítek, a ještě před psaním programu si musím dobře navrhnout HW. Takže jsem potřeboval zjistit, kam musím zapojit tlačítka. Už vím, že DPSka bude o trošku komplikovanější. :wink: Ale lepší na to přijít teď, kdy to je zatím jenom návrh někde v PC, než někde ve fázi, kdy DPSky jsou vyleptatné a napájené. :smiley: :smiley:

Je jedno kde tlacitka das vzhladom k ich vyhodnoteniu.

Je nezmysel kvoli tlacitkam zaberat externy interrupt :slight_smile:

A co je teda lepší na interup připojit? RTC člen? Tlačítko? Nebo je to vlastně úplně jedno?? Si chci postavit hodiny, které bych si sám naprogramoval. Aspoň, tak si trošku osahám časovače a přerušení,… Protože C-čko se teprve učím.

Ti to povím jinak.
Pracuji ve vývoji jedné menší firmičky a specializuji se na programování SW do procesorů.
Za celou tu dobu (cca 7let) jsem použil externí interrupt pouze jednou.
A to byl případ, kdy jsem potřeboval opravdu bleskurychle reagovat na
vnější podnět. (vypnutí napájení)
Ovlání tlačítkem opravdu ten případ není a mám ten dojem, že ani RTC
žádné nepotřebuje.
Doporučuji ti tedy externí přerušení pustit z hlavy.

Dík moc. Zase jsem o něco chytřejší. :wink: Aspoň budu mít hezčí DPSku a možná i jednodušší program. Kdo ví. :wink:

Zrovna s tímto bych nesouhlasil.

RTCčka které znám mají extra vyvedený výstup pro použití pro přerušení - na co zatěžovat mcu neustálým vyptáváním se nějakého DS8583 “kolik je hodin?” :smiley: když mu to může říct každou sekundu sám ?

Pokud ten mcu nebude dělat nic jiného tak budiž, ale pokud budeš mít RTC jako systémový čas v nějaké složitější aplikaci pak bych to externí přerušení určitě použil.

Skusim to povedat jednoduchsie.
Nic nebrani pouzit INT vstupy a ich interrupt rutiny pre vyhodnotenie tlacitok.
Nie je to vsak absolutne nevyhnutne, skor budes mat problemy s vyhodnotenim tlacitka kvoli kratkodobym zakmitom pri spinacoch procesoch kontaktov. Lahko sa Ti moze stat, ze jedno stlacenie na zaklade vyhodnotenia interruptu zachytis ako 5-10 stlaceni. To budes musiet osetrovat bud sw, alebo hw pomocou kondenzatora. Skratka ziadna veda.

Mnohonasobne volanie interruptu (bez dobreho hw osetrenia) moze naopak vyvolat kratkodobo uplne zbytocne “zahltene” procesora jalovym kodom a moze sa napriklad nestinut spracovat znak z UARTU pri vysokych prenosovych rychlostiach.

Aj preto Ti odporucame tlacitka spracovat cisto sw, lebo sice ich spracujes vela krat zbytocne, ale zato viac menej deterministicky vies spocitat cas ktory ma mcu k dispozicii a mcu v nevhodnom case (podla zakona schvalnosti) nezahltis. Cas na spracovanie tlacitok sa da odhadnut tak do 0.3% vykonu procesora (tak 5-10us (do 80-160instukcii mcu) na 5ms pri 16MHz) rovnomerne.
Spracovanie vsetkych tlacitok naraz v cykle - ak nepouzijes interrupt na mega8 prinesie usetrenie casu pre obsluhu prerusenia (push, pop). Ale bavime sa celkovo o uspore desatin az stotin percent vykonu procesora.

Přesně tak, i u RTC je vyveden ALARM, který sepne když nastane daný čas (u mikrokontroléru se aktivuje externí přerušení).
Třeba já to budu používat u dlouhých časů, kde musím co nejvíce šetřit energií :slight_smile:

ahoj, mam presne podobny problem, potrebujem obsluhovať viac tlacitok povodne som to chcel cez externe prerusenia.
Mohol by si mi sem hodit kusok kodu ako by to malo vyzerat?
Chcem to pouziť pri LED svetelnom efekte, kde by som mal nejake tlacitka s nimi by som menil efetky a podobne. Dakujem :wink:

NIeco som rychlo napisal, tie vstupne a vystupne piny som rychlo kopiroval a nesedia.

[code]#include <avr/io.h>
#include <avr/interrupt.h>

volatile unsigned char i,pom;

ISR (TIMER1_OVF_vect)
{
if((PIND & (1 << PD7)) == 0) pom=1;
if((PIND & (1 << PD7)) == 0) pom=2;
if((PIND & (1 << PD7)) == 0) pom=3;
}

int main(){
//PORTY**
DDRD |= (1 << PD7); //vystup pre led
DDRD &= ~(1 << PD4);//vstupy
PORTD |= (1 << PD4);

TCCR0 |= (1 << CS01) | (1 << CS00); // preddelicka 64 
TIMSK |= (1 << TOIE0); 

sei();  

while(1);
switch(pom)
{
	case '1':  efekt1(); break;
	case '2':  efekt2(); break;
	case '3':  efekt3(); break;
}  

return 0;
} [/code]

Najprv priklad ako vyuzit casovac bez potreby hned riesit prerusenie.
Jednoducho sa casovac nastavi na nejaku rozumnu casovu zakladnu, ktorej dobehnutie sa testuje a po jej vyprsani sa spustia prislusne programy. Cas sa da samozrejme nastavit napriklad na 1ms alebo 10ms. Na samotne tlacitko to staci.

Program na zaklade stavu tlacitka rozne reaguje na blikanie LED
Priklad je pre jedno tlacitko z nejakeho vyucboveho sw, ktory som kedysi spachal, viac tlacitok sa da dat samozrejme do cyklu.
Priklad je pre nejaky vyucbovy hw s ATtiny25/45/85 co som volakedy spachal pre par priatelov. :slight_smile:
Nieco malo nepodstatneho som odmazal, dufam, ze to bude zrozumitlene.

Ak ano, potom by som vedel poslat verziu vyuzivajucu prerusenie, ale to bude v podstate to co tu uz bolo uverejnene jeden prispevok nado mnou.



// 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.
*/

// 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_init_cz(void);
uint8_t fn_testuj_cz(void);
void fn_blik_led_blue_test_tlacitka(void);
uint8_t fn_hodnota_tlacitka(uint8_t cinnost);


// Definicia GLOBALnych funkcii

// Lokalne #define

// Definicia LOCALnych premennych

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

		if (fn_testuj_cz() == FALSE) continue;

		fn_hodnota_tlacitka(FN_RUN);
		fn_blik_led_blue_test_tlacitka();
	}
}


// Definicia LOCALnych funkcii
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);
}


void fn_blik_led_blue_test_tlacitka(void)
{
#define PREDVOLBA_PERIODA_LED_BLUE 10000
#define SVIETENIE_LED_BLUE 1000

static uint16_t cnt_perioda = 0;
static uint8_t stav_tlacitka_mem = FALSE, led_blikaj = FALSE;

	// nastavenie pinu na vystup
	SET(DDRB,LED_BLUE);

	// otestovanie stavu tlacitka a v zavislosti od neho sa na striedacku meni
	// stav premennej led_blikaj
	if (fn_hodnota_tlacitka(FN_STAV) == TRUE) {
		if (stav_tlacitka_mem == FALSE) {
			stav_tlacitka_mem = TRUE;
			if (led_blikaj == FALSE) led_blikaj = TRUE;
			else led_blikaj = FALSE;
		}
	}
	else stav_tlacitka_mem = FALSE;

	// ak premenna led_blikaj = FALSE, neblikaj LED
	if (led_blikaj == TRUE) {
		cnt_perioda++;
		if (cnt_perioda < SVIETENIE_LED_BLUE) RES(PORTB,LED_BLUE);
		else SET(PORTB,LED_BLUE);

		if (cnt_perioda > PREDVOLBA_PERIODA_LED_BLUE) cnt_perioda = 0;
	}
	else {
		cnt_perioda = 0;
		SET(PORTB,LED_BLUE);
	}

	return;
}

uint8_t fn_hodnota_tlacitka(uint8_t cinnost)
{
#define PREDVOLBA_FILTER_TLACITKA 100

static uint8_t cnt_filter_tlacitko = 0, stav_tlacitka = FALSE;

	switch (cinnost) {
		case FN_INI : {
			// nastavenie pinu ako vstup
			RES(DDRB,TL_3);

			break;
		}
		case FN_RUN : {
			// ak tlacitko nie je stlacene
			if (TST(PINB, TL_3)) {
				if (stav_tlacitka == TRUE) {
					cnt_filter_tlacitko++;
					if (cnt_filter_tlacitko >= PREDVOLBA_FILTER_TLACITKA) {
						stav_tlacitka = FALSE;
						cnt_filter_tlacitko = 0;
					}
				}
				else cnt_filter_tlacitko = 0;
			}
			// ak tlacitko je stlacene
			else {
				if (stav_tlacitka == FALSE) {
					cnt_filter_tlacitko++;
					if (cnt_filter_tlacitko >= PREDVOLBA_FILTER_TLACITKA) {
						stav_tlacitka = TRUE;
						cnt_filter_tlacitko = 0;
					}
				}
				else cnt_filter_tlacitko = 0;
			}
			break;
		}
		case FN_STAV : {
			break;
		}
	}
	return(stav_tlacitka);
}

no a tu je ten #include subor"tiny_kit_zaklad.h"
obsahuje aj nejake ine #define nesuvisiace priamo s prikladom, ale snad mi bude odpustene. :slight_smile:


// vsetky potrebne #include
#include <avr/io.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

#define FN_INI 1				// parameter pre inicializaciu nejakej funkcie
#define FN_RUN 2				// parameter pre beh nejakej funkcie
#define FN_STAV 3				// parameter pre poskytnutie stavu nejakej funkcie

ak bude dalsi zaujem aj o to prerusenie, dajte vediet :slight_smile:

Taky jsem pro někoho cosi psal :slight_smile:.
Určitě to bez dodělávek nepůjde přeložit (je to vystřiženo z programu, mcu byla snad m16), ale obsluha tlačítek vypadala nějak takto:[code]#include <avr/io.h>
#include <avr/interrupt.h> // přerušení

#define TL_PIN PIND
//#define TL_DDR DDRD
#define TL_UP 4
#define TL_DOWN 5

#define TL_UP_PRESSED (!(TL_PIN & (1<<TL_UP)))
#define TL_DOWN_PRESSED (!(TL_PIN & (1<<TL_DOWN)))

typedef struct // struktura, zde bitove pole
{
unsigned keyChange: 1; // “1” = 1bit
unsigned keyUp: 1;
unsigned keyDown: 1;
} tlacitka_t;

volatile static tlacitka_t tlacitka;

int main(void) // start programu
{

timer0Init();
sei();															// global enable interrupts

while(1)
{
	if (tlacitka.keyChange)
	{
		tlacitka.keyChange = 0;

		if (tlacitka.keyUp)  ; // ... neco
		if (tlacitka.keyDown)  ; // ...
	} // end if (tlacitka.keyChange)

	// jiná užitečná činnost (třeba spánek :))

}

}

void timer0Init(void)
{
TCCR0 = 1<<WGM01 | 1<<CS02 | 1<<CS00;
OCR0 = 26; // f = 11059200 / 1024 / 27 = 400 ==> T = 2.5ms
TIMSK |= 1<<OCIE0; // Timer/Counter0 Output Compare Match Interrupt Enable
}

ISR(TIMER0_COMP_vect)
{
static unsigned char up, down; // promenne “static” zustavaji v pameti
// i po opusteni funkce (u globalni prom. ma slovo jiny vyznam))

// kazdych 2.5 ms se precte stav tlacitek. Kdyz bude 8x za sebou prectena stejna hodnota
// a ta se bude lisit od hodnoty v glob. prom., bude nova hodnota ulozena a oznacena "keyChange".

up <<= 1;
if(TL_UP_PRESSED) up |= 1;

down <<= 1;
if(TL_DOWN_PRESSED) down |= 1;

if(up == 0 || up == 0xFF)
{
	// nacteny 8x za sebou 0 nebo 1
	if((up && !tlacitka.keyUp) || (!up && tlacitka.keyUp))
	{
		// prectena hodnota je jina nez byla predchozi (v global. prom.) => probehla zmena
		tlacitka.keyChange = 1;
		if(up) tlacitka.keyUp = 1;
		else tlacitka.keyUp = 0;
	}
}

if(down == 0 || down == 0xFF)
{
	if((down && !tlacitka.keyDown) || (!down && tlacitka.keyDown))
	{
		tlacitka.keyChange = 1;
		if(down) tlacitka.keyDown = 1;
		else tlacitka.keyDown = 0;
	}
}

}[/code]Pro více tlačítek by se samozřejmě hodily cykly, pole a i nějaké pointerování pokud by nebyly tlačítka na stejném portu.

moc som tomu zatial nepochopil, strasne nerad opisujem/upravujem od niekoho zdrojak :smiley:
Takze princip spociva vlastne v tom ze prerušenie nastava každych napr 2ms, a tam sa testuje aktualny stav tlacidla.
V tomto kusku vlastne inicializujem kedy ma prerušenie nastat atd…??

void timer0Init(void) { TCCR0 = 1<<WGM01 | 1<<CS02 | 1<<CS00; OCR0 = 26; // f = 11059200 / 1024 / 27 = 400 ==> T = 2.5ms TIMSK |= 1<<OCIE0; // Timer/Counter0 Output Compare Match Interrupt Enable }

Cize vlastne ak mi bude bezat hlavny main program tak kazdych 2ms si odskoci do toho prerušenia?

ešte by som mal otazku k tejto podmienke
if (tlacitka.keyChange), vlastne “tlacitka.keyChange” je nejaka premenna alebo funckia? Ak je to premenna prorovnavam ju scim? ak je to funckia, tak sa funkcia vyhodnoti ale zase kde ju porovnavam. Dakujem :smiley:

presne tak. V programe samozrejme musis mat ten kus kodu

ISR(TIMER0_COMP_vect)
{
// …

}

inak by to samozrejme nefungovalo

Ale no tak, kolega. Co tak pozriet si co je to struktura a ako sa volaju jej prvky?

Vyraz

if (premenna) {

}

sa vykona, ak je hodnota premennej nenulova, cize je to presne to iste ako

if (premenna != 0) {
}

aha ano uz chapem :slight_smile: ja som si zauzival ten druhy sposob

este taka otazka, ked budem robit tie “efetky”, potrebujem tam casovanie(led zap led vyp), a funkcia sleep zdrzuje program, ako to je potom ak pouzijem povedzme casovac, predsa ten tiez pocita cize ho zaneprazdnuje rovnako nie?

Casovac podobne ako ine periferie si ziju svojim vlastnym hardverovym zivotom a CPU otravuju az vtedy ked svoju cinnost bud skoncia (napr. UART) alebo dosiahnu vytuzenu zivotu metu (stav citaca/casovaca) a po nej bezia uplne autonomne dalej bez ohladu na to, kedy si CPU vsimne ze sa nieco udialo/dosiahlo. Nemusi byt CPU pritomny kazdemu novemu tiku z preddelica casovaca. Ved co by tam aj robil :slight_smile:

To sa da dvoma sposobmi.

  1. Bud sw v CPU pravidelne kontroluje ci nenastala vytuzena udalost (moj priklad), alebo

  2. po dosiahnuti udalosti a za najblizsich priaznivych okolnosti (nie je zakazane globalne prerusenie, je povolene prerusenie od prislusnej periferie a dokonca na mieste skoku po prepruseni je zmysluplny kod a nebezi ine prerusenie [podmienka skoro totozna s prvou podmienkou]) si CPU odskoci na presne stanovene miesto.

Len taka mala napoveda. Je uplna zhovadilost do prerusovacej rutiny vkladat nejaky delay dlhsi ako par us.