Forum: Compiler & IDEs ATMEGA32 USART sendet ein Zeichen zuviel


von Michael E. (micde)


Lesenswert?

Hallo Zusammen,

bin gerade dabei eine USART Kommunikation (ATmega32) mit FIFO und durch 
Interrupts gesteuert einzurichten.
Funktioniert eigentlich recht gut. Nur, dass jedesmal nach Abschluss der 
Kommunikation ein 0xFF zusätzlich gesendet wird.
Dabei ist es egal ob die TX Routine mehrfach ausgeführt wird um den 
Puffer zu befüllen oder nicht.

In dem Beispiel unten möchte ich "Hallo Welt!" senden.

Die Ausgabe in hterm ist allerdings etwas anders (und ohne Hochkommas):
"Hallo Welt![0xFF]"

Das 0xFF in eckigen Klammern wird natürlich als ein einzelnes Zeichen 
dargestellt :)

Verwende ich eine nicht Interrupt gesteuerte Routine wie
1
for(i=0; i<8 ;i++)              //gibt maximal 16 Zeichen aus 
2
{                    
3
  if(data[i] == 0) break;        //Abbruch wenn Nullterminator entdeckt
4
  while( !( UCSRA & (1<<UDRE) ));      //warten bis "USART Data Register Empty" gesetzt ist
5
  UDR = data[i];
6
}
funktioniert das Senden ohne Fehler.

Entdeckt jemand von Euch wo sich der Denkfehler in der Routine unten 
befinden könnte?
Ich suche jetzt schon seit 2 Tagen. Bekomme das 0xFF am Ende einfach 
nicht weg :(
1
#ifndef F_CPU
2
#define F_CPU 14745600
3
#endif 
4
5
#define RX_BUFFER_SIZE  256
6
#define TX_BUFFER_SIZE  256
7
8
#include <avr/io.h>                  //Headerdatein einbinden
9
#include <string.h>                  
10
#include <avr/interrupt.h>
11
#include <stdbool.h>
12
13
//Deklarationen von Funktionen
14
void initialize(void);
15
void wait(unsigned int ms);
16
int TX(char data[TX_BUFFER_SIZE]);
17
18
int TX_free_size(void);
19
20
21
//Globale Variablen Deklarieren
22
unsigned int * volatile pt_1;
23
24
static unsigned char TX_Buffer[TX_BUFFER_SIZE];      //Puffer für Transmitting Data, 256 Byte
25
static unsigned char TX_Buffer_In;            //TX Puffer Position für Eingabe
26
static unsigned char TX_Buffer_Out;            //TX Puffer Position für Ausgabe
27
static bool TX_FULL;                  //TX Puffer voll
28
29
30
31
int main(void)
32
{
33
        initialize();
34
35
  while(TX("Hallo Welt!") != 0);
36
  while(1);
37
}
38
//------------------------------------------
39
//Funktionen
40
//------------------------------------------
41
42
//------------------------------------------
43
//Mikroprozessor initialisieren
44
//------------------------------------------
45
void initialize(void)
46
{
47
  //Port Einstellungen
48
  DDRD = 0b00100000;  //PD5 auf Ausgang
49
  DDRA = 0b11111111;  //PA auf Ausgang
50
  //USART Einstellungen
51
  UCSRB = (1<<RXEN) | (1<<TXEN);          //RX und TX einschalten
52
  UCSRC = (1<<URSEL)|(3<<UCSZ0);            //Asynchron, keine Parität, 1 Stoppbit, 8 Datenbits
53
  //UCSRC = 0b1000011;      //Asynchroner Modus, 8 Bit, keine Parität, 1 Stoppbit; URSEL setzen für Zugriff auf Baudrate
54
  UBRRH =  0;          //Achtung! Hängt von der Frequenz ab! 19,2kBaud eingestellt (U2X = 0!)
55
  UBRRL = 47;
56
  
57
  //Variablen initialisieren
58
  TX_Buffer_In = TX_Buffer_Out;  //Sendepuffer auf 0 Byte setzen
59
  TX_FULL = false;
60
  
61
  sei();
62
}
63
64
//------------------------------------------
65
//Bestimmte Zeit in ms warten; Achtung: Immer nur 10ms Schritte möglich
66
//Benutzt Timer2
67
//------------------------------------------
68
69
void wait(unsigned int ms)
70
{
71
  unsigned int cycles_10ms;
72
  ms = ms / 10;        //ms durch 10 teilen, so oft muss der Timer aufgerufen werden
73
  cycles_10ms = F_CPU / 1024 / 100;  //Berechnet die Taktzyklen, die für 10ms gewartet werden müssen
74
            //Bei 14,7456 MHz und einem Prescaler von 1024 muss man 144 Zyklen warten für 10ms
75
                    
76
  pt_1 = &ms;        //Globalen Pointer auf die ms Variable erstellen
77
  //Timer initialisieren
78
  
79
  OCR2 = cycles_10ms;      //Zyklen, die gewartet werden müssen ins Compare Register schreiben
80
  TIMSK |= 0b10000000;      //Interrupt für Timer2 setzen (bei Compare Match)
81
  TCCR2 = 0b00001111;      //Timer an mit Prescaler 1024, Clear Timer on Compare Match 1
82
  
83
  while(*pt_1 > 0);      //Warten bis Zeit abgelaufen
84
            //Achtung: TX und RX lösen immernoch Interrupts aus
85
                  
86
  TCCR2 = 0b00000000;      //Timer stoppen
87
}
88
//------------------------------------------
89
//Sendet Daten via USART
90
//Rückgabewerte bedeuten folgendes:
91
//0: OK
92
//1: Sendepuffer zu klein für Daten, die gesendet werden sollen
93
//------------------------------------------
94
int TX(char data[TX_BUFFER_SIZE])
95
{
96
  unsigned int i;
97
  unsigned int data_len;
98
  //TX_Buffer_In: Position an die geschrieben werden kann
99
  //TX_Buffer_Out: Position von der als nächstes gelesen und verschickt wird
100
  
101
  data_len = strlen(data);
102
  if(data_len>TX_free_size())
103
  {
104
    return 1;
105
  }  
106
  
107
  //Schreibe Daten in den TX Puffer
108
  for(i=0; i < data_len; i++) 
109
  {
110
    TX_Buffer[TX_Buffer_In] = data[i];    //Schreibe Byte in TX Buffer
111
    if(TX_Buffer_In == (TX_BUFFER_SIZE-1))
112
    {
113
      TX_Buffer_In = 0;      //Wenn am Ende des Speichers angekommen, gehe zum Anfang des Speichers
114
    }
115
    else
116
    {
117
      TX_Buffer_In++;        //Gehe zur nächsten Puffersektion
118
    }
119
    
120
  }
121
  if(TX_Buffer_In == TX_Buffer_Out)
122
  {
123
    TX_FULL = true;          //Schreib-Adresse erreicht Lese-Adresse
124
  }
125
  
126
  
127
  
128
  UCSRB |= (1<<UDRIE);          //UDRE Interrupt aktivieren. Na dann mal senden :)
129
  
130
  return 0;            //Gebe 0 zurück --> Alles in Ordnung
131
}
132
//------------------------------------------
133
//Gibt freie Anzahl an Byte zurück im TX Puffer
134
//------------------------------------------
135
int TX_free_size(void)
136
{
137
  if(TX_Buffer_In > TX_Buffer_Out)
138
  {
139
    return (TX_BUFFER_SIZE-(TX_Buffer_In - TX_Buffer_Out));
140
  }
141
  else
142
  {
143
    if(TX_Buffer_In < TX_Buffer_Out)
144
    {
145
      return (TX_BUFFER_SIZE - TX_Buffer_Out + TX_Buffer_In);
146
    }
147
    else
148
    {
149
      if(TX_FULL)
150
      {
151
        return 0;
152
      }
153
      else
154
      {
155
        return TX_BUFFER_SIZE;
156
      }
157
    }
158
  }
159
}
160
//------------------------------------------
161
//Interrupts
162
//------------------------------------------
163
//------------------------------------------
164
//USART Data Register Empty 
165
//------------------------------------------
166
ISR(USART_UDRE_vect)
167
{
168
  UDR = TX_Buffer[TX_Buffer_Out];      //Sende aktuelles Byte
169
  
170
  if(TX_Buffer_Out == (TX_BUFFER_SIZE-1))    //Wenn die alte Position, die letzte Position im Array ist
171
  {
172
    TX_Buffer_Out = 0;      //Neue Position ist erste Position im Array
173
  }
174
  else
175
  {
176
    TX_Buffer_Out++;      //Sonst: Nächste Position im Puffer
177
  }
178
  TX_FULL = false;
179
180
181
  if(TX_free_size() == TX_BUFFER_SIZE)    //Keine Daten mehr zum Senden
182
  {
183
    UCSRB = ~(UCSRB & (1<<UDRIE));    //UDRE Interrupt deaktivieren
184
  }
185
  return;
186
}
187
//------------------------------------------
188
//Timer/Counter2 Compare Match 
189
//------------------------------------------
190
ISR(TIMER2_COMP_vect )
191
{
192
  *pt_1 = *pt_1 - 1;    //Speicherinhalt um 1 dekrementieren (Pointer 1, sollte auf ms Variable zeigen)
193
  return;
194
}

von Stefan E. (sternst)


Lesenswert?

1
    UCSRB = ~(UCSRB & (1<<UDRIE));    //UDRE Interrupt deaktivieren
Das deaktiviert zwar tatsächlich den UDRE-Interrupt, aktiviert dafür 
aber die beiden anderen Interrupts und setzt überhaupt alle anderen Bits 
in dem Register auf 1.

von Matthias L. (Gast)


Lesenswert?

Du solltest das als kompetten Ringpuffer mit dynamischer Länge aufbauen. 
Etwa so:
1
//** globale Variablen ************************************************
2
volatile  struct
3
{
4
  //-- Tx-Ablauf -------------------------------------
5
  uint8_t               u8TxState;
6
  uint8_t               u8TxStateLast;
7
  //-- Sende-FIFO ---------------------------------
8
  uint8_t         au8TxData [1024];     // Datenpuffer
9
  uint16_t       au16TxRef [  16];       // Ref-Tabelle
10
  uint8_t         au8TxLen  [  16];        // Länge
11
  uint8_t         u8TxInp;                      // INP-Referenz
12
  uint8_t         u8TxOut;                      // OUT-Referenz
13
  uint8_t         u8TxSend;                  // Sendezähler;
14
  uint8_t         u8Msg;                       // Anzahl der Nachrichten im FIFO
15
  uint16_t              u16Fill;                // Anzahl der Bytes im FIFO
16
}
17
scRs232;
18
19
20
void     os_rs232_send_direct( uint8_t* pau8Data, uint8_t u8Len )
21
{
22
  //-- lokale Variablen ------------------------------------------------
23
  uint16_t    u16Buff   = (uint16_t) sizeof(scRs232.au8TxData);
24
  uint16_t    u16Array  = ( u16Buff - scRs232.u16Fill );
25
  uint8_t     u8Max     = (uint8_t)  sizeof(scRs232.au8TxLen );
26
  uint8_t     u8Idx     = 0;
27
  //-- Eingangsvariablen prüfen -------------------------------------
28
  if ( pau8Data == 0 ) return;
29
  if ( u8Len    == 0 ) return;
30
  //-- genügend Platz im FIFO? -------------------------------------
31
  if ( (uint16_t)u8Len >  u16Array ) return;
32
  if ( scRs232.u8Msg   == u8Max    ) return;
33
  //-- ins FIFO eintragen -----------------------------------------------
34
  u16Array = scRs232.au16TxRef[ scRs232.u8TxInp ];
35
  u8Idx    = 0;
36
  while ( u8Idx != u8Len )
37
  {
38
    scRs232.au8TxData[u16Array] = *pau8Data;
39
    /*-- weiter ---------------------------------*/
40
    pau8Data++;
41
    u8Idx++;
42
    u16Array++;
43
    if ( u16Array == u16Buff ) u16Array = 0;
44
  }
45
  //-- Referenz aktualisieren --------------------------------------------------
46
  scRs232.u16Fill  += (uint16_t)u8Len;
47
  scRs232.au8TxLen[scRs232.u8TxInp] = u8Len;
48
  if ( ++scRs232.u8TxInp == u8Max )  scRs232.u8TxInp = 0;
49
  scRs232.au16TxRef[ scRs232.u8TxInp ] = u16Array;
50
  scRs232.u8Msg++;
51
}
52
53
54
55
ISR    ( USART1_UDRE_vect )
56
{
57
  //-- lokale Variablen ------------------------------------------------------------
58
  uint16_t    u16Buff   = (uint16_t) sizeof(scRs232.au8TxData);
59
  uint16_t    u16Idx    = scRs232.au16TxRef[scRs232.u8TxOut];
60
  uint8_t     u8Len     = scRs232.au8TxLen [scRs232.u8TxOut];
61
  uint8_t     u8Max     = (uint8_t)  sizeof(scRs232.au8TxLen );
62
  //-- Datum senden --------------------------------------------------------------
63
  UDR1 = scRs232.au8TxData[u16Idx];
64
  if ( ++u16Idx == u16Buff )
65
  {
66
    u16Idx = 0;
67
  }
68
  scRs232.au16TxRef[scRs232.u8TxOut] = u16Idx;
69
  //-- vollständig gesendet? ----------------------------------------------------
70
  if ( ++scRs232.u8TxSend != u8Len ) return;
71
   //-- Msg aus FIFO entfernen ------------------------------------------------
72
  scRs232.u16Fill  -= (uint16_t) u8Len;
73
  scRs232.u8Msg--;
74
  if ( ++scRs232.u8TxOut == u8Max   ) scRs232.u8TxOut = 0;
75
  //-- fertig, ISR abschalten -----------------------------------------------------
76
  UCSR1B   &=  ~(1<<UDRIE1);
77
}
78
79
80
81
82
void     os_rs232_run( void )
83
{
84
  switch ( scRs232.u8TxState )
85
  {
86
    //-- TX: Init Ablauf -------------------------------------------------------
87
  case cRS232_TxInit:
88
    //-- ENTRY -----------------------------------
89
    scRs232.u8TxStateLast = scRs232.u8TxState;
90
    //-- ACTION -----------------------------------
91
    //-- TRANSITION ----------------------------
92
    scRs232.u8TxState = cRS232_TxIdle;
93
    //-- EXIT ---------------------------------------
94
    break;
95
96
   //-- TX: warten auf Msg ------------------------------------------------
97
  case cRS232_TxIdle:
98
    //-- ENTRY ------------------------------------
99
    if ( scRs232.u8TxStateLast != scRs232.u8TxState )
100
    {
101
      scRs232.u8TxStateLast = scRs232.u8TxState;
102
    }
103
    //-- ACTION ------------------------------------
104
    //-- TRANSITION -----------------------------
105
    if ( scRs232.u8Msg != 0 )
106
    {
107
      scRs232.u8TxState = cRS232_TxSend;
108
    }
109
    //-- EXIT ----------------------------------------
110
    break;
111
112
   //-- TX: Senden -----------------------------------------------------------
113
  case cRS232_TxSend:
114
    //-- ENTRY ------------------------------------
115
    if ( scRs232.u8TxStateLast != scRs232.u8TxState )
116
    {
117
      scRs232.u8TxStateLast = scRs232.u8TxState;
118
      //-- Senden anstoßen -----
119
      scRs232.u8TxSend = 0;
120
      UCSR1B   |=  (1<<UDRIE1);
121
    }
122
    //-- ACTION ------------------------------------
123
    //-- TRANSITION -----------------------------
124
    if (  !( UCSR1B & (1<<UDRIE1) )  )
125
    {
126
      scRs232.u16CntTxD++;
127
128
      if (  UCSR1A & (1<<TXC1) )
129
      {  
130
        scRs232.u8TxState = cRS232_TxIdle;
131
        if ( scRs232.u8Msg != 0 )
132
        {
133
          scRs232.u8TxState = cRS232_TxWait;
134
        }
135
      }
136
    }
137
    //-- EXIT -----------------------------------------
138
    break;
139
140
  //-- TX: Pause generieren ---------------------------------------------
141
  case cRS232_TxWait:
142
    //-- ENTRY --------------------------------------
143
    if ( scRs232.u8TxStateLast != scRs232.u8TxState )
144
    {
145
      scRs232.u8TxStateLast = scRs232.u8TxState;
146
      //-- Verzögerung setzen ----
147
      OCR3A =  ( TCNT3 + scRs232.u16Delay );
148
      TIFR3 |= (1<<OCF3A);
149
    }
150
    //-- ACTION ---------------------------------------
151
    //-- TRANSITION --------------------------------
152
    if ( TIFR3 & (1<<OCF3A) )
153
    {
154
      scRs232.u8TxState = cRS232_TxSend;
155
    }
156
    //-- EXIT -------------------------------------------
157
    break;
158
159
  //-- TX: ungültig ----------------------------------------------------------
160
  default:
161
    scRs232.u8TxState = cRS232_TxInit;
162
  }

Das hab ich so für meine Propelleruhr programmiert. Der Vorteil ist, 
dass die Länge der zu sendenden Nachrichten variieren kann. Dort, wo man 
Senden will, nur das os_rs232_send_direct aufrufen und fertig. Das 
os_rs232_run muss zyklisch aufgerufen werden und managt im Hintergrund 
das Senden der Nachrichten im FIFO. Zwischen den Nachrichten wird 
automatisch eine Pause von ca. 2..4 Bytelängen eingefügt.

PS: Ich hab die c und h Datei für Interessenten mal angehängt. WIe 
besonders am RX_Teil zu erkennen ist, bin ich da noch dran.

Das Ganze arbeitet in einem speziellen Kontest, deshalb mögen einige 
Details kurios erscheinen.
http://www.avrs-at-leipzig.de/dokuwiki/projekte/propelleruhr

;-)

von Matthias L. (Gast)


Angehängte Dateien:

Lesenswert?

Hier die Dateien. Das hat erst nachträglich nicht geklappt.

PS: Wieso muss ich denn hier einen längeren Text eingeben?

von Michael E. (micde)


Lesenswert?

Stefan Ernst schrieb:
> Das deaktiviert zwar tatsächlich den UDRE-Interrupt, aktiviert dafür
> aber die beiden anderen Interrupts und setzt überhaupt alle anderen Bits
> in dem Register auf 1.

Du hast Recht! Das habe ich gerade auch nochmal durchgespielt.

das sollte wohl eher so aussehen:

UCSRB = (UCSRB & ~(1<<UDRIE));

Vielen Dank für den Hinweis!

Vielen Dank auch an Dich Matthias. Ich schau mir Deinen Code gleich mal 
an.

Macht irgendwo auch Sinn den Puffer dynamisch aufzubauen. Dann kann der 
Speicherbereich genutzt werden wenn gerade nichts zum Senden ansteht! :)

Viele Grüße

Michi


Nachtrag:

Das war das Problem Stefan :) Ich glaub' ich spiel nächstes Mal die 
Bitoperationen nicht mehr im Kopf, sondern auf nem Blatt Papier durch!
Nochmals vielen Dank!

Das Problem an der Sache war (neben dem versehentlichen Setzen von 
sämtlichen USART Interrupts außer dem USART Data Register Empty 
Interrupt), dass auch das UCSZ2 Bit gesetzt wurde von 011 (8 Bit Data 
Mode) auf 111 (9 Bit Data Mode).

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.