PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Disign Frage (Interrupt oder Main)



NumberFive
06.01.2008, 12:48
Ich gebe zu so viel habe ich noch nicht in c für den AVR geschrieben.
Aber ich möchte hinterher nicht alles neu machen weil ich eine dumme gebaut habe.

Zu meiner Frage:
Zur Zeit arbeitet ich so das in den Interrupt Routinen nur das wichtigste gemacht wird und die eingleiche arbeit finden im Main statt.
Das mache ich so damit die Interrupts sich nicht gegenseitig unter Brechen.

Wenn ich mir jetzt andere Software so angucken gibt es welche die haben überhaupt kein Main (spich da steht nur wihle) und wieder ander arbeiten komplett ohen interupts bzw. mit nur sehr wenig.



/**************************************
mains.c
Autor: Numberfive
versuchen das ganze mal zu verstehen.
Alle die im Netzt beispiel code habe
sei an dieser stelle gedankt
man komme nie auf die Idee irgend wo
Interupts zu enablen wenn dafür keine
signal procedure hat dann springt
der avr wieder in die main routine
Danke an Picknick für das Seriale Protokoll
*************************************/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
//#include <stdlib.h>

//#define F_CPU 16000000 // 16 MHz jetzt im Makefile
#define UART_BAUD_RATE 19200 // 19200 Baud

#define UART_BAUD_SELECT (F_CPU/(UART_BAUD_RATE*16l)-1)
#define SCLOCK 16;

// _BV = BitValue
#define SETBIT(ADDRESS,BIT)(ADDRESS |=(1<<BIT))
#define CLEARBIT(ADDRESS,BIT)(ADDRESS &= ~(1<<BIT));

typedef unsigned char BYTE;
typedef unsigned short WORD;

//MN Funktions
void WDT_off(void);
void InitCom(void);
void InitTimer1(void);
void InitAD(void);
void SendAlive(void);

//RS232 Protokoll funktionen
#define CTL_M_MASK 0xF8
#define CTL_M_ADON 0x08
#define CTL_C_BASE 0xA8
#define CTL_C_STX CTL_C_BASE + 1
#define CTL_C_ETX CTL_C_BASE + 2
#define CTL_C_PFX CTL_C_BASE + 3
void TxSendStuffByte (BYTE bTxChar);
void TxSendFrameByte (BYTE bTxChar);
void TxStartFrame ( void );
void TxCloseFrame ( void );
void SendChar(BYTE Zeichen);
void SendADValue(BYTE nToClass,BYTE nToIdent,BYTE nPort);

// Public Vars

volatile BYTE IsHardBeat = 0;
volatile BYTE IsPCData = 0;
volatile BYTE HardBeatCount = 0;
volatile BYTE bTxBcc; // Checksum für BCC

volatile BYTE bSerialInBuffer[128];
volatile BYTE bBefehlbuffer[128];
volatile BYTE bInBufferPos = 0;
volatile BYTE waitforad = 1;

//Interupts

SIGNAL(SIG_OVERFLOW1)
{
IsHardBeat = 1;
TCNT1 = 49910;//Reload Timer
}

SIGNAL(SIG_UART_RECV)
{
BYTE Zeichen = UDR;
if(bInBufferPos == 0)
{
if(Zeichen == CTL_C_STX)
{
bSerialInBuffer[0] = CTL_C_STX;
bInBufferPos++;
}
}
else
{
if(Zeichen == CTL_C_ETX)
{
bSerialInBuffer[bInBufferPos] = CTL_C_ETX;
//Copy Buffer resetInbuffer
for(BYTE nSize=0;nSize<=bInBufferPos;nSize++)
{
bBefehlbuffer[nSize] = bSerialInBuffer[nSize];
}
bInBufferPos = 0;
IsPCData = 1;
}
else
{
bSerialInBuffer[bInBufferPos] = Zeichen;
bInBufferPos++;
if(bInBufferPos == 128)
{
//Übergelaufen
bInBufferPos = 0;

}
}
}
}

SIGNAL(SIG_UART_TRANS)
{
// nix machen aber sonst komme ich immer wider forne an
}

SIGNAL(SIG_UART_DATA)
{
// nix machen aber sonst komme ich immer wider forne an
}

SIGNAL(SIG_ADC)
{
//Mal sehen der ad wander ist fertig
waitforad = 0;
}
//Interupts ende

int main (void)
{
//hauptprg hier geht der controler immer als erstes hin
InitCom();
WDT_off();
InitAD();

SETBIT(DDRD,PD7); // Das ist der lautsprecher

bSerialInBuffer[0] = 0X00;
bSerialInBuffer[1] = 0X00;
bSerialInBuffer[2] = 0X00;
bSerialInBuffer[3] = 0X00;
bSerialInBuffer[4] = 0X00;

InitTimer1();//Timer 1 auf 1 sec
sei();//interupt enable

for(;;)
{
if(IsHardBeat == 1)
{
IsHardBeat = 0;
HardBeatCount++;
if(HardBeatCount == 5)
{
HardBeatCount=0;
SendAlive();
}
}
if(IsPCData == 1)
{
IsPCData = 0;
BYTE nPos = 1;
BYTE nCheckSum = 0;
// Das geht schief wenn das BCC maskiert ist
while(bBefehlbuffer[nPos+1] != CTL_C_ETX)
{
if(bBefehlbuffer[nPos] != CTL_C_PFX)
{
nCheckSum ^= bBefehlbuffer[nPos];
}
else
{
nPos++;
nCheckSum ^= bBefehlbuffer[nPos]-CTL_M_ADON;
}
nPos++;
}
if(nCheckSum == bBefehlbuffer[nPos])
{
//BCC OK
if(bBefehlbuffer[1] == 0x05)
{
//NXT Bekommen
if(bBefehlbuffer[5] == 0x00)
{
//Erste Gerät auf diesem Roboter ist ein ADWandler mit der Adresse 0x25
TxStartFrame();
TxSendFrameByte(0x09);
TxSendFrameByte(0x00);
TxSendFrameByte(0x25);
TxSendFrameByte(0x00);
TxSendFrameByte(0x01);
TxSendFrameByte(0x82);
TxSendFrameByte(0x00);
TxCloseFrame();
}
if(bBefehlbuffer[5] == 0x01)
{
//.. ist ein ADWandler mit der Adresse 0x29
TxStartFrame();
TxSendFrameByte(0x09);
TxSendFrameByte(0x00);
TxSendFrameByte(0x25);
TxSendFrameByte(0x01);
TxSendFrameByte(0x02);
TxSendFrameByte(0x82);
TxSendFrameByte(0x01);
TxCloseFrame();
}
if(bBefehlbuffer[5] == 0x02)
{
// Ich bin das Letzt und ein RN-Control
TxStartFrame();
TxSendFrameByte(0x09);
TxSendFrameByte(0x00);
TxSendFrameByte(0x2D);
TxSendFrameByte(0x00);
TxSendFrameByte(0xFF);
TxSendFrameByte(0x62);
TxSendFrameByte(0x00);
TxCloseFrame();
}

}
else if(bBefehlbuffer[1] == 0x25)
{
if(bBefehlbuffer[2] == 0x00)
{
SendADValue(bBefehlbuffer[3],bBefehlbuffer[4],0);
}
}
}
}
} //Ende MainLoop
}

void InitCom(void)
{
/* Set baud rate */
/* wenn wir in das h register schreiben wollen muß da ein 0 drin sein */
UCSRC = (0<<URSEL);
UBRRH = (UART_BAUD_SELECT>>8);
UBRRL = UART_BAUD_SELECT;
/* Enable receiver and transmitter und die Interupts*/
UCSRB = _BV(RXEN)|_BV(RXCIE);//|_BV(TXCIE);
/* Set frame format: 8data, 1stop bit */
UCSRC = (1<<URSEL)|(0<<USBS)|(0<<UCSZ2)|_BV(UCSZ1)|_BV(UCSZ0);
// Braucht man nicht aber das ist dir register umschaltung ob man
// das hight byte des UBRR sieht oder UCSRC
UCSRC != _BV(URSEL);
}

void WDT_off(void)
{
//WDR();
WDTCR |=(1<<WDTOE) | (1<<WDE);
WDTCR = 0x00;
}

void InitTimer1(void)
{
SETBIT(TCCR1B,CS10);
SETBIT(TCCR1B,CS11);
//SETBIT(TCCR1B,CS12);

//enable interrupt timer
SETBIT(TIMSK,TOIE1);

// 1 sec bei Teiler 1024
TCNT1 = 49910;

TCNT1 = 100;//<- Für SIM
}

void InitAD(void)
{
//Init ADwandler
ADCSRA = (1<<ADEN)|(1<<ADSC)|(0<<ADATE)|(0<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
//CLEARBIT(ADCSRA,ADFR); // Kein Freilauf
CLEARBIT(ADMUX,REFS1);
SETBIT(ADMUX,REFS0); // externe vergleichs Quelle
SETBIT(ADCSRA,ADIE);
}

void SendAlive(void)
{
TxStartFrame();
TxSendFrameByte(0x01);
TxSendFrameByte(0x00);
TxSendFrameByte(0x00);
TxSendFrameByte(0x00);
TxCloseFrame();
}

void SendADValue(BYTE nToClass,BYTE nToIdent,BYTE nPort)
{
CLEARBIT(ADCSRA,ADEN); // aus schlaten damit ich den port wechseln kann
//ADMUX = nPort; geht nicht da sind noch 2 bit's
if(nPort == 0)
{
CLEARBIT(ADMUX,MUX4);
CLEARBIT(ADMUX,MUX3);
CLEARBIT(ADMUX,MUX2);
CLEARBIT(ADMUX,MUX1);
CLEARBIT(ADMUX,MUX0);
}
else if(nPort == 1)
{
CLEARBIT(ADMUX,MUX4);
CLEARBIT(ADMUX,MUX3);
CLEARBIT(ADMUX,MUX2);
CLEARBIT(ADMUX,MUX1);
SETBIT(ADMUX,MUX0);

}
// Bit invertieren messung starten
ADCSRA |=_BV(ADSC);
waitforad = 1;
// warten bis messung abgesclossen ist und wert gültig
while(waitforad != 0)
{
//warten auf den adwandler;
};
// den wert aus dem register holen
if(nPort == 0)
{
TxStartFrame();
TxSendFrameByte(nToClass);
TxSendFrameByte(nToIdent);
TxSendFrameByte(0x25);
TxSendFrameByte(0x00);
TxSendFrameByte(ADCL);
TxSendFrameByte(ADCH);
TxCloseFrame();
/*
pBuffer[0] = nToClass;
pBuffer[1] = nToIdent;
pBuffer[2] = 0x25;
pBuffer[3] = 0x00;
pBuffer[4] = LOBYTE(nValue);
pBuffer[5] = HIBYTE(nValue);*/

}
//Result = ADCH*256 + ADCL;
}

void SendChar(BYTE Zeichen)
{
SETBIT(UCSRB,TXEN);
//Warten bis schnittstelle bereit
loop_until_bit_is_set(UCSRA,UDRE);

// Zeichen ins register schreiben
UDR = Zeichen;

CLEARBIT(UCSRB,TXEN);
}

// --------------------------------------------------------------
void TxStartFrame ( void )
{
bTxBcc = 0;
SendChar ( CTL_C_STX );
}
// --------------------------------------------------------------
void TxCloseFrame ( void )
{
TxSendStuffByte ( bTxBcc );// auch das BCC mit ev. prefixed werden
SendChar ( CTL_C_ETX );
}
// --------------------------------------------------------------
void TxSendFrameByte (BYTE bTxChar)
{
bTxBcc ^= bTxChar;
TxSendStuffByte( bTxChar );
}
// --------------------------------------------------------------
void TxSendStuffByte (BYTE bTxChar)
{
if((bTxChar & CTL_M_MASK) == CTL_C_BASE)
{
SendChar( CTL_C_PFX );
SendChar( bTxChar + CTL_M_ADON );
}
else
{
SendChar( bTxChar );
}
}


Das ist das was ich gestern verbrochen habe. Im Simulator tut es.

purebasic
06.01.2008, 15:47
....Das mache ich so damit die Interrupts sich nicht gegenseitig unter Brechen. ...


das machen die beim atmega nicht. jeder wird sauber beendet.
das datenblatt vom atmega ist deine glaskugel.

purebasic
06.01.2008, 15:52
......Wenn ich mir jetzt andere Software so angucken gibt es welche die haben überhaupt kein Main (spich da steht nur wihle) und wieder ander arbeiten komplett ohen interupts bzw. mit nur sehr wenig. ......

jeder , so wie er es braucht. der eine braucht es, der andere braucht es nicht. was spricht dagegen?

oberstes gebot für die interrups : so wenig scheiss wie möglich darein packen.

Dirk
06.01.2008, 17:34
Hallo purebasic,

willkommen im Forum.

Ich hoffe, dass du hier im neuen Jahr konstruktiv und nicht konfrontativ mitarbeitest!

Also pure-konstruktiv und pure-basic! O:)

Gruß Dirk

PicNick
06.01.2008, 18:02
..die interrups : so wenig scheiss wie möglich ..

diese Strategie hat was, die kann man auch beim "main()" gut brauchen :-)

Dirk
06.01.2008, 18:28
diese Strategie hat was, die kann man auch beim "main()" gut brauchen

Yes! ... und auch sonst im "mainstream des Lebens"! O:)
Ist ja fast philisophisch! :-k

Verspäteter Neujahrsgruß
Dirk

NumberFive
13.01.2008, 11:49
Das man beim codieren so optimal wie möglich denken soll ist normal.
Da ist aber "normaler" Weise in der Windowswelt unterwegs bin bei Programmieren ist es schon eine massive Veränderung.

Wenn jetzt die Interrupt Routinen immer bis zum Ende abgearbeitet werden.
Heist das sie so klein wie Möglich sein müssen. Den sonst Warten ja die anderen oder ? gehen vieleicht welche verloren ?

Frage am Rande:
Ist es normal das die Simulation des AVR Studios 100 % CPU braucht ?

Gruß

wkrug
13.01.2008, 22:34
Heist das sie so klein wie Möglich sein müssen. Den sonst Warten ja die anderen oder ? gehen vieleicht welche verloren ?
Bei allen Punkten 100% - Genau so ist es.
Interruptroutinen sollten so kurz wie nur irgend möglich sein.
Ausserdem sollte man in Interrupts nicht auf irgendetwas warten - z.B. eine Änderung eines Eingangspins - da dadurch die komplette Programmabarbeitung ins stocken kommt und auch keine weiteren Interrupts ausgeführt werden können.
Interrupt Anfragen laufen bei den AVR Controllern über Flags.
Tritt ein Interrupt auf wird das entsprechende Flag gesetzt.
Befindet sich der Controller gerade in einer Interruptroutine oder das I - Flag im Statusregister ( siehe Assembler CLI Befehl ) ist 0 wird so lange gewartet bis der Controller die Abarbeitung eines Interruptes zulässt.

Treten in dieser Zeit mehrere Interrupts auf, werden diese nach ihrer Priorität ( Platz in der Interruptvektortabelle ) abgearbeitet.
Tritt bei einem aktiven Interruptflag ein weiterer Interupt der selben Instanz auf, wird die Interruptroutine nur 1x ausgeführt, also 1 Interrupt verschluckt.
Die Interruptflags werde übrigens nach Ausführung der dazugehörigen Interruptroutine automatisch gelöscht.
Man kann aber ein aktives Interruptflag auch manuell per Programmcode löschen - Das musste man des öfteren schon mal bei den alten AT90S... Controllern machen, die nach der Initialisierung mancher Instanzen auch selbstständig gleich das zugehörige Interrupt Flag gesetzt haben.

NumberFive
14.01.2008, 07:26
@wkrug erstmal danke.

Wie ich sehe kann man das wohl leider nicht auf dem Papier klären das heist werde das mit echten Controller testen müssen. Leider scheif die Simulation vom studio auch nicht viel mit der realität zu tun zu haben den der Interupt des AD's (messung fertig) kommt nicht.

da da jetzt auch noch I²C rein muss werden ich mit wohl zwei AVR's zum Testen suchen müssen.

Also noch viel arbeit mal sehen wie ich das Hardware Problem löse.

Gruß

purebasic
14.01.2008, 09:20
.....Leider scheif die Simulation vom studio auch nicht viel mit der realität zu tun zu haben den der Interupt des AD's (messung fertig) kommt nicht. ....

zum zeitmessen und fehlersuche ist es gut aber nicht für die überprüfung der realität.

so ist es. 2 atmega und es geht los.

wkrug
15.01.2008, 09:51
der realität zu tun zu haben den der Interupt des AD's (messung fertig) kommt nicht
Das mag ich jetzt fast nicht glauben.
Hast Du den Simulator auch wirklich lange genug laufen lassen ?
Die benötigten Flags werden auf jeden Fall auch im Simulator gesetzt.
Das Problem dabei ist, das die Simulation mit der tatsächlichen Taktrate gemacht wird, also erstmal 1000...2000 Befehle durchlaufen bevor der AD Interrupt kommt.
Gib beim Simulator mal den Cursor in die AD Interruptroutine und starte den Simulator mit Run to Cursor. Wenn sich da dann nach 1...2 Minuten! immer noch nichts tut würde ich die Interrupteinstellungen überprüfen.

Periphere Bausteine und Simulator sind allerdings wirklich ein Problem.
Man kann natürlich die Änderung an den einzelnen Pins, die die Peripherie erzeugt hätte, per Hand eingeben. Das ist aber schwierig und man vertut sich leicht.

Eine Möglichkeit wäre noch ein Debugging über JTAG mit Breakpoints. Das geht aber nur bei den größeren Controllern ( Ab ATMEGA 16 ) und so ein JTAG Interface ist auch nicht gerade billig.

T.J.
15.01.2008, 11:37
Um mal auf die Designfrage zurückzukommen: ich persönlich bevorzuge mehr Interruptgesteuert als polling. Beides hat natürlich vor und nachteile, aber die Vorteile der Interrupts überwiegen in meinen Augen deutlich.

a) es wird strukturierter und übersichtlicher
b) das ereignis wird immer überwacht, beim polling nur dann wenn du es abfragst
c) stromsparender, denn dann ist der sleepmode möglich ;)

Besserwessi
17.01.2008, 20:52
Besonders wenn man nur eine Interruptquelle benutzt darf die ISR Routine auch recht lang sein und sogar warten. Bei mehreren Interruptquellen oder wenn die Interrrupts schnell kommen sollte die ISR Routine aber kurz sein, sonst verpaßt man was.

Der AD Wandler Ready Interrupt wird so weit ich weiss leider nicht richtig simuliert, den muß man dann gelegentlich per Hand auslösen. Den neuen Simulator habe ich aber auch noch nicht probiert.

sechsrad
17.01.2008, 21:23
Besonders wenn man nur eine Interruptquelle benutzt darf die ISR Routine auch recht lang sein und sogar warten.



neiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii n.

Interruptquelle > immer kurz

neiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiin .

sogar warten > total vermeiden.

izaseba
17.01.2008, 21:40
@sechsrad,
reinige mal Deine Tastatur, irgendwas klemmt da.

So pauschal kannst Du das nicht sagen, warum darf ich in einer ISR kein "Krieg und Frieden" machen, wenn der Anwendungsfall das zulässt ?

wkrug
17.01.2008, 23:54
warum darf ich in einer ISR kein "Krieg und Frieden" machen, wenn der Anwendungsfall das zulässt ?
Wenn ich nur eine einzige Anwendung mit nur einem Interrupt laufen habe, kann ich, bis auf ein paar spezielle Ausnahmen, die Anwendung genausogut im Main Programm laufen lassen.
Ein Interrupt soll ja möglichst schnell auf ein "Ereignis" reagieren.
Das funktioniert aber dann schon nicht mehr richtig, wenn bei 2 Interrupts einer "Krieg und Frieden" spielt. Der zweite Interrupt kann da sehr häufig durch den langen verzögert werden.

Für Dich als erfahrenen Programmierer ist es sicher kein Problem abzuschätzen, ob Du dir eine lange Interruptroutine leisten kannst oder nicht.
In 99% aller Anwendungsfälle wird man aber zu möglichst kurzen Interruptroutinen greifen müssen.
In einer Interruptroutine z.B. auf die Änderung eines Portzustandes zu warten, der eventuell nie eintritt, blockiert die komplette Abarbeitung des weiteren Programmes.
Ich versuche, soweit das irgendwie möglich ist, den weiteren Programmablauf fortzuführen (z.B. mit Zeitüberwachungen), auch wenn ein erwartetes Eingangssignal nicht kommt. Das Ganze natürlich im Main Programm.
Beispiel:
Ich starte einen externen A/D Wandler und warte auf ein "Conversation Ready".
Allerdings hat sich der A/D Wandler aufgehängt und tut schlichtweg gar nichts. In der "Ready" Warteroutine wird ein Timer abgefragt.
Erreicht dieser Timer einen Wert der höher ist als die maximale "Conversation Time" wir ein Fehlerzähler hochgezählt.
Wenn dieser Fehlerzähler den Schwellwert ( z.B. 3 ) erreicht wird der A/D Wandler resettet. Das Programm kann sich somit im Prinzip nicht festlaufen und liefert im schlimmsten Fall ( A/D Wandler defekt ) keine, bzw. falsche Wandlerergebnisse. Andere Subroutinen, wie eine Drehzahlmessung, können aber unabhängig davon weiterlaufen.

Besserwessi
18.01.2008, 01:11
In der Regel sollte die ISR schon kurz sein und nicht auf etwas externes warten. Wenn ein Interrupt zeitkritisch ist, muß man eventuell sogar die ISR Routine sogar in Assembler schreiben, denn zumindest GCC erzeugt ziehmlich viel extra code am Anfang und Ende der ISR Rotine.

Es gibt aber auch Ausnahmen, wo man den Datenaustausch von der Interrupts Routine zu Hauptprogramm sparen kann.
Ich habe zum Beispiel eine Anwendung wo im Main Teil nur ein SLEEP in einer Endlosschleife ist. Die ISR Routine gibt 10 Sekunden lang etwas aus. Nach der ISR routine geht der Controller dann in den Tiefschlaf bis zum Nächsten externen Interrrupt.

sechsrad
18.01.2008, 12:42
.....In der Regel sollte die ISR schon kurz sein.....


nicht nur wenn du die regel hast , sondern immer!

die freizeit kannst du in der main verbringen und nicht in einer isr.