Hallo Zusammen,
ich bin gerade dabei ein bisschen mit UART herumzuspielen und habe mir
ein kleines Programm geschrieben. Das senden funktioniert einwandfrei,
aber wenn ich dem Controller per Terminalprogramm etwas sende, wird der
uC angehalten, So als ob er in der Interruptrutine stecken bleibt. Habe
ich was falsch gemacht?
Hier der Code:
1
#define F_CPU 16000000
2
#include<avr/io.h>
3
#include<util/delay.h>
4
#include<stdlib.h>
5
#include<stdint.h>
6
#include<avr/interrupt.h>
7
8
9
uint8_tBAUD_Wert=103;//Baudrate (9600)
10
uint8_tUART_Daten=10;
11
12
ISR(UART_RXC_vect)
13
{
14
UART_Daten=UDR0;
15
}
16
17
18
voidAVR_Init(void)
19
{
20
21
DDRD|=(1<<PB1);// PD1 als Ausgang (TXD)
22
23
}
24
25
26
voidUART_Init(void)
27
{
28
29
UBRR0=BAUD_Wert;//Baudrate setzen
30
UCSR0B|=(1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);// RX, TX und RX Interrupt aktivieren
31
UCSR0C=(1<<UCSZ01)|(1<<UCSZ00);// 8 Bit Paketgröße einstellen
Die erste while-Schleife bei dir ist ziemlich sinnlos da sie durch die
zweite while-Schleife eh nicht Kreise ziehen wird.
Die ISR kann man so machen, ist aber ziemlich witzlos.
Am besten schaust du dir hier auf der Seite mal das UART-Beispiel in C
genauer an.
>> DDRD |= (1<<PB1); // PD1 als Ausgang (TXD)> Wozu soll das gut sein ?
Bei ATtiny und ATmega ist das nicht notwendig. Nur bei Xmega
Controllern.
> aber wenn ich dem Controller per Terminalprogramm etwas sende,> wird der uC angehalten
Du musst die Interrupts noch mit "sei()" freigeben. Ich denke, dein
Programm bleibt in der Zeile "while (!(UCSR0A & (1<<UDRE0))){}" hängen,
weil wegen gesperrter Interrupts das UDR register nicht frei wird.
Probier mal, den RXCIE0 weg zu lassen und schau nach, ob das Programm
dann immer noch stehen bleibt. Vielleicht bringt uns das der
fehlerursache etwas näher.
Erstelle mal ein LST File, damit wir die Interrupt-Vektoren
kontrollieren können.
Hallo zusammen,
erstmal Danke für die Antworten.
1.
Marc V. schrieb:> Christian W. schrieb:>> DDRD |= (1<<PB1);// PD1 als Ausgang (TXD)>> Wozu soll das gut sein ?
Ich hatte das mal irgendwo gelesen und dachte das braucht man.
2.
M. K. schrieb:> Die erste while-Schleife bei dir ist ziemlich sinnlos da sie durch die> zweite while-Schleife eh nicht Kreise ziehen wird.
Hab ich garnicht bemerkt. Ist mir anscheinend beim Kopieren aus meinem
anderen Projekt passiert.
3.
M. K. schrieb:> Die ISR kann man so machen, ist aber ziemlich witzlos.
Was ist daran denn falsch? Falls du meinst, weil sonst kein anderer Code
ausgeführt wird, der kommt noch.
4.
Stefan U. schrieb:> Probier mal, den RXCIE0 weg zu lassen und schau nach, ob das Programm> dann immer noch stehen bleibt. Vielleicht bringt uns das der> fehlerursache etwas näher.
Habe ich mal weggelassen. Das Programm bleibt nicht mehr stehen wenn
etwas empfangen wird. Allerdings kann der uC nichts empfangen, weil ja
der Interrupt deaktiviert ist.
Mein aktueller Code sieht so aus:
1
#define F_CPU 16000000
2
#include<avr/io.h>
3
#include<util/delay.h>
4
#include<stdlib.h>
5
#include<stdint.h>
6
#include<avr/interrupt.h>
7
8
9
uint8_tBAUD_Wert=103;//Baudrate (9600)
10
volatileuint8_tUART_Daten=10;
11
12
ISR(USART_RX_vect)
13
{
14
PORTB^=(1<<PB5);
15
UART_Daten=UDR0;
16
}
17
18
19
voidUART_Init(void)
20
{
21
22
UBRR0=BAUD_Wert;//Baudrate setzen
23
UCSR0B|=(1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);// RX, TX und RX Interrupt aktivieren
24
UCSR0C=(1<<UCSZ01)|(1<<UCSZ00);// 8 Bit Paketgröße einstellen
25
26
}
27
28
29
intmain(void)
30
{
31
32
DDRB|=(1<<PB5);
33
UART_Init();
34
sei();//Interrupts aktivieren
35
PORTB|=(1<<PB5);
36
37
while(1)
38
{
39
40
while(!(UCSR0A&(1<<UDRE0))){}
41
UDR0=UART_Daten;
42
43
_delay_ms(2000);
44
}
45
46
}
Anscheinend habe ich die ISR falsch geschrieben. ISR (USART_RX_vect).
Bleibt zwar nicht mehr stehen, der Code in der ISR wird aber trotzdem
nicht ausgeführt.
Ich habe jetzt noch einen LED toggle Befehl eingefügt um optisch sehen
zu können was passiert. Es passiert aber nichts.
Welchen Controller verwendest Du?
Register wie z.B. "UDR0" klingen nach einem Controller mit mehreren
UARTs. In diesem Fall würde ich aber für jeden UART einen eigenen
Interruptvektor erwarten. "ISR (USART_RX_vect)" passt nach meinem
Verständnis dann eher zu einem Controller mit nur einem UART.
Magnus M. schrieb:> Register wie z.B. "UDR0" klingen nach einem Controller mit mehreren> UARTs.Magnus M. schrieb:> Und dein Compiler haut Dir keine Fehler um die Ohren? RESPEKT!
Sorry, vergiss bitte den Quatsch, den ich da geschrieben habe. Liege
schon im Bett und bin nur mit dem Smartphone online.
Habe es mittlerweile dann auch geschafft mir auf dem Smartphone das
Datenblatt anzusehen und siehe da - deine Registerbezeichnungen scheinen
korrekt zu sein...
Asche auf mein Haupt!
Ich leg das Smartscheißding jetzt beiseite und schlaf mal ein wenig.
Grüße
Magnus
Christian W. schrieb:> Anscheinend habe ich die ISR falsch geschrieben. ISR (USART_RX_vect).> Bleibt zwar nicht mehr stehen, der Code in der ISR wird aber trotzdem> nicht ausgeführt.> Ich habe jetzt noch einen LED toggle Befehl eingefügt um optisch sehen> zu können was passiert. Es passiert aber nichts.
Probiere zuerst mal ohne Echo bei dir:
1
intmain(void)
2
{
3
DDRB|=(1<<PB5);
4
UART_Init();
5
sei();//Interrupts aktivieren
6
PORTB|=(1<<PB5);
7
8
while(1)
9
{
10
UART_Daten+=1;
11
_delay_ms(10);
12
}
13
}
Dein Programm muss ganz einfach die ISR ausführen.
P.S.
Falls auf der anderen Seite Echo eingeschaltet ist, ausschalten.
Christian W. schrieb:> Was ist daran denn falsch? Falls du meinst, weil sonst kein anderer Code> ausgeführt wird, der kommt noch.
witzlos != falsch. Witzlos ist es jedes empfangene Byte an den selben
Speicher zu schreiben. Normalerweise schreibt man den in einen Loop
rein, für gewöhnlich in eine Zeichenkette. Das ist schon das
ungewöhnliche bei dir: Dein Speicher wo du das Byte reinschreibst ist
kein char sondern ein int. Aber OK, es ist anscheinend nur zum testen.
Christian W. schrieb:> void UART_Init (void)> {>> UBRR0 = BAUD_Wert;//Baudrate setzen> UCSR0B |= (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);// RX, TX und RX> Interrupt aktivieren> UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);// 8 Bit Paketgröße einstellen>> }
Beim Einstellen des UBRR0 empfiehlt es sich, zunächst das High-Byte zu
setzen, dann das Low-Byte. Und BAUD_Wert sollte ein 16 bit Integer sein
wobei man in dem Fall auch das High-Byte zunächst zu 0 setzen kann und
BAUD_Wert dann ans Low-Byte übergibt. Es kann sonst sein, dass es nicht
klappt. Geht denn das Senden sicher (was empfängst du wenn du eine 65
sendest?). Möglicherweise hast du auch das Datenformat auf der
Empfangsseite falsch eingestellt.
Paketgröße stellst du auf 8 Bit ein. Kann man machen, ist aber unnötig.
8 Bit Paketgröße ist der Initialisierungswert. Genauer: 8N1 ist der
Initialisierungswert des UART am Atmega328.
Und wie stehen die Fuses bei dir? Ist der Atmega auch richtig
eingestellt sodass er mit 16 MHz läuft?
M. K. schrieb:> Beim Einstellen des UBRR0 empfiehlt es sich, zunächst das High-Byte zu> setzen, dann das Low-Byte. Und BAUD_Wert sollte ein 16 bit Integer sein> wobei man in dem Fall auch das High-Byte zunächst zu 0 setzen kann und> BAUD_Wert dann ans Low-Byte übergibt. Es kann sonst sein, dass es nicht
Glücklicherweise macht das der Compiler schon richtig. Aber man sollte
trotzdem aufpassen bei solchen Sachen.
M. K. schrieb:> Und wie stehen die Fuses bei dir? Ist der Atmega auch richtig> eingestellt sodass er mit 16 MHz läuft?Christian W. schrieb:> Ich verwende einen ATmega328, der auf einem Arduino Nano sitzt und den> ich per XLoader flashe.
Das wird schon in Ordnung sein.
Ich glaube eher, dass Echo sein Problem war.
Marc V. schrieb:> Das wird schon in Ordnung sein.
Ist halt die Frage ob er einen Original Nano hat oder ein Clone. Bei den
Clones hab ich es schon erlebt, dass die Megas nicht geflasht sind.
M. K. schrieb:> Ist halt die Frage ob er einen Original Nano hat oder ein Clone. Bei den> Clones hab ich es schon erlebt, dass die Megas nicht geflasht sind.
Es ist ein Nano Nachbau. Wenn der atmega nicht geflasht wäre könnte ich
ja kein Programm über den Com Port aufspielen.
Das Programm ist vorerst nur zu Test und Übungszwecken gedacht.
M. K. schrieb:> Geht denn das Senden sicher (was empfängst du wenn du eine 65 sendest?).> Möglicherweise hast du auch das Datenformat auf der Empfangsseite falsch> eingestellt.
Ich benutze das Program hterm und das empfängt alles einwandfrei vom uC.
Es funktioniert nur das empfangen am Atmega nicht.
Christian W. schrieb:> Ich benutze das Program hterm und das empfängt alles einwandfrei vom uC.> Es funktioniert nur das empfangen am Atmega nicht.
Doch, das tut es.
Christian W. schrieb:> Ich verwende einen ATmega328
Aha. Dann heisst dein ISR Vektor aber nicht richtig. Beim Mega328 muss
der ISR Vektor
1
ISR(USART_RX_vect)
heissen, wenn du die avr-libc benutzt. Den ganzen 'Du hast die Baudrate
falsch' o.ä. kannst du in die Tonne kloppen, denn Senden geht ja
korrekt. Ist vermutlich nur der falsche Vektorname.
Das ganze kannst du in der Online Doku zur avr-libc nachschauen:
http://nongnu.org/avr-libc/user-manual/group__avr__interrupts.html
Den Vektor 'ISR (UART_RXC_vect)' gibt es nur bei ATmega16, ATmega32,
ATmega323 und ATmega8.
Matthias S. schrieb:> Beim Mega328 muss> der ISR VektorISR(USART_RX_vect)> heissen
Und wie man oben lesen kann heißt er auch inzwischen
Christian W. schrieb:> ISR (USART_RX_vect)
Darauf wurde nämlich schon hingewiesen.
Hab das Programm mal bei mir aufgespielt. Funktioniert. Ist denn auch
sonst alles richtig angeschlossen? Hat alles vernünftigen Kontakt?
Funktioniert der serielle Wandler?
Christian W. schrieb:> Aktuell ja nicht. Der Code stoppt ja nicht mehr. Allerdings wird auch> der Code in der ISR nicht ausgeführt.
Ziehe dein USB-Kabl aus PC raus, stecke es in irgendeine PowerBank
oder ähnliches, verbinde Rx un Tx am Nano und lasse dein korrigiertes
Programm (USART_RX_vect) laufen.
Wetten, dass es geht ?
Hallo zusammen,
ich habe jetzt mal den Code verändert. Das aktuelle Program funktioniert
perfekt. Das heißt Senden und Empfangen geht.
1
intmain(void)
2
{
3
4
DDRB|=(1<<PB5);
5
UART_Init();
6
7
while(1)
8
{
9
10
while(!(UCSR0A&(1<<RXC0)));
11
UART_Daten=UDR0;
12
PORTB^=(1<<PB5);
13
14
while(!(UCSR0A&(1<<UDRE0))){}
15
UDR0=UART_Daten;
16
17
}
18
}
Ich würde aber gerne den Interrupt verwenden. Ich habe auch versucht die
ganze main leer zu lassen und nur beim Empfangen etwas zu tun, also ohne
echo, nur ein Pin Toggle. Aber das geht einfach nicht, aber warum.:
ISR (USART_RX_vect)
{
PORTB ^= (1<<PB5);
UART_Daten = UDR0;
}
M. K. schrieb:> Das funktioniert hier hervorragend.
Bestimmt nicht.
Ohne überhaupt näher auf deinen Code einzugehen, aber UBBR0 gibt
es bei AVR nicht - UBRR0 vielleicht ?
Auch ist TCEN0 etwas fraglich - TXEN0 vielleicht ?
while( !(UCSR0A & (1 << UDRE0) );
Wieder mal an Klammern gespart ?
Christian W. schrieb:> Ich würde aber gerne den Interrupt verwenden. Ich habe auch versucht die> ganze main leer zu lassen und nur beim Empfangen etwas zu tun, also ohne> echo, nur ein Pin Toggle. Aber das geht einfach nicht, aber warum.:
Also, folgendes geht sicher:
1
#define F_CPU 16000000UL
2
#include<util/delay.h>
3
#include<stdlib.h>
4
#include<stdint.h>
5
6
#include<avr/io.h>
7
#include<avr/interrupt.h>
8
9
10
uint16_tBAUD_Wert=103;//Baudrate (9600)
11
volatileuint8_tUART_Daten=10;
12
13
ISR(USART_RX_vect)
14
{
15
PORTB^=(1<<PB5);
16
UART_Daten=UDR0;
17
}
18
19
20
voidUART_Init(void)
21
{
22
UBRR0=BAUD_Wert;//Baudrate setzen
23
UCSR0B|=(1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);// RX, TX und RX Interrupt aktivieren
24
UCSR0C=(1<<UCSZ01)|(1<<UCSZ00);// 8 Bit Paketgröße einstellen
25
}
26
27
28
intmain(void)
29
{
30
31
DDRB|=(1<<PB5);
32
UART_Init();
33
sei();//Interrupts aktivieren
34
PORTB|=(1<<PB5);
35
36
while(1)
37
{
38
39
// while (!(UCSR0A & (1<<UDRE0))){}
40
// UDR0 = UART_Daten;
41
42
_delay_ms(20);
43
}
44
45
}
Beim Start muss die LED leuchten.
EDIT:
Jetzt ist auch die HEX-Datei dabei.
Probieren und melden wie es geht, OK ?
Inwiefern unterscheidet sich das von meinem Code. Die Baudrate wird bei
mir korrekt eingestellt. Das Senden in der Endlosschleife habe ich auch
schon weggelassen.
Bei mir Leuchtet die Led auch beim Start, nur wird der Code in der ISR
nicht ausgeführt.
Ich werde dein Hex File dennoch ausprobieren.
Marc V. schrieb:> Ohne überhaupt näher auf deinen Code einzugehen, aber UBBR0 gibt> es bei AVR nicht - UBRR0 vielleicht ?
Ja, hab ich aus dem Kopf getippt. Aber das funktioniert mit Sicherheit
wenn die Rechtschreibfehler raus sind. Das garantiere ich.
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
volatileunsignedcharempfang='A';
5
6
voiduart_setup(uint16_tubrr_value){
7
UBRR0=ubrr_value>>8;
8
UBRR0=ubrr_value&0xff;
9
10
UCSR0B|=(1<<TXEN0)|(1<<RXEN0)|(1<<RXCIE0);
11
}
12
voidsendChar(unsignedcharcharToSend){
13
//Warten, dass der Sendespeicher frei wird.
14
while(!(UCSR0A&(1<<UDRE0)));
15
//Und Zeichen reinschieben zum Senden.
16
UDR0=charToSend;
17
}
18
19
intmain(void){
20
//Initialisierungen/Einstellungen des AVR
21
uart_setup(103);
22
DDRB|=(1<<PB5);
23
//interrupts ein.
24
sei();
25
//begin der Endlosschleife/ des Programms
26
for(;;){
27
sendChar(empfang);
28
}
29
//hier kommt man nie hin
30
return0;
31
}
32
ISR(USART_RX_vect){
33
//Empfangenes Zeichen speichern
34
empfang=UDR0;
35
//Und Pin togglen
36
PORTB^=(1<<PB5);
37
}
spess53 schrieb:> sei() vergessen?
Einfach mal den Thread lesen ;). Bist nicht der erste, der das meint und
oben kann mans nachlesen, dass das sei() drin ist.
Christian W. schrieb:> Inwiefern unterscheidet sich das von meinem Code.
Dadurch, dass in der main() ein '_delay_ms(20);' steht.
> Das Senden in der Endlosschleife habe ich auch schon weggelassen.
Gerade weil du (wahrscheinlich) eine leere Schleife in der main() hast,
wird das Ganze vom Compiler wegoptimiert, dein Program landet beim
<exit>, da wird dann ordentlich 'cli' ausgefuhrt und ein endloses
'rjmp .-2'.
Also, Interrupts sind gesperrt, da kannst du ewig warten, OK ?
Probiere es doch mal endlich.
Marc V. schrieb:> Gerade weil du (wahrscheinlich) eine leere Schleife in der main() hast,> wird das Ganze vom Compiler wegoptimiert, dein Program landet beim> <exit>, da wird dann ordentlich 'cli' ausgefuhrt und ein endloses> 'rjmp .-2'.
Dann würde ich den Compiler aber wegwerfen. Der avr-gcc macht sowas mit
Sicherheit nicht.
M. K. schrieb:> Dann würde ich den Compiler aber wegwerfen. Der avr-gcc macht sowas mit> Sicherheit nicht.
Mit Sicherheit macht der das.
Für Compiler ist eine leere Schleife nutzlos, nach der Schleife gibt
es auch keine Befehle mehr, was soll der deiner Meinung nach tun ?
Deswegen gibt es auch Optionen für Optimierung.
Christian W. schrieb:> Achja wegen der Baudrate: Warum Recourcen für ein 16 Bit Integer> verschwenden wenn ein 8 Bit auch reicht.
Aber nur weil sich der Compiler besser beim AVR auskennt als du.
Marc V. schrieb:> Aber nur weil sich der Compiler besser beim AVR auskennt als du.
Nein. Ich weiß das das Register 16 Bit groß ist. Ich habe es ausprobiert
und es hat geklappt. Der Compiler benutzt immer erst das Low Byte.
Christian W. schrieb:> Marc V. schrieb:>> Aber nur weil sich der Compiler besser beim AVR auskennt als du.>> Nein. Ich weiß das das Register 16 Bit groß ist. Ich habe es ausprobiert> und es hat geklappt. Der Compiler benutzt immer erst das Low Byte.
Seit wann hat ein uint8_t auch ein High-Byte ?
Marc V. schrieb:> M. K. schrieb:>> Dann würde ich den Compiler aber wegwerfen. Der avr-gcc macht sowas mit>> Sicherheit nicht.>> Mit Sicherheit macht der das.> Für Compiler ist eine leere Schleife nutzlos, nach der Schleife gibt> es auch keine Befehle mehr, was soll der deiner Meinung nach tun ?> Deswegen gibt es auch Optionen für Optimierung.
Optimierung soll einen funktionsidentischen Ersatz für das, was dasteht
liefern. Ein Ersatz der Endlosschleife in main() durch ein return wäre
KEIN solcher.
Wenn in der Schleife eine nicht-volatile Variable hochgezählt wird, die
nirgends sonst benutzt wird, dann darf das weg. Nicht aber die Schleife
selbst.
Zum Glück kennt sich der XYZ-GCC aber besser mit C* und Optimierung aus,
als so manscher Poster hier.
Marc V. schrieb:> Mit Sicherheit macht der das.> Für Compiler ist eine leere Schleife nutzlos, nach der Schleife gibt> es auch keine Befehle mehr, was soll der deiner Meinung nach tun ?> Deswegen gibt es auch Optionen für Optimierung.
Quatsch. Eine leere Schleife der Form
1
while(1){
2
}
wird der Compiler nicht weg optimieren da er nicht weiß was man damit
bezwecken will und die Schleifenbedingung immer erfüllt ist. Er müsste
dann auch sowas wie:
1
while(ADCSRA&(1<<ADSC));
oder, um beim obigen Beispiel zu bleiben
1
while(!(UCSR0A&(1<<UDRE0)));
Das sind nämlich auch leere Schleifen. Das Einzige, was ein Compiler
vielleicht wegoptimiert ist ein
Carl D. schrieb:> Wenn in der Schleife eine nicht-volatile Variable hochgezählt wird, die> nirgends sonst benutzt wird, dann darf das weg. Nicht aber die Schleife> selbst.
Richtig, die Schleife selbst optimiert der gcc mit Sicherheit nicht weg.
Carl D. schrieb:> Zum Glück kennt sich der XYZ-GCC aber besser mit C* und Optimierung aus,> als so manscher Poster hier.
Das will ich mal schwer hoffen. Ich bin mir sicher, ich hätte sonst auch
einige Probleme mehr. Ein Glück weis der gcc es besser als ich, deshalb
überlasse auch ich ihm die Optimierungen.
M. K. schrieb:> Carl D. schrieb:>> Wenn in der Schleife eine nicht-volatile Variable hochgezählt wird, die>> nirgends sonst benutzt wird, dann darf das weg. Nicht aber die Schleife>> selbst.>> Richtig, die Schleife selbst optimiert der gcc mit Sicherheit nicht weg.>> Carl D. schrieb:>> Zum Glück kennt sich der XYZ-GCC aber besser mit C* und Optimierung aus,>> als so manscher Poster hier.>> Das will ich mal schwer hoffen. Ich bin mir sicher, ich hätte sonst auch> einige Probleme mehr. Ein Glück weis der gcc es besser als ich, deshalb> überlasse auch ich ihm die Optimierungen.
Das ging nicht gegen dich, sondern gegen den Urheber dieser schrägen
Vorstellung. Wie auch immer optimiert wird, es darf nie die Semantik
eines Programms ändern.