UART läuft nicht auf einem AVR

Gast #6860137
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.
Gast #6860169
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.
Gast #6860192
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.
Gast #6860203
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
}
Gast #6860215
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
    }
Gast #6860216
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.
Gast #6860217
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.
Gast #6860230
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.
Gast #6860237
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
Gast #6860243
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)
(Firma: Funkenflug Industries) #6860318
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren