Hallo,
bin mir nicht sicher, aber für mich sieht das folgende fast nach einem
Compilerbug aus...
Ich hab versucht, eine interruptgesteuerte UART auf einem Mega32 zu
machen, die Strings bis zu einem definierten Endzeichen empfängt /
sendet.
Das ganze sollte so funktionieren: Ich lege globale Variablen für
Eingangspuffer (RXBUF) und Ausgangspuffer (TXBUF) an und stoße dann das
Senden / Empfangen über eine Funktion an, die die Pointer übergibt und
die Interrupts freigibt, dann läuft alles von alleine weiter.
So weit, so gut. Ich rufe die Funktion auf und übergebe ihr einen
Pointer, am Beispiel "senden":
1
voiduart_puts(unsignedchar*t)
in der Funktion weise ich dann die Adresse dem Sendepuffer zu:
1
TXBUF=t;
(TXBUF ist ein Pointer vom Typ char).
und dann sende ich irgendwo die Sache weg mit:
1
UDR=TXBUF;
2
TXBUF++;
und warte, bis es weg ist, wieder von vorne, und so weiter, also fast
wie im Beispiel im Tutorial.
Klappt auch wunderbar - so lange ich das außerhalb der Interrupt Service
Routine mache. Sobald ich UDR=TXBUF in der ISR mache, kommt nur noch
Datenmüll heraus, oder gar nichts, mal so, mal so.
Folgende Definitionen von TXBUF habe ich schon versucht:
1
unsignedchar*TXBUF;
2
unsignedchar*volatileTXBUF;
3
volatileunsignedchar*TXBUF;
4
volatileunsignedchar*volatileTXBUF;
alle mit dem gleichen Ergebnis - außerhalb der ISR lässt sich die
Pointeradresse anderen Variablen über mehrere Ecken zuweisen,
hochzählen, und es stimmt immer alles. In der ISR geht genauso
zuverlässig alles daneben.
In meinem Buch (C für Mikrocontroller) steht drin, dass es so
funktioniert, im Tutorial auch. Wo hakt es denn jetzt noch???
>TXBUF ist ein Pointer vom Typ char
Sicherlich ist es das nicht. Sowas gibt es nämlich in C nicht.
Vielmehr ist TXBUF, wenn überhaupt ein Zeiger auf eine Variable vom Typ
char.
Ein Pointer hat keinen Typ.
Als ich das hier:
UDR = TXBUF;
las, wurde mir kariert vor den Augen. ;-)
Falls und wenn TXBUF ein Zeiger ist, was steht dann danach in UDR?
Ein Zeiger? Was soll der dort?
Wenn, dann also
UDR = *TXBUF;
Die ganze Zeigerei in C ist etwas schwierig. Das Kapitel in K&R lesen,
und ein halbes Jahr unters Kopfkissen tun, wäre angemessen.
>Als ich das hier:>UDR = TXBUF;>las, wurde mir kariert vor den Augen. ;-)
Klar, war ja auch n Tippfehler im Beitrag... ^^
So, aber nu zum Sourcecode der Routine:
1
volatileunsignedchar*TXBUF;// Sendepuffer
2
3
4
5
ISR(USART_UDRE_vect)//Interrupt: UART UDR empty
6
{
7
if(*TXBUF)//anderes Zeichen als NULL zu senden
8
{
9
UDR=*TXBUF;//Zeichen aus aktueller Pufferstelle an TX-Register
>Klar, war ja auch n Tippfehler im Beitrag... ^^
Deswegen steht ja auch in allen möglichen Foren und Beiträgen im
Internet zum Posten, das man Code NICHT abtippt sonder kopiert.
Und als ich das las, wurden aus den Karos bunte Kreise:
TXBUF=Ausgang; //zu übertragenden String in Puffer schreiben
Das kopiert eigentlich nur den Zeiger aber nicht den String aus einem
Puffer in den anderen.
Was heisst "Datenmüll"? Kannst Du das mal genauer beschreiben?
Und poste bitte kompletten Code. Hier fehlt die Initialisierung des
UARTs und main.
Und bitte nicht alles einzeln aus der Nase ziehen lassen, ja? Danke.
Was ist das überhaupt? Wenn schon eine Übertragung läuft, dann kommt die
Funktion mit Fehler zurück?
Und was macht dann Dein Programm?
Mit zusammengekniffenen Beinen herumhopsen?
Mach lieber einen gescheiten Ringpuffer.
Du hast also einen Zeiger auf den Sendepuffer. Und wo legst Du den
Sendepuffer selbst an? Welche Größe hat er?
Bevor man einen Zeiger inkrementiert oder dekrementiert, sollte man sich
vergewissern, dass er danach nicht ins Nirvana zeigt. ;-)
Beliebt ist auch der Fehler, einen Zeiger anzulegen, ihn aber nicht
vernünftig zu initialisieren. Also z.B. nach
volatile unsigned char *TXBUF;
hat man einen Zeiger, der noch überhaupt nichts kann. Besser wird's mit
z.B.
TXBUF = &Mein_Puffer;
Das Programm soll so funktionieren:
1. schicke String vom Typ Char an Funktion uart_puts
2. kopiere den Pointer auf den String an TXBUF, d.h. TXBUF soll an die
gleiche Adresse zeigen wie der übergebene Pointer auf den String.
3. Interrupt einschalten, damit die ISR den String Zeichen für Zeichen
sendet.
4. wenn fertig, Interrupt wieder ausschalten und Senden stoppen.
Während des Sendens soll uart_puts keine weiteren Daten annehmen, damit
der Inhalt von *TXBUF sich nicht mitten in einer Übertragung ändern
kann, und dann gibt es den Returncode 1, damit die aufrufende Funktion
Bescheid bekommt, es später nochmal zu versuchen.
>TXBUF=Ausgang; //zu übertragenden String in Puffer schreiben>Das kopiert eigentlich nur den Zeiger aber nicht den String aus einem>Puffer in den anderen.
Keine bunten Kreise, sondern genau das, was ich will!
Nochmal, um es zu verdeutlichen. Ich möchte NICHT den String kopieren,
ich möchte einen Pointer auf die Anfangsadresse des bereits im Speicher
vorhandenen Strings in TXBUF schreiben und damit der ISR übergeben.
Das kann nicht so "schlimm" sein, weil ich es 1. in vielen Beispielcodes
so gesehen habe und 2. es klappt ja!!! Außerhalb der ISR!
Übrigens, wenn ich in der ISR ein fest definiertes Zeichen statt dem
String schicke, klappt es auch, also die ISR wird einwandfrei
angesprochen.
>hat man einen Zeiger, der noch überhaupt nichts kann. Besser wird's mit>z.B.>TXBUF = &Mein_Puffer;
laut meinem Buch sehe ich das auch so. GCC schluckt das "&" bei mir aber
nicht, der erwartet immer einen zweiten Operanden.
Hier nochmal das gaze Programm, nicht über die komische Initialisierung
von "Line" wundern, das war Teil meiner Testerei. Es soll die Zeichen
"abc" einmal in der Sekunde über die UART ausgeben.
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<avr/interrupt.h>
4
#include<util/setbaud.h>
5
6
#define BAUD 38400
7
8
9
volatileunsignedchar*TXBUF;// Sendepuffer
10
11
12
13
ISR(USART_UDRE_vect)//Interrupt: UART UDR empty
14
{
15
if(*TXBUF)//anderes Zeichen als NULL zu senden
16
{
17
UDR=*TXBUF;//Zeichen aus aktueller Pufferstelle an TX-Register
Hundertvolt wrote:
>>TXBUF = &Mein_Puffer;>> laut meinem Buch sehe ich das auch so. GCC schluckt das "&" bei mir aber> nicht, der erwartet immer einen zweiten Operanden.
Der Adressoperator ist C-Standard... was für eine Meldung bringt denn
der Compiler? Vielleicht will er ein
Ist Mein_Puffer ein char array, so sind Mein_Puffer und &Mein_Puffer[0]
identisch. &Mein_Puffer macht keinen sinn, da es ja die Adresse von der
Adresse wäre, die der Compiler hier aber in den meisten Fällen wohl fest
einsetzen kann, da er weis wo die Variable liegt.
Ich würde dir mal dazu raten mit Single Stepping im Simulator
dadurchzugehen, dann siehst du wo die Daten verloren gehen.
Hundertvolt wrote:
> Hier nochmal das gaze Programm, nicht über die komische Initialisierung> von "Line" wundern, das war Teil meiner Testerei. Es soll die Zeichen> "abc" einmal in der Sekunde über die UART ausgeben.>> #include <avr/io.h>> #include <util/delay.h>> #include <avr/interrupt.h>> #include <util/setbaud.h>>> #define BAUD 38400
1
./lib/gcc/../../avr/include/util/setbaud.h:117:4: error: #error "setbaud.h requires BAUD to be defined"
2
./lib/gcc/../../avr/include/util/setbaud.h:125:4: error: #error "BAUD must be a constant value"
3
./lib/gcc/../../avr/include/util/setbaud.h:197:11: error: division by zero in #if
4
./lib/gcc/../../avr/include/util/setbaud.h:212:10: error: division by zero in #if
5
./lib/gcc/../../avr/include/util/setbaud.h:217:10: error: division by zero in #if
./lib/gcc/../../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>"
2
./lib/gcc/../../avr/include/util/setbaud.h:213:4: warning: #warning "Baud rate achieved is higher than allowed"
3
n function '__vector_14':
4
error: 'StatusBits' undeclared (first use in this function)
5
error: (Each undeclared identifier is reported only once
6
error: for each function it appears in.)
> unsigned char uart_puts (unsigned char *Ausgang) //Zeichen über UART> senden> {> if (UCSRB & (1<<UDRIE)) //schon eine Übertragung am Laufen, d.h.> Interrupt aktiviert?> {> return 1; //Fehler: Bereits am Senden> }> else> {> TXBUF=Ausgang; //zu übertragenden String in Puffer> schreiben> UCSRB |= (1 << UDRIE); //Interrupt einschalten zum Sendestart> return 0; //TX beginnt fehlerfrei> }> }
Setze ein Flag der ISR, und zwar wenn die Kommunikation fertig ist;
nicht wenn das Datenregister leer ist!
> unsigned char Line[22]; // String zum Senden
Line ist nach Verlassen von main ungültig.
Die UART-Initialisierung ist unvollständig (TXC löschen, evtl. Flush
receive buffer)
Johann
Hundertvolt wrote:
> StatusBits.TX_Active = 0; //Merkerbit zurücksetzen
Und woher ist StatusBits? Das ist doch schon wieder nicht das echte
Programm, du verarschst hier die Leute :-(
Hundertvolt wrote:
> Das kann nicht so "schlimm" sein, weil ich es 1. in vielen Beispielcodes> so gesehen habe und 2. es klappt ja!!! Außerhalb der ISR!
Kannst Du mal nen Link auf solche Beispiele angeben, ich hab sowas noch
nie gesehen.
Ich hab auch noch nie gesehen, daß Ausgabefunktionen nen Returnwert
abprüfen und die Sendung später wiederholen.
UART-Ausgaben benutzen üblicher Weise einen Ringpuffer und nen Funktion
putchar, mit der man Bytes in den Ringpuffer stellt. Ist kein Platz mehr
im Puffer, wartet putcher.
Geprüft wird nix, nachdem putchar beendet ist, geht man davon aus, daß
die Bytes auch gesendet werden.
Dem Ringpuffer ist es auch egal, ob es Text- oder Binärdaten sind.
Ein funktionierender Ringpuffer:
Beitrag "AVR-GCC: UART mit FIFO"
Er benutzt mit Absicht keine Pointer, da die auf nem 8-Bitter nicht
atomar zugegriffen werden können. Es wären also mit Pointer in den
Main-Funktionen an mehreren Stellen CLI/SEI-Sequenzen nötig.
Peter
@TE: Ich kenn mich jetzt weder mit dem Mega32, noch mit der UART
Schnittstelle gut aus, aber sollte man nicht üblicherweise das
Interruptflag zurücksetzten, nachdem die ISR abgearbeitet wurde?
So in etwa:
1
ISR(USART_UDRE_vect)//Interrupt: UART UDR empty
2
{
3
if(*TXBUF)//anderes Zeichen als NULL zu senden
4
{
5
UDR=*TXBUF;//Zeichen aus aktueller Pufferstelle an TX-Register
peterguy wrote:
> @TE: Ich kenn mich jetzt weder mit dem Mega32, noch mit der UART> Schnittstelle gut aus, aber sollte man nicht üblicherweise das> Interruptflag zurücksetzten, nachdem die ISR abgearbeitet wurde?
1
When the Data Register empty Interrupt Enable (UDRIE) bit in
2
UCSRB is written to one, the USART Data Register Empty Interrupt
3
will be executed as long as UDRE is set (provided that global
4
interrupts are enabled). UDRE is cleared by writing UDR. When
5
interrupt-driven data transmission is used, the Data Register
6
Empty Interrupt routine must either write new data to UDR in
7
order to clear UDRE or disable the Data Register empty Interrupt,
8
otherwise a new interrupt will occur once the interrupt routine
> Und woher ist StatusBits? Das ist doch schon wieder nicht das echte> Programm, du verarschst hier die Leute :-(
Ein übersehenes Überbleibsel aus der Testerei. Genau, und weil meine
Seele so tiefschwarz ist, mache ich das alles nur, um Leuten Böses zu
tun - und an ganz schlechten Tagen schieße ich mit Absicht Hochspannung
in Mikrocontroller ;-)
Okay, Spaß beiseite, das im Artikel "Interrupt" ist ja fast sowas wie
meins. Die Idee bei mir war halt, mir das strcpy zu sparen und damit RAM
zu sparen.
Was mich bei dem Beipiel wundert, da sind Strings als globale char
definiert, die auch ohne "volatile" in den ISRs benutzt werden - das
geht gut?? Bei der RX-Routine wird direkt draufgeschrieben, bei der
TX-Routine wird zusätzlich ein lokaler Pointer erzeugt. Warum?
Und um es nochmal kurz zu machen, ich will bei mir in einer globalen
Variablen einen lokalen Pointer auf char speichern, der dann von einer
ISR benutzt wird und der nach der Rückkehr aus der ISR wieder lokal -
aber sein Ziel gefüllt mit den Daten - verfügbar ist. Dann muss ich nur
einmal das RAM belegen, nicht 2mal. Geht das prinzipiell?
Hundertvolt wrote:
>> Und woher ist StatusBits? Das ist doch schon wieder nicht das echte>> Programm, du verarschst hier die Leute :-(>> Ein übersehenes Überbleibsel aus der Testerei. Genau, und weil meine> Seele so tiefschwarz ist, mache ich das alles nur, um Leuten Böses zu> tun
Immerhin versuchen andere deine Probleme nachzuvollziehen. Man sollte
daher soweit entgegenkommend sein, daß sich die Probleme auch möglichst
git reproduzieren/nachvollziehen lassen. Siehe
Beitrag "Re: Pointer in ISRs"
Wenn im Code private Header sind, Pünktchen ... (ausser in
varargs-Funktionen), oder sonstige unbekannte
Makros/Deklarationen/Funktionen kann das irgendwas sein. Ebenso
mysteriös ist oft die verwendeten Privat-Hardware, der verwendete
Compiler, seine Optionen, Version, etc.
> Und um es nochmal kurz zu machen, ich will bei mir in einer globalen> Variablen einen lokalen Pointer auf char speichern, der dann von einer> ISR benutzt wird und der nach der Rückkehr aus der ISR wieder lokal -> aber sein Ziel gefüllt mit den Daten - verfügbar ist. Dann muss ich nur> einmal das RAM belegen, nicht 2mal. Geht das prinzipiell?
Nein, auch wenn es hier in dem Beispiel keine Probleme macht: Du legst
eine Kopie eines lokalen Zeigers an. Die Kopie bleibt auch noch dann
bestehen, wenn der Zeiger selbst ungültig ist, weil die Funktion (main)
in der der Zeiger (Line), und das worauf er zeigt (der Text) verlassen
wurden.
Johann
Also in anderen Worten, ich komme um strcopy nicht herum, hab ich das
richtig verstanden?
Ich dachte halt, es gäbe vielleicht irgendeine Möglichkeit, beim
Empfangen clever die empfangenen Bytes direkt in einen String einer
Funktion reinschreiben, ohne sie nochmal zwischenpuffern zu müssen.
Wirklich keine Chance...?
Danke so weit!
Hundertvolt wrote:
> Ein übersehenes Überbleibsel aus der Testerei. Genau, und weil meine> Seele so tiefschwarz ist, mache ich das alles nur, um Leuten Böses zu> tun - und an ganz schlechten Tagen schieße ich mit Absicht Hochspannung> in Mikrocontroller ;-)
Es gibt nen ganz einfachen simplen Trick, wenn man Code in Foren posten
will, man compiliert ihn.
Und wenn er dann mit 0 Error/Warnings durchläuft, postet man ihn genauso
(als Anhang).
Natürlich prüft man auch, ob er das Fehlerverhalten immer noch zeigt.
Damit hat man schonmal mindestens 80% an unnötigen Rückfragen und
Mißverständnissen erschlagen.
Und wenn man zu diesem kleinen Schritt zu faul ist, muß man eben
unwirsche Reaktionen oder Schweigen im Walde ertragen können.
Peter
Hundertvolt wrote:
> Also in anderen Worten, ich komme um strcopy nicht herum, hab ich das> richtig verstanden?>> Ich dachte halt, es gäbe vielleicht irgendeine Möglichkeit, beim> Empfangen clever die empfangenen Bytes direkt in einen String einer> Funktion reinschreiben, ohne sie nochmal zwischenpuffern zu müssen.> Wirklich keine Chance...?
Verwende als Puffer keine auto, sondern ne statische Variable. Die ist
unabhängig vom Aufrufer deiner Ausgabefunktion gültig. Du must
allerdings sicherstellen, daß vor Beendigung der Sendung niemand in der
Puffer schreibt. D.h. nicht ver Versenden ist zu prüfen, ob noch eine
Transaktion läuft, sondern beim Befüllen des Puffers.
Johann
Hundertvolt wrote:
> Ich dachte halt, es gäbe vielleicht irgendeine Möglichkeit, beim> Empfangen clever die empfangenen Bytes direkt in einen String einer> Funktion reinschreiben, ohne sie nochmal zwischenpuffern zu müssen.> Wirklich keine Chance...?
Es gibt noch die Möglichkeit, anstelle eines Ringpuffers einen
Linearpuffer zu verwenden. Dann stehen alle Zeichen am Anfang und man
kann direkt im Puffer die Zeichen auswerten.
Man muß allerdings nach der Auswertung alle zwischenzeitlich empfangenen
Bytes wieder an den Anfang zurückkopieren, damit der Linearpuffer nicht
überläuft.
Der RAM-Bedarf sinkt etwas, aber der Code wird komplexer.
Ich hab das früher mal gemacht, benutze jetzt aber doch lieber den
Ringpuffer.
Peter
Nach reichlich Überlegung bin ich inzwischen dazu gekommen, das auch als
Ringpuffer zu machen.
Erstmal großes Dankeschön an alle!
Eine Frage hätte ich aber noch: Im Tut und im Interrupt-Artiklel steht,
alle globalen Variablen sowie Pointer und / oder deren Ziele, die sowohl
in ISRs als auch in Funktionen benutzt und verändert werden, sollte man
als volatile anlegen.
Jetzt werden im Interrupt-Artikel die Status-Flags als volatile, die
Puffer als auto angelegt. Im Ringpuffer von Peter sind die Puffer
static. So wie ich das verstanden habe, werden volatile Variablen nicht
in Registern gehalten, sondern immer direkt ins RAM geschrieben. Will
ich das nicht mit meinen Pointerzielen machen??
Was stimmt denn nun, bzw. wann ist welche Deklaration nötig?
Hundertvolt wrote:
> Eine Frage hätte ich aber noch: Im Tut und im Interrupt-Artiklel steht,> alle globalen Variablen sowie Pointer und / oder deren Ziele, die sowohl> in ISRs als auch in Funktionen benutzt und verändert werden, sollte man> als volatile anlegen.
Das ist erstmal vollkommen korrekt.
Allerdings kann es dann passieren, daß dadurch der Interrupt-Code größer
und länger wird.
Das volatile wird eigentlich nur im Main gebraucht, da der Interrupt die
Variable zwischendurch ändern kann, aber nicht umgekehrt.
Daher habe ich mir Macros definiert, die eine Variable als volatile
casten.
Das habe ich mir von Jörg Wunsch abgeschaut, der hatte das mal gepostet.
Der Puffer selber muß nicht volatile sein. Wenn der Index oder Pointer
volatile ist, darf trotzdem kein Zugriff wegoptimiert werden.
Eine Ausnahme wäre, wenn man eine feste Adresse (d.h. ohne Pointer oder
Index) in dem Puffer liest, dann muß man wieder das Macro nehmen.
Peter