ATmega 168 PCINT: jak počítat přerušení od PinChangeInterrup

Ahojte, chtěl bych vás poprosit o radu. Potřebuji počítat počet průchodů “těles” přes optické závory. Je jich 6. Vybral jsem si ATmegu168 prožože umí vyvolat přerušení při změně na jednotlivých pinech.
Úspěšně jsem toto přerušení rozchodil ISR(SIG_PIN_CHANGE1), ale už si nevím rady jak efektivně tyto přerušení rozlišovat a počítat.
Potřeboval bych tedy něco co mi při každém pulzu (každý pin by měl svou proměnou) přičetl do příslušné proměnné 1. Prosím někoho alespoň o nasměrování. Zatím mě absolutně nenapadá krom neelegantních řešení nic. Díky

Já sice zkouším dělat programy v Bascomu. Princip bych očekával stejný. U mě každé přerušení vyvolá skok do zvoleného podprogramu. Tam stačí dát načítání proměnné.

ISR(SIG_PIN_CHANGE1)
{
proměnná++;
}

Asi jsem se blbě vyjádřil. K přerušení ISR(SIG_PIN_CHANGE1) dojde vždy dojdeli ke změně na pinech PC0 - PC6 (potažmo PC5) a to jak při změně z LO to HI tak z HI to LO, pokud vím nelze to ovlivnit.
Na těchto pinech sbírám pulzy zcela náhodné délky a frekvence.
A potřebuji aby se mi do proměnné PC0_pulzy přičetl správně pulz jen pokud přerušení vyvolal PC0 pin a to ještě z LO to HI. Vzhledem k tomu že může být relativně dlouhý ale i krátký může dojít mezi tím k dalšímu přerušení od jiného pinu. Musím tedy hlídat pomocí blokovacích proměnných jestli na pinu PC0 již došlo ke změně z HI to LOW což samo o sobě vyvolá přerušení.
V tom co sem popsal problém nevidím, jde o to že testovat všech 6 pinů zabere vcelku dost času a vzhledem k tomu že ty pulzy mají dost náhodný charakter mám strach abych o žádný nepřišel, takže program který jsem schopen zvládnout může být nespolehlivý.
Má vize je taková že se někam načte hodnota PORTC a z ní se to nějak vykoumá. No na mě už je tohle složité jelikož tomu moc nerozumím… nebo spíš vůbec. :slight_smile:
základní myšlenka mého kódu v přerušení :
if(bit_is_clear(PINC,3)) PC3_pulzy++ ;
if(bit_is_clear(PINC,4)) PC4_pulzy++;
což je nespolehlivá prasárna… nicmméně sem to zatím ani nevyzkoušel

Asi ti moc neporadím, ale ve tvé situaci bych si vybral bych jiný chip, např. ATmega324 - INT0, INT1, INT2, PCINT0-7, PCINT8-15, PCINT16-23. Každý pin by měl svoji přerušovací rutinu, absolutně spolehlivé.

Pokud chápu dobře jak si to myslel tak tak že mám na každý vektor přerušení picnout jeden “pulz”. Takto jsem si mohl “usnadnit” i u 168 , ta jich má sice jen 5 ale pořád lepčí testovat 2společně než 6… ale potřebuju i všechna PWM a konkrétně u 168 je INT2 společně s OC2B. A na druhou stranu nechci zabíjet komára velbloudem.
Fakt to nejde nějak programátorsky vymyslet, to se mi ani nechce věřit.

Přesně tak jsem to myslel. Určitě to jde programově vyřešit, ale zrovna mě žádné elegantní řešení nenapadá. Zkusím o něčem popřemýšlet. Tou výměnou chipu jsem měl na mysli spíš to, že bys si ušetřil programování a určitě by to bylo spolehlivější.

Byl bych velmi vděčný. Pokoušel se sám na něco přijít ale mé dovednosti jsou limitně blížící se nule. Mám v hlavě jednu myšlenku, až si to urovnám zkusím to sem nastřelit. Každopádně moc dík

mna napada taka vec, ktora by kodu ku krase moc nepomohla, ale mohlo by to usetrit par cilkov procesora. mam na mysli ze by stacilo pripocitat k premennej len negaciu bitu:
PC3_pulzy +=(~PINC,3) ;
vychadzal som z tvojho zapisu:
if(bit_is_clear(PINC,3)) PC3_pulzy++ ;
if(bit_is_clear(PINC,4)) PC4_pulzy++;
je to len take zjednodusenie a neviem ci by to fungovalo…

Tak mě tak napadlo řešení, které by snad mělo fungovat.


ISR(SIG_PIN_CHANGE1) 
{ 

if((PINC & 0x01) != (pomocna & 0x01)) pc1pulzy++;
if((PINC & 0x02) != (pomocna & 0x02)) pc2pulzy++;
.
.
.
if((PINC & 0x20) != (pomocna & 0x20)) pc6pulzy++;

pomocna = PINC;

}

pot0 pulzy vydelis dvemi a mas hotovo

Navrhované metody nefungují správně.
Započítají např. dlouhý impuls několikrát.

Lépe je použít přerušení od timeru, častější než nejkratší impuls.
V přerušení pak porovnáme minulou hodnotu portu s aktuální hodnotou.
U bitů, ve kterých došlo ke změně 0 -> 1, přičteme v poli “hodnoty” jedničku.

Pak v poli “hodnoty”
hodnoty[0] = počet impulsů portc.0
hodnoty[1] = počet impulsů portc.1
atd.

[code]//mega8, 8 MHz
#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint16_t hodnoty[6];

ISR(TIMER0_OVF_vect) // přeteče za cca 0,5 ms
{
static uint8_t nova_hodnota, stara_hodnota, temp, i;

nova_hodnota = PINC;

// bity které se změnily z 0 na 1 budou mít v proměnné temp hodnotu 1
temp = nova_hodnota ^ stara_hodnota;
temp = temp & nova_hodnota;

//zapíšeme do pole “hodnoty”:
//hodnoty[0] = hodnoty[0] + temp.0
//hodnoty[1] = hodnoty[1] + temp.1
//atd
for(i=0; i<6; i++)
{
hodnoty* += (temp & (1<<i))>>i;
}

stara_hodnota = nova_hodnota;
}

//===============================================================================
int main(void)
{
uint16_t pocet_impulsu;

DDRC = 0; //portc input
TIMSK = (1<<TOIE0); //povol timer0_ovf int.
TCCR0 = (1<<CS00)|(1<<CS01); //prescaler 64
sei();

for(;:wink:
{
//z pole hodnoty] musíme číst atomicky
cli();
pocet_impulsu = hodnoty[0];
sei();
}
}
[/code]
Nezkoušel jsem v reálu.
.*

Provedením se mi nejvíce líbí právě tvoje řešení AB… to je ta elegance kterou neumím… v podstatě je ale jedno jestli přerušení generuje timer nebo externí přerušení ne ? Jen bude častější díky vyvolání i opačnou změnou z H to L. No zkusím to nějakým způsobem zrealizovat, mozek mi dnes už vypnul takže nevím jeslti se povede. Vůbec mi to už nemyslí.

Mělo by to chodit i s přerušením PCINT.
(Atmega8 ho nemá)

AB, můžete mi prosím vysvětlit, proč by měl tento kod dlouhé pulsy započítat vícekrát než dvakrát?


ISR(SIG_PIN_CHANGE1) 
{ 

if((PINC & 0x01) != (pomocna & 0x01)) pc1pulzy++; 
if((PINC & 0x02) != (pomocna & 0x02)) pc2pulzy++; 
. 
. 
. 
if((PINC & 0x20) != (pomocna & 0x20)) pc6pulzy++; 

pomocna = PINC; 

} 

Zalda:
Kód má jinou chybu.

Po přečtení např pinc.2 se jeho hodnota může změnit ještě před řádkem
pomocna = PINC;

Hodnota proměnné “pomocna” pak není správná.

Proto testování portu vícenásobným čtením není dobré.
Správné je přečíst hodnotu portu do proměnné a tu pak testovat.

Plne suhlasim s AB. Takto nieco testovat nie je dobre. Splocny Interupt na viacerych pinoch bol vymysleny hlavne kvoli tlacitkam a komplikovane (ale dalo by sa) by sa osetrovala zmena pinu v preruseni. Riesenie s casovacom je exaktnejsie a ak “ujde” nejaka zmena pri prvom nacitani vsetkych bitov od casovaca, tak “neujde” pri najblizsom nacitani. Okrem toho by este bolo dobre uviest aka je frekvencia vstupnych impulzov a ako dlho trvaju.

No zkusil sem to propočítat a pravděpodobná nejvyšší frekvence má periodu 5ms a délka takového impulzu je 1.25ms. Což by se asi timerem dalo s přehledem sledovat ale já všechny tři potřebuji pro PWM,
Je nějaký problém ve využití toho PIN CHANGE přerušení? Důvod proč to není příliš vhodné ?

No co sa stane ak na jednom vstupe nastane zmena.
Spravi sa prerusenie.
OK nacitas vsetky vstupy,

napriklad 1, 2, 3, … pri nacitanie tretieho nastane zmena na druhom, ktory si chvilku predtym identifikoval ako bez zmeny. Ukoncis prerusenie, cim sa automaticky zhodi jeho priznak.

Druhe prerusenie Ti jednoducho uslo. Musi nastat prerusenie na niektorom inom, napriklad na tej dvojke len opacnym smerom, prerusenie sa vyvola a Ty nezistis ziadnu hranu. A uz si jeden impulz stratil. Tak pravdepodobnost moze byt sice mala, ale je nenulova a moze byt dovodom na vela chomacov vytrhanych vlasov pri hladani chyby “kde sa mi tie impulzy nahodile a obcas stracaju”.

Istotu neziskas ani ked pred ukoncenim prerusenia opat vsetky vstupy skontrolujes.

Moznost na lepsiu detekciu je povolit prerusenie v preruseni. Nie je to zla taktika, ale treba s noiu velmi opatrne. Musis presne vediet teoreticky do akej urovne si ochotny prerusenia vnorovat a celkovo, je to ako skrabat sa okolo celej hlavy.

Daleko elegatnejsie je spsutit casovac, napriklad 1x za 500us alebo aj 1x za 1ms. Pri preruseni porovnas vsetky bity na detekciu hrany. Ak sa niektory pin zmeni tesne za detekciou, chytis ho bezpecne o dalsiu ms. Cas testovanie pinov musi byt kratsi, ako najkratsi cas zmeny na lubovolnom pine.

Co s PWM tyka. Neviem ake rychle zmeny musis generovat na vystupe, ale od PWM sa principialne nejake extra rychlosti necakaju. Ak Ti staci 8b presnost (co asi ano), kludne mozes mat na ten casovac pod 500us (ver mi, ze je to fura casu, za 500us spravi MCU na 18.432MHz 9216 instrukcii ) zavesenych sw PWM co hrdlo raci :slight_smile:

Zakladna perioda bude potom 128ms, co by pri filtracii 10k//10uF nemal byt problem. Pocitat sa mi to teraz nechce,. Ak, tak treba pozriet na osciloskope v polovici rozsahu. Napatie na vystupe nema kolisat o viac ako +/-1LSB (+/- 1/256).

Ak sa Ti to sva byt pomale, tak si sprav PWM z AD vstupov. Okrem toho, ze nepotrebujes X casovacov, presnost moze byt kludne 10b a rychlost nastavenia daleko vyssia ako pri klasickom PWM.

Na AD vstup zapoj odpor 470R a za nim k zemi C 10uF s paralelnym vysokym odporom, radovo stovky kohm.

Princip je taky, ze najprvnastavis pin ako vstupny. Zmerias hodontu AD. Ak je rozdiel vacsi ako 1LSB od zelanej hodnoty (tentokrat to moze byt 1/1024), prepni pin na vystupny a vygeneruj Xmilisekund bud do nuly alebo do jednotky. Cas X zavisi od rozdielu od zelanej hodnoty, ale urcite to budu nejake nasobky [ms]. Takze zase na to bude stacit jeden spolocny casovac. Po tom prepni pin na vstup a pravidelne (ak pojdes do kolecka so 6-timi AD vstupmi tak cca do 10ms) zmeraj hodnotu AD. Napatie na C bude pomaly klesat v dosledku vysokeho R paralelne s C. Ak sa hodnota zase bude od zelanej lisit, spravis maly “tuk” spravnym smerom.

I tak se da :slight_smile:

P.S. Vsetky uvadzane hodnoty suciastok su ilustrance a v pripade potreby si ich treba v zmysle nacrtnutych principov upravit.

Myslím, že příznak se nuluje hned na začátku přerušení.
Ale změna je i tak ztracená, protože v následujícím přerušení se bit2 vyhodnotí jako nezměněný.

Martin tu ale popisuje kód od zaldy.
Můj kód by měl chodit i s přerušením PCINT, jak už jsem psal.
Nejlepší by bylo vyzkoušet ho v reálu na desce.

Díky Martine za vyčerpávající odpověd, velmi si toho vážím. Avšak nejsem schopný převést tvoje úvahy do praxe.

Martin nakopl mé úvahy. Spíš než že ztratím nějaký impuls (to je v ABho kódu řekl bych nemožné) tam mám teď obavy že ho teoreticky můžu přičíst 2x, 3x možná í vícekrát, ale možná jen nechápu jak přesně ABho kód funguje.

Situace:
přijde mi přerušení od pin 4
načtu, přemaskuju, začnu přičítat do proměných, sem u pinu 5, ouha ->
přijde přerušení od pin 5
načtu, premaskuju (ale výchozí maskou, nová se nestačila přepsat)
přičítám do proměných sem u 4 (přičetlo mi to znova 1)
sem u 5 (aktuálně 1) ->
přijde přerušení od pinu 6
ta samá anabáze, akorát teď projde do konce
výsledek v proměných mám nesprávné hodnoty

Je to tak, nebo sem nepochopil kód ?