Forum: Mikrocontroller und Digitale Elektronik AVR32 Ringpuffer Problem


von Tobi (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich versuche mich gerade an einer Interrupt-gesteuerten USARt Ausgabe. 
Das kleine testprogramm findet ihr im Anhang.

Problem ist, dass ich keine Ausgabe bekomme. Die Ausgabe wird in einen 
Ringpuffer zwischengespeichert. In der Funktion, die genau dies tut, 
habe ich zum "Test" 3 verschiedene LEDs angesteuert. Problem ist nur, 
dass die dritte LED (LED2) dunkel bleibt. Daher sieht es für mich so 
aus, als ob das Programm beim hochzählen des Pufferindex hängen bleibt.

Ich kann mir kaum vorstellen, dass da Hochzählen ein Problem darstellt, 
aber eventuell findet ihr eine Lösung. Eine Ausgabe bekomme ich 
natürlich auch nicht.

Das Programm ist für einen AT32UC3A0512-UES, welcher auf einem EVK1100 
verbaut ist. AVR32Studio und die AVR32-Toolchain habe ich erst vor ein 
paar Wochen geladen. Sollten also aktuell sein.


~Tobi

von Stephan (Gast)


Lesenswert?

Hi,

schon gelöst???
Wenn nein dann sieh dir nochmal deine SendeFunktion "puts" an!!!
Du bekommst keinen Output, wie kann das Sein???
Richtig, du hast vergessen die Daten auszugeben.

Hilfe:
1) Du bekommst nur einen "Tx Empty Int" wenn vorher was gesendet wurde.
1
void usart0_puts(char const * str)
2
{
3
  while(*str && (countTX < TX_BUFFER_SIZE)) {
4
    LED_On(LED0);
5
    tx_buffer0[tx_writer] = *str++;
6
    countTX++;
7
    LED_On(LED1);
8
    //#define next_index(index, buf_size)  (index = ((++index >= buf_size) ? 0 : index))
9
    //next_index(tx_writer, TX_BUFFER_SIZE);
10
    tx_writer = tx_writer + 1;
11
    if(tx_writer >= TX_BUFFER_SIZE) {
12
      tx_writer = 0;
13
    }
14
    LED_On(LED2);
15
  }
16
17
  (&AVR32_USART0)->ier = AVR32_USART_IER_TXEMPTY_MASK;
18
  tx_reader = tx_reader + 1;
19
  if(tx_reader >= TX_BUFFER_SIZE) {
20
      tx_reader = 0;
21
  }
22
  countTX--;
23
  usart_putchar(&AVR32_USART0, tx_buffer0[tx_reader]);   
24
}
jetzt solltest du etwas sehen im Terminal-Prog.

2) was passiert wenn du einen String grösser 128 Zeichen senden 
möchtest?
Es wird bei dir nur bis zur Buffer-Size Länge gesendet. :-(
Hier nochmal das Konzept prüfen!

Frage:
Was bedeutet diese Zeile?
1
(&AVR32_USART0)->ier = AVR32_USART_IER_TXEMPTY_MASK;
Du benutzt Sie an 2 Stellen, die aber beider vom "Ablauf" her 
verschieden sind. In der "puts"-Funktion sollte das ein aktivieren des 
"Ints" sein und bei der "Tx Empty Int"-Funktion ein deaktivieren???
Da passt was nicht! Oder? Kenn mich mit den AVR32 nicht aus!

LED:
Da kann ich dir leider nicht helfen, sollte so gehen. Was mir aber 
auffällt ist das es keine eigene Init für die LEDs gibt, kann da was 
nicht stimmen???

mfg
Stephan

von THaala (Gast)


Lesenswert?

Ich bin zwar nicht er AVR - Crack....

... ich glaube aber zu wissen wozu diese TX_EMPTY_MASK gut ist.

Zum einen ist es sicherlich das Interupt - Enable für den UART.
Aber es kann auch in einem weiteren Sinne geutzt werden.

Es gibt ein Problem mit der Initialisierung des UARTS.
Man weiss im Moment wenn man Daten an die Senderoutine übergibt nicht ob 
der UART derzeit noch sendet (und damit ein weiterer TX_EMPTY - 
Interrupt kommen wird) oder der UART bereits sein Pulver verschossen hat 
und der letzte Interrupt schon da war, und fand dass keine weiteren 
Daten su senden waren. In diesem Fall muss die Senderoutine den UART 
interrupt setzen, damit die ISR aktiv wird. Dies kann man sicher an der 
Maske festmachen. Als letzte Aktion in der ISR für diesen Fall muß also 
das Flag beeinflusst werden.

letzte Aktion in ISR (im Fall - "keine weiteren Daten"):
1
   if(!countTX) {
2
      (&AVR32_USART0)->ier &= ~AVR32_USART_IER_TXEMPTY_MASK; // disallow TX_EMPTY
3
   }

letzte Aktion in puts:
1
   if( (&AVR32_USART0)->ier & AVR32_USART_IER_TXEMPTY_MASK ){
2
      // Tue nichts - es steht noch mindestens ein TX_EMPTY interrupt aus
3
   }
4
   else{
5
     (&AVR32_USART0)->ier |= AVR32_USART_IER_TXEMPTY_MASK; // allow TX_EMPTY
6
     // Und jetzt noch den TX_EMPTY - interrupt setzen
7
     den Befehl kenne ich jetzt nicht! ist es evtl:
8
       (&AVR32_USART0)->ir |= AVR32_USART_IR_TXEMPTY_MASK; // set TX_EMPTY -interrupt
9
10
  }

Gruß,
Thaala

von Tobi (Gast)


Lesenswert?

Stephan schrieb:
> Richtig, du hast vergessen die Daten auszugeben.

Dachte das wird in der ISR gemacht.

Stephan schrieb:
> 1) Du bekommst nur einen "Tx Empty Int" wenn vorher was gesendet wurde.

Ich dachte, dafür sei der "TX Ready Int" und für den "TX Empty Int" 
bräuchte vorher nichts gesendet zu werden. Jedenfalls war das so bei den 
8 Bittern.

Stephan schrieb:
> 2) was passiert wenn du einen String grösser 128 Zeichen senden
> möchtest?
> Es wird bei dir nur bis zur Buffer-Size Länge gesendet. :-(
> Hier nochmal das Konzept prüfen!

Was wäre denn ein sinnvolleres Konzept als so viel zu senden wie man 
kann? Wenn ich dort warten würde bis wirklich alles gesendet wäre, dann 
würd ich wieder eine nicht vorhersagbare Zeit für eine Ausgabe 
benötigen. Aber darum geht es ja auch garnicht.

Stephan schrieb:
> Frage:
> Was bedeutet diese Zeile?(&AVR32_USART0)->ier = AVR32_USART_IER_TXEMPTY_MASK;
> Du benutzt Sie an 2 Stellen, die aber beider vom "Ablauf" her
> verschieden sind. In der "puts"-Funktion sollte das ein aktivieren des
> "Ints" sein und bei der "Tx Empty Int"-Funktion ein deaktivieren???
> Da passt was nicht! Oder? Kenn mich mit den AVR32 nicht aus!

In der "puts" verwende ich "&AVR32_USART0)->ier = 
AVR32_USART_IER_TXEMPTY_MASK;" zum aktivieren (ier = interrupt enable 
register) des "TX Empty Int" und in der ISR, sofern nichts mehr zum 
Senden vorliegt, verwende ich "(&AVR32_USART0)->idr = 
AVR32_USART_IER_TXEMPTY_MASK;" zum deaktivieren (idr = interrupt disable 
register) des selben Interrupts.
Dies entspricht im grunde genau der Routine, wie ich sie auf 8 Bittern 
von Atmel erfolgreich verwende.

Stephan schrieb:
> LED:
> Da kann ich dir leider nicht helfen, sollte so gehen. Was mir aber
> auffällt ist das es keine eigene Init für die LEDs gibt, kann da was
> nicht stimmen???

Es funktioniert ohne Probleme ohne extra Initialisierung. Daher finde 
ich es auch so seltsam, dass LED2 nicht angeht, die anderen beiden aber 
schon. Anscheinend wird also die Codestelle mit "LED_On(LED2)" garnicht 
erst erreicht und das macht für mich einfach keinen Sinn.

@ THaala
Ich verstehe nicht ganz was du meinst. ALs letzte aktion der 
Sendefunktion aktiviere ich den Interrupt. Daher ist es doch im Grunde 
irrelevant ob zu dem Zeitpuntk schon alles gesendet ist oder nicht. Wenn 
noch etwas zu senden da ist, dann sollte die ISR das ganz normal 
raussenden. Wenn nichts mehr da ist, dann wird die ISR den Interrupt 
einfach wieder deaktivieren.

von Stephan (Gast)


Lesenswert?

Hi,

ok hab nicht gesehen das es 2 verschiedene Aufrufe waren:
1
(&AVR32_USART0)->idr = AVR32_USART_IER_TXEMPTY_MASK;
2
3
// und
4
(&AVR32_USART0)->ier = AVR32_USART_IER_TXEMPTY_MASK;
dann ist das ok.

>Es funktioniert ohne Probleme ohne extra Initialisierung. Daher finde
>ich es auch so seltsam, dass LED2 nicht angeht, die anderen beiden aber
>schon. Anscheinend wird also die Codestelle mit "LED_On(LED2)" garnicht
>erst erreicht und das macht für mich einfach keinen Sinn.
Vielleicht das DEFINE für LED2 nicht richtig? Richtiger PIN am MC???

>Ich dachte, dafür sei der "TX Ready Int" und für den "TX Empty Int"
>bräuchte vorher nichts gesendet zu werden. Jedenfalls war das so bei den
>8 Bittern.

Das glaube ich nicht, denn ein Interrupt tritt erst nach einer Aktion 
auf und nicht einfach so. ;-)

"TX Empty Int"
Das Datenregister zum Senden ist leer.

"TX Ready Int"
Das gesendete Byte ist raus.

Hinweis aus den ATMEGA32 Datenblatt:

TXC: USART Transmit Complete -> "TX Ready Int"
This flag bit is set when the entire frame in the transmit Shift 
Register has been shifted out and
there are no new data currently present in the transmit buffer (UDR). 
The TXC Flag bit is automatically
cleared when a transmit complete interrupt is executed, or it can be 
cleared by writing
a one to its bit location. The TXC Flag can generate a Transmit Complete 
interrupt (see description
of the TXCIE bit).

und

UDRE: USART Data Register Empty -> "TX Empty Int"
The UDRE Flag indicates if the transmit buffer (UDR) is ready to receive 
new data. If UDRE is
one, the buffer is empty, and therefore ready to be written. The UDRE 
Flag can generate a Data
Register empty Interrupt (see description of the UDRIE bit).

Versuchmal das mit dem Schreiben des ersten Zeichen (Byte) zu 
machen("put-Funktion", ob sich dann was im Terminal-Prog tut.

mfg
Stephan

PS: Wenn möglich zeigmal den Code den du versuchst nach zu bauen.

von Tobi (Gast)


Lesenswert?

Stephan schrieb:
> Vielleicht das DEFINE für LED2 nicht richtig? Richtiger PIN am MC???

LEDs ein- und ausschalten mittels mitgelieferter Funktionen und Defines 
funktioniert. Nur an dieser Stelle halt nicht und daher geht ich davon 
aus, dass die Codestelle nicht erreicht wird.

Stephan schrieb:
> Das glaube ich nicht, denn ein Interrupt tritt erst nach einer Aktion
> auf und nicht einfach so. ;-)

Laut deinem Auszug aus dem ATmega32 Datenblatt kommt der Interrupt aber 
"einfach so", was sich auch mit meinen Erfahrungen auf den 8 Bittern 
deckt. Wenn das UDR leer ist, dann gibts nen Interrupt.

Ich werden trotzdem mal das erste Zeichen direkt in der Routine 
ausgeben, aber ich bezweifel, dass es die Lösung ist. Ich kanns 
wahrscheinlich auch erst morgen testen, da ich das EVK1100 nicht zu 
Hause habe.

von Stephan (Gast)


Lesenswert?

Hi,

lass dir doch mit der normalen "puts"-Funktion Statusinformationen 
ausgeben.
Ich hoffe das die funktioniert.
Dann kannst du das Problem mit den Ints und der LED besser untersuchen.
( alle wichtigen Int-Register und das Output-Register für den LED-Port)

mfg
Stephan

von THaala (Gast)


Lesenswert?

Hallo,

-- @ THaala
-- Ich verstehe nicht ganz was du meinst. ALs letzte aktion der
-- Sendefunktion aktiviere ich den Interrupt. Daher ist es doch im 
Grunde
-- irrelevant ob zu dem Zeitpuntk schon alles gesendet ist oder nicht. 
Wenn
-- noch etwas zu senden da ist, dann sollte die ISR das ganz normal
-- raussenden. Wenn nichts mehr da ist, dann wird die ISR den Interrupt
-- einfach wieder deaktivieren.

"ier" steht typischwerweise für "Interrupt enable register" das heisst 
nicht unbedingt dass bei dem Setzen des Enable der Interrupt auch gleich 
"anzieht"! Kann sein muss aber nicht - wenn nicht - musst du in dir 
selbst seten. Aber eben nur dann wenn der UART nicht mehr akiv ist.
Wenn der UART noch im shiften ist und du setzt den Interrupt wann immer 
du gerade lustig bist, wird ein neues Byte in den UART geschriebn - auch 
wenn der gar nicht dazu bereit ist - daher ist es keinesweg irrelevant 
wann und ob man den Interrupt setzt.

Gruß,
Thaala

P.S. Auch ich habe übserehen das es zwei verschiedene (ier,idr) Aufrufe 
waren.....

von Tobi (Gast)


Lesenswert?

@ THaala

Aber es wird doch lediglich der Interrupt aktiviert. Es wird also 
gesagt, dass es ab jetzt möglich ist, das ein solcher Interrupt 
auftritt. Wenn ich an dieser Stelle per Hand einen Interrupt auslösen 
würde, dann würde ich deine Argumentation verstehen.

Im Grunde sage ich am Ende der "puts" nur, dass beim nächsten mal, wenn 
das UDR leer ist, ein Interrupt ausgelöst werden soll. Und genau das 
will ich doch, da die entsprechende ISR sich dann um die Ausgabe 
kümmert.


Folgender Code funktioniert auf einem ATmega32 bislang fehlerfrei.
1
void usart_puts(char const * str)
2
{
3
  while(*str && (countTX < TX_BUFFER_SIZE)) {
4
    buffer_out[writer_out] = *str++;
5
    countTX++;
6
    next_index(writer_out, TX_BUFFER_SIZE);
7
  }
8
9
  UCSRB |= (1<<UDRIE); // UART Data Register Empty Interrupt enable
10
}
11
12
// Transmit
13
ISR(USART_UDRE_vect)
14
{
15
  if(!countTX) {
16
    UCSRB &= ~(1<<UDRIE); // UART Data Register Empty Interrupt disable
17
  }
18
  else {
19
    next_index(reader_out, TX_BUFFER_SIZE);
20
    UDR = buffer_out[reader_out];
21
    countTX--;
22
  }
23
}

von THaala (Gast)


Lesenswert?

Hallo Tobi,

--@ THaala
--Im Grunde sage ich am Ende der "puts" nur, dass beim nächsten mal, 
wenn
--das UDR leer ist, ein Interrupt ausgelöst werden soll. Und genau das
--will ich doch, da die entsprechende ISR sich dann um die Ausgabe
--kümmert.

Hast Recht,

Problem ist nur, dass die ISR einmal mehr ausgeführt wird als du Bytes 
hast. Letzer Aufruf sagt : Es ist nichts mehr da.
Wenn das Interrupt - Handling Flankengetriggert arbeitet und die 
Durchführung der ISR den Interrupt löscht (was ich nicht genau weiß, 
bitte selbst nachlesen) dann bekommst Du unter Umständen eben keinen 
Interrupt mehr wenn du den Interrupt nur enablest.
Die ISR wurde bereits durchgeführt und der anstehende Interrupt 
gelöscht.
Ein erneutes enable bringt dann nichts.

Im Grunde zeigen Stephan's posts in die gleiche Richtung. Nur das er es 
unter diesen umständen vorziehen würde das erste byte nicht durch die 
ISR in den UART zu werfen sondern direkt über putchar.

Bei der Routine die du zeigst handelt es sich um einen anderen 
Interrupt...
Du musst einfach nur herausfinden unter welchen Umständen der TX_EMPTY 
Interrupt ansteht. Wenn der TX_EMPTY nach Abarbeitung der ISR weiterhin 
ansteht und durch ein disable/enable die ISR erneut ausgeführt wird, 
will ich nichts gesagt haben.

Stellt sich noch die Frage wann eigentlich der Interrupt gelöscht wird.
Wenn der auch nach deinem Aussprung aus deiner ISR noch ansteht (will 
sagen Du musst den evtl. noch löschen - oder es steht ein anderer 
freigegbener Iterrupt an, den du eben nicht löschst in dieser ISR) wäre 
das auch eine Erklärung dafür, das deine LED nicht leuchtet. Die ISR 
wird einfach dauernd ausgeführt...

Gruß,
Thaala

von Tobi (Gast)


Lesenswert?

THaala schrieb:
> Stellt sich noch die Frage wann eigentlich der Interrupt gelöscht wird.
> Wenn der auch nach deinem Aussprung aus deiner ISR noch ansteht (will
> sagen Du musst den evtl. noch löschen - oder es steht ein anderer
> freigegbener Iterrupt an, den du eben nicht löschst in dieser ISR) wäre
> das auch eine Erklärung dafür, das deine LED nicht leuchtet. Die ISR
> wird einfach dauernd ausgeführt...

Die LED wird aber eingeschaltet bevor überhaupt irgendein Interrupt 
aktiviert ist. Der Interrupt wird ja erst aktiviert, nachdem die Ausgabe 
in den Buffer geschrieben wurde und die LED wird beim Beschreiben des 
Buffers eingeschaltet.

von THaala (Gast)


Lesenswert?

Hallo,

demnach hast du schon ein Problem mit dem Schreiben des ersten Byte.
Der code sieht aber so unverdächtig aus, das da nix passieren kann!
Es sei denn der Buffer liegt in einem Bereich, der mit 
Funktionsregsistern überlappt, dann dürfte aber auch die LED 1 nicht 
angehen!

Hast du das LED_On(2) schon mal in die ISR verlegt ?

Gruß,
THaala

von Tobi (Gast)


Lesenswert?

THaala schrieb:
> Hast du das LED_On(2) schon mal in die ISR verlegt ?

Nein noch nicht. Werd ich morgen machen. Das Board liegt leider am 
Arbeitsplatz, wo ich heut net bin.

von Tobi (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal die aktuelle Version. Nun funktioniert es. Ich weiß auch wo 
der Fehler war, aber nicht was der Fehler war.

Der Fehler trat bei der Überprüfung von tx_reader bzw. tx_writer auf. 
Die if-Anweisung, welche prüfen sollte, ob sich diese Variablen noch im 
erlaubten Wertebereich befinden, verursachte wohl einen "crash". Nun wo 
ich diese If-Abfragen durch Modulo-Operationen ersatz habe geht es.

Würde nur zu gern wissen warum.

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.