PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Variablenübergabe zwischen RS232 und Atmega32



Natureengeneer
24.09.2012, 18:34
Hi liebe Forengemeinde, nun ist wieder ein Punkt erreicht andem ich eure Unterstützung benötige.
Für meinen Aquarien-/Terrarien-/Paludariumcontroller baue ich gerade ein kleines Interfaceprogramm mit VB2010Express, mit dessen Hilfe ich auch vom PC aus Einstellungen ändern möchte und diverse Sensorwerte Loggen will.
Meine Verbindung vom Atmega zur RS232 Schnittstelle funktioniert Hardwareseitig (getestet mit Hterminal), Sourcecode für UART von Peters lib.

Ich Frage mich nur wie ich das Programm so Grundsätzlich aufbauen muss, wenn ich z.b. an den uC über UARTden Befehl "Einstellungen einlesen" Sende und mir der uC die Variablen für die Einstellungen nacheinander an den PC Senden soll, da das meine ersten Versuche mit der RS232 Schnitstelle sind.

Stehe grad echt auf dem Schlauch :confused:


c = uart_getc();
if ( c & UART_NO_DATA )
{
/*
* no data available from UART
*/
}
else
{
/*
* new data available from UART
* check for Frame or Overrun error
*/
if ( c & UART_FRAME_ERROR )
{
/* Framing Error detected, i.e no stop bit detected */
uart_puts_P("UART Frame Error: ");
}
if ( c & UART_OVERRUN_ERROR )
{
/*
* Overrun, a character already present in the UART UDR register was
* not read by the interrupt handler before the next character arrived,
* one or more received characters have been dropped
*/
uart_puts_P("UART Overrun Error: ");
}
if ( c & UART_BUFFER_OVERFLOW )
{
/*
* We are not reading the receive buffer fast enough,
* one or more received character have been dropped
*/
uart_puts_P("Buffer overflow error: ");
}
/*
* send received character back
*/
uart_putc( (unsigned char)c );
}
Code in "C"

Lieben Dank schonmal.

Chris266
25.09.2012, 06:07
Hi,


Du solltest Dir zunächst eine Grundsätzliche Frage stellen: sollen die Daten zwischen Deinem Board und dem PC binär oder im ASCII Format übertragen werden?


a) binär
Bei einer binären Übertragung kannst Du viel mehr Messwerte pro Sekunde übertragen. Auch die Befehle vom PC zum Board sind einzelne Opcodes (Bytes). Benötigt allerdings eine spezielle Software auf der PC Seite die Du selbst schreiben musst.


b) im ASCII Format
Hierbei werden Messwerte und Befehle zum Board im Textformat übertragen. Dies hat den Vorteil, dass Du auch Hyperterminal zur Kommunikation nutzen kannst. Nachteil: viel langsamer als binär.


Grundsätzlich:
Deine Idee mit dem Echo auf Seiten des Boards zu implementieren ist gut. So kannst Du auf der PC Seite gleich sehen, ob die Bytes/Zeichen, die Du zum Board geschickt hast, korrekt angekommen sind.


Da Du offensichtlich zu dem Thema "Datenaustausch zwischen PC und Board" noch nicht viel Erfahrung hast, empfehle ich Dir Variante "b" - alleine schon um Hyperterminal nutzen zu können.


Wenn Variante "b" genutzt werden soll:
Es ist üblich, Kommandos vom PC an das Board mit "ENTER" abzuschließen, d.h. der ASCII Code 13 wird am Ende des Befehles gesendet.


D.h. auf Deinem Board müsstest Du auf das Byte 13 warten und erst dann die bis dahin empfangenen Zeichen auf einen bekannten Befehl hin zu vergleichen.


Beispiel eines Pseudocodes für das Board um Befehle zu erkennen:




if (<Zeichen empfangen>==TRUE)
{
SendeZeichenkette (<Empfangenes Zeichen>); // ECHO
if (<Empfangenes Zeichen>!=13)
{
// KEIN ENTER EMPFANGEN, ALSO BUCHSTABEN WEITER SAMMELN:
<Empfangene Zeichenkette> = <Empfangene Zeichenkette> + <Empfangenes Zeichen>;
}
else
{
// ENTER EMPFANGEN (Achtung: das ENTER wird nicht zur <Empfangenen Zeichenkette> hinzugefügt!
if (<Empfangene Zeichenkette>=="GET TEMP")
{
// Temperatur abfrage: d.h. Temperatur an den PC senden
SendeZeichenkette ("TEMP=");
SendeZeichenkette (itoa(iTemperature));
SendeZeichenkette (13);
<Empfangene Zeichenkette> = "";
}
else
if (<Erste Neun Zeichen der empfangenen Zeichenkette>=="SET TEMP ")
{
<Parameter Zeichenkette> = <von der empfangenen Zeichenkette die Zeichen ab der zehnten Stelle>;
atoi (<Parameter Zeichenkette>,&iParameterzahl); // ASCII Zahl in binär Zahl umwandeln. Bsp."23" in 23
if (iParameterzahl>=15) AND (iParameterzahl<=20)
{
SetzeTemperatur (iParameterzahl);
SendeZeichenkette ("OK");
}
else
{
SendeZeichenkette ("INVALID VALUE FOR SET TEMP. VALID VALUE = 15 .. 20");
}
SendeZeichenkette (13);
<Empfangene Zeichenkette> = "";
}
else
{
SendeZeichenkette ("INVALID COMMAND.");
SendeZeichenkette (13);
<Empfangene Zeichenkette> = "";
}
}
}

Beachte jedoch: der obige Pseudocode enthält noch keine Überprüfung ob die empfangenen Zeichen noch in den Empfangspuffer (<Empfangene Zeichenkette>) hineinpassen.

Ich empfehle Dir dringend, dies hier aufmerksam durch zu lesen:

http://www.rn-wissen.de/index.php/Avr-gcc#Darstellung_in_C
http://www.rn-wissen.de/index.php/C-Tutorial#Strings_.28Zeichenketten.29

Dort findest Du einige Hinweise zum Thema Strings in C.

Natureengeneer
27.09.2012, 00:15
Vielen Dank schonmal für die Tipps ich verstehe jetzt was ich zu tun habe nur happerts ein bisschen an der Umsetzung.

int index=0;
char ch[20];

while(c!=13)
{
ch[index]=c;
index++;
}
if (c==13)
{
index = 0;
}

Mein Versuch aber irgendwie machts ned so wie es soll ;)

Bumbum
27.09.2012, 08:00
Guten Morgen,



while(c!=13)


und



if (c==13)


schließt sich aus, das kann so nichts werden. ;-)

Viele Grüße
Andreas

Chris266
27.09.2012, 08:58
Dein Ansatz gefällt mir.
Es freut mich, dass Du selbst das Problem lösen willst, statt fertige Codeschnipsel
einzusetzen.


Deinem Code fehlt noch der Stringvergleich, d.h. die Prüfung auf empfangene Befehle.
Um Dir die Suche zu verkürzen: schau mal bei "strcmp" nach. Diese C Funktion vergleicht zwei Strings miteinander.


In Deinem Code hast Du "index" korrekt angewendet, lediglich eines hast Du übersehen:
C Strings sind Nullterminiert. D.h. am Ende eines jeden Strings muss das Byte 0 stehen.


Diese 0 muss selbstverständlich nicht immer am Ende eines Strings stehen, da Du bis zum Eintreffen der ENTER Taste (13) die Variable "index" nutzt, um die Stringlänge zu indizieren.


Deshalb ist es bei Deinem Codebeispiel erst dann notwendig, das Byte 0 an das Ende des Strings zu hängen, bevor Du die Funktion strcmp nutzt (denn diese Funktion verwendet nicht wie Du einen Index für die Identifikation der Stringlänge sondern sucht nach dem Byte 0).


Deshalb wäre es genug, wenn Du nach dem Empfang von 13 das 0 Byte an Deinen String anhängst.
Beispielsweise so:


if (c==13)
{
ch[index]=0;
index=0;
}


In die selbe if Bedingung müsstest Du nun den empfangenen String auf bekannte Befehle hin überprüfen.
Hierfür kannst Du strcmp nutzen. Ein Beispiel:


if (strcmp (ch,"GET TEMP")==0)
{
// Temperatur soll angefragt werden
}


Nähere Infos zu strcmp findest Du im Internet.


Ich vermisse in Deinem Code noch das Setzen von "c". D.h. "c" muss ja das empfangene Byte enthalten, was über die serielle Schnittstelle reinkommt.
Dies machst Du selbstverständlich in der While Schleife.


Bevor die While Schleife beginnt, solltest Du "c" unbedingt auf einen Wert ungleich 13 setzen, sonst wird beim nächsten Durchlauf nicht mehr in die While Schleife gesprungen, da "c" ja noch vom letzten Durchlauf 13 enthält.


Das "if (c==13)" kannst Du Dir eigentlich sparen, denn c ist immer 13, wenn die While Schleife verlassen wird (sofern Du kein break einsetzt).
Dein Compiler wird diese Zeile ohnehin löschen sofern er kein break in der While Schleife findet (die sogenannte Code Optimierung).


Wenn Du Deinen Code sicherer machen möchtest: sei so gut und prüfe die Anzahl der Empfangenen Zeichen auf die Anzahl der Zeichen, die in Deinen String "ch" hineinpassen.
Wie ich sehe hat dein Array/String platz für 19 Zeichen (das 20. enthält später das Byte mit dem Wert 0!).
ACHTUNG: die Zählung beginnt bei 0! D.h. index darf Werte von 0 bis einschließlich 19 annehmen.

Noch ein Hinweis zu Deiner While Schleife:
Du verwendest eine While Schleife zum Abfragen auf das Eintreffen der ENTER Taste.
Dies würde bedeuten, dein Prozessor ist solange mit nichts anderes beschäftigt als Zeichen zu empfangen, bis die ENTER Taste eintrifft.
Ansich nichts schlechtes, sofern Dein Prozessor keine Ausgänge ansteuert, wie beispielsweise die Aquarium Heizung.
D.h. solange die "13" nicht empfangen wurde, bleiben alle Ausgänge wie sie waren.
Sofern der Prozessor aber AUCH automatisch / selbstständig die Heizung oder Pumpe ein/ausschalten soll, ist diese vorgehensweise nicht klug.

Natureengeneer
27.09.2012, 15:32
Ja vielen Herzlichen Dank für diese tollen Informationen :p

Es läuft! :-)

Hier mal nen Beispiel falls jemand das gleiche Prob hat.

C Code:

int index=0;
char ch[19];
c = uart_getc();
if ( c & UART_NO_DATA )
{
/*
* no data available from UART
*/
}
else
{
/*
* new data available from UART
* check for Frame or Overrun error
*/
if ( c & UART_FRAME_ERROR )
{
/* Framing Error detected, i.e no stop bit detected */
uart_puts_P("UART Frame Error: ");
}
if ( c & UART_OVERRUN_ERROR )
{
/*
* Overrun, a character already present in the UART UDR register was
* not read by the interrupt handler before the next character arrived,
* one or more received characters have been dropped
*/
uart_puts_P("UART Overrun Error: ");
}
if ( c & UART_BUFFER_OVERFLOW )
{
/*
* We are not reading the receive buffer fast enough,
* one or more received character have been dropped
*/
uart_puts_P("Buffer overflow error: ");
}
/*
* send received character back
*/
uart_putc( (unsigned char)c );

if((unsigned char)c!=13)
{
ch[index]=c;
index++;
}
else
{
ch[index]=0;
index = 0;
if (strcmp (ch,"TEMP1")==0)
{
// Temperatur soll angefragt werden
uart_puts("Temperatur1=..........\n");
}
if (strcmp (ch,"TEMP2")==0)
{
// Temperatur soll angefragt werden
uart_puts("Temperatur2=..........\n");
}
if (strcmp (ch,"PH")==0)
{
// Temperatur soll angefragt werden
uart_puts("PH=..........\n");
}
if (strcmp (ch,"HUMIDITY")==0)
{
// Temperatur soll angefragt werden
uart_puts("Humidityr=..........\n");
}
ch[0]=0;



}

Chris266
27.09.2012, 18:54
Das ist ja großartig.

Ich möchte Dich noch auf ein paar Dinge hinweisen, wenn Du Wert auf ein stimmiges Programm legst (zumal Du Deinen Code auch anderen hier zur Verfügung stellst):

a) Im Falle eines Fehlers, beispielsweise UART_BUFFER_OVERFLOW, UART_OVERRUN_ERROR, UART_FRAME_ERROR wird dessen Wert (inkl. den UART_BUFFER... Bits) nochmals an den PC gesendet (Stichwort "Echo").
b) dein Prozessor ist unnötig belastet: er vergleicht die bisher empfangene Zeichenkette jedesmal wenn ein einzelnes Zeichen empfangen wurde. D.h. er wird einen Befehl zwei mal ausführen auch wenn Du kein ENTER gedrückt hast (aber nur wenn die Zeichenlänge identisch ist mit dem zuvor abgesetzten Befehl ist, bsp. TEMP1 und TEMP2. Mach Dir bitte die Mühe, und überprüfe den empfangenen String erst dann wenn tatsächlich ENTER empfangen wurde (zumal Du Deinen Code hier als Beispiel veröffentlicht hast)

Ich mag an dieser Stelle etwas pingelig klingen, aber glaub mir, viele unvorhergesehene Verhaltensweisen eines Roboters oder einer Steuerung haben mit unsachgemäßer Programmierung zu tun. Nicht um sonst gibt es den "Programmierer" als Beruf mit entsprechender Ausbildung.

Für ein solch' kleines Projekt ist es ggf. noch verschmerzbar, aber spätestens wenn es um das automatische Füttern Deiner Fische geht (wenn Du beispielsweise 3 Wochen in Urlaub bist), wäre es mehr als tragisch wenn deine Fische verhungern oder verkocht sind, weil die Heizung zu hoch heizt.

Die Erfahrung zeigt: was schief gehen kann, passiert auch. Es ist nur eine Frage der Wahrscheinlichkeit wann ein Bug im Programmcode zum tragen kommt.

Falls Du mir nicht glaubst, versuche folgendes:

a) sende an dein Board "TEMP1" und drücke ENTER
b) sende den Buchstaben "T" an Dein Board und es wird mit "Temperatur1=.......\n" antworten, noch bevor Du ENTER gedrückt hast

Sei so gut und mache Deine "if (strcmp" Anweisungen bitte in die Klammer von "if ((unsigned char)c==13)" hinein.

Wenn Du noch Fragen zum Code hast, poste einfach. Ich schau mir den gerne nochmal an.

Chris266
27.09.2012, 19:06
Möglicherweise habe ich etwas übersehen (wer schaut mal drüber?), aber so gefällt es besser:


int index=0;
char ch[19];

while (1)
{
c = uart_getc();
if ( c & UART_NO_DATA )
{
/*
* no data available from UART
*/
}
else
{
/*
* new data available from UART
* check for Frame or Overrun error
*/
if ( c & UART_FRAME_ERROR )
{
/* Framing Error detected, i.e no stop bit detected */
uart_puts_P("UART Frame Error: ");
ch[0]=0;
index=0;
}
else
if ( c & UART_OVERRUN_ERROR )
{
/*
* Overrun, a character already present in the UART UDR register was
* not read by the interrupt handler before the next character arrived,
* one or more received characters have been dropped
*/
uart_puts_P("UART Overrun Error: ");
ch[0]=0;
index=0;
}
else
if ( c & UART_BUFFER_OVERFLOW )
{
/*
* We are not reading the receive buffer fast enough,
* one or more received character have been dropped
*/
uart_puts_P("Buffer overflow error: ");
ch[0]=0;
index=0;
}
else
{
/*
* send received character back
*/
uart_putc( (unsigned char)c );

if((unsigned char)c!=13)
{
ch[index]=c;
index++;
}
else
{
ch[index]=0;
index = 0;
if (strcmp (ch,"TEMP1")==0)
{
// Temperatur soll angefragt werden
uart_puts("Temperatur1=..........\n");
}
else
if (strcmp (ch,"TEMP2")==0)
{
// Temperatur soll angefragt werden
uart_puts("Temperatur2=..........\n");
}
else
if (strcmp (ch,"PH")==0)
{
// Temperatur soll angefragt werden
uart_puts("PH=..........\n");
}
else
if (strcmp (ch,"HUMIDITY")==0)
{
// Temperatur soll angefragt werden
uart_puts("Humidityr=..........\n");
}
ch[0]=0;
}
}
}
}

PS: es fehlt immernoch die Abfrage, ob die Anzahl der Empfangenen Zeichen länger als 18 sind. Das 19. ist das "0" Byte.

Natureengeneer
27.09.2012, 20:38
Danke Danke habe den Fehler Korrigiert. :) Habs auch oben abgeändert. Die Abfrage ob der String länger ist bau ich dann später noch ein.

In VB2010 habe ich das Senden eines Befehls so angestellt:

Verbindungsbutton RS232:

Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Try
SerialPort1.PortName = TextBox12.Text
SerialPort1.BaudRate = TextBox13.Text
SerialPort1.DataBits = 8
SerialPort1.StopBits = IO.Ports.StopBits.One
SerialPort1.Parity = IO.Ports.Parity.None
SerialPort1.Handshake = IO.Ports.Handshake.None
SerialPort1.Open()
Button2.Enabled = False
Button1.Enabled = True
Button3.Enabled = True
Button4.Enabled = True
Button5.Enabled = True
Button6.Enabled = True
Button7.Enabled = True
ProgressBar1.Value = 100
MsgBox("Connected")
Catch ex As Exception
ProgressBar1.Value = 0
MsgBox("Connection Error 001")
End Try
End Sub


SendeButton:

Private Sub Button7_Click(sender As System.Object, e As System.EventArgs) Handles Button7.Click
Dim buffer(200) As Byte
Dim offset As Integer
Dim count As Integer
Dim returnValue As Integer
offset = 0
count = 17
SerialPort1.Write(vbCr)
SerialPort1.Write("TEMP1")
SerialPort1.Write(vbCr)
System.Threading.Thread.Sleep(1000)
Dim IncommingData = SerialPort1.ReadExisting
TextBox15.Text = IncommingData
End Sub