Forum: Mikrocontroller und Digitale Elektronik Atmega128 - Wieder mal Probleme mit UART Interrupt


von Thomas (Gast)


Lesenswert?

Hallo,

jetzt probier ich schon seit einigen Stunden Daten mittels dem 
USART0_RX_vect Interrupt zu empfangen, aber aus irgendeinen Grund wird 
die ISR anscheinend nie aufgerufen.
Daten Senden mittels USART0_UDRE_vect funktioniert ohne Probleme. Daten 
empfangen mittel Polling ebenfalls.

Hier mal der vereinfachte Code der nur überprüft ob die ISR aufgerufen 
wird.
Wenn ja, dann sollten sich 4 LEDs die am PortC hängen einschalten. Nach 
spätestens 4 sek sollen sich die LEDs aber auf jedenfall einschalten. 
Und da passiert jetzt was seltsames: Wenn ich im Hyperterminal nichts 
eingebe werden die LEDs nach 4 sekunden eingeschalten, aber wenn ich 
Daten sende, dann scheint das Programm abzustürzen, weil dann garnichts 
mehr passiert.
1
...
2
3
void uart_init()
4
{
5
  uint8_t sreg = SREG;
6
7
    UBRR0H = UBRR_VAL >> 8;
8
    UBRR0L = UBRR_VAL & 0xFF;
9
10
    // Interrupts kurz deaktivieren
11
    cli();
12
13
    // UART Receiver und Transmitter anschalten, Receive Interrupt setzen
14
    // Data mode 8N1, asynchron
15
    UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);
16
    UCSR0C = (1<<UCSZ1) | (1<<UCSZ0);
17
18
19
    // Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte)
20
    do
21
    {
22
        // UDR auslesen (Wert wird nicht verwendet)
23
        UDR0;
24
    }
25
    while (UCSR0A & (1 << RXC0));
26
27
    // Rücksetzen von Receive und Transmit Complete-Flags
28
    UCSR0A = (1 << RXC0) | (1 << TXC0);
29
30
    // Global Interrupt-Flag wieder herstellen
31
    SREG = sreg;
32
}
33
34
ISR(USART0_RX_vect)
35
{
36
    uint8_t sreg = SREG;
37
    cli();
38
39
    // LEDS einschalten
40
    DDRC = 0xFF;
41
    PORTC = 0b11110000;
42
43
    UDR0;
44
45
    SREG = sreg;
46
}
47
48
int main (void)
49
{
50
    sei();
51
52
    uart_init();
53
54
    // Nach 4 Sekunden sollen die LEDs auf jedenfall eingeschalten werden
55
    for(int i = 0; i< 4000; i++)
56
        _delay_ms(1);
57
58
    DDRC = 0xFF;
59
    PORTC = 0b11110000;
60
    while(1);
61
62
    return 0;
63
}

Sieht hier jemand einen Fehler? Kann das irgendwie mit AT103 
Kompatibilitätsmodus zusammenhängen? Oder liegts vielleicht sogar am 
Bootloader?.

Danke schon mal im vorraus,

Thomas

von Johannes M. (johnny-m)


Lesenswert?

Was soll das cli() im Interrupt Handler? Das ist unsinnig, weil die 
Hardware das automatisch macht. Auch vom SREG sollte man als 
Hochsprachen-Programmierer weitgehend die Finger lassen!

von Thomas (Gast)


Lesenswert?

Hab ich nur sicherheitshalber gemacht um zu verhindern, dass da 
vielleicht irgendein anders Interrupt "hineinpfuscht". Egal, daran 
liegts nicht.

von Johannes M. (johnny-m)


Lesenswert?

BTW: Das RXC-Flag ist Read-Only. Das kann man mit
> UCSR0A = (1 << RXC0) | (1 << TXC0);
nicht löschen.

Außerdem: Was soll diese Abfrage bewirken:
> while (UCSR0A & (1 << RXC0));

von Thomas (Gast)


Lesenswert?

>BTW: Das RXC-Flag ist Read-Only. Das kann man mit
>> UCSR0A = (1 << RXC0) | (1 << TXC0);
>nicht löschen.

Ah ok, hab das von irgendeinem Musterbeispiel kopiert.

>Außerdem: Was soll diese Abfrage bewirken:
>> while (UCSR0A & (1 << RXC0));

Hab ich ebenfalls kopiert. Die Schleife läuft halt solangen das Receive 
Complete Flag gesetzt ist und liest solange UDR0 aus.

Weglassen der beiden Programmteile ändert leider auch nichts.
Meinst nicht das der Bootloader vielleicht an dem Problem schuld sein 
kann? Oder irgendeine Fuse?

von Stefan E. (sternst)


Lesenswert?

Thomas wrote:

> Kann das irgendwie mit AT103 Kompatibilitätsmodus zusammenhängen?

Ja. Deaktiviere die Fuse.

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:

> kann? Oder irgendeine Fuse?

Schon möglich.
Hast du denn die M103 Fuse verändert?

von Johannes M. (johnny-m)


Lesenswert?

Thomas wrote:
>>Außerdem: Was soll diese Abfrage bewirken:
>>> while (UCSR0A & (1 << RXC0));
>
> Hab ich ebenfalls kopiert. Die Schleife läuft halt solangen das Receive
> Complete Flag gesetzt ist und liest solange UDR0 aus.
Die Schleife liest außer UCSR0A überhaupt nichts aus...

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:

> Hab ich ebenfalls kopiert. Die Schleife läuft halt solangen das Receive
> Complete Flag gesetzt ist und liest solange UDR0 aus.

Genau genommen, liest sie UDR0 immer mindestens 1mal aus.
1
  ...
2
    // Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte)
3
    while (UCSR0A & (1 << RXC0))
4
        // UDR auslesen (Wert wird nicht verwendet)
5
        UDR0;
6
  ...

von Stefan E. (sternst)


Lesenswert?

Johannes M. wrote:

> Die Schleife liest außer UCSR0A überhaupt nichts aus...

Schau nochmal hin und beachte den do-Block davor.

von Johannes M. (johnny-m)


Lesenswert?

Stefan Ernst wrote:
> Johannes M. wrote:
>
>> Die Schleife liest außer UCSR0A überhaupt nichts aus...
>
> Schau nochmal hin und beachte den do-Block davor.
Yep, hast natürlich Recht. Oben ging es mir nur um die Abfrage an sich, 
hab dann beim Weiterkopieren den Kontext nicht mehr mitgeschleppt...

Allerdings habe ich auf den zweiten Blick auch gesehen, dass die 
Schleife und die Abfrage doch für was gut sind...

von Thomas (Gast)


Lesenswert?

> Schon möglich.

Komisch ist dabei, dass das die USART0_UDRE_vect Interrupt funktioniert 
und ich auch sonst noche keine Probleme hatte.

> Hast du denn die M103 Fuse verändert?
Nein, ich hab programmiere nur per USB. Siehe 
Beitrag "Programmvorstellung "CCPro-Loader" - natives C (ohne Interpreter) auf der C-Control Pro"
Mittels einem Programm was auf den MC läuft kann man die Fuses sicher 
nicht rücksetzen, oder? Würde dabei eigentlich auch der Bootloader 
entfernt werden? Ich nehm mal an schon, sonst könnte man den Lese-Fuse 
ja ganz einfach umgehen.

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:

> Komisch ist dabei, dass das die USART0_UDRE_vect Interrupt funktioniert
> und ich auch sonst noche keine Probleme hatte.

Das wäre allerdings ein Indiz, dass die 103 Fuse richtig steht.
Steht die falsch, geht bereits der erste Funktionsaufruf in die Hose.
Bei deinen eigenen Funktionen könnte der Compiler per inline den 
Funktionsaufruf überflüssig gemacht haben. Aber bei einer ISR muss ein 
Aufruf erfolgen.

>> Hast du denn die M103 Fuse verändert?
> Nein, ich hab programmiere nur per USB. Siehe

LOL. Das hat damit nicht das geringste zu tun.
Die 103 Fuse sorgt dafür, dass sich ein Mega128 wie ein Mega103 verhält.
Blöderweise ist die per Default im Auslieferungszustand des Prozessors 
aktiviert.

von Thomas (Gast)


Lesenswert?

>>> Hast du denn die M103 Fuse verändert?
>> Nein, ich hab programmiere nur per USB. Siehe

>LOL. Das hat damit nicht das geringste zu tun.
Wie soll ich den die Fuses verändert haben? Ich verwende weder ISP noch 
JTAG.

>Die 103 Fuse sorgt dafür, dass sich ein Mega128 wie ein Mega103 verhält.
Schon klar...

von Karl H. (kbuchegg)


Lesenswert?

Dreh hier auch mal die Reihenfolge um:
1
int main (void)
2
{
3
    sei();
4
5
    uart_init();

Die normale, übliche und auch logisch nachvollziehbare Reihenfolge ist 
doch wohl: zuerst alles einstellen und erst dann die Interrupts 
freigeben. Und nicht umgekehrt.

von Stefan E. (sternst)


Lesenswert?

Datenblatt:
1
ATmega103 Compatibility Mode
2
...
3
• Boot Loader capabilities is not supported.

Die Fuse fällt als mögliche Fehlerquelle also definitiv raus.

von Thomas (Gast)


Lesenswert?

>Dreh hier auch mal die Reihenfolge um:

Danke für den Tipp, zumindest schalten sich die LEDs jetzt nach 4 
sekunden wieder ein. Aber auf eine gesendetes Zeichen wird noch immer 
nicht reagiert.

> Die Fuse fällt als mögliche Fehlerquelle also definitiv raus.
Hab ich mir auch schon gedacht. Dann kanns doch eigentlich nurmehr am 
Bootloader liegen, oder?

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:
>>Dreh hier auch mal die Reihenfolge um:
>
> Danke für den Tipp, zumindest schalten sich die LEDs jetzt nach 4
> sekunden wieder ein. Aber auf eine gesendetes Zeichen wird noch immer
> nicht reagiert.

Wie sieht dann das aktuell im µC laufende Programm jetzt aus?

von Thomas (Gast)


Lesenswert?

Es hat sich übrigens noch was geändert. Interessanterweise werden die 
LEDs erst 4 sekunden nach dem ich das letztes Zeichen im Hyperterminal 
eingegeben habe eingeschalten. Irgendwie scheint der MC doch auf das 
Interrupt zu reagieren.

Hier ist der aktuell Code:
1
void uart_init()
2
{
3
  uint8_t sreg = SREG;
4
5
    UBRR0H = UBRR_VAL >> 8;
6
    UBRR0L = UBRR_VAL & 0xFF;
7
8
    // Interrupts kurz deaktivieren
9
    cli();
10
11
  // Buffer initialisieren
12
13
    // UART Receiver und Transmitter anschalten
14
    // Data mode 8N1, asynchron
15
    UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);
16
    UCSR0C = (1<<UCSZ1) | (1<<UCSZ0);
17
18
19
    // Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte)
20
    do
21
    {
22
        // UDR auslesen (Wert wird nicht verwendet)
23
        UDR0;
24
    }
25
    while (UCSR0A & (1 << RXC0));
26
27
    // Rücksetzen von Receive und Transmit Complete-Flags
28
    //UCSR0A = (1 << RXC0) | (1 << TXC0);
29
30
    // Global Interrupt-Flag wieder herstellen
31
    SREG = sreg;
32
}
33
34
ISR(USART0_RX_vect)
35
{
36
  DDRC = 0xFF;
37
  PORTC = 0b11110000;
38
39
  UDR0;
40
}
41
42
int main (void)
43
{
44
  uart_init();
45
  sei();
46
47
  for(int i = 0; i< 4000; i++)
48
    _delay_ms(1);
49
  
50
  DDRC = 0xFF;
51
  PORTC = 0b11110000;
52
  while(1);
53
54
  return 0;
55
}

von Thomas (Gast)


Lesenswert?

Irgendwie hab ich den Verdacht, dass beim Interrupt nicht die ISR 
sondern wieder main() von vorn aufgerufen wird

von Stefan E. (sternst)


Lesenswert?

Du bekommst nicht zufällig eine "return type defaults to 'int'"-Warnung 
für die ISR, oder?

Wenn ja, dann:

1)
"#include <avr/interrupt.h>" einfügen.

2)
Warum ignorierst du Warnungen, oder postest sie nicht zumindest?

von Thomas (Gast)


Lesenswert?

>Du bekommst nicht zufällig eine "return type defaults to 'int'"-Warnung
>für die ISR, oder?

Nein, bekomm ich nicht, außer ich verwende SIGNAL statt ISR

von Stefan E. (sternst)


Lesenswert?

Richtiger µC-Typ ist auch ganz sicher ausgewählt?

Ansonsten hätte ich keine Idee mehr.

von Thomas (Gast)


Lesenswert?

> Nein, bekomm ich nicht, außer ich verwende SIGNAL statt ISR

Korrektur: Es kommt nur bei Signal. SIGNAL bringt keine Warnungen

von Thomas (Gast)


Lesenswert?

> Richtiger µC-Typ ist auch ganz sicher ausgewählt?
Das ist gar nicht so einfach zu beantworten. Laut beigelegten Datasheet 
ist da ein AT90CAN128 drinnen. Wenn ich den auswähle startet aber gar 
kein Programm mehr.
Mit normalen Atmega128 hat bis auf das eine Interrupt jetzt alles 
funktioniert.

> Ansonsten hätte ich keine Idee mehr.
Trotzdem danke für deine Mühe.

von Juppi (Gast)


Lesenswert?

Warum versucht man eigentlich Jemandem zu helfen, der sich beharrlich 
weigert, den ganzen Code zu posten?

von Thomas (Gast)


Lesenswert?

> Warum versucht man eigentlich Jemandem zu helfen, der sich beharrlich
> weigert, den ganzen Code zu posten?

He mal ruhig Blut, ich wollte nur nicht den Thread zumüllen und den Code 
auf die wichtigsten Teile beschränken. Keiner liest sich gerne durch zig 
Zeilen trivialen Code.
Aber bitte sehr:
1
#define F_CPU 14745600L
2
3
#define BAUD 38400L
4
5
// Berechnungen
6
#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1)   // clever runden
7
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))     // Reale Baudrate
8
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // Fehler in Promille
9
 
10
#if ((BAUD_ERROR>10) || (BAUD_ERROR<-10))
11
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
12
#endif
13
14
#include <avr/io.h>
15
#include <avr/interrupt.h>
16
#include <util/delay.h>
17
18
void uart_init()
19
{
20
  uint8_t sreg = SREG;
21
22
    UBRR0H = UBRR_VAL >> 8;
23
    UBRR0L = UBRR_VAL & 0xFF;
24
25
    // Interrupts kurz deaktivieren
26
    cli();
27
28
  // Buffer initialisieren
29
30
    // UART Receiver und Transmitter anschalten
31
    // Data mode 8N1, asynchron
32
    UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);
33
    UCSR0C = (1<<UCSZ1) | (1<<UCSZ0);
34
35
36
    // Flush Receive-Buffer (entfernen evtl. vorhandener ungültiger Werte)
37
    do
38
    {
39
        // UDR auslesen (Wert wird nicht verwendet)
40
        UDR0;
41
    }
42
    while (UCSR0A & (1 << RXC0));
43
44
    // Rücksetzen von Receive und Transmit Complete-Flags
45
    UCSR0A = (1 << RXC0) | (1 << TXC0);
46
47
    // Global Interrupt-Flag wieder herstellen
48
    SREG = sreg;
49
}
50
51
ISR(USART0_RX_vect)
52
{
53
  DDRC = 0xFF;
54
  PORTC = 0b11110000;
55
56
  UDR0;
57
}
58
59
int main (void)
60
{
61
  uart_init();
62
  sei();
63
64
  for(int i = 0; i< 4000; i++)
65
    _delay_ms(1);
66
  
67
  DDRC = 0xFF;
68
  PORTC = 0b11110000;
69
  while(1);
70
71
  return 0;
72
}

von Stefan E. (sternst)


Lesenswert?

Thomas wrote:
>> Richtiger µC-Typ ist auch ganz sicher ausgewählt?
> Das ist gar nicht so einfach zu beantworten. Laut beigelegten Datasheet
> ist da ein AT90CAN128 drinnen. Wenn ich den auswähle startet aber gar
> kein Programm mehr.

Also du musst schon genau wissen, welchen µC du da eigentlich 
programmierst. Bei AT90CAN128 und ATmega128 liegt der 
USART-RXC-Interrupt z.B. auf unterschiedlichen Vektoren. Und wenn der 
Vektor nicht stimmt, wird halt nach 0 gesprungen, das Programm also neu 
gestartet (was du ja auch beobachtet hast).

Wieso eigentlich CAN? War nicht von USB die Rede? Ist es nicht 
vielleicht doch ein AT90USB128?

von Juppi (Gast)


Lesenswert?

Der SREG-Muell ist ja immer noch drin!

von Karl H. (kbuchegg)


Lesenswert?

Grrrrrrrrr
Lass endlich das SREG in Ruhe!

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:

> He mal ruhig Blut, ich wollte nur nicht den Thread zumüllen und den Code
> auf die wichtigsten Teile beschränken. Keiner liest sich gerne durch zig
> Zeilen trivialen Code.

Keiner liest sich aber auch durch 25 Postings mit jeweils einem 
nichtssagenden Code Schnipsel, nur um den Rest zu erraten.

von Thomas (Gast)


Lesenswert?

> Also du musst schon genau wissen, welchen µC du da eigentlich
> programmierst. Bei AT90CAN128 und ATmega128 liegt der
> USART-RCX-Interrupt z.B. auf unterschiedlichen Vektoren.

Hab im Datenblatt schon nachgesehen, wenn es wirklich ein AT90CAN128 
wäre, würde auch USART0_UDRE_vect nicht funktionieren.

>Wieso eigentlich CAN? War nicht von USB die Rede? Ist es nicht
>vielleicht doch ein AT90USB128?

Hab nur gesagt, dass ich das Programm per USB und integrierten 
Bootloader raufspiele. Sry, hätt noch dazusagen soll, dass in dem 
Programmierkabel ein Rs232 Wandler drinnen ist. Es wird also eigentlich 
über UART programmiert.

>Grrrrrrrrr Lass endlich das SREG in Ruhe!
Sry, habs von der ISR raus, aber im Init noch vergessen.
Ändert aber auch nichts.

von Karl H. (kbuchegg)


Lesenswert?

Thomas wrote:
>> Also du musst schon genau wissen, welchen µC du da eigentlich
>> programmierst. Bei AT90CAN128 und ATmega128 liegt der
>> USART-RCX-Interrupt z.B. auf unterschiedlichen Vektoren.
>
> Hab im Datenblatt schon nachgesehen, wenn es wirklich ein AT90CAN128
> wäre, würde auch USART0_UDRE_vect nicht funktionieren.

Im Zweifel: Gerät aufschrauben - nachsehen

von Thomas (Gast)


Lesenswert?

> Keiner liest sich aber auch durch 25 Postings mit jeweils einem
> nichtssagenden Code Schnipsel, nur um den Rest zu erraten.

Dieser nichtsagende Teil ist jener der nicht funktioniert. Und wenn man 
noch Information braucht, kann man nachfragen und muss nicht 
herummotzen.

von Thomas (Gast)


Lesenswert?

> Im Zweifel: Gerät aufschrauben - nachsehen
Das ist bei der C-Control Pro leider nicht möglich

von Thomas (Gast)


Lesenswert?

Nachdem ich jetzt alle ISR's durchprobiert habe (auch BADISR_vect) und 
noch mal das Datenblatt studiert habe, gehe ich davon aus, dass Conrad 
das IVSEL Bit aktiviert hat, was bedeuted, dass die Interrupts in den 
Bootloader-Bereich abgelenkt werden. Da kann ich wohl nichts dagegen 
machen.

Danke noch mal alle die mitgeholfen haben.

von Stefan E. (sternst)


Lesenswert?

So so, und was ist dann damit:
> Komisch ist dabei, dass das die USART0_UDRE_vect Interrupt funktioniert
?

(ganz davon abgesehen, dass man das Bit auch wieder löschen könnte)

von Thomas (Gast)


Lesenswert?

>So so, und was ist dann damit:
>> Komisch ist dabei, dass das die USART0_UDRE_vect Interrupt funktioniert

Ich nehme an, dass bestimmte Interrupts vom Bootloader wieder umgeleitet 
werden.

> (ganz davon abgesehen, dass man das Bit auch wieder löschen könnte)
Ohne ISP oder JTAG? Echt? Wie?

von Stefan E. (sternst)


Lesenswert?

Thomas wrote:

> Ich nehme an, dass bestimmte Interrupts vom Bootloader wieder umgeleitet
> werden.

Ja stimmt, die unbenutzten könnten auf die "unteren" Vektoren 
weiterleiten.

>> (ganz davon abgesehen, dass man das Bit auch wieder löschen könnte)
> Ohne ISP oder JTAG? Echt? Wie?
Das ist keine Fuse. Das ist ein "normales" Bit, das zur Laufzeit 
modifiziert wird.

Datenblatt:
1
• Bit 1 – IVSEL: Interrupt Vector Select
2
When the IVSEL bit is cleared (zero), the interrupt vectors are placed at the start of the Flash
3
memory. When this bit is set (one), the interrupt vectors are moved to the beginning of the Boot
4
Loader section of the flash. The actual address of the start of the Boot Flash section is determined
5
by the BOOTSZ fuses. Refer to the section “Boot Loader Support – Read-While-Write
6
Self-Programming” on page 273 for details. To avoid unintentional changes of interrupt vector
7
tables, a special write procedure must be followed to change the IVSEL bit:
8
1. Write the Interrupt Vector Change Enable (IVCE) bit to one.
9
2. Within four cycles, write the desired value to IVSEL while writing a zero to IVCE.
10
Interrupts will automatically be disabled while this sequence is executed. Interrupts are disabled
11
in the cycle IVCE is set, and they remain disabled until after the instruction following the write to
12
IVSEL. If IVSEL is not written, interrupts remain disabled for four cycles. The I-bit in the Status
13
Register is unaffected by the automatic disabling.

von Thomas (Gast)


Lesenswert?

>Das ist keine Fuse. Das ist ein "normales" Bit, das zur Laufzeit
>modifiziert wird.

Ah danke hab ich auch gerade gesehen.
Der Aufruf von:

MCUCR = (1<<IVCE);
MCUCR &= ~(1<<IVSEL);

vor dem Programmstart hat allerdings nichts gebracht. Der Fehler muss 
wohl doch woanders liegen. Komisch hab echt alle ISRs durchprobiert.

von Thomas (Gast)


Lesenswert?

Yessss, es geht!!!
Mit:

MCUCR = (1<<IVCE);
MCUCR = ~(1<<IVSEL);

Nochmals vielen dank an alle :):)

von Thomas (Gast)


Lesenswert?

Sry, Schmarrn.
Mit:

MCUCR = (1<<IVCE);
MCUCR = 0;

funktionierts.

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.