Forum: Mikrocontroller und Digitale Elektronik UART läuft nicht auf einem AVR


von Markus (Gast)


Lesenswert?

guten abend,
ich habe ein Problem mit dem UART auf einem ATMega328 welcher auf einem 
Arduino Board sizt.
Dieser möchte nicht funktionieren.

main.c:
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
#include "defines.h"
6
#include "uart/uart.h"
7
8
int main(void)
9
{
10
    DDRB |= (1 << PINB5);
11
    PORTB &= ~(1 << PINB5);
12
    uart_init(MYUBRR);
13
    sei();
14
    while(1)
15
    {
16
        uart_sendStringWait("Test");
17
        PORTB ^= (1 << PINB5);
18
        _delay_ms(1000);
19
    }
20
}
uart.c:
1
void uart_init(unsigned int ubrr)
2
{  
3
  /* Set baud rate */
4
  UBRR0H = ubrr >> 8;
5
  UBRR0L = ubrr & 0xFF;
6
7
  /* Enable receiver and transmitter */
8
  UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0); // RXCIE0: RX Interrupt
9
10
  /* Set frame format: 8data, 1stop bit */
11
  UCSR0C = (1<<UCSZ00)|(1<<UCSZ01);
12
}
13
14
UART_ERROR uart_sendStringWait(char *s)
15
{
16
    UART_ERROR ret;
17
    if(1 != (UCSR0B & (1<<RXEN0)))
18
    {
19
        ret = UART_TX_NOT_ENABLED;
20
    }
21
    else
22
    {
23
      while(*s != 0x00)
24
      {
25
            ret = uart_transmit(*s);
26
            if(UART_NO_ERROR == ret)
27
            {
28
                s++;
29
            }
30
      }
31
        ret = UART_NO_ERROR;
32
    }
33
    return ret;
34
}
35
36
static UART_ERROR uart_transmit( unsigned char data );
37
static UART_ERROR uart_transmit( unsigned char data )
38
{
39
    UART_ERROR ret = UART_UDR_NOT_FREE;
40
    if(UCSR0A & (1<<UDRE0))
41
    {
42
        UDR0 = data;
43
        ret = UART_NO_ERROR;
44
    }
45
46
    return ret;
47
}

Angeschlossen habe ich dies über einem FTDI zum PC.
1
Verbunden (Arduino <-> FTDI)
2
TX <-> RX
3
RX <-> TX
4
GND <-> GND
Auch am FTDI an für sich sehe ich nicht das UART daten gesendet bzw. 
empfangen worden sind.
Sende ich etwas vom PC, sehe ich eine LED am FTDI kurz aufblitzen 
(sprich es ist etwas angekommen)
Die LED toggelt. Sprich der Code an für sich läuft.

Aber den Fehler im Code für den UART sehe ich nicht.

von holger (Gast)


Lesenswert?

Du wartest nicht bis das Byte in UDR0 auch gesendet wurde. Das geht 
nicht in SOFORT. Das dauert. Dann rennst du das nächste mal in 
uart_transmit() und fliegst gleich mit einem Fehler raus.

von S. Landolt (Gast)


Lesenswert?

> if(1 != (UCSR0B & (1<<RXEN0)))
? 'bitwise and', müsste also (1 != 16) sein, oder?

von Markus (Gast)


Lesenswert?

holger schrieb:
> Du wartest nicht bis das Byte in UDR0 auch gesendet wurde. Das geht
> nicht in SOFORT. Das dauert. Dann rennst du das nächste mal in
> uart_transmit() und fliegst gleich mit einem Fehler raus.

Warte jetzt direkt nach dem senden bit UDR0 wieder frei ist
1
static UART_ERROR uart_transmit( unsigned char data )
2
{
3
    UART_ERROR ret = UART_UDR_NOT_FREE;
4
    if(UCSR0A & (1<<UDRE0))
5
    {
6
        UDR0 = data;
7
        ret = UART_NO_ERROR;
8
        while(!(UCSR0A & (1<<UDRE0)))
9
    }
10
    return ret;
11
}
aber auch jetzt sehe passiert nichts.

von Stefan F. (Gast)


Lesenswert?

Bevor du das UDR Register beschreibst, musst du warten dass es frei 
wird. Nicht danach.

Was mir negativ aufgefallen ist:
> UCSR0B |= ein paar Bits
> UCSR0C = ein paar Bits

Warum einmal |= und direkt darunter = ? Beides könnte man plausibel 
begründen, aber dieser Mischmasch gefällt mir gar nicht.

von Markus (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Bevor du das UDR Register beschreibst, musst du warten dass es frei
> wird. Nicht danach.

Ich warte ja indirekt, indem ich nur das zeichen schreibe wenn UDRE0 
auch frei ist.
Aber auch wenn ich davor (mit der while) warte, passiert nichts
1
static void uart_transmit( unsigned char data )
2
{
3
    while(!(UCSR0A & (1<<UDRE0))){}
4
    UDR0 = data;
5
}

von Stefan F. (Gast)


Lesenswert?

Deine sendStringWait Funktion kann nicht senden. Hast du den vorherigen 
Hinweis beachtet und es korrigiert?

S. Landolt schrieb:
>> if(1 != (UCSR0B & (1<<RXEN0)))
> ? 'bitwise and', müsste also (1 != 16) sein, oder?

von Stefan F. (Gast)


Lesenswert?

Gehe Schrittweise vor, wenn du den Fehler nicht auf die Schnelle 
erkennst. Ändere deine Hauptschleife in:
1
    while(1)
2
    {
3
        UDR0 = 'x';
4
        PORTB ^= (1 << PINB5);
5
        _delay_ms(1000);
6
    }

Wenn das nicht geht, haben wir viel weniger Zeilen zu untersuchen.

Und wenn es geht, geht kommt der nächste Schritt:
1
    while(1)
2
    {
3
        uart_transmit('y');
4
        PORTB ^= (1 << PINB5);
5
        _delay_ms(1000);
6
    }

Blinkt die LED richtig im 1s Intervall?

Wenn es geht kommt der nächste Schritt:
1
    while(1)
2
    {
3
        uart_sendStringWait("z");
4
        PORTB ^= (1 << PINB5);
5
        _delay_ms(1000);
6
    }

von holger (Gast)


Lesenswert?

S. Landolt schrieb:
>> if(1 != (UCSR0B & (1<<RXEN0)))
> ? 'bitwise and', müsste also (1 != 16) sein, oder?

Wobei die Abfrage auch ziemlich sinnfrei ist wenn man das darunter 
liest:

        ret = UART_TX_NOT_ENABLED;

Es wird abgefragt ob RX Enabled ist. Einfach erst einmal den ganzen Kram 
weglassen und schauen ob TX funktioniert. Dann weiß man auch ob die 
Baudrate stimmt usw.

von Markus (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Deine sendStringWait Funktion kann nicht senden. Hast du den vorherigen
> Hinweis beachtet und es korrigiert?
>
> S. Landolt schrieb:
>>> if(1 != (UCSR0B & (1<<RXEN0)))
>> ? 'bitwise and', müsste also (1 != 16) sein, oder?

Ja, das hatte ich auch schonmal auskommentiert.
Aber auch das hat nichts gebracht.

Habe was anderes gefunden.
Direkt nach uart_init habe ich den receive buffer gelöscht
1
void uart_flush(void)
2
{
3
    uint8_t dummy;
4
    while (UCSR0A & (1<<RXC0))
5
    {
6
        dummy = UDR0;
7
    }
8
}

Warum das jetzt aber das Senden geholfen hat, verstehe ich nicht.

von holger (Gast)


Lesenswert?

UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0); // RXCIE0: RX Interrupt

Du hast einen RX Interrupt aktiv geschaltet ohne Interruptroutine.
Das endet in einer Endlosschleife.

von Stefan F. (Gast)


Lesenswert?

holger schrieb:
> Du hast einen RX Interrupt aktiv geschaltet ohne Interruptroutine.

Das erklärt, warum

Markus schrieb:
> Direkt nach uart_init habe ich den receive buffer gelöscht
> Warum das jetzt aber das Senden geholfen hat, verstehe ich nicht.

Jetzt wird die (nicht existierende) Interrupt-Routine nicht aufgerufen 
-> keine Endlosschleife.

von Markus (Gast)


Lesenswert?

holger schrieb:
> UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0); // RXCIE0: RX Interrupt
>
> Du hast einen RX Interrupt aktiv geschaltet ohne Interruptroutine.
> Das endet in einer Endlosschleife.

Tatsache.
durch das flush habe ich den Buffer geleert und es ging normal.
Habe jetzt mal ganz viele Daten gesendet und es funktioniert wieder 
nicht.
Durch das rausnehmen des Interrupts ist das Problem nicht mehr

von Markus (Gast)


Lesenswert?

Jetzt habe ich aber noch einmal eine Frage.
Wie programmiert ihr euer UART?
Ich sehe momentan zwei wege.

So wie es jetzt momentan implementiert ist.

Sprich aus einer Funktion rufe ich die UART funktion auf um etwas zu 
senden. Erst wenn dies gesendet worden ist, geht es mit der eigentlichen 
Funktion weiter.

Eine andere Möglichkeit wäre ein buffer zu erstellen, welche ich nur 
befülle.
Also meine Funktion füllt den Buffer und macht mit der eigentlichen 
Arbeit weiter. Das senden wird dann vom UART-Modul selber übernommen 
(Also mit Interrupts dann)

von Stefan F. (Gast)


Lesenswert?

Markus schrieb:
> Wie programmiert ihr euer UART?

Kommt auf die Anforderungen der Anwendung an. Immer nur so komplex wie 
nötig. Ich sende oft ohne Puffer, aber empfange mit Puffer und 
Interrupt.

Dies ist meine Kopiervorlage mit der ich meistens neue Projekte beginne: 
http://stefanfrings.de/avr_hello_world/index.html

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Ich lasse immer so viel wie möglich von der Hardware erledigen. Um 
alles, was die Hardware macht, brauche ich mich in der Software nicht zu 
kümmern. Also UART immer komplett interruptgesteuert mit Sende- und 
Empfangspuffer. Ich habe mir einmal vernünftige Routinen dafür 
geschrieben, die sich immer wieder recyclen lassen wenn man sie braucht. 
Nachteil: Auf kleinen AVRs brauchen die Puffer unschön viel RAM und dann 
muß man sich da ggf. anpassen wenn man das RAM für andere Dinge braucht.

Kommt halt auch immer drauf an was man möchte. Für kurze Steuerdaten 
braucht man keine Puffer, bei einem zeitkritischen Hauptprogramm, was 
nicht auf das Ende einer Übertragung warten kann, geht's nicht ohne 
Puffer.

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.