Forum: Mikrocontroller und Digitale Elektronik Mehrere Bytes über UART empfangen


von Florian (Gast)


Lesenswert?

Hallo,

ich habe ein vllt ganz triviales Problem. Ich bekomme seriell Daten 
(9600)von einem PC auf einen Mega16 (8MHz). Das Problem ist jetzt, dass 
ich nur das erste Byte mitbekomme. Danach empfange ich nur noch Nullen. 
Kann es sein dass der MC zu langsam ist? Die ISR wird ja auch erst 
aufgrufen wenn Daten im Puffer sind oder? Dann kommen je bereits die 
nächsten an, während ich noch die ersten verabreiten will? Stimmt das so 
weit und wie kann man das lösen?


Vielen Dank schon mal
Florian

von Johannes M. (johnny-m)


Lesenswert?

Wenn Du bei 9600 Baud einen Buffer-Overflow kriegst, dann stimmt mit 
großer Wahrscheinlichkeit in Deinem Programm etwas fundamentales nicht. 
Da Du uns den Programmcode aber nicht zeigst, kann man da auch nicht 
viel zu sagen!

Bei 9600 Baud dauert die Übertragung eines Zeichens ungefähr 1 ms. Das 
ist für einen µC ne halbe Ewigkeit, wenn man nicht gerade innerhalb der 
ISR mit sperrigen Bibliotheksfunktionen arbeitet, was man generell nicht 
tun sollte. Einen Buffer-Overflow kann man übrigens detektieren, dafür 
gibts ein extra Flag...

von Timmo H. (masterfx)


Lesenswert?

Wenigstens den Inhalt der ISR könntest du mal Posten.

von Rolf Magnus (Gast)


Lesenswert?

> ich habe ein vllt

Ein was?

> Ich bekomme seriell Daten (9600)von einem PC auf einen Mega16 (8MHz).
> Das Problem ist jetzt, dass ich nur das erste Byte mitbekomme. Danach
> empfange ich nur noch Nullen. Kann es sein dass der MC zu langsam ist?

Eher nicht. Bei 9600 Baud und 8 Mhz hat dein AVR pro Byte über 8000 
Taktzyklen zur Verfügung.

> Die ISR wird ja auch erst aufgrufen wenn Daten im Puffer sind oder?

Ja.

> Dann kommen je bereits die nächsten an, während ich noch die ersten
> verabreiten will?

Ja, aber das macht nichts, da die Schnittstelle double buffering 
verwendet. Die beiden Puffer werden nicht getauscht, solange du nicht 
gelesen hast.

> Stimmt das so weit und wie kann man das lösen?

Keine Ahnung. Du hast ja nicht gezeigt, wie du es machst.

von Nicht B. (florian01)


Lesenswert?

Danke erstmal allen. Hier mein Programm:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
5
#define SYSCLK    8000000
6
#define BAUD    9600UL
7
#define UBRR_BAUD  ((SYSCLK/(16*BAUD))-1)
8
9
void uart_init(void);
10
11
uint8_t BYTE0 = 0;
12
uint8_t BYTE1 = 0;
13
14
15
int main(){
16
17
  uart_init();
18
  sei();
19
  
20
21
  while(1){
22
23
    if(BYTE1 != 0){
24
      while ( !( UCSRA & (1<<UDRE)) );                    
25
      UDR = BYTE0;
26
      while ( !( UCSRA & (1<<UDRE)) );                    
27
      UDR = BYTE1;
28
29
      BYTE0 = 0;
30
      BYTE1 = 0;
31
    }                                
32
33
  }  
34
35
return 0;
36
}
37
38
39
void uart_init(void)
40
{
41
 
42
  UBRRH = (unsigned char) (UBRR_BAUD>>8);                  
43
  UBRRL = (unsigned char) UBRR_BAUD;
44
45
  UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);                  
46
  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);                
47
}
48
49
50
51
52
ISR(USART_RXC_vect)                          
53
{  
54
  uint8_t tmp_sreg = SREG;                                                      
55
56
  cli(); 
57
                                          
58
  if (BYTE0 == 0){
59
    BYTE0 = UDR;
60
  } else {
61
    BYTE1 = UDR;
62
  }
63
64
  SREG = tmp_sreg;                               
65
}

von Karl H. (kbuchegg)


Lesenswert?

Nicht Bekannt wrote:
> Danke erstmal allen. Hier mein Programm:

> ISR(USART_RXC_vect)
> {
>   uint8_t tmp_sreg = SREG;
>
>   cli();
>
>   if (BYTE0 == 0){
>     BYTE0 = UDR;
>   } else {
>     BYTE1 = UDR;
>   }
>
>   SREG = tmp_sreg;
> }


Lass das SREG in Ruhe. Du programmierst in C und musst dich
nicht mit so niedrigen Dingen wie den tatsächlichen CPU
Registern herumschlagen.
Auch der cli() ist unnötig. Wenn eine ISR betreten wird, dann
werden die Interrupts von Haus aus disabled und beim verlassen
der ISR wieder angeschaltet.

Mach mal die beiden Variablen BYTE1 und BYTE0 als volatile
Variablen:

volatile uint8_t BYTE0 = 0;
volatile uint8_t BYTE1 = 0;

von Johannes M. (johnny-m)


Lesenswert?

Das cli() und das SREG-Sichern in der ISR übernimmt der Compiler(*). Das 
solltest Du weglassen.

Abgesehen davon solltest Du mal überlegen, wie das mit senden und 
empfangen läuft und v.a. dran denken, was passiert, wenn gerade nach dem 
Ende des Sendens die ISR aufgerufen wird und nach dem Rücksprung beide 
BYTEs auf 0 gesetzt werden...

(*)Das cli() macht genaugenommen die Hardware und nicht der Compiler...

von Nicht B. (florian01)


Lesenswert?

Kann es leider erst wieder am WE testen, werden dann berichten...

von Nicht B. (florian01)


Lesenswert?

Das mit dem Senden ist im Moment nur um zu sehen ob er beide bekommen 
hat.

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.