16F54 spínání výstupů přes přerušení

Dobrý večer,
potřeboval bych pomoci, potřebuji spínat výstupy po určité době a furt dokola. Něco jsem zkusíl napsat a to by mělo fungovat. Ovšem mělo by to být přes přerušení a externí oscilátor. A to je kámen úrazu, o přerušení vím maximálně to, že existuje (+ softwarové,hardwarové,nějáká priorita,…) nemáte prosím nějáký odkaz kde je to vysvětlené pro delfíny, co jsem hledal na internetu, tak jsem to z ničeho nepochopil. Děkuju

[code]#include <htc.h>
#define _XTAL_FREQ 4000000

void main (void)
{

TRISB = 0b00000000;

while(1)
{
	PORTB = 0b00000001;
	__delay_ms(100);
	PORTB = 0b00000010;
	__delay_ms(100);
	PORTB = 0b00000100;
	__delay_ms(100);
	PORTB = 0b00001000;
}	

}[/code]

To, co jsi napsal sice funguje, ale přehazuješ LEDky v hlavním programu a ne v přerušení. Přerušení znamená, že program, který běží v mcu “všeho nechá” a zareaguje na nějaký podnět, který rychle zpracuje a pak pokračuje dál.

Jak funguje přerušení :

Představ si, že jsi mcu a provádíš program (například řežeš dříví), v tom někdo zazvoní u dveří - Ty přestaneš řezat a jdeš se podívat, kdo zazvonil. Pak se vrátíš a pokračuješ v řezání. Takhle nějak vypadá přerušení. To, že odejdeš ke dveřím, vyřídí potřebné a vrátíš se zpět, tomu se říká obsluha přerušení. Přerušení se dále dělí na externí a interní. Tohle byl příklad externího přerušení (podnět přišel z vnějšku mcu na pin pro externí přerušení - v tomhle případě je ten pin pro externí přerušení Tvoje ucho). Interní přerušení by bylo, že řežeš dřevo a chce se Ti na záchod - postup je stejný - přestaneš řezat, odskočíš si (obsloužíš přerušení) a vrátíš se k řezání. To je interní přerušení vzniklé uvnitř mcu.

Já bych tedy zadání pochopil tak, že máš mcu a nějaký oscilátor. Výstup tohoto oscilátoru přivedeš na nožičku externího přerušení mcu a programově ho obsloužíš.

smyčka hlavního programu
.
.
.
tady v nějakým intervalu pravidelně zhasínáš LEDky
(to aby nezůstaly trvale rozsvícené, to bys pak nepoznal,
jestli ti to funguje).
.
.
.
skok na smyčku hlavního programu



rutina pro obsluhu externího přerušení
tady rozsvítíš LEDky (záměrně nepíšu ty tečky,
protože každá obsluha přerušení by měla být co možná nejkratší)
návrat

Druhá varianta:


smyčka hlavního programu
tady neděláš nic
skok na smyčku hlavního programu



rutina pro obsluhu externího přerušení
Tady měníš stav LEDky nebo LEDek.
návrat

Interní přerušení je třeba přerušení od časovače, sériového portu mcu, AD převodníku apod.

Priorita přerušení znamená, že je předem dáno, které přerušení se obslouží dříve v případě, že přijdou dvě (nebo víc) najednou. Například - přijde přerušení od časovače - skočíš na jeho obsluhu. Při obsluze přerušení jsou vždy přerušení zakázány. Mezitím přijde přerušení od AD převodníku a třeba od externího pinu. Po ukončení přerušení mcu zkontroluje příznaky a podle priority přerušení začne postupně obsluhovat všechny podněty. Pak se vrátí k hlavnímu programu.

Snad jsem Ti dokázal aspoň trošku přiblížit, jak obsluha přerušení v mcu funguje.

Ve Tvým případě by bylo asi optimální řešení pomocí druhé varianty. Uděláš si inicializaci portů a hlavní smyčku necháš prázdnou. V obsluze přerušení si zjistíš, jaký je stav portu, podle toho nastavíš následující stav a ukončíš přerušení.

Vysvětlení přerušení super, díky moc!

Ten oscilátor by byl připojenej jako zdroj taktovací frekvence a z něj by mělo být odvozeno to přepínání těch výstupů pomocí toho přerušení.

Ale furt nechápu,jak to udělat :-/ asi je blikání s ledkama přes delay vrchol mého programování

Asi bych to měl udělat od čítače a jakmile přeteče T0IF bude jedna, inkrementoval bych nějáký ukazatel a podle něj bych spínal výstupy, vynuluju T0IF a čítač znova čítá… je todle zprávná cesta? NAšel jsem nějáký kód z dávných dob, tak jsem ho zkusil upravit

[code]#include <htc.h>
int a=0;

void interrupt _int()
{
if(T0IF == 1)
{
a++;
T0IF=0;
}
}

void main (void)
{
TRISC = 0b00000000;
OPTION = 0b00000111;
INTCON = 0b10100000;

while(1)
{
	if(a == 0)
		PORTB = 0b00000001;
	if(a == 1)  
  		PORTB = 0b00000010;
	if(a == 2) 
		PORTB = 0b00000100; 
	if(a == 3)
	{
		PORTB = 0b00001000;
		a = 0;
	} 
}

}[/code]
Pak už jenom doladit aby mi to spínalo časy tak jak chci

Aha, takže ne externí oscilátor jako zdroj přerušení, ale externí oscilátor jako zdroj hodin pro mcu. V tom případě jde o HW zapojení pohonu mcu, který nepojede na interní hodiny, ale na tento oscilátor a přerušení budeš muset tedy použít interní, konkrétně od nějakého časovače v mcu.

jj přesně tak, ty příspěvky jsou tu nějak poházené :slight_smile: něco jsem vymyslel, tak je to nad tvojím příspěvkem, prosím podívej se mi na to, jestli je to dobrá cesta. Děkuji

Nejsem PICař, takže úplně do detailu Ti neporadím, ale pokusím se. Jsem především assemblerář, takže když jsem přecházel z architektury 8051 na něco novějšího, tak jsem vybíral mezi AVRkama a PICama, a když jsem viděl ty instrukce PICů, tak jsem si málem vykloubil oko. V tu chvíli bylo víceméně rozhodnuto. Když jsem pak zjistil, jak je to s výkonem PIC vs AVR, tak byla volba jasná. Kromě toho když něco chci u PICu přepnout, tak musím popřepínat půl švába tam a pak zase zpátky. A je toho víc, proč jsem PICy nechal u ledu. Ale to jsem poněkud odbočil.

Takže :

  1. TOIF předpokládám, že je interrupt flag od časovače. Pokud bude void interrupt _int() jako přerušovací rutina - resp. jako obsluha přerušení, pak TOIF bude při jejím volání VŽDY 1, takže ho kontrolovat nemusíš a pokud funguje stejně, jako u AVR, pak ho ani na konci nemusíš nulovat, protože se nuluje automaticky. Teď mě napadá, že se možná vynuluje v okamžiku volání obsluhy přerušení, takže už na vstupu do obsluhy přerušení nulový.

  2. Vzhledem k tomu, že nejsem PICař a ještě k tomu Cčkař, tak nedokážu na 100% říct, jestli máš rutinu definovanou správně, ale to Ti tu doufejme někdo z PICařů poradí - budu předpokládat, že ano.

Rutina by pak mohla vypadat následovně :

void interrupt _int()
{
      a++;
}

Takže LEDky budeš přepínat v návaznosti na přerušení, nicméně není to úplně čisté řešení. Většinou se takovéhle věci řeší už během přerušení. Takto měníš LEDky vlastně při každém průběhu hlavní smyčky, jen do nich pořád zapisuješ pořád stejnou hodnotu, takže navenek není vidět, že s nima vlastně hýbeš pořád. S tím bys asi neuspěl. Osobně bych program upravil takto :

Správně inicializuješ proměnnou a před prvním použitím. Následně bych před hlavní smyčkou ještě nastavil “výchozí” hodnotu portu B - **PORTB = 0b00000001; **, hlavní smyčku bych nechal prázdnou a přepínání LEDek bych udělal v přerušení.

Procházel jsem nějaké PICové zdrojáky na Internetu a vypadá to, že T0IE (ne TOIE, jak jsem psal chybně) musíš nulovat ručně i v případě přerušení, takže přerušovací rutina by měla obsahovat tohle :

void interrupt _int()
{
      a++;
      T0IF = 0;
}

Plus změnu LEDek …

Cau, nechci vam nejak kazit zabavu (nebo sem spatne koukal) ale PIC16F54 ma nejake preruseni ?

slo by to napsat i takle (neskouseno)[code]void interrupt _int()
{
if(T0IF == 1)
{
PORTB =(PORTB<<1);
if(PORTB>0x08)PORTB = 0b00000001;
T0IF=0;
}
}

void main (void)
{
TRISB = 0b00000000;
OPTION = 0b00000111;
INTCON = 0b10100000;
PORTB = 0b00000001;

while(1);

}[/code]

jako vystup mas portB v inicializaci mas portC , f54 nema portC, to ti prekladac nerek ?! nebo jenom pises kod v textaku ?
pokud pouzivas jen jedno preruseni tak nemusis testovat flag, jinak ano, jestly ma vice preruseni a jeden vektor preruseni
flag se musi nulovat rucne pokud to nedela prekladac , nutno se podivat
a pokud mas tohleif(a == 3) { PORTB = 0b00001000; a = 0; } tak to ani nepostrehnes protoze tam mas a=0; a hned dalsi instrukce je if(a == 0) PORTB = 0b00000001;

To je teda hloupá poznámka! Ale fakt nemá. :slight_smile: Navíc má jen 2 úrovně zásobníku a skoro žádnou RAM, takže by to nemělo jít ani přeložit v C (jedině ASM).

kdyz si da pozor tak to nevadi :laughing: , ta mala pamet na to blikani staci i v C

a za dalsi

tak to mas smulu …, jestly jsi tu f54 uz koupil tak si z ni udelej nejakou peknou dekoraci :laughing:

void interrupt _int() { if(T0IF == 1) { PORTB =(PORTB<<1); if(PORTB>0x08)PORTB = 0b00000001; T0IF=0; } }

Takhle nějak jsem to myslel.

Mimochodem, dá se to napsat i takhle :

void interrupt _int() { if(T0IF == 1) { PORTB =((PORTB<<1)>0x08)?0x01:(PORTB<<1); T0IF=0; } }

Jinak děkuji za info o struktuře přerušení u PICů. To vysvětluje, proč se příznaky přerušení musí nulovat ručně. AVRka mají pro přerušení tabulku skoků a priority a nulování příznaků řeší kontroler na HW úrovni.

Opravuji :

void interrupt _int()
{
   if(T0IF == 1)
      {
   PORTB =(PORTB<<1);
   if(PORTB>0x08)PORTB = 0b00000001;
         T0IF=0;
      }
}

Tento kód není úplně správně. Po provedení PORTB =(PORTB<<1); se na portu B objeví něco, co by tam nemělo být a pak teprve dochází k opravě a přesto, že je to jen na pár mikrosekund a oko to asi nepostřehne, pokud by místo LEDky bylo připojené nějaké zařízení, pak i tech pár mikrosekund bude dost dlouhá doba na to, aby to bylo (nebo mohlo být) vyhodnoceno jako impulz…

Bylo by vhodné kód přepsat na :

void interrupt _int()
{
   if(T0IF == 1)
      {
        if (PORTB==0x08)
        {
           PORTB = 0b00000001;
        }
        else
        {
           PORTB = (PORTB<<1);
        }
        T0IF=0;
      }
}

Opravit bych asi měl i ten kód, co jsem psal na ukázku :

void interrupt _int() { if(T0IF == 1) { PORTB =(PORTB==0x08)?0x01:(PORTB<<1); T0IF=0; } }

Děkuji za vaše přopomínky a jdu se asi někam pověsit na kabelu!!
Jsem se měl podívat, jestli má přerušení než jsem to začal řešit.
Z jakého důvodu by něměl jít MPLAB + HI-TECH + PICKIT2? 16f54 icsp má a přímo v MPLABU jsem si vybral C kompiler a přímo ten typ picky.

jen sem narazel na tohle (viz priloha)
no ale beru zpet , jak to napravit se doctes v tomto vlakne PIC16F150X a PICKIT2 - JAK NAPROGRAMOVAT? -Vyřešeno!

pravda, diky
PIC16F54.JPG

Tak jsem se s tím nějak popral, použil jsem 16F676

[code]#include “htc.h”
#include <stdio.h>

void main (void)
{
TRISC = 0b00000000;
int count = 0;

TMR0 = 0;//48
PSA = 0; // Prescaler is assigned to the Timer0 module
PS0 = 1; // Prescaler rate bits
PS1 = 1; // are set to “111”
PS2 = 1; // which means divide by 256
T0SE = 0; // rising edge
T0CS = 0; // Internal instruction cycle clock

while(1)
{
	while(!T0IF);
		T0IF = 0;
	count++;

	if(count == 0)
		PORTC = 0b00000001;
	if(count == 1)	
		PORTC = 0b00000011;
	if(count == 2)
		PORTC = 0b00000111;
	if(count == 3)	
		PORTC = 0b00001111;
	if(count == 4)
		PORTC = 0b00011111;
	if(count == 5)	
		PORTC = 0b00111111; 
	if(count == 5)
	{
		count = 0;
	}
}

} [/code]

ten 16F676 uz preruseni ma takze nemusis cekat na ten flag ,muzes pouzit to preruseni,a nebude to fungovat podle tvych predstav ze 2 duvodu, pouzivas port kde je sdileno vice periferii , nastavujes jen TRIS
pokud mas count=0 a nastavi se flag ,nasledne se vynuluje ,count++ coz je 1, tak if(count == 0) se nikdy nevykona…

pouzivas simulator :question:

Tím flagem myslíš toto

while(!T0IF);

Jsem to zkoušel přepsat a funguje to i když to zapíšu takto a dokonce už to count ==0 nevynechává

while(T0IF) { T0IF = 0; count++; }
K tomu , že to nebude fungovat. Máš pravdu, když sjem si to zpomalil, tak opravdu to stav při count ==0 vynechávalo, to jsem upravil. S tím TRIS, mi to dělá v pořádku. Nebo bych měl pro jistotu ještě něco nastavovat?
Jinak po úpravě

[code]#include “htc.h”
#include <stdio.h>

void main (void)
{
TRISC = 0b00000000;
int count = 0;

TMR0 = 0;//48
PSA = 0; // Prescaler is assigned to the Timer0 module
PS0 = 1; // Prescaler rate bits
PS1 = 1; // are set to “111”
PS2 = 1; // which means divide by 256
T0SE = 0; // rising edge
T0CS = 0; // Internal instruction cycle clock

while(1)
{
	while(T0IF)
	{
		T0IF = 0;
		count++;
	} 
  	if(count == 0)    
    	PORTC = 0b00111111; 
   	if(count == 1) 
		PORTC = 0b00111110; 
   	if(count == 2)    
		PORTC = 0b00111100; 
   	if(count == 3) 
		PORTC = 0b00111000; 
   	if(count == 4)    
		PORTC = 0b00110000; 
   	if(count == 5)
		PORTC = 0b00100000;
  	if(count == 6)    
    	PORTC = 0b00000000;
  	if(count == 7) 
   	{ 
    	count = 0; 
   	}
} //end while

}//end main[/code]

Simulátor nepoužívám, nahrávám to rovnou do nepájivého pole.

Na větvení podle konstanty je přehlednější (a efektivnější) používat switch:[code] while(1)
{
while(!T0IF) {}
T0IF = 0;
count++;

	switch(count)
	{
	case 1: PORTC = 0b00111111; break;
	case 2: PORTC = 0b00111110; break;
	case 3: PORTC = 0b00111100; break;
	case 4: PORTC = 0b00111000; break;
	case 5: PORTC = 0b00110000; break;
	case 6: PORTC = 0b00100000; break;
	default: PORTC = 0b00000000; count = 0; break;
	}
} //end while[/code]

Případně s výrazem: while(1) { while(!T0IF) {} T0IF = 0; PORTC = (~((1 << count) - 1)) & 0b00111111; count++; if (count == 7) count = 0; } //end while

Tak aby to fungovalo, tak jak má a reágovalo to na TMR0, tak musí být umístěno

TMR0 = 20;//

ve smyčce

while(T0IF) { TMR0 = 20; T0IF = 0; count++; }
jinak to po prvním cyklu přičítá od 0