Text senden über UART im Interruptbetrieb

Gast #1479061
Lesenswert?

Hi, ich versuche mich gerade an dem Aufbau einer seriellen komunikation 
zwischen einem atmega88 und dem rechner.

einzelne zeichen zu senden war nicht das problem. Bei dem versuch eine 
Zeile zu senden stoße ich auf probleme.

ziel ist es, den String in einen puffer zu kopieren und den dann nach 
und nach zu senden. dass soll alles im hintergrund geschehen, gesteuert 
durch den interrupt vector "USART_TX_vect".

mein code sieht wie folgt aus:
1
#ifndef F_CPU
2
#define F_CPU 12000000UL  // Systemtakt in Hz - Definition als unsigned long beachten 
3
                         // Ohne ergeben sich unten Fehler in der Berechnung
4
#endif
5
 
6
#define BAUDRATE 9600UL      // Baudrate
7
 
8
// Berechnungen
9
#define BAUD_PRESCALE (F_CPU/(16*BAUDRATE)-1)       
10
#define BAUD_REAL (F_CPU/(16*(BAUD_PRESCALE+1)))    // Reale Baudrate
11
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUDRATE)      // Fehler in Promille, 1000 = kein Fehler.
12

13
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
14
  #error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch! 
15
#endif
16

17
#include <avr/io.h>
18
#include <avr/interrupt.h>
19
#include <util/delay.h>
20

21
char    inbuffer[32];              //einganspuffer
22
char     *inbufferpos=&inbuffer[0];          //positions zeiger
23
char    outbuffer[32];              //ausgangspuffer
24
char    *outbufferpos=&outbuffer[0];          //positionszeiger
25

26
void send(char * pstr){
27
  while ((1<<TXCIE0)& UCSR0B);    //warten bis Senderoutine fertig ist
28
  outbufferpos=&outbuffer[0];      //Zeiger auf Anfang setzen
29
  while (*pstr!=0x00){        //prüfe auf String Ende
30
    *outbufferpos = *pstr;      //kopiere Zeichen
31
    outbufferpos++;          //zeiger incrementieren
32
    pstr++;
33
  }
34
  *outbufferpos=0x00;          //String-Ende in Puffer schreiben
35

36
  outbufferpos=&outbuffer[0];      //Zeiger auf Anfang setzen
37
  
38
  UCSR0B |= (1<<TXCIE0);        //Erfolgreich gesendet interrupt aktiviren
39

40
  UDR0= *outbuffer;          //erstes Zeichen senden
41
  outbufferpos++;            //zeiger auf 2. Zeichen setzen
42
}
43

44
void long_delay(uint16_t ms) {
45
    for(; ms>0; ms--) _delay_ms(1);
46
}
47

48
int main()
49
{
50
//USART Initialisieren
51
UCSR0B |= (1 << RXEN0) | (1 << TXEN0);     // Turn on the transmission and reception circuitry 
52
UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01);   // Use 8-bit character sizes 
53
UBRR0L = BAUD_PRESCALE;           // Load lower 8-bits of the baud rate value into the low byte of the UBRR register 
54
UBRR0H = (BAUD_PRESCALE >> 8);         // Load upper 8-bits of the baud rate value into the high byte of the UBRR register 
55
UCSR0B |= (1 << RXCIE0);           // Enable the USART Recieve Complete interrupt (USART_RXC)
56

57
sei();
58
char str[]="Hallo Welt";
59

60
while (1) {
61

62
  while (!(UCSR0A & (1<<UDRE0)));
63
  send(& str[0]);
64

65
  long_delay(500);
66
  PORTB |= (1<<PORTB0);
67
  long_delay(500);
68
  PORTB &=~(1<<PORTB0);
69

70
  }
71
}
72

73
ISR(USART_TX_vect)
74
{
75
  
76
  if (*outbufferpos!=0x00)
77
  {  
78
    while (!(UCSR0A & (1<<UDRE0)));
79
    UDR0=*outbufferpos;
80
    outbufferpos++;
81
  } 
82
  else 
83
  {
84
    while (!(UCSR0A & (1<<UDRE0)));
85
    UDR0=0x0a;
86
    while (!(UCSR0A & (1<<UDRE0)));
87
    UDR0=0x0d;
88
    while (!(UCSR0A & (1<<UDRE0)));
89
    UCSR0B &= ~(1<<TXCIE0);
90
  
91
  }
92
}

Nach dem Reset wird genau einmal "Hallo Welt" übertragen. Dannach 
erhalte ich nur noch das "H" im sekundentakt.
also laut dem internen debugger von avr-studio funktioniert die 
angelegenheit, auch mehrmals, aber die realität sieht leider anders aus
Jemand ne idee, wo mein fehler liegt?

grüße klaus
#1479079
Lesenswert?

Wieso pollst du im Interrupt das UDRE0-Bit? Das ist doch gerade der Witz 
an der Sache mit den Interrupts, dass man eben nicht mehr pollen und auf 
irgendwas warten muss..
Generell ist es absolut tabu, in einer ISR auf irgendwelche Ereignisse 
zu warten! Eine ISR muss so schnell wie möglich verlassen werden, und 
darf auf keinen Fall das System länger blockieren.

Außerdem wäre ein Fifo als Zwischenspeicher wesentlich flexibler. Bei 
dir kannst du immer nur 32 Zeichen am Stück schreiben, und musst dann 
warten, bis alle gesendet wurden. Wenn du nun zB zweimal 4 Zeichen 
senden willst, musst du nach dem ersten Mal warten, bis diese Zeichen 
gesendet wurden. Bei nem richtigen Fifo könntest du fortlaufend 
schreiben, solange noch Platz im Fifo ist.
#1479122
Lesenswert?

Wenn du das Programm nicht bloß schreibst, um was dabei zu lernen, 
sondern einfach nur die Schnittstelle zum Laufen bringen willst, dann 
würde ich dir die fertige uart-lib von Peter Fleury empfehlen:
http://homepage.hispeed.ch/peterfleury/avr-software.html
Die arbeitet mit nem Fifo, und läuft bei mir seit Jahren in sämtlichen 
AVR-Projekten ohne Probleme...
#1479144
Lesenswert?

Dein Problem ist das:
1
    while (!(UCSR0A & (1<<UDRE0)));
2
    UDR0=0x0a;
3
    while (!(UCSR0A & (1<<UDRE0)));
4
    UDR0=0x0d;
5
    while (!(UCSR0A & (1<<UDRE0)));
6
    UCSR0B &= ~(1<<TXCIE0);
Du stopfst Daten in den UART und schaltest dann den TX-Interrupt ab. Das 
hat zur Folge, dass beim nächsten "UCSR0B |= (1<<TXCIE0);" in send das 
Interrupt-Flag noch von der letzten Übertragung gesetzt ist, und daher 
sofort ein TX-Interrupt ausgelöst wird. Da outbufferpos nicht volatile 
ist, ist der geänderte Wert in send noch nicht zurück gespeichert, 
weshalb für diesen sofortigen TX-Interrupt der Pointer immer noch auf 
die Null-Terminierung zeigt, und er sofort wieder in den else-Zweig 
läuft (wo der Interrupt gleich wieder abgeschaltet wird).

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