PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : USART atmega32 mit Interrupt



walterk
05.12.2009, 14:38
Hallo Forum,

möchte ein Byte von der seriellen Schnittstelle eines PC auf einen Atmega32 mit einem Interrupt übertragen.

Die Interruptroutine bleibt in der gekennzeichneten Stelle //verharrt hier stehen und die Variable Empfang wird nicht mehr auf 1 gesetzt.

Die Wertzuweisung a = UDR; arbeitet im Pollingbetrieb einwandfrei, auch Hardwareinterrupts funktionieren.

Umgebung: RN-Control 1.4, Ausgabe auf 8 LEDs, AVR-Studio 4.18. Suche im Forum und Tutorials waren leider nicht hilfreich.

Wo soll ich weitersuchen?

Danke für die Antworten.



#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>
#include <stdlib.h>
#include <avr/interrupt.h>

unsigned char a;
unsigned char empfang = 0;

ISR(USART_RXC_vect){

//PORTC = ~'D'; //verharrt hier während des delay

a = UDR;
empfang = 1;
}

int main(void){

sei();
//Schnittstelle parametrisieren
UBRRH = 0;
UBRRL = 51;
//Empfang, Senden, Empfangsinterrupt
UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE);
//Defaultwerte zementieren, 8N1
UCSRC |= (1<<URSEL) | (3<<UCSZ0);

DDRC = 0xff;


while(1){

PORTC = ~empfang; //wird nicht ausgeführt
_delay_ms(1000);

PORTC = ~'E'; //wird ausgeführt
_delay_ms(1000);

if(empfang){


PORTC = ~a;
empfang = 0;
}
}
}

Ceos
05.12.2009, 15:14
verwende doch bitte erstmal die [c o d e] - tags für den quellcode, macht das ganze übersichtlicher

dann zu folgenden zeilen ein paar fragen

PORTC = ~empfang; //wird nicht ausgeführt

wie kommst du auf die aussage ?

PORTC = ~'D'; //verharrt hier während des delay

WELCHES delay ?

also nur mal so vom programmablauf, du setzt PORTC auf 0 oder 'E', ein interrupt kommt, logischerweise springt die anzeige sofort auf 'D' ... dann kommst du in deine if-abfrage, zeigst das empfangene byte für ein paar µS an und löscht es dann sogleich wieder mit

PORTC = ~empfang;

nimm einfach mal alles was du mit PORTC machst raus und belasse es mal bei der if abfrage mit

PORTC = ~a;

sternst
05.12.2009, 15:23
Das ganze "wird ausgeführt ja/nein" ist mit Sicherheit nur Simulator/Debugger-Verwirrung, die daher rührt, dass bei eingeschalteter Optimierung keine eindeutige Zuordnung von C und ASM Code mehr möglich ist.

Dass der Code nicht macht was du willst, liegt an zwei vergessenen volatiles.

walterk
05.12.2009, 15:56
Danke an Ceos und sternst für die schnelle Hilfe!

Funktioniert nun.

Die beiden volatile habe ich nach einigem Probieren wieder rausgelöscht und viel zu kompliziert gedacht ==> Fehler

Ceos
05.12.2009, 17:46
hmmm beides sind doch unsigned char, da sollte volatile doch überflüssig sein ?! oder kann es im ungünstigen fall zur überschreibung des wertes aus dem stack kommen ?!

sternst
05.12.2009, 18:01
hmmm beides sind doch unsigned char, da sollte volatile doch überflüssig sein ?!Was soll das eine mit dem anderen zu tun haben?


oder kann es im ungünstigen fall zur überschreibung des wertes aus dem stack kommen ?!Hä? Was für ein Wert auf dem Stack?

Besserwessi
05.12.2009, 22:12
Ohne Optimierung geht es fast immer auch ohne volatile, aber mit optimierung sollte das volatile schon rein. Nach den Sprachdefintionen gehört das volatile auch hin, denn der Compiler weiß sonst nicht, das sich im Hauptprogramm was an "a" und "empfang" ändern kann. Ohne Optimierung wird gcc das noch nicht ausnutzen, mit Optimierung wahrscheinlich schon.

Ceos
05.12.2009, 23:28
@sternst zu 1. die variablen können in einem zug gelesen werden(8bit), deshalb sind sie generell unkritisch beim zugriff

zu 2. bin ich mir nicht ganz sicher ob während eines interrupt veränderte variablen nicht eventuell durch den stack des unterbrochenen programms überschrieben werden, wenn der interrupt beendet ist und die variiable nicht volatile deklariert ist

sternst
06.12.2009, 05:38
@sternst zu 1. die variablen können in einem zug gelesen werden(8bit), deshalb sind sie generell unkritisch beim zugriff

zu 2. bin ich mir nicht ganz sicher ob während eines interrupt veränderte variablen nicht eventuell durch den stack des unterbrochenen programms überschrieben werden, wenn der interrupt beendet ist und die variiable nicht volatile deklariert ist
Offenbar vermischt du hier ganz kräftig "volatile" und "atomarer Zugriff". Auch wenn beides gerne im Doppel auftritt, sind es doch verschiedene Sachen. Die Notwendigkeit für "volatile" hat prinzipiell nicht das Geringste mit dem Datentyp zu tun. Am besten schaust du nochmal genau nach, was das volatile überhaupt macht.

Aber auch in Bezug auf "atomarer Zugriff" stimmt die Aussage "zu 1" nicht. Read-Modify-Write-Zugriffe (z.B. x++) sind bei 8-Bit-Variablen ebenso kritisch (und entsprechend zu schützen), wie bei größeren Datentypen.

Ceos
07.12.2009, 00:36
volatile sagt dem compiler, dass die variable direkt aus/in dem spiecher geladen werden muss nach/vor jedem bearbeiten ich dachte dass das gerade mit dem atomaren zugriffen in zusammenhang steht aber solche ++ operationen sind ja nicht atomar, da hab ich etwas zu blauäugig gedacht ^^ recht haste

aber in dem obigen fall sind es doch atomare zugriffe! eine vergleichsoperation und eine zuweisung, also defakto kein volatile nötig oder ?

sternst
07.12.2009, 10:11
volatile sagt dem compiler, dass die variable direkt aus/in dem spiecher geladen werden muss nach/vor jedem bearbeitenSo in etwa.

aber in dem obigen fall sind es doch atomare zugriffe! eine vergleichsoperation und eine zuweisung, also defakto kein volatile nötig oder ?Wieso würfelst du das jetzt schon wieder zusammen?

Was sieht der Compiler in Bezug auf die Variable empfang in main?

unsigned char empfang = 0;

int main(void){

while(1){

PORTC = ~empfang;

if(empfang){
PORTC = ~a;
empfang = 0;
}
}
}Die Variable ist zu Anfang 0 und ändert sich nie in der Endlosschleife, also muss sie in der Endlosschleife immer 0 sein. Aus "PORTC = ~empfang" macht der Compiler daher gleich ein "PORTC = 0xff" und der ganze if-Block wird komplett rausgeschmissen (kann ja nie true werden).

"volatile" sagt dem Compiler "Achtung, diese Variable kann sich unerwartet außerhalb des linearen Code-Ablaufs ändern!" (deine Erklärung oben ist nur die direkte Konsequenz daraus). Er kann jetzt nicht mehr davon ausgehen, dass sie in der Schleife ja immer nur 0 sein kann. Überall wo empfang steht, muss die Variable auch tatsächlich gelesen/geschrieben werden.

Mit "atomaren Zugriffen" oder dem Typ der Variable hat das nicht das Geringste zu tun.

Ceos
07.12.2009, 10:23
O_o dass der beim optimieren SOOOO weit geht war mir nicht bewusst O_o

danke dass du mir die augen geöffnet hast, das würde auch erklären warum meine bisherigen versuche einen meiner eignen quellcodes zu optimieren so herb in die hose gegangen sind -.-

ich habe das immer so verstanden, dass der compiler die variable unter gewissen umständen länger in den registern behält und erst nach abschluss aller arbeiten an der variable in den speicher zurückschreibt und es genau DA eben um den atomaren zugriff auf die variable geht und das volatile eben genau das verhindern soll, indem nach und vor jeder aktion an der variable mit dem speicher abgeglichen wird

aber kann man denn nicht die variable denn nicht irgendwie anders deklarieren? ich habe in einen meiner eigenen codes ein winziges timingproblem, dass ich mit selbstgebauten criticalsections gesichert habe, aber daran scheitert, dass die zugriffe auf die variable scheinbar zu lange dauern!

um es genau zu sagen ist es eine abfolge von if-abfragen (die sich von der statemachine bedingt nicht weiter reduzieren lassen) und unmengen rechenzeit verbrauchen und dazu führen, dass ich das entscheidende event verpasse wenn ich den zeitrahmen zu knapp wähle!

wäre es ratsam einen nicht volatile temp variable zu nehmen (vll. sogar ne registervariable) in die ich den wert der variable lege und dann damit vergleiche anstelle ?

sternst
07.12.2009, 11:14
wäre es ratsam einen nicht volatile temp variable zu nehmen (vll. sogar ne registervariable) in die ich den wert der variable lege und dann damit vergleiche anstelle ?Ja, so was ist gängige Praxis.

volatile uint8_t var;
...
while (1) {

uint8_t tmp_var = var;

if (tmp_var== ....
...So ist sichergestellt, dass 1 mal pro Schleifendurchlauf der aktuelle Wert von var aus dem Speicher gelesen wird, und die ganze "Arbeit" kann mit einer lokalen Kopie in einem Register gemacht werden.