Forum: Mikrocontroller und Digitale Elektronik ATMega USART: Senden funktioniert nicht


von Zaphod (Gast)


Lesenswert?

Ich habe hier einen ATMega328p und versuche auf diesem, Daten über USART 
zu senden und zu empfangen. Beispielcode, der beides per Interrupt 
macht, funktioniert problemlos. Allerdings möchte ich jetzt nur noch per 
Interrupt empfangen und per Polling/manuellem Schreiben in UDR0 senden. 
Leider klappt das nicht, ich empfange keine Daten mehr.

Meine Initialisierung:
1
    uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2;
2
    UCSR0A |= (1 << U2X0);
3
    UBRR0H = UBRR0_value >> 8;
4
    UBRR0L = UBRR0_value;
5
    UCSR0B |= (1<<RXEN0 | 1<<TXEN0 | 1<<RXCIE0);
Der Empfänger-IRQ (funktioniert):
1
    ISR(SERIAL_RX)
2
    {
3
       uint8_t data = UDR0;
4
       ...
5
    }
Die Sendefunktion (funktioniert nicht):
1
    void serial_send(void)
2
    {
3
       if ((UCSR0A & UDRE0)!=0)
4
       {
5
          UDR0=tx_buffer[idx];
6
          idx++;
7
          ...
8
       }
9
    }
Hat jemand eine Idee, was ich falsch mache? "..." ist jeweils ein 
Platzhalter, der sich um die korrekte Behandlung der Daten kümmert.

Danke!

von Karl M. (Gast)


Lesenswert?

Hallo,

ich arbeite immer it RX- und TX-Fifos bei der Usart Nutzung auf einem 
Atmega, warum du nicht?

Was kommt hier heraus?
1
if ((UCSR0A & UDRE0)!=0)
Bit-Nummer vers. Bit-Maske!

von S. Landolt (Gast)


Lesenswert?

(0xC0 & 0x05) == 0

von Logi Ker (Gast)


Lesenswert?

Zaphod schrieb:
> Beispielcode, der beides per Interrupt
> macht, funktioniert problemlos. Allerdings möchte ich jetzt nur noch per
> Interrupt empfangen und per Polling/manuellem Schreiben in UDR0 senden.

Dann suche dir doch im Internet einen Beispielcode der das kann.
Wer einmal abschreiben kann der kann es auch ein zweites Mal.

von Karl M. (Gast)


Lesenswert?

Noch etwas,

wo hast Du diese Formel her?
1
UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) -1)/2;

Man sollte sie mal erweitern:
1
UBRR0_value = (F_CPU / (8L * BAUD_RATE)) -2;

Aus dem Datenblatt folgt:
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf

Table 24-1. Equations for Calculating Baud Rate Register Setting
Mit U2X0 = 1 im UCSR0A Register
1
UBRR0 = F_CPU/8L/BAUD_RATE -1;

von Zaphod (Gast)


Lesenswert?

Karl M. schrieb:
> Was kommt hier heraus?if ((UCSR0A & UDRE0)!=0)Bit-Nummer vers.
> Bit-Maske!

Äh...ja...natürlich - Danke!

> ich arbeite immer it RX- und TX-Fifos bei der Usart Nutzung auf einem
> Atmega, warum du nicht?

Klingt gut...wie mache ich das?

von Zaphod (Gast)


Lesenswert?

Karl M. schrieb:
> wo hast Du diese Formel her?

Aus den GRBL-Sourcen. allerdings habe ich eh' das Gefühl, das die 
ziemlich wackelig sind...

von Stefan F. (Gast)


Lesenswert?

Versuche es mal so:
1
loop_until_bit_is_set(UCSR0A, UDRE0);
2
UDR0 = c;

https://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html

von Zaphod (Gast)


Lesenswert?

Karl M. schrieb:
> ich arbeite immer it RX- und TX-Fifos bei der Usart Nutzung auf einem
> Atmega, warum du nicht?

OK, um Missverständnisse zu vermeiden: Der Codeteil mit "..." nutzt 
einen Ringbuffer, die Daten werden separat dort abgelegt.

Was ist mit FIFO gemeint: ein Hardware-FIFO des ATMega-USART?

von Karl M. (Gast)


Lesenswert?

Hallo,

siehe mal hier bei Peter Dannegger:

Beitrag "AVR-GCC: UART mit FIFO"

Funktioniert 1A.

Habe ich auch als Assembler Variante implementiert.

von Zaphod (Gast)


Lesenswert?

Karl M. schrieb:
> siehe mal hier bei Peter Dannegger:

OK, aber das ist kein Hardware-FIFO, das ist alles in Software 
realisiert!?

von Karl M. (Gast)


Lesenswert?

Hallo,

wie kommst Du zu dieser Einsicht?

Die erste Procedure in UART0.C ist
1
void init_uart0( u16 bauddivider )
2
{
3
  UBRR0H = bauddivider >> 8;
4
  UBRR0L = bauddivider;      // set baud rate
5
  UCSR0A = 0;        // no U2X, MPCM
6
  UCSR0C = 1<<UCSZ01^1<<UCSZ00    // 8 Bit
7
#ifdef URSEL0
8
     ^1<<URSEL0      // if UCSR0C shared with UBRR0H
9
#endif
10
     ;
11
  UCSR0B = 1<<RXEN0^1<<TXEN0^    // enable RX, TX
12
     1<<RXCIE0;      // enable RX interrupt
13
  rx_in = rx_out;      // set buffer empty
14
  tx_in = tx_out;
15
}

Hier werden einige Usart Register bedient:
UCSR0A, UCSR0B, UCSR0C und ein RX Interrupt wird freigegeben.

Somit wäre es doch logischer, dass ein Hardware Usart eine Software 
Gerüst erhält.

Man mag vielleicht diese Codierung nicht, aber mit Deklarationen in 
MYDEFS.H und UART0.H sollte der Code schon verständlich sein.

Peter schreibt gerne ein "^" XOR-, anstatt der häufig zu sehenden "|" 
OR-Verknüpfung.
Was ganz klar in diesem Zusammenhang auch richtig ist!
Wenn man die Logiktabelle von XOR im Kopf hat, sollte auch verständlich 
sein, wie diese Verknüpfung funktioniert.

Ein weiteres Stichwort ist "3.19 Operator Precedence", die man auch 
verinnerlichen sollte:

Von oben nach unten:
5. Bitwise shifting expressions.
:
9. Bitwise exclusive OR expressions.

_Link_: 
https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html#Operator-Precedence

von Karl M. (Gast)


Lesenswert?

Sorry,

ich habe als "kein Hardware-FIFO" mit "Software Uart" gleich gesetzt - 
"Freudscher Fehler".

Klar ist, man muss auf Programmebene immer einen Software RX- und 
TX-Fifo Aufsetzen, auch wenn das Hardware Usartmodul, Hardware Fifo 
intern verwendet.

Der Scharm ist halt, man schreibt seinen Datensatz, z.B. "Hallo" in den 
TX-Fifo und das serielle "Versenden" erfolgt vollständig automatisiert 
im Hintergrund.

Gleiches beim Empfangen, hängt das Hauptprogramm in einer zeitintensive 
Berechnung "fest", dann können über einen RX-Fifo (schreibend), in 
Verbindung mit dem Hardware Usart, auch unabhängig Daten empfangen und 
zwischen gespeichert werden.

Im Hauptprogramm erfolgt der Zugriff nur auf den RX-Fifo (lesend).

von Zaphod (Gast)


Lesenswert?

Karl M. schrieb:
> Klar ist, man muss auf Programmebene immer einen Software RX- und
> TX-Fifo Aufsetzen, auch wenn das Hardware Usartmodul, Hardware Fifo
> intern verwendet.

Sorry, aber ich sehe den Hardware-FIFO immer noch nicht.

Es gibt doch genau ein Empfangsregister UDR0 und der Interrupt wird 
ausgelöst, wenn dieses vollständig beschrieben ist. Anschließend kann 
man dort genau ein Byte lesen und muss dann warten, bis das nächste Byte 
angekommen ist.

Wäre ein echter Hardware-FIFO von z.B. 56 Byte vorhanden, dann würde der 
Interrupt ausgelöst werden, wenn dieser voll ist bzw. einen bestimmten, 
meist programmierbaren Füllstand erreicht hat. Dann könnte man aus dem 
Datenregister mehrere Bytes direkt hintereinander lesen - so lange, bis 
irgend ein anderes Register signalisiert, dass der FIFO leer ist.

Und genau das kann ich in diesem Beispielcode nirgends erkennen! Wo wird 
denn da ein Hardware-FIFO aktiviert und wie groß ist der?

von Karl M. (Gast)


Lesenswert?

Hallo,

ich hatte nicht postuliert, das es eine "Stück" Software ist, das mehr 
als den im AVR schon vorhandenen Hardware-FIFO nutzt. ==> Datenblatt.

Danach folgt ein per Software kontrollierter RX- und TX-Fifo.
Anm. das ist nicht "Böses", das ist ein ganz normales vorgehen, um 
asynchrone Prozesse vom Hauptprogramm zu trennen.

Im Quellcode, insbesondere in der UART0.H Datei, werden die 
Moduleinstellungen vorgenommen.
1
          // size must be in range 2 .. 256
2
#define RX0_SIZE  10    // usable: RX0_SIZE + 2 (4 .. 258)
3
#define TX0_SIZE  8    // usable: TX0_SIZE + 1 (3 .. 257)
Das ist wie sonst üblich, alles in einer .h Datei, realisiert. :-)

Somit bleibt die Frage, wo ist dein Verständnisproblem?

Über die Funktionsprototype in UART0.H kann man schließlich auf die 
Funktionen der Methoden zurückgreifen:
1
u8 ukbhit0( void );    // 0 = rx buffer empty
2
u8 ugetchar0( void );    // get received byte
3
u8 utx0_ready( void );    // 0 = tx still busy
4
void uputchar0( u8 c );    // send byte
5
void uputs0_( u8 *s );    // send string from SRAM
6
void init_uart0( u16 bauddivider );
Es steht da ja schon im Kommentar!

Über die Implementierung im Beispielcode MAIN.C folgt dann auch reale 
Verwendung/ Nutzung der Methoden.

von Thomas F. (igel)


Lesenswert?

Karl M. schrieb:
> Beitrag "AVR-GCC: UART mit FIFO"

> Habe ich auch als Assembler Variante implementiert.

Würdest du den Code hier veröffentlichen?

von Karl M. (Gast)


Lesenswert?

Nein,

der ist nicht für den avr gcc, sondern als Interface in LunaAVR 
integriert.

von Zaphod (Gast)


Lesenswert?

Karl M. schrieb:

> Somit bleibt die Frage, wo ist dein Verständnisproblem?

Das kann ich dir sagen:

> Danach folgt ein per Software kontrollierter RX- und TX-Fifo.
> Anm. das ist nicht "Böses", das ist ein ganz normales vorgehen, um
> asynchrone Prozesse vom Hauptprogramm zu trennen.

Wie oben geschrieben, ist sowas in meinem Code vorhanden und - da für 
meine Ursprungsfrage nicht relevant - an den Codestellen mit dem "..." 
ausgelassen.

> ich hatte nicht postuliert, das es eine "Stück" Software ist, das mehr
> als den im AVR schon vorhandenen Hardware-FIFO nutzt. ==> Datenblatt.

Noch mal: wo ist dieser Hardware-FIFO? Wie groß ist der? Und wo finde 
ich das FIFO-Full-Register?

Das Hardware-Datenblatt schmeißt zwar auch manchmal den Begriff "FIFO" 
in die Runde, aber ich kann nirgends eine Information finden, wie groß 
der ist!

> Im Quellcode, insbesondere in der UART0.H Datei, werden die
> Moduleinstellungen vorgenommen.          // size must be in range 2 ..
> 256

Das ist alles die Softwareseite, die mich nicht interessiert, weil ich 
sowas bereits habe...

von Stefan F. (Gast)


Lesenswert?

Du kannst ohne zu warten 2 Bytes in das UDR Register schreiben (wenn es 
vorher leer war). Das erste wird direkt zum Ausgabe-Schieberegister 
durch gereicht. Das zweite verbleibt im Puffer, bis das Schieberegister 
wieder frei ist.

Gleiches gilt für die Empfangsrichtung.

Da ich die Schieberegister nicht mitzählen würde, könnte man meiner 
Meinung nach von einem 1-Byte FIFO reden. Allerdings ist dieser FIFO so 
klein, dass er diesen Namen nicht verdient hat.

Einen Chip, der ein Byte speichern kann, nenne ich ja auch nicht 
Speicher oder gar RAM.

Beim PC wurden in den 90er Jahren UART Chips beliebt, die den bisherigen 
1-Byte Puffer durch 14 Bytes ersetzten, damit die high-Speed Modems (das 
war damals alles über 9600 Baud) zuverlässig funktionierten.
Wen es interessiert: Original war der 8250, der pin-kompatible Chip mit 
FIFO hatte die Nummer 16550A.

von Zaphod (Gast)


Lesenswert?

Stefanus F. schrieb:
> könnte man meiner
> Meinung nach von einem 1-Byte FIFO reden. Allerdings ist dieser FIFO so
> klein, dass er diesen Namen nicht verdient hat.

So sehe ich das auch - und das erklärt, warum für diesen ominösen FIFO 
nirgends eine Größe angegeben ist.

Danke für die Klarstellung, ich dachte schon, ich bin zu blöd, die 
Spezifikation zu lesen!

von Karl M. (Gast)


Lesenswert?

Danke,
nun ist klar, was Du meintest.

Im vom Dir gezeigten Codestück wird nicht über einen TX-Fifo und Usart 
Dten gesendet.
Hier läuft der Datentransfer blockierend ab!
1
void serial_send(void)
2
{
3
  if ((UCSR0A & UDRE0)!=0)
4
   {
5
      UDR0=tx_buffer[idx];
6
      idx++;
7
      ...
8
   }
9
}

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.