Forum: Compiler & IDEs Text senden über UART im Interruptbetrieb


von KlausF (Gast)


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

von Uwe .. (uwegw)


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.

von KlausF (Gast)


Lesenswert?

hi, schon mal Danke für die Antwort.
Über dieses FiFO bin ich auch schon drübergestolpert. Es war mir nur 
viel zu kompliziert undunübersichtlich.

Das "pollen" ist eigendlich nur nochmal ne Sicherheitsvorkehrung, in der 
regel werden sehr wenig Daten übertragen.

andere ideen?

grüße

von Lutz (Gast)


Lesenswert?

Für Zeichen würd ich lieber unsigned char statt char nehmen; um ganz 
sicher zu gehen uint8_t.
>while (*pstr!=0x00){        //prüfe auf String Ende
würde ich generell '\0' nehmen     //prüfe auf String Ende

Aber das wird nichts an dem Problem ändern.

von Hc Z. (mizch)


Lesenswert?

1
> UCSR0B |= (1 << RXCIE0);           // Enable the USART Recieve Complete interrupt (USART_RXC)

Wo ist die Interrupt-Routine dafür?  Wenn es keine gibt (und alles 
deutet darauf hin), mach das mal weg.

von KlausF (Gast)


Lesenswert?

danke für den hinweis, aber auch das war nicht des Räzels Lösung.
Ich habe die zwei Steuerzeichen im 'else' Teil der Interruptroutine mit 
in den Puffer geschrieben, und alle pollings aus der Interruptroutine 
entfernt. Jetzt läuft es.
danke an alle!!

grüße

von Uwe .. (uwegw)


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...

von Stefan E. (sternst)


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).

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.