Forum: Mikrocontroller und Digitale Elektronik Interrupt wird dauerhaft angesteuert.


von Maximilian G. (chainex)


Angehängte Dateien:

Lesenswert?

Hallo liebes Forum,

ich habe mal wieder ein Anfängerproblem und komme ohne Hilfe nicht 
weiter.

Und zwar will ich (erstmal) per Tastendruck eine Nachricht per UART über 
die serielle SS senden.
Das funktioniert auch super, die Daten kommen richtig am PC an.

Nur habe ich Probleme beim Interrupt EXT0.
Leider löst dieser aus sobald ich den LED Ausgang umschalte. Und er 
funktioniert auch mit dem Taster nicht.
Ich habe bei anderen Projekten schon Interrupts benutzt, finde aber 
trotzdem keinen Fehler.
Die Potenziale am PB0 sind OK (pulled high) und werden auch nicht durch 
den Ausgang beeinflusst.

Habe das ganze sogar schon auf PB1 EXT1 umprogrammiert dort verhält es 
sich aber gleich.

Vielen dank im Voraus!

von Frank L. (florenzen)


Lesenswert?

Hallo Maximilian,

in deinem Code:
1
static uint8_t  Taste = 0;

mach mal :
1
volatile uint8_t Taste;

Der Compiler optimiert das sonst.

Gruss,
f

von Max M. (jens2001)


Lesenswert?

Maximilian G. schrieb:
> static uint8_t  Taste = 0;

Warum "static" ?
Mach da mal ein "volatile" draus.

von Carl D. (jcw2)


Lesenswert?

Max M. schrieb:
> Maximilian G. schrieb:
>> static uint8_t  Taste = 0;
>
> Warum "static" ?

Weil minimal notwendige Sichtbarkeit ausreichend ist.

> Mach da mal ein "volatile" draus.

Das fehlt zwar, ist aber keine Alternative zu static.

von Max M. (jens2001)


Lesenswert?

Carl D. schrieb:
> Weil minimal notwendige Sichtbarkeit ausreichend ist.

Bei einer globalen Variablen?!
LOL!

von Carl D. (jcw2)


Lesenswert?

Max M. schrieb:
> Carl D. schrieb:
>> Weil minimal notwendige Sichtbarkeit ausreichend ist.
>
> Bei einer globalen Variablen?!
> LOL!

Manche machen Projekte, die aus mehr als einem Source-File bestehen.
Die verstehen das.

von Maximilian G. (chainex)


Lesenswert?

Naja ich wollte mir eigentlich gleich einen sauberen programmierstil 
angewöhnen deshalb.
Hatte es auch mit short und int (ohne static) versucht genau das 
gleiche.

Hab es nun auch mit volatile probiert --> selbes Ergebnis.

von c-hater (Gast)


Lesenswert?

Maximilian G. schrieb:

> Nur habe ich Probleme beim Interrupt EXT0.

Hmm, eigenartig. Der kommt in deinem Code überhaupt nicht vor...

Wie kann er da Probleme machen?

von Weg mit dem Troll (Gast)


Lesenswert?

Eine Taste auf einen Intrrupt ?  Genau. Das macht man nicht so.
Eine Taste entprellt man mit einem timer. Bedeutet man fragt alle Tasten 
in einem timer ab. Zb Alle 10ms.
Alles andere ist quatsch.

von Maximilian G. (chainex)


Lesenswert?

Weg mit dem Troll schrieb:
> Eine Taste auf einen Intrrupt ?  Genau. Das macht man nicht so.
> Eine Taste entprellt man mit einem timer. Bedeutet man fragt alle Tasten
> in einem timer ab. Zb Alle 10ms.
> Alles andere ist quatsch.

Naja der Sendevorgang sollte ersteinmal entprellung genug sein.
Und warum den code weiter verkomplizieren wenn er noch nichteinmal so 
funktioniert...

von Weg mit dem Troll (Gast)


Lesenswert?

Der Sendevorgange entprellt ? Nein tut er nicht. Die erste Flanke ergibt 
einen Interrupt. Also Zeichen ins UART. 0.1ms spaeter der Prell. Das 
naechste Zeichen ins Uart, weil das Uart schon wieder leer ist.

von was soll das? (Gast)


Lesenswert?

wo ist der Unterschied?

> Senden(&Text[0]);

Senden(Text);

von neuer PIC Freund (Gast)


Lesenswert?

Hi

Du bist anscheinend relativ neu im uC Business der AVR.

Weil der AVR so einfach ist, muss man eine 1 schreiben, damit eine 0 
ankommt. Sieht bestimmt in deinem Datenblatt auch aus wie "... , or it 
can be cleared by writing a one to its bit location." (UCSRA).


Ansonsten rödelt dein Senden(""); was raus. Sollte nicht sein. Warum 
Fußgeprüf? Warum loop0 anstatt Zeigerarithmetik?

Stück für Stück. Teste ersmal das Senden() ohne Interrupt, dann nur 
Interrupt mit LED und am Ende beides Kombiniert. Oder nimm einen 
Debugger. Oder Simulator.


go on

von Maximilian G. (chainex)


Lesenswert?

neuer PIC Freund schrieb im Beitrag #6187009:
> Hi
>
> Du bist anscheinend relativ neu im uC Business der AVR.
>
> Weil der AVR so einfach ist, muss man eine 1 schreiben, damit eine 0
> ankommt. Sieht bestimmt in deinem Datenblatt auch aus wie "... , or it
> can be cleared by writing a one to its bit location." (UCSRA).

Habe im Datenblatt nichts dazu gefunden...
Sender wird mit 1 eingeachaltet...

>
> Ansonsten rödelt dein Senden(""); was raus. Sollte nicht sein. Warum
> Fußgeprüf? Warum loop0 anstatt Zeigerarithmetik?

Also das Senden funktioniert einwandfrei, fußgeprüft weil ich auch einen 
leeren String mit Endzeichen ("\0") senden will.

Wie meinen?  Quasi nur pointer++?
Würde sagen ist historisch gewachsen... Wie gesagt bin noch etwas neu..

von Oliver S. (oliverso)


Lesenswert?

Maximilian G. schrieb:
> Habe im Datenblatt nichts dazu gefunden...
> Sender wird mit 1 eingeachaltet...

Ist auch richtig so. Nur Interrupt-Flags werden durch schreiben einer 1 
gelöscht, aber die benutzt du gar nicht.

Oliver

von Maximilian G. (chainex)


Lesenswert?

1
void Senden(unsigned char *pInput)
2
{
3
  
4
  
5
  UCSRB |= (1 << TXEN);                  // Sender einschalten
6
7
  do                             // So lange senden bis Zeichenkette Ende!
8
  {        
9
    while (!(UCSRA && (1 << TXC)));            // Warten bis vorheriger Sendevorgang fertig
10
    UCSRA &= (~(1 << TXC)); 
11
    while (!(UCSRA && (1 << UDRE)));          // Warten bis Buffer leer
12
    
13
    UDR  = *pInput;                    // Daten in Buffer schreiben
14
                              // Zeiger auf nächste Position schieben.
15
    pInput++;
16
  }
17
  while(*pInput != '\0');
18
19
  UCSRB &= ~(1 << TXEN);                  // Sender ausschalten
20
21
}

Habe (nur falls es jemanden interessiert) das Senden überarbeitet und 
mir das loop gespart.
Funktioniert immernoch.

von STK500-Besitzer (Gast)


Lesenswert?

TXC brauchst du nur auswerten, wenn du sowas wie Halbduplex-RS485 
machst.

Bevor du den Sender ausschaltet, solltest du überprüfen, ob alle deine 
Daten übertragen wurde.
Das kannst du per TXC erreichen.
Warum man den aber "dauernd" ein- und ausschalten muss, ist mir unklar 
(vielleicht habe ich die Begründung aber auch einfach überlesen).

von Maximilian G. (chainex)


Lesenswert?

STK500-Besitzer schrieb:
> Warum man den aber "dauernd" ein- und ausschalten muss, ist mir unklar
> (vielleicht habe ich die Begründung aber auch einfach überlesen).

Naja wird nach der gesamten Zeichenkette ausgeschaltet nicht nach jedem 
Zeichen.
Du hast recht, dass es nicht nötig ist ich dachte nur es wäre vllt 
sauberer so und so kann ich die Funktion auch einfacher wiederverwenden.

Btw. Hast du auch mit der letzten abfrage recht ich muss vermutlich die 
schleife umbauen da sonst eventuell das letzte zeichen nicht gesendet 
wird...

von Peter D. (peda)


Lesenswert?

Maximilian G. schrieb:
> ich dachte nur es wäre vllt
> sauberer so und so kann ich die Funktion auch einfacher wiederverwenden.

Nö.
Sauber ist es, den TXD permanent an der UART zu belassen und nicht auf 
low oder floatend zu setzen.
Auch RXD abschalten ist gefährlich, da so die Startbiterkennung 
fehlschlagen kann.

von neuer PIC Freund (Gast)


Lesenswert?

> Funktioniert immernoch.

Und ich frag mich wie. Initial Value von TXC ist 0. Beim erstmaligen 
Aufruf von Senden() sollte dein Code bis zum Sankt-Nimmerleinstag oder 
Watchdog in
1
while (!(UCSRA && (1 << TXC)));            // Warten bis vorheriger Sendevorgang fertig

verweilen. Welchen AVR verwendest du eigentlich?

Ansonsten lass TXC einfach außen vor. Brauchst du nicht.

von STK500-Besitzer (Gast)


Lesenswert?

Maximilian G. schrieb:
> Du hast recht, dass es nicht nötig ist ich dachte nur es wäre vllt
> sauberer so und so kann ich die Funktion auch einfacher wiederverwenden.

Die Schnittstelle schaltet man bei der Initialisierung ein.
Zusammen mit dem Empfang (RXE) und der Konfiguration bestehen aus der 
Baudrate und dem Datenwortbreite (z.B. 8N1).

Sofern du nicht irgendwelche Halbduplex-Schnittstellen implementieren 
willst (z.B. RS485), brauchst du dich für TXC nicht interessieren.
Das Flag zeigt dir an, wenn die letzten Daten den Sendepuffer verlassen 
haben.

UDRE zeigt dir an, ob im Sendepuffer platz für ein neues Byte ist.


> Btw. Hast du auch mit der letzten abfrage recht ich muss vermutlich die
> schleife umbauen da sonst eventuell das letzte zeichen nicht gesendet
> wird...

Du schmeoßt einfach die Ein-Ausschalt-Orgie und das TXC-Geraffel aus der 
Übertragungsfunktion.
Einzig die UDRE-Abfrage am Anfang ist nötig.

von Maximilian G. (chainex)


Lesenswert?

1
void Senden(unsigned char *pInput)
2
{
3
  while (*pInput != '\0')
4
  {
5
    while (!(UCSRA && (1 << UDRE)));          // Warten bis Buffer leer
6
    UDR  = *pInput;                    // Daten in Buffer schreiben
7
    pInput++;
8
  }
9
}

Soo abgeändert.

neuer PIC Freund schrieb im Beitrag #6187234:
> Und ich frag mich wie. Initial Value von TXC ist 0. Beim erstmaligen
> Aufruf von Senden() sollte dein Code bis zum Sankt-Nimmerleinstag oder
> Watchdog in
> while (!(UCSRA && (1 << TXC)));            // Warten bis vorheriger
> Sendevorgang fertig

Ehrlich gesagt wundert mich das gerade selbst, ist ja klar ein Fehler.

neuer PIC Freund schrieb im Beitrag #6187234:
> Welchen AVR verwendest du eigentlich?

Ich verwende einen AtTiny2313A

von S. Landolt (Gast)


Lesenswert?

> ... wundert mich ...
Was ist eigentlich der Unterschied zwischen '&' und '&&'?

von neuer PIC Freund (Gast)


Lesenswert?

Aaargh. Das && hab ich doch glatt übersehen. Wird wegen UDRE triggern. 
Deswegen läuft es, aber wahrscheinlich nicht so, wie gewollt.

von S. Landolt (Gast)


Lesenswert?

> Wird wegen UDRE triggern.
Ein paar Fehlerflags könnte ich noch anbieten.

von S. Landolt (Gast)


Lesenswert?

Doch kein gutes Angebot, es wird ja nur gesendet.

von Holger L. (max5v)


Lesenswert?

Ich habe zur Zeit nur ein Smartphone zur Verfügung und kann deshalb 
nicht nachschauen.

  DDRB  = (1 << PORTB1);
  PORTB = (1 << PORTB0);

Ist denn sicher das PORTB1 das selbe wie PB1 ist?
Wenn das Atmel Studio verwendet wird, kann man sich im Debugmodus die 
I/0 eunfach anschauen.

von W.S. (Gast)


Lesenswert?

Maximilian G. schrieb:
> ich habe mal wieder ein Anfängerproblem und komme ohne Hilfe nicht
> weiter.

Das glauben wir dir gern und ungeprüft - aber es ist nicht relevant.

Also: Schreib zuerst mal, was du überhaupt für einen Chip benutzt und 
dann, was du damit für ein Problem hast. Ansonsten wird das Ganze zu 
einem Textadventure, wo man die Informationen nur scheibchenweise 
herauskriegt - wenn überhaupt.

Maximilian G. schrieb:
> Nur habe ich Probleme beim Interrupt EXT0.
> Leider löst dieser aus sobald ich den LED Ausgang umschalte.

Dann lies zuvörderst das zugehörige Manual! Manche Interrupts sind 
flankengetriggert, andere sind pegelgetriggert. Manche Interrupts werden 
automatisch zurückgesetzt, wenn der zugehörige Handler ausgeführt wird, 
andere muß man dediziert zurücksetzen, sonst kommen sie unmittelbar 
immer wieder.

Und manche Chips haben Interrupts auf Zustandswechsel eines Portes, die 
eben auf alle Portpins und deren Änderung reagiert - andere Chips 
haben dafür spezielle Register, wo man sich aussuchen kann, auf welche 
Pins der Int kommen soll und auf welche eben nicht.

Sowas sollte man lesen, bevor man seine Schaltung und Leiterplatte 
macht.

W.S.

von Maximilian G. (chainex)


Lesenswert?

W.S. schrieb:
> Und manche Chips haben Interrupts auf Zustandswechsel eines Portes, die
> eben auf alle Portpins und deren Änderung reagiert - andere Chips
> haben dafür spezielle Register, wo man sich aussuchen kann, auf welche
> Pins der Int kommen soll und auf welche eben nicht.
>
> Sowas sollte man lesen, bevor man seine Schaltung und Leiterplatte
> macht.

Tolle Antwort die mich sehr motiviert, mich weiter mit der Materie zu 
beschäftigen.

Außerdem habe ich das Datenblatt gelesen. Es kann natürlich sein, dass 
ich etwas überlesen habe aber selbst dann kann man doch jemanden nett 
auf seine Fehler hinweisen. Anstatt einfach ein "lies halt die 
Anleitung" rauszuhauen.

Auszug aus dem Datenblatt:

• Bit 6 – INTF0: External Interrupt Flag 0
When an edge or logic change on the INT0 pin triggers an interrupt 
request, INTF0 becomes set (one). If the I-bit in SREG and the INT0 bit 
in GIMSK are set (one), the MCU will jump to the corresponding Interrupt 
Vector. The flag is cleared when the interrupt routine is executed.
Alternatively, the flag can be cleared by writing a logical one to it. 
This flag is always cleared
when INT0 is configured as a level interrupt.


Danke für die Antworten bisher.
Werde mich heute Abend mal nochmal dahinterklemmen.

von W.S. (Gast)


Lesenswert?

Maximilian G. schrieb:
> Es kann natürlich sein, dass
> ich etwas überlesen habe aber selbst dann kann man doch jemanden nett
> auf seine Fehler hinweisen. Anstatt einfach ein "lies halt die
> Anleitung" rauszuhauen.

Was erwartest du?

Daß jemand anderes hier sich durch dein Vorhaben hindurchwühlt bis er 
den Fehler findet, ohne daß du ihn mit wirklich ausreichenden 
Informationen versehen hast?

Oder daß du gelobt wirst (und wofür)?

Nein mein Lieber, dir anzuraten, das Manual gründlich zu lesen und zu 
verstehen, ist bei dieser Sachlage der einzige wirklich sinnvolle Rat.

Alles andere wäre nur Rätselraten.

Und nochwas: du scheinst einer von der fordernden und zugleich 
undankbaren Sorte zu sein. Ich hab dir im Schnelldurchgang all die 
wahrscheinlichen Ursachen genannt, die zu einem quasi "dauerhaften" 
Interrupt führen können - aber das Manual zu deinem Chip lese ich nicht 
stellvertretend für dich durch. Das machst du gefälligst selber.

W.S.

von Maximilian G. (chainex)


Lesenswert?

Ich möchte hiermit die Diskussion beenden:

W.S. schrieb:
> Und nochwas: du scheinst einer von der fordernden und zugleich
> undankbaren Sorte zu sein.

W.S. schrieb:
> Nein mein Lieber,

Ich bin der Meinung solche Antworten gehören nicht in ein Fachforum.
Außerdem würde ich ihnen nahelegen ihre Umgangsformen noch einmal zu 
überdenken. Auch wenn ihre fachlichen Kenntnisse die meinen bei weitem 
übersteigen, gibt es ihnen nicht das Recht herablassende Kommentare zu 
meiner Person zu verfassen!

von Carl D. (jcw2)


Lesenswert?

@WS:
SREG, GIMSJ, PORTB, DDRB, was könnte das wohl sein?

Tipp: die Lieblings-Chip-Serie dieses Forums.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

@Maximilian G:
W.S. kann einfach nicht anders.
Er hat selber von nichts ne Ahnung aber spielt den Oberlehrer.
Vor allem zeigt er selbst unterirdische Verhaltensweisen (wie fu gemerkt 
hast), aber wenn man mal selber zu dem Typen mal etwas unfreundlich ist 
fängt er gleich an rumzuflennen.

Also:
Lass den dummen Troll einfach links liegen.

von Maximilian G. (chainex)


Angehängte Dateien:

Lesenswert?

Vielen dank nochmal für die vielen Tipps.

Habe das ganze Ding jetzt nochmal geschrieben und auf den Interrupt 
verzichtet.
Werde mich dann morgen nochmal an den Interrupt ranwagen.
Soweit funktioniert alles und ich bin auch zufrieden.


Eine Frage hätte ich allerdings noch:

warum löst
1
const uint32_t  Teiler_Baud = (TAKT / (BAUD * 16)) - 1;
immer einen Integer ov aus? kann der Compiler nicht mit mehr als 8 Bit 
rechen? ((4800 * 16) > int)
Das sollte er doch automatisch machen wenn er doch eine 32Bit breite 
Variable beschreibt?

Genutzt wird Atmel Studio mit einem AtTiny2313A

: Bearbeitet durch User
von Kann s nicht mehr ertragen (Gast)


Lesenswert?

Holger L. schrieb:
>   DDRB  = (1 << PORTB1);
>   PORTB = (1 << PORTB0);

Wieder dieser Shift Sch...

Muss ja in die Hose gehen. Benutzt endlich richtige defines.

von STK500-Besitzer (Gast)


Lesenswert?

Kann s nicht mehr ertragen schrieb:
> Benutzt endlich richtige defines.

Die wie aussehen?

von neuer PIC Freund (Gast)


Lesenswert?

>Ist denn sicher das PORTB1 das selbe wie PB1 ist?

Aus iotn2313a.h
1
#define DDRB _SFR_IO8(0x017)
2
#define DDB0 0
3
#define DDB1 1
4
#define DDB2 2
5
#define DDB3 3
6
#define DDB4 4
7
#define DDB5 5
8
#define DDB6 6
9
#define DDB7 7
10
11
#define PORTB _SFR_IO8(0x018)
12
#define PORTB0 0
13
#define PORTB1 1
14
#define PORTB2 2
15
#define PORTB3 3
16
#define PORTB4 4
17
#define PORTB5 5
18
#define PORTB6 6
19
#define PORTB7 7

Sollte also passen. Vielleicht eher was wie
1
#define LED_PORT PORTB
2
#define LED_BIT  PORTB2
3
4
...
5
LED_PORT |= (1 << LED_BIT);
6
...

und keine Magic-Numbers aka PORTB  =  0b00000001;

von Peter D. (peda)


Lesenswert?

Maximilian G. schrieb:
> Das sollte er doch automatisch machen wenn er doch eine 32Bit breite
> Variable beschreibt?

Das Schreiben erfolgt nach der Rechnung, also nach dem int-Überlauf.
Der Compiler arbeitet streng nach den Vorrangregeln, Hellsehen ist nicht 
sein Ding.

von Maximilian G. (chainex)


Lesenswert?

Wie kriege ich es dann hin, dass er mir das korrekte ergebnis 
ausrechnet? (theoretisch)

von STK500-Besitzer (Gast)


Lesenswert?

Maximilian G. schrieb:
> const uint32_t  Teiler_Baud = (TAKT / (BAUD * 16)) - 1;

versuch mal 16L oder 16UL
1
 const uint32_t  Teiler_Baud = (TAKT / (BAUD * 16UL)) - 1;

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.