Forum: Mikrocontroller und Digitale Elektronik Datenaustausch von µC und PC


von Sebastian S. (audionaut)


Lesenswert?

Hallo Leute!

Hab mir vor Kurzem mal so einen schönen UART - USB - Adapter geholt.
Durch das AVR-GCC Tutorial läuft alles auch soweit.

Zurzeit schalte ich damit nur eine LED über ein C# GUI an und aus.

Wie man im Code sieht kann so aber nur die Eine LED angesprochen werden, 
da diese einfach auf jedes eintreffende Byte reagiert. (Bzw auf '1' und 
'0')

Ich würd aber gern zum Beispiel die vom µC über ein DS18B20 gemessene 
Temperatur auf dem PC anzeigen lassen. Oder auch eine Solltemperatur vom 
PC auf den µC übertragen.

Jetzt zu meiner Frage:

Wie macht man das am Besten.

Meine Idee wäre:

So wie bei jeder Variablenzuweisung.

1. Byte "Adresse" zum Bsp: 0x01
2. Byte Wert zum Bsp: 0x00

Dann im µC die "Adresse" vergleichen Bsp. 0x01 -> LED1
Dann wert abseichern Bsp. 0x00 -> LED1 aus
Danach beide Bytes auf 0 setzen und neue Übertragung abwarten.



Wäre dieser Ansatz eurer Meinung nach in Ordnung oder hat jemand Die 
Standardlösung für dieses Problem parat?

Den Code der GUI kann ich auch noch posten, aber ich denke der ist hier 
eher zweitrangig.

Schonmal vielen Dank für die Hilfe.
1
#define F_CPU 8000000UL
2
#define BAUD 9600UL
3
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
#include "MyLib/UART.h"
7
8
void init_timer0()
9
{
10
   // Timer 0 konfigurieren
11
   TCCR0 = (5<<CS00); // Prescaler 1024
12
   
13
   // Overflow Interrupt erlauben
14
   TIMSK |= (1<<TOIE0);
15
}
16
17
volatile unsigned char receivedbyte;
18
volatile unsigned char status = '0';
19
volatile unsigned int timer0flag = 0;
20
21
22
int main(void)
23
{
24
  DDRB |= ( 1 << PINB0);
25
  
26
  init_uart();
27
  init_timer0();
28
  
29
  sei();
30
    
31
    while(1)
32
    {
33
    if(receivedbyte == '1')
34
    {
35
      PORTB |= 1 << PINB0;
36
    }
37
    else if(receivedbyte == '0')
38
      PORTB &= !(1 << PINB0);
39
      
40
    if(PINB & (1<<PB0) == 1)
41
    {
42
      status = '1';
43
    }
44
    else
45
    {
46
      status = '0';
47
    }
48
    
49
    if(timer0flag == 3)  // (8000000/1024)/256 = 30,5175   Vergleich auf 3 -> 100ms
50
    {
51
      uart_send_sign(status);
52
      timer0flag = 0;
53
    }
54
    
55
    }
56
}
57
58
ISR (USART_RXC_vect)
59
{
60
  receivedbyte = UDR;
61
}
62
63
ISR (TIMER0_OVF_vect)
64
{
65
  timer0flag++;
66
}

von Marcel Hellwig (Gast)


Lesenswert?

Sebastian Schubert schrieb:
> Ich würd aber gern zum Beispiel die vom µC über ein DS18B20 gemessene
> Temperatur auf dem PC anzeigen lassen. Oder auch eine Solltemperatur vom
> PC auf den µC übertragen.
>
> Jetzt zu meiner Frage:
> Wie macht man das am Besten.

Indem du einfach eine feste Anzahl von Bits (z.B. 32) überträgst und die 
dann auf deinem PC zusammenbastelst und dann entsprechend 
interpretierst. Entweder als int, wenn du eine ganzzahl hast oder als 
float bei fließkommazahlen. Natürlich kannst du auch 64bit übertragen, 
damit deine Genauigkeit steigt. Strings z.B. überträgt man meistens nach 
dem Ascii standard mit abschließenden 0 Terminator (also ein C-String, 
im Gegensatz zum pascal-string (wikipedia sollte hier helfen)).
Im Prinzip legst du fest, was wie übertragen wird. Das nennt man dann 
ein Protokoll.

>
> Meine Idee wäre:
>
> So wie bei jeder Variablenzuweisung.
>
> 1. Byte "Adresse" zum Bsp: 0x01
> 2. Byte Wert zum Bsp: 0x00
>
> Dann im µC die "Adresse" vergleichen Bsp. 0x01 -> LED1
> Dann wert abseichern Bsp. 0x00 -> LED1 aus
> Danach beide Bytes auf 0 setzen und neue Übertragung abwarten.

Sowas in etwa wird auch normalerweise gemacht. Dabei gibt's dann s.g. 
Register oder Steuerbefehle und der µC antwortet dann entsprechend 
darauf. Ein Beispiel ist dafür hier: 
http://invensense.com/mems/gyro/documents/RM-MPU-6000A.pdf

Hoffe ich konnte dir helfen.

Gruß
Marcel

von Sebastian S. (audionaut)


Lesenswert?

Danke für deine Antwort Marcel.

Das heißt ich liege garnich mal so falsch.

Aber noch eine Frage zum UART.
Ich dachte der Puffer (atmega8) ist nur 8bit groß und deswegen lassen 
sich Sachen wie int die 32 Bit haben garnicht ohne weiteres übertragen. 
Da hab ich wohl was falsch verstanden(?)

Also wenn ich dann die 32 Bit auf dem uC habe, muss ich die dann wieder 
auseinander shiften? Erste 16bit sind Adresse und letzte 16 dann wert?

Werde aus dem PDF leider nicht ganz schlau.

von Marcel H. (hellow)


Lesenswert?

Sebastian Schubert schrieb:
> Das heißt ich liege gar nicht mal so falsch.
:)
>
> Aber noch eine Frage zum UART.
> Ich dachte der Puffer (atmega8) ist nur 8bit groß und deswegen lassen
> sich Sachen wie int die 32 Bit haben gar nicht ohne weiteres übertragen.
> Da hab ich wohl was falsch verstanden(?)
Jein. Wenn ich das noch richtig in Erinnerung habe (schon lange nicht 
mehr mit dem mega8 operiert), dann musst du deine bits auseinander 
klabüstern, also ja: immer 8 bit zur Zeit, wie du das machst ist deine 
Sache und Teil des "Protokolls", das du dir überlegst.
>
> Also wenn ich dann die 32 Bit auf dem uC habe, muss ich die dann wieder
> auseinander shiften? Erste 16bit sind Adresse und letzte 16 dann wert?
Das versteh nicht. :/ Das Problem am Mega8 ist, dass es ein 8-bit 
Mikrocontroller ist und mit 16- oder 32-bit Werten nicht optimal 
arbeiten kann. Wahrscheinlich kriegst du eh einen 16bit Wert von deinem 
Temperatursensor (welcher ist denn das? Datenblatt?!), oder wenn das 
über ADC geht, dann maximal 10bit. Das funktioniert dann so, dass du 
entweder periodisch (alle n Sekunden) die Werte an den PC schickst oder 
der PC aktiv nachfragt und der µC antwortet (siehe oben mit der 
Registertable).

> Werde aus dem PDF leider nicht ganz schlau.
Das war ein Beispiel, wie so eine Registertable aussieht. Z.B. schickst 
du 3B zu dem Chip (Seite 7 oben) und kriegst den Wert ACCEL_XOUT[15:8] 
(also die oberen 8bit von einem 16bit Wert) zurück. Wenn du die unteren 
8 Bit auch noch willst, dann musst du noch ein 3C schicken und kriegst 
das dann zurück.
Wie das nachher aussieht ist dir komplett überlassen aber sowas solltest 
du dokumentieren, damit du später nicht auf deinen Code guckst und dir 
denkst "hä?". Leider schon zu häufig passiert... :)

von Sebastian S. (audionaut)


Lesenswert?

Jap der Tempsensor gibt 16bit raus.

Datenblatt:
http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf

> Das versteh nicht. :/
Na ich wollte mein Protokoll ;-) ja so anlegen, dass ich einen int, sei 
es nun 16 oder 32 bit, verschicke und davon 8 bit z.B. 0x01(LED1) die 
Adresse ergeben und 8 bit z.B. 0x00 den Wert. Und diese dann vom µC 
interpretiert werden.

Also 0000 0001 0000 0000
     -Adresse- - Wert  -

Und der resultierende Wert des gesamten int garnicht interessiert.
So hätte man erstmal 255 Adressen für Variablen und einen Wertebreich 
von 0-255. Würde mir auch erstmal ausreichen.

> Das Problem am Mega8 ist, dass es ein 8-bit
> Mikrocontroller ist und mit 16- oder 32-bit Werten nicht optimal
> arbeiten kann.

Ich hab bis jetzt auschließlich mit dem Mega8 gearbeitet... Aber ich 
würde gern einen 16 oder 32 bit basierten µC benutzen.
Nur habe ich gerade mal geschaut auch 48, 168 und 328 haben 8bit 
register. Auch der Mega16-16. Irgendwie blick ich da nicht durch. Die 
erste Zahl steht ja für den Flash-Speicher wenn ich das richtig 
verstanden habe. Gibt es denn überhaupt µC mit Registern größer 8 Bit?

> Z.B. schickst
> du 3B zu dem Chip (Seite 7 oben) und kriegst den Wert ACCEL_XOUT[15:8]
Ok jetz hab ich's kapiert. Man schickt dem µC ne Adresse und er 
antwortet dann mit dem Wert. So wollte ich das auch machen. Ich druck's 
mir mal aus und nehms mit ins Bett ;-D

von Mike (Gast)


Lesenswert?

Sebastian Schubert schrieb:
> Ich dachte der Puffer (atmega8) ist nur 8bit groß und deswegen lassen
> sich Sachen wie int die 32 Bit haben garnicht ohne weiteres übertragen.

Das stimmt. Du brauchst auf dem µC noch ein Programm, was die Bytes 
nacheinander überträgt.
In einem Buch stehen doch auch mehrere Buchstaben hintereinander, um 
Worte zu formen.

von Martin K. (dschadu)


Lesenswert?

Ich empfehle dir, eine fertige UART-Library zu nutzen. Z.b. die von 
Peter Fleury: http://homepage.hispeed.ch/peterfleury/avr-software.html
Einfach zu verstehen, zu integrieren und zu verwenden.

Diese Lib hat einen Ringbuffer, den du in einer .h Datei definierst (ist 
alles beschrieben). Dieser ist z.B. 32Byte groß.
In deiner Main-Schleife fragst du einfach dauernd ab, ob neue Daten über 
UART kamen. Wenn nicht, machst du mit deinem Programm weiter. Sollten 
neue Daten gekommen sein, kopierst du die Daten aus dem Ringbuffer in 
ein Array. So lange, bis du ein '\0' bzw ein '\n' empfangen hast.
Zum Beispiel:
#D.R\n (Data.Request)
Das wären 5 Byte Daten. Natürlich müsstest du hier einen Timeout 
einbauen und dich vor Buffer-Overflow schützen.
Mit strtok(); kannst du die Daten aufteilen und in meinem Beispiel am 
"." trennen. Dann hättest du ein 2d-Array mit #D und R\n

Jetzt kannst du mithilfe von str Funktionen (strcmp();) abfragen, wie 
der String aussieht. Entspricht er einem gültigen befehl, schickst du 
Daten zurück.
Das kann ein String sein (uart_puts();) oder einzelne Zeichen 
(uart_putc();) oder Zahlen, die vorher in einen String gewandelt werden 
müssen (itoa();).
Das abschließende \n nicht vergessen!

Einstellungen kannst du mit der Selben Methode machen.
Zum beispiel: #TS.23\n (Temperatur Soll 23°C)
Teilst du wieder mit strtok(); auf und das Element [1] machst du mit 
atoi(); zu einem int.

von TriHexagon (Gast)


Lesenswert?

Sebastian Schubert schrieb:
>> Das Problem am Mega8 ist, dass es ein 8-bit
>> Mikrocontroller ist und mit 16- oder 32-bit Werten nicht optimal
>> arbeiten kann.
>
> Ich hab bis jetzt auschließlich mit dem Mega8 gearbeitet... Aber ich
> würde gern einen 16 oder 32 bit basierten µC benutzen.
> Nur habe ich gerade mal geschaut auch 48, 168 und 328 haben 8bit
> register. Auch der Mega16-16. Irgendwie blick ich da nicht durch. Die
> erste Zahl steht ja für den Flash-Speicher wenn ich das richtig
> verstanden habe. Gibt es denn überhaupt µC mit Registern größer 8 Bit?

Das macht doch überhaupt keinen Sinn, die UART überträgt immer jeweils 
ein Byte und das ist nunmal 8 Bit groß (obwohl es UARTs gibt die auch 9 
Bits zulassen). D.h. auch bei einem 32 Bit Controller bleibt das 
Register 8 Bit groß. Das ist auch überhaupt kein Problem und auch besser 
so, warum sollte ich immer automatisch 4 Bytes senden, wenn ich nur ein 
Byte senden will? Man sendet einfach nacheinander seinen Bytestrom.

Im Namen steht nur wie viel Flash und mit welcher max. Frequenz der 
Mikrocontroller betrieben werden kann. Alle AVRs sind 8 Bit Controller. 
Die XX Bits stehen auch nur mit welcher nativer Wortbreite der Prozessor 
rechnet (bitte keine Diskussion über die Bedeutung, ich weiß das es 
wesentlich kompliziert ist).

von Sebastian S. (audionaut)


Lesenswert?

Danke für eure Antworten.

Ok also ist wirklich alles auf 8 Bit.
Dann werde ich mir die Lib von Peter Fleury mal anschauen. Davon hatte 
ich schonmal gelesen, aber ich wollte nicht einfach ne Lib einbinden und 
das war's. Ich wollte erstmal den UART an sich etwas verstehen.
Aber jetzt nen Programm zu schreiben, dass mir paar Byte zusammen 
bastelt ist im endeffekt unnötig, wenn es schon jemand gemacht hat. Da 
weiß ich auch wie es funktionieren würde.

So dann hab ich erstmal bisschen was zu tun. Ich meld mich mal wieder 
wie's so klappt.

Danke nochmal :)

von Jobst M. (jobstens-de)


Lesenswert?

Das Protokoll sollte vor allem aber auch eine Syncronisation erlauben.
Denn sonst bekommst Du die richtigen Bytes nie zusammen.
Wenn die SW auf dem PC das 1. Byte erwartet, der µC aber das 3. Byte 
sendet, werden die Daten nie dort sein, wo sie sein sollen.

ASCII-Strings würde ich mit CR, LF oder beidem enden lassen. Dann kann 
man die Daten auch in einem einfachen Terminalprogramm betrachten.


Gruß

Jobst

von Sebastian S. (audionaut)


Lesenswert?

Einen schönen Samstag euch!

So ich hab mich jetzt erstmal mit der UART-Lib beschäftigt und ich 
versuche jetzt seit gestern Abend einen String vom PC einzulesen.

Code hier:
1
#define F_CPU 8000000UL
2
#define UART_BAUD_RATE 9600
3
4
#include <stdlib.h>
5
#include <avr/io.h>
6
#include <string.h>
7
#include <avr/interrupt.h>
8
#include <avr/pgmspace.h>
9
#include <util/delay.h>
10
#include "MyLib/uart.h"
11
12
void uart_gets( char* Buffer, uint8_t MaxLen )
13
{
14
  uint8_t NextChar;
15
  uint8_t StringLen = 0;
16
  
17
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen
18
  
19
  // Sammle solange Zeichen, bis:
20
  // * entweder das String Ende Zeichen kam
21
  // * oder das aufnehmende Array voll ist
22
  while( NextChar != 'A' && StringLen < MaxLen - 1 ) {
23
    *Buffer++ = NextChar;
24
    StringLen++;
25
    NextChar = uart_getc();
26
  }
27
  
28
  // Noch ein '\0' anhängen um einen Standard
29
  // C-String daraus zu machen
30
  *Buffer = '\0';
31
}
32
33
int main(void)
34
{
35
  
36
DDRB |= ( 1 << PINB0);
37
  
38
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
39
  
40
sei();
41
42
char state_request[] = "SR$";  // Statusabfrage
43
char mystring[20];
44
45
  while(1)
46
  {
47
    uart_gets(mystring, 20);
48
    uart_puts(mystring);
49
  }
50
}

Ich lasse mir den String Probehalber einfach zurückgeben.

Das Problem ist:

while( NextChar != 'A' && StringLen < MaxLen - 1 )

Verursacht mir nur Müll in der GUI. Mit '\0' als abbruchbedingung 
funktioniert es aber wunderbar!

Wenn ich also "Hallo Universum!A" Schreibe, müsste doch "Hallo 
Universum!" zurück kommen...
(nebenbei: ich finde den Standardsatz "Hallo Welt" einfach ungerecht den 
anderen da draußen gegenüber...)

Es kommt aber "veHaoumlou rs rsUsulrs!HUsuaniv!" bei 10 mal abschicken. 
Also es kommen zufällige Stückchen zurück.

Der CPU-Takt kommt von einem 8Mhz Quarzoszillator und glaube ich somit 
nen Baudfehler von 0,3. Außerdem funzt es ja mit der terminierenden 0.
Werd da echt nicht schlau draus...

> Jobst M.
> ASCII-Strings würde ich mit CR, LF oder beidem enden lassen.

Hallo. Was genau sind das für Zeichen? Das eine müsste Line Feed sein. 
Aber das ist doch '\r\n' unter windoof.

: Bearbeitet durch User
von Jobst M. (jobstens-de)


Lesenswert?

Sebastian Schubert schrieb:
>> Jobst M.
>> ASCII-Strings würde ich mit CR, LF oder beidem enden lassen.
>
> Hallo. Was genau sind das für Zeichen? Das eine müsste Line Feed sein.
> Aber das ist doch '\r\n' unter windoof.

CR ist carriage return (Wagenrücklauf)
LF ist line feed

Ist das selbe wie \r\n


Gruß

Jobst

von Jobst M. (jobstens-de)


Lesenswert?

Sebastian Schubert schrieb:
> Der CPU-Takt kommt von einem 8Mhz Quarzoszillator

Fällt mir gerade noch auf: Ist es wirklich ein Quarzoszillator? Oder der 
interne 8MHz Oszillator? Der interne ist für UART nicht zu gebrauchen.


Gruß

Jobst

von Sebastian S. (audionaut)


Angehängte Dateien:

Lesenswert?

Hier mal mein Brettboard. Ist wirklich ein externer Quarzoszillator :)

___________

Was mir gerade auch noch aufegfallen ist. Sobald ich nen strcmp oder 
sonstwas mit dem String mache kommt auch nur Müll zurück...
1
while(1)
2
{
3
  uart_gets(mystring, 20);
4
  uart_puts(mystring);
5
    
6
  itoa(strcmp(mystring, state_request), result, 10);
7
    
8
  uart_putc(result);
9
    
10
  _delay_ms(500);
11
}

von Sebastian S. (audionaut)


Lesenswert?

Hm... Also offensichtlich wird das Array beim senden gelöscht. Sonst 
würde er mir ja den Bildschirm voll machen.
Das is ja aber eigentlich ziemlich blöd so. Kann man das irgendwie 
verhindern?

von Lenny D. (le-do)


Lesenswert?

Ha, ich glaub ich habs ;)
Der Schlüssel ist dein Kommentar:
1
  NextChar = uart_getc();         // Warte auf und empfange das nächste Zeichen

In der library lese ich, dass diese Funktion eben nicht wartet auf das 
nächste Zeichen, sondern UART_NO_DATA (=0x0100) zurückgibt.
Das erklärt auch, warum deine Methode bei '\0' trotzdem funktioniert:
Wenn kein Byte verfügbar ist, ist im Speicher 0x00 = '\0'!

Ob das dein Problem mit dem strcmp löst, weiß ich nicht.

von Sebastian S. (audionaut)


Lesenswert?

Das war tatsächlich der Fehler!

Hab jetzt mal geändert zu

while(UART_RxHead == UART_RxTail){}

Und jetzt klappt es. Auch der strcmp funktioniert jetzt. Danke! Da stand 
ich echt bisschen auf dem Schlauch :D

Aber warum steht das dann in der Lib. Is doch irgendwie unvorteilhaft. 
Wenn ich die Lib wieder zurückändere müsste ich zumindest die uart_getc 
auf "No Data" abfragen und dann doch wieder warten bis was ankommt.

von Sebastian S. (audionaut)


Lesenswert?

Habe mir jetzt nochmal was überlegt.
1
#define F_CPU 8000000UL
2
#define UART_BAUD_RATE 9600
3
4
#include <stdlib.h>
5
#include <avr/io.h>
6
#include <string.h>
7
#include <avr/interrupt.h>
8
#include <avr/pgmspace.h>
9
#include <util/delay.h>
10
#include "MyLib/uart.h"
11
12
void uart_gets( char* Buffer, uint8_t MaxLen )
13
{
14
  uint8_t NextChar;
15
  uint8_t StringLen = 0;
16
  
17
  NextChar = uart_getc();         // nächstes Zeichen im Ringbuffer
18
  
19
  if (NextChar != UART_NO_DATA)  // Zeichen vorhanden
20
  {
21
    while(NextChar != '$' && StringLen < MaxLen - 1) //So lange behandeln bis $ kommt oder String voll ist
22
    {
23
      if (NextChar == UART_NO_DATA)        // Wenn NextChar leer
24
      {
25
        
26
      }
27
      else
28
      {
29
        *Buffer++ = NextChar;
30
        StringLen++;
31
      }
32
      
33
      NextChar = uart_getc();         // nächstes Zeichen im Ringbuffer
34
    }
35
    
36
    *Buffer = '\0';
37
  }      
38
}
39
40
int main(void)
41
{
42
  
43
DDRB |= ( 1 << PINB0);
44
  
45
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
46
  
47
sei();
48
49
char state_request[] = "SR";  // Statusabfrage
50
char mystring[40];
51
52
  while(1)
53
  {
54
    uart_gets(mystring, sizeof(mystring));
55
    
56
    if(strcmp(mystring, state_request) == 0)
57
    {
58
      uart_puts("state_request\r\n");
59
      PORTB ^= (1 << PINB0);
60
    }
61
    else
62
    {
63
      uart_puts(mystring);
64
    }
65
  }
66
}

Leider kommt da auch nur Buchstabensalat. Vielleicht sieht einer den 
Fehler.

von Sebastian S. (audionaut)


Lesenswert?

So ich hab's jetzt. Das "No Data" ist 16Bit groß und wenn man das 
abfragt sollte auch die Variable dafür min 16Bit haben...

Hier nochmal das funktionsfähige Programm. Wenn ihr noch Verbesserungen 
seht bin ich immer neugierig!
1
#define F_CPU 8000000UL
2
#define UART_BAUD_RATE 9600
3
4
#include <stdlib.h>
5
#include <avr/io.h>
6
#include <string.h>
7
#include <avr/interrupt.h>
8
#include <avr/pgmspace.h>
9
#include <util/delay.h>
10
#include "MyLib/uart.h"
11
12
void uart_gets( char* Buffer, uint8_t MaxLen )
13
{  
14
  uint16_t NextChar;
15
  uint8_t StringLen = 0;
16
  
17
  NextChar = uart_getc();         // nächstes Zeichen im Ringbuffer
18
  
19
  if(NextChar != UART_NO_DATA)  // Zeichen vorhanden
20
  {
21
    while(NextChar != '$' && StringLen < MaxLen - 1)  //So lange behandeln bis $ kommt oder String voll ist
22
    {
23
      *Buffer = NextChar;
24
      Buffer++;
25
      StringLen++;
26
      
27
      do
28
      {
29
        NextChar = uart_getc();
30
      }while(NextChar == UART_NO_DATA);        // Wenn NextChar leer      
31
    }
32
    
33
    *Buffer = '\0';
34
  }      
35
}
36
37
void clear_string(char* string, uint8_t Len)
38
{
39
  int i = 0;
40
  
41
  for (i = 0; i < Len; i++)
42
  {
43
    string[i] = '\0';
44
  }
45
}
46
47
int main(void)
48
{
49
  
50
DDRB |= ( 1 << PINB0);
51
  
52
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
53
  
54
sei();
55
56
char state_request[] = "SR";  // Statusabfrage
57
char mystring[40];
58
59
60
  while(1)
61
  {
62
    uart_gets(mystring, sizeof(mystring));
63
    
64
    
65
    if(strcmp(mystring, state_request) == 0)
66
    {
67
      uart_puts("state_request\r\n");
68
      clear_string(mystring, sizeof(mystring));
69
    }
70
    else
71
    {
72
      uart_puts(mystring);
73
      clear_string(mystring, sizeof(mystring));
74
    }
75
  }
76
}

von Lenny D. (le-do)


Lesenswert?

Das mit den 16bit hätte ich gleich dazuschreiben können, sry.

Im großen und ganzen sieht das recht funktionsfähig aus, ich würde mir 
nur überlegen ob der Code das gewünschte Ziel auf bestem/ klarsten Wege 
erreicht.

zB ist die Funktion getc absichtlich so ausgelegt, nicht auf ein Zeichen 
zu warten und du blockierst durch das warten deinen uC, das ist bei 
diesem simplen Ablauf natürlich kein Problem aber bei komplexeren 
Programmen mindestens ineffizient. Um klarzustellen was hier passiert 
würde ich daher eine Funktion einführen wie "waitforc" und dann diese 
verwenden, dadurch vereinfacht sich deine gets und die Logik ist besser 
aufgeteilt.
1
uint16_t uart_waitforc() //muss hier void in die klammer in C?
2
{
3
  uint16_t ret;
4
  do
5
  {
6
    ret = uart_getc();
7
  }
8
  while(ret == UART_NO_DATA); //wiederhole dies so lange bis Daten vorhanden
9
  return ret; //hier könnte man auch gleich ein 8bit zurückgeben, weiß nicht wie man den type cast macht.
10
}
11
12
void uart_gets( char* Buffer, uint8_t MaxLen )
13
{  
14
  uint16_t NextChar;
15
  uint8_t StringLen = 0;
16
17
  NextChar = uart_waitforc(); //les 1 zeichen
18
  while(NextChar != '$' && StringLen < MaxLen - 1)  //So lange behandeln bis $ kommt oder String voll ist
19
  {
20
    *Buffer = NextChar; //zeichen in string schreiben
21
    Buffer++;
22
    StringLen++;
23
24
    NextChar = uart_waitforc(); //nächstes zeichen lesen
25
  }
26
  *Buffer = '\0'; //string terminieren
27
}

von Sebastian S. (audionaut)


Lesenswert?

Hi Lenny,

Lenny D. schrieb:
> Im großen und ganzen sieht das recht funktionsfähig aus, ich würde mir
> nur überlegen ob der Code das gewünschte Ziel auf bestem/ klarsten Wege
> erreicht.
>
> zB ist die Funktion getc absichtlich so ausgelegt, nicht auf ein Zeichen
> zu warten und du blockierst durch das warten deinen uC

Die Funktion wird ja sofort komplett übersprungen wenn "No Data" gelesen 
wird. Und wenn ein Zeichen kommt muss ja erst der ganze String 
eingelesen werden bevor damit gearbeitet werden kann.
Wenn man die Funktion zwischendrin verlässt müsste man die Position des 
Zeigers auch extern zwischenspeichern bis der String beendet ist und 
alle Funktionen die mit dem String arbeiten, dürften ihn zwischen drin 
nicht verwenden, da er nicht komplett ist - müssten dann also 
übersprungen werden usw. ... Finde das so für mich persönlich schon 
recht gut.

Zumal bei kompletten 39 Zeichen 39/9600 = 4ms Zeit beansprucht werden (+ 
die Zeit zum Vergleichen und abspeichern natürlich). Was für mich 
erstmal vollkommen ausreichend ist.


> Um klarzustellen was hier passiert
> würde ich daher eine Funktion einführen wie "waitforc" und dann diese
> verwenden, dadurch vereinfacht sich deine gets und die Logik ist besser
> aufgeteilt.

Statt einer do while eine neue Funktion die eben diese do while 
beinhaltet? Ist das nicht overkill?

von Lenny D. (le-do)


Lesenswert?

Ja ich sehe was du meinst, unsere Programme machen logisch was anderes: 
Meines blockiert sofort, deines erst ab dem ersten Zeichen und beide 
blockieren ab da bis zum '$'.

Das Auslagern in eine eigene Funktion erhöht finde ich einfach die 
Leserlichkeit, ich habe zB erst mal nicht verstanden dass deins 
absichtlich 2mal nach UART_NO_DATA fragt, zu 2 verschiedenen Zwecken 
eben (und hab dabei das Überspringen "wegoptimiert" ;) ).

Daher versuche ich einfach immer, möglichst Dinge logisch zu trennen die 
nicht unbedingt zusammengehören müssen, das reduziert finde ich auch 
Fehlerquellen.
Meine gets() Funktion ist zB unabhängig von der Implementierung der 
Schnittstelle, sondern wartet wie der Name waitforc() sagt auf ein 
Zeichen.
Spätestens wenn du das an einer anderen Stelle benutzt, hast du dir 
somit Mühe und Fehlerquellen gespart.

Aus persönlichem Stil würde ich zu deinem Zweck auch eine Funktion 
definieren "uart_ischaravailable()" oder so, eventuell gibts die sogar 
schon in der Lib?
Diese dürfte dann den Ringbuffer nicht verschieben, wenn ich einfach nur 
UART_NO_DATA==getc() schreibe kann es ja sein dass ich ein Zeichen 
verschlampe. Damit wäre ich schlussendlich jedes Vorkommen von 
UART_NO_DATA in meiner gets() methode los, ich finde das hübscher.

NB: Egal wie kurz, manchmal ist auslagern sinnvoll. Lerne grad den XMC 
kennen, da gibts sogar vordefinierte Einzeiler, nämlich Funktionen um 
jeden Pin zu setzen:
1
__STATIC_INLINE void P0_1_set(void){
2
    PORT0->OMR = 0x00000002UL;
3
}
Plus in Leserlichkeit, plus in Abstahiertheit, plus in 
Fehleranfälligkeit. Da diese inline definiert sind, werden sie beim 
compilieren eingesetzt und somit sind sie genauso schnell als stünde der 
Befehl in deiner Funktion.

von stefanus (Gast)


Lesenswert?

Ich bevorzuge Text-basierte Protokolle, denn die kann man sehr bequem 
mit einem Terminal-Programm debuggen.

Der Rechenaufwand ist dann für den µC natürlich etwas größer.

von Karl H. (kbuchegg)


Lesenswert?

Sebastian Schubert schrieb:

> Die Funktion wird ja sofort komplett übersprungen wenn "No Data" gelesen
> wird. Und wenn ein Zeichen kommt muss ja erst der ganze String
> eingelesen werden bevor damit gearbeitet werden kann.

Dann wäre es aber sinnvoll, seine Denkweise etwas zu verändern.

Geh mal gedanklich weg von der Vorstellung, dass du darauf warten musst, 
das eine komplette 'Zeile' übertragen wurde.

Geh hin zum Ablauf
* die erste Frage lautet: gibt es ein Zeichen auf der UART - ja oder 
nein?

* wenn ja, was soll mit diesem Zeichen geschehen, bzw. was verrät es 
mir?
Wenn es nicht das finale Ende-Zeichen ist (welches auch immer das sein 
mag, in deinem Fall ein '$', dann ist es ein Teil der Zeile, die ich 
gerde im Begriff bin zu empfangen. In diesem Fall interessiert den 
AUfrufer der Funktion noch nicht, das etwas empfangen wurde und genau 
das melde ich ihm auch: die empfangene Zeile (String) ist noch nicht 
vollständig

Ist es hingegen das finale Ende Zeichen, dann macht die Funktion den 
String damit fertig und meldet auch seinem Aufrufer, dass der String 
jetzt vollständig ist und bearbeitet werden kann.

Auf die Art hast du jegliche Form von Warterei komplett aus dem Programm 
draussen. Und genau das will man ja. Man will ja weg von Programmen die 
aktiv auf irgendetwas warten und man will statt dessen hin zu 
Programmen, die auf Ereignisse reagieren. Ein Ereignis tritt auf und für 
das Programm stellt sich die Frage: wie reagiere ich auf das Auftreten 
dieses Ereignisses. In diesem Fall ist dann eben das Ereignis: Ein 
Zeichen wurde empfangen.

> Wenn man die Funktion zwischendrin verlässt müsste man die Position des
> Zeigers auch extern zwischenspeichern bis der String beendet ist

ja. das macht ja nichts.

> und
> alle Funktionen die mit dem String arbeiten, dürften ihn zwischen drin
> nicht verwenden, da er nicht komplett ist - müssten dann also
> übersprungen werden usw.

Ja. Und?
Das ist kein Beinbruch. In der Hauptschleife lautet es dann eben an 
einer einzigen Stelle
1
....
2
int main()
3
{
4
  ....
5
  while( 1 )
6
  {
7
    if( completeReceived( receivedString, sizeof(receivedString), &receivPtr ) )
8
    {
9
       Bearbeite den String 'receivedString'
10
11
    }
12
  }

alles in allem ... recht überschaubar

> Zumal bei kompletten 39 Zeichen 39/9600 = 4ms Zeit beansprucht werden (+
> die Zeit zum Vergleichen und abspeichern natürlich). Was für mich
> erstmal vollkommen ausreichend ist.

4ms sind eine halbe Ewigkeit. Und wenn ich mal mein Lieblingsbeispiel 
heranziehe: Wenn von der Putzfrau beim Fegen während der Übertragung das 
Kabel irrtümlich abgezogen wird, dann steht erst mal alles, weil der µC 
auf die Beendigung einer String Übertragung wartet, die die nächsten 
paar Stunden nicht passieren wird.
Aktiv auf etwas zu warten ist meist keine gute Idee. Vor allen Dingen 
deshalb, weil die Alternative auch nicht komplizierter ist. Es ist nur 
eine etwas andere Herangehensweise.

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