Forum: Mikrocontroller und Digitale Elektronik Problem beim Empfang von Strings


von Marco G. (grmg2010)


Lesenswert?

Moin,

ich habe Probleme beim Empfangen von Strings auf meinem ATMEGA168.

Hintergrund: Ich versende vom PC mehrere Strings mit Header und /crlf am 
Ende an den Controller. Mit Hilfe der Header sollen verschiedene 
Aktionen ausgeführt werden. Zum Testen habe ich zwei Strings genommen, 
die einmalig per Knopfdruck im Abstand von 250ms versendet werden.

Der erste String wird richtig übernommen, nur der zweite ist immer Null. 
Als Empfangsroutine nutze ich die hier in der Wiki eingetragene 
Funktion. Der restliche Quellcode sieht wie folgt aus:
1
#define F_CPU 8000000UL
2
#define UART_MAXSTRLEN 20
3
4
#include <avr/io.h>
5
#include <avr/pgmspace.h>
6
#include <avr/eeprom.h>
7
#include <avr/interrupt.h>
8
#include <util/delay.h>
9
#include <stdio.h>
10
#include <string.h>
11
#include "avr.h"
12
#include "M41T93MYZ.h"
13
#include "SPI.h"
14
#include "UART.h"
15
16
17
18
uint8_t Alarmanlage, status = 0, counterTemp = 0, TempAvailable = 0, UARTREceived = 0;
19
uint8_t Thour, TMinute,TSecond, TWDay, TDay, TMonth, TYear = 0;
20
char Buffer[10], BUfferHour[3], BufferMinute[3], BufferSecond[3], BufferTemperatur[5];
21
char *ptr, BufferBCD[10], BMinute[10], BHour[10], BSecond[10];
22
uint8_t test, x, y, z;
23
uint8_t Hour_bcd, SetHourBCD, minute_bcd,SetMinuteBCD, second_bcd, SetSecondBCD;
24
25
/*UART Receive Variablen*/
26
char Received_String[UART_MAXSTRLEN + 1];
27
uint8_t uart_str_complete = 0;
28
uint8_t uart_str_count = 0;
29
char uart_string[UART_MAXSTRLEN + 1] = "";
30
31
ISR(USART_RX_vect)
32
{
33
  unsigned char nextChar;
34
  
35
  nextChar = UDR0;
36
  if( uart_str_complete == 0 )
37
  {  
38
    if( nextChar != '\n' &&
39
    nextChar != '\r' &&
40
    uart_str_count < UART_MAXSTRLEN )
41
    {
42
      uart_string[uart_str_count] = nextChar;
43
      uart_str_count++;
44
    }
45
    else
46
    {
47
      uart_string[uart_str_count] = '\0';
48
      uart_str_count = 0;
49
      uart_str_complete = 1;
50
    }
51
  }
52
}
53
54
int main(void)
55
{  
56
  avr_init();
57
58
    while(1)
59
    {
60
    
61
    if (uart_str_complete == 1)
62
    {
63
      strcpy(Received_String, uart_string);
64
      strcpy(uart_string, " ");
65
      uart_str_complete = 0;
66
    }
67
    
68
    if (strstr("h", Received_String) == 0)
69
    {
70
      strcpy(BHour, Received_String + 1);
71
      /*ptr = Received_String + 1;*/
72
      
73
      Hour_bcd = atoi(BHour);
74
      SetHourBCD = DezToBCD(Hour_bcd);
75
//       itoa(SetHourBCD, BufferBCD, 2);
76
//       Send_UART_Char(BufferBCD);  
77
      strcpy(Received_String, " ");
78
      x = 1;
79
      led_bl(1);
80
    }
81
    
82
    if (strstr("m", Received_String) == 0)
83
    {
84
      strcpy(BMinute, Received_String + 1);
85
      Send_UART_Char(BMinute);
86
      /*ptr = Received_String + 1;*/      
87
      minute_bcd = atoi(ptr);
88
//       SetMinuteBCD = DezToBCD(minute_bcd);
89
//       itoa(SetMinuteBCD, Buffer, 2);
90
      Send_UART_Char(Buffer);
91
      strcpy(Received_String, " ");
92
      y = 1;
93
      led_rt(1);
94
    }
95
    
96
    if (x == 1 && y == 1)
97
    {
98
      itoa(SetHourBCD, BufferBCD, 2);
99
      Send_UART_Char(BufferBCD);
100
      itoa(SetMinuteBCD, Buffer, 2);
101
      Send_UART_Char(Buffer);
102
      //led_rt(1);
103
      set_clock(0x00, SetMinuteBCD, SetHourBCD );
104
      x = 0;
105
      y = 0;
106
    }
107
108
    }
109
}

Was habe ich übersehen? Ist die von mir veranlasste Pause beim senden zu 
kurz? Oder liegt es an etwas Anderem?

Gruß

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Es lohnt sich, Variablen die als Kommunikation zwischen ISR und 
Hauptprogramm dienen, als 'volatile' zu erklären. Denn der Compiler 
sieht im Unterprogramm der ISR erstmal ein Software Stückchen, das nie 
ausgeführt wird. Im Hauptprogramm wird z.B. 'uart_str_complete' immer 
nur gelesen, und das erkennt der Compiler.

Also
1
volatile uint8_t uart_str_complete = 0;
2
volatile uint8_t uart_str_count = 0;
3
volatile char uart_string[UART_MAXSTRLEN + 1] = "";
 sollte dich weiterbringen. Leider schreibst du ja nix über das 
Verhalten der Test LEDs.
Es könnte auch sein, das es sich lohnt, das alles zu vereinfachen. Zum 
Stellen der Uhr würde auch ein 'h0945' String reichen. die ersten beiden 
Stellen sind dann Stunden und die letzten die Minuten.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

1
strcpy(BHour, Received_String + 1);

BHour ist nur 10 Zeichen lang, Received_String kann aber bis 20 Zeichen 
enthalten.


Welche Strings überträgst du denn?

Was willst du damit immer erreichen?
1
strcpy(Received_String, " ");

Was soll das Leerzeichen im String? Wenn du den String leer machen 
willst, dann schreibe einfach ein \0 rein. Aber das ist eigentlich gar 
nicht notwendig, weil der Inhalt ja eh immer überschrieben wird.
1
Received_String[0] = 0;


µC haben wenig Speicher, warum verschwendest du ihn dann?
1
char BMinute[10], BHour[10], BSecond[10];

du brauchst doch immer nur einen davon, dann kann man immer die gleiche 
Variabel nutzen. Es ist sogar so, das du es überhaupt nicht bracuhst.
1
strcpy(BHour, Received_String + 1);
2
Hour_bcd = atoi(BHour);

warum nicht gleich so?:
1
Hour_bcd = atoi(Received_String + 1);

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Argumente von strstr sind "Heuhaufen" und "Nadel", nicht umgekehrt: In 
einem Heuhaufen "m" wirst nicht viele Nadeln finden :o)

: Bearbeitet durch User
von Marco G. (grmg2010)


Lesenswert?

Matthias S. schrieb:
> Es könnte auch sein, das es sich lohnt, das alles zu vereinfachen. Zum
> Stellen der Uhr würde auch ein 'h0945' String reichen.

Daran hatte ich auch schon gedacht, nur fehlt mir die richtige Idee, um 
die Stunden daraus zu extrahieren. Die Minuten wären kein Problem, da 
kann ich ja einfach die entsprechende Anzahl von Stellen Springen. Ich 
könnten bei den Stunden ja zweimal den String mit strcpy bearbeiten, 
einmal von vorne gezählt und einmal von hinten. Aber es muss doch auch 
einfacher gehen.

Die Leds machen was sie sollen. ich erhalte auch die Ausgabe der 
Variablem auf dem PC

Peter II schrieb:
> Wenn du den String leer machen
> willst, dann schreibe einfach ein \0 rein. Aber das ist eigentlich gar
> nicht notwendig, weil der Inhalt ja eh immer überschrieben wird.

Stimmt, aber ich sende nicht immer einen String. Das heißt ich würde 
immer in einer der if-Abfragen landen.

Johann L. schrieb:
> Argumente von strstr sind "Heuhaufen" und "Nadel", nicht umgekehrt: In
> einem Heuhaufen "m" wirst nicht viele Nadeln finden :o)

Sehr schöner Vergleich, der stimmt. Habe ich jetzt korrigiert.

Auf den ersten Blick schein es jetzt zu funktionieren. Ich muss noch mal 
weiter testen.

Vielen Dank schon einmal für die Tipps.

von S. R. (svenska)


Lesenswert?

Marco G. schrieb:
> Matthias S. schrieb:
>> Es könnte auch sein, das es sich lohnt, das alles zu vereinfachen. Zum
>> Stellen der Uhr würde auch ein 'h0945' String reichen.
>
> Daran hatte ich auch schon gedacht, nur fehlt mir die richtige Idee, um
> die Stunden daraus zu extrahieren.

Du weißt, dass das erste Zeichen der Befehl ist, das zweite und dritte 
Zeichen die Stunden, das vierte und fünfte Zeichen die Minuten. Außerdem 
weißt du, dass ein C-String ein Array aus Zeichen ist, und dass der 
ASCII-Code für die Ziffern aufsteigend ist und mit '0' anfängt. Fertig 
ist der simple Parser:
1
uint8_t h,m;
2
if(Received_String[0] == 'h') {
3
  h = (Received_String[1] - '0') * 10 + (Received_String[2] - '0');
4
  m = (Received_String[3] - '0') * 10 + (Received_String[4] - '0');
5
} else {
6
  // anderer Befehl
7
}

Vermutlich ist es sinnvoll, noch abzuprüfen, ob Received_String[1..4] 
auch Ziffern sind (d.h. zwischen einschließlich '0' und '9' liegen) und 
ob der String auch vollständig empfangen wurde.

Nachtrag: BCD ist übrigens des Teufels. ;-)

Du brauchst den String nicht vom UART extra kopieren, weil du nach dem 
Parsen sämtliche Informationen daraus extrahiert hast und eleganter (in 
Integern) speicherst. Der String kann danach weg.

Den Parser selbst kannst du übrigens - je nach Komplexität der Eingaben 
- auch direkt im UART-Interrupt als Zustandsautomat implementieren. 
Jedes Zeichen wird direkt beim Empfang ausgewertet und wenn der String 
vollständig war, wird das Hauptprogramm informiert. Damit bekommst du 
keine Probleme, wenn jemand zehntausend 'X' schickt, denn die werden 
alle nacheinander ignoriert.

: Bearbeitet durch User
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.