PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Brauche Hilfe beim UART



Kampi
28.08.2012, 19:23
Hallo Forum,

ich arbeite zur Zeit etwas an meinem CAN-Bus weiter.
Nun möchte ich, dass der Controller bestimmte Dinge automatisch einstellt sobald ein bestimmter Befehl gesendet wurde. Ein Beispiel:
Der Filter 0 von Buffer 0 soll von 1 auf 2 geändert werden. Dazu möchte ich z.B. den Befehl "Config B0 F0 2" per UART an den Controller senden und er soll es empfangen und in einen String packen.
Nach dem Empfangen des Strings soll er diesen auswerten und anschließend von Buffer 0 RXF0 mit 2 beschreiben.
Allerdings tu ich mich gerade beim Empfangen und Auswerten des Strings schwer -.-
Ich würde das gerne per UART_Interrupt erledigen, damit es möglichst wenig Einfluss auf das Programm hat. Das Ende des Befehls möchte ich am besten mit nem CR und nem LF, sprich 0x0D und 0x0A, kennzeichnen.
Hat jemand eine Idee wie man das am besten bewerkstelligen könnte?

Ich habe es schon so probiert:



Uart_recieved:
Index = Index + 1
Input_uart(Index) = Input_uart(Index) + Chr(UDR)


Aber die Lösung war nicht wirklich zufrieden stellend bzw. ich weiß jetzt nicht wie ich den String nach bestimmten Parametern trennen kann.
Danke für die Hilfe!

for_ro
28.08.2012, 19:40
Hallo Kampi,
ist dies hier Input_uart(Index) als String oder als Byte Array definiert?
Wenn du sagst, dass du den String nach bestimmten Parametern trennen willst, was meinst du damit genau? Zum Trennen hast du doch schon die Leerzeichen in dem übertragenen String. Kannst du die nicht dafür nutzen?
Ich würde mir auch nicht einen String im UART zusammenbauen, dass dauert meist viel zu lang.
Lege einfach ein Byte-Array über deinen String und speicher das gelesene Zeichen in das Array.

Input_string as string*20
Input_string_overlay as Byte(20) at Input_string Overlay
...
Uart_isr:
incr bytes_received
Input_string_overlay(bytes_received)=UDR
Return

Thomas E.
28.08.2012, 19:44
Hallo Kampi!


Meine "ClockTemp" für den Schreibtisch kann so etwas ähnliches. Dabei werden Befehle per Terminal über die UART an den Controller gesendet und ausgewertet. Die Terminalsteuerung übernimmt vollständig der Controller.
Das Prinzip funktioniert so: Wenn eine Taste gedrückt wird, wird das entsprechende Zeichen an den Controller gesendet. Dieser speichert es ab und gibt es an das Terminal wieder aus, damit es auch sichtbar ist. Wird ein CR empfangen (=ENTER auf der Tastatur) wird eine Auswertung gestartet, welche die gespeicherte Zeichenkette zerlegt und zuordnet. Wenn zuviele Zeichen eingegeben werden gibt es eine Fehlermeldung.

Bei meiner Methode muss allerdings der Befehl immer gleich aufgebaut sein. Zum Beispiel:
BEFEHL [leerzeichen] VARIABLE
Ein realer Befehl aus der ClockTemp-Firmware V1.3 lautet:
TIM 2034 --> setze die Uhrzeit auf 20.34 Uhr.

Die Variable nach dem Leerzeichen ist dabei allerdings optional. Ich verwende zum Beispiel STL (ohne Leerzeichen oder sonstiges, einfach nur mit ENTER abschließen) für die Statusausgabe (STL="Status list").

Eine weitere Besonderheit ist, dass der Befehl nur drei Zeichen lang sein darf. Es wird nämlich der String nach fixen Vorgaben zerlegt: Die ersten drei Zeichen sind der auszuführende Befehl, das Leerzeichen wird ignoriert und ab der fünften Stelle im String kommt eine Variable.

Ursprünglich wollte ich die Sache noch etwas weiter entwickeln, allerdings habe ich das Projekt inzwischen abgeschlossen und erfreue mich daran. Im Anhang findest du einen Auszug aus dem Quellcode. Ich habe diese Beschreibung aus Zeitmangel nur schnell zusammengeschrieben, da ich etwas unter Zeitdruck bin aber trotzdem mal einen Lösungsansatz liefern möchte - wenn du Fragen zu meiner Variante hast, dann frag bitte einfach. Warscheinlich ist das jetzt nicht genau die Lösung die du suchst, aber vielleicht konnte ich dir einen Denkansatz geben. Eventuell könnten wir die Sache auch gemeinsam angehen, da mich die Thematik auch interessiert.

Übrigens: Ich weiß, dass diese Eingabemethode vielleicht nicht unbeding elegant und sauber programmiert ist, sie funktioniert allerdings tadellos auf meiner Anwendung und erfüllt ihren Zweck.



Communication:

Led = 0
Buffer = Inkey() 'Variable "Buffer" mit dem empfangenen Zeichen laden
If Buffer <> Chr(13) Then 'Wenn "Buffer" KEIN ENTER ist, dann an "Eingabe" anreihen
Print Buffer; 'Ausgeben des angereihten Zeichens
Eingabe = Eingabe + Buffer
If Len(eingabe) = 9 Then 'Wenn Eingabe neun Zeichen lang ist, dann abbrechen
Eingabe = "" 'Eingabe = leer
Print
Print "TOO LONG..."
End If
Else 'Wenn "Buffer" ENTER beeinhaltet
Ein_command = Mid(eingabe , 1 , 3) '"Ein_command" = erste drei Zeichen
Ein_value = Mid(eingabe , 5) '"Ein_valaue" = Fünftes bis Achtes Zeichen
Eingabe = "" '"Eingabe" = leer
Print
End If
If Buffer = "°" Then 'Wenn Buffer "Grad"-Zeichen beeinhaltet
Eingabe = "" '"Eingabe" = leer
Print "ABORTED" '"ABORTED" ausgeben
Print "-----"
End If
If Ein_command = "tim" Then 'Eingabe "tim" (Neue Zeit)
Ein_command = Mid(ein_value , 1 , 2) 'Ein_command für Stunde missbrauchen
Stunde = Val(ein_command) 'Direktes Umwandeln der Zeichen
Ein_command = Mid(ein_value , 3 , 2) 'Ein_command für Minute missbrauchen
Minute = Val(ein_command) 'Direktes Umwandeln der Zeichen
Sekunde = 0 'Sekunde auf null setzen
Print "New Time: " ; Stunde ; ":" ; Minute 'Neue Zeit ausgeben
Print "-----"
End If
If Ein_command = "stl" Then 'Eingabe "stl" (Status-List)
Print "Status:"
Print "--------------------"
Print "Voltage in: " ; Volt_single ; "V"
Print "ADC-Value: " ; Volt
Print "Temp MSB: " ; Temp_h
Print "Temp LSB: " ; Temp_l
Print "Temp: " ; Temp_single
Print "Block: " ; Block
Print "Adress: " ; Adresse
Print "Space: " ; Speicher ; "%"
Print "Log-interval: " ; Intervall_soll ; "min"
Print "Interval-Ct: " ; Intervall_ist ; "min"
Print "-----"
End If
If Ein_command = "red" Then Gosub I2c_readtemp 'Eingabe "red" (Log-Lesen und ausgeben)
If Ein_command = "del" Then Gosub I2c_delete 'Eingabe "del" (Löschen)
If Ein_command = "low" Then 'Eingabe "low" (Anzeige letzter Spannungseinbruch)
Print "Last low voltage condition:"
Print "--------------------"
Readeeprom Stunde_read , 1
Readeeprom Minute_read , 2
Readeeprom Sekunde_read , 3
Readeeprom Volt_single , 4
Print Stunde_read ; ":" ; Minute_read ; ":" ; Sekunde_read
Print "Voltage: " ; Volt_single
Print "-----"
Volt_single = 10 'Notlösung: Volt_single auf 10 setzen, damit nicht der falsche Wert angezeigt wird
End If
If Ein_command = "int" Then 'Eingabe "int" (Neues Log-Intervall setzen)
Ein_command = Mid(ein_value , 1 , 3) 'Ein_command missbrauchen
Intervall_soll = Val(ein_command) 'Direktes Umwandeln der Zeichen aus Ein_command
If Intervall_soll >= 1 And Intervall_soll =< 99 Then 'Wenn Intervall innerhalb der zul. Werte
Print "Set new interval: " ; Intervall_soll ; "min"
Print "-----"
Else 'Wenn Intervall ausserhalb der zul. Werte
Print "Not allowed (only 1 to 99 minutes allowed)"
Print "Interval is set to 1 minute"
Print "-----"
Intervall_soll = 1
End If
Intervall_ist = 0
End If
If Ein_command = "inf" Then 'Eingabe "inf" (Info/Help)
Print "Info/Help"
Print "--------------------"
Print "[tim hhmm] : Set Time hh=hours, mm=minutes"
Print "[stl] : Status list"
Print "[red] : Read temperature-log"
Print "[del] : Delete temperature-log (means delete EEPROM)"
Print "[low] : Show last low-voltage-condition"
Print "[int mm] : Set interval for logging (1 to 99 minutes)"
Print "[inf] : You're here! ;-)"
Print "-----"
End If
Ein_command = "" 'Abschließend Ein_command = leer und
Ein_value = "" 'Ein_value = leer
Led = 1

Return

Kampi
28.08.2012, 19:52
Hallo,

danke für die Antworten.

@for_ro:
Die Variable war als

Input_UART(200) as String * 1

deklariert.
Die Geschichte mit dem Overlay hört sich aber besser an. Ich denke mal das übernehme ich so.
Danke für den Hinweis. Die Trennung durch das Leerzeichen........wieso zum Geier bin ich nicht dadrauf gekommen >.<.
Nochmals danke für den Tipp :D

@Thomas:
Danke für das Beispiel.
Ich schau es mir mal an. Ein identischer Aufbau der Befehle sollte nicht das Problem sein, aber da muss ich nochmal genauer drüber Grübeln.
Vom Prinzip hört sich das genau nach dem an was ich benötige.

Kleiner edit:
Ich weiß nicht ob ich die Funktion vom Overlay richtig verstanden habe.
Der Overlay legt jetzt einfach ein 20 Bytes langes 1 Byte Array über den String, sodass ich den String wie ein Array oder einen String behandeln kann.
Ist das so korrekt?

Thomas E.
29.08.2012, 19:09
@Thomas:
Danke für das Beispiel.
Ich schau es mir mal an. Ein identischer Aufbau der Befehle sollte nicht das Problem sein, aber da muss ich nochmal genauer drüber Grübeln.
Vom Prinzip hört sich das genau nach dem an was ich benötige.

Man könnte bestimmt auch den Eingangsstring nach einem Leerzeichen absuchen und dann die Stelle ermitteln, ab welcher ein anderer Teil des Befehls (zum Beispiel eine Variable) beginnt. Ich habe mich nur aus Speicherplatzgründen für die einfachere Variante entschieden.

Warscheinlich werde ich diese oder eine leicht abgewandelte Variante für ein in Planung befindliches Projekt benötigen, also wäre eine Weiterentwicklung von mir vielleicht eine gute Idee. Mal sehen. ;)

Kampi
30.08.2012, 00:00
Ich werde mal schauen wie ich das mache.
Ich habe heute festgestellt, dass es wohl Probleme gibt wenn ich nen Remoteframe sende und anschließend einen Datenframe.....muss erstmal das Problem lösen bevor es an den UART gehen kann :/

Kampi
30.08.2012, 10:42
Whuuuu es funktioniert :)
Habe jetzt erstmal nur ne Lösung mittels Input und Select Case aber ich kann die Frames problemlos nacheinander schicken :) :) :)
Jetzt kommt der UART dran :D

Thomas E.
30.08.2012, 17:02
Zeigst du uns deine Lösung? Würde mich interessieren, wie du es gemacht hast. :)

Kampi
30.08.2012, 17:13
Zeigst du uns deine Lösung? Würde mich interessieren, wie du es gemacht hast. :)

Es ist noch nicht die Lösung die ich haben will. Die Problemlösung bezog sich darauf das mein Programm für den MCP ein Problem damit hatte wenn ich erst nen Datenframe sende und anschließend einen Remoteframe. Ich dachte ich hätte das Problem gelöst aber es besteht immernoch :/
Den UART selber habe ich noch nicht bearbeitet.
Die Lösung war mehr eine Testlösung ^.^
Aber sobald ich was habe zeige ich sie euch.

Thomas E.
30.08.2012, 17:19
Hatte ich wohl irgendwie falsch interpretiert und gedacht du wertest bereits fleißig Strings aus... :)

Kampi
30.08.2012, 17:23
Hatte ich wohl irgendwie falsch interpretiert und gedacht du wertest bereits fleißig Strings aus... :)

Ne leider noch nicht :(
Die CAN-Controller zicken noch etwas rum -.-

Kampi
30.08.2012, 23:40
So kleines Update.....hab diesmal wirklich das Problem mit dem zickigen Controller gelöst ;)
Das hier ist mein erster Ansatz zum UART:



'ISR vom UART
Uart_recieved:

Uart_buffer = Udr 'Eingehendes Zeichen zwischenspeichern
If Uart_buffer <> Chr(13) Then 'Abfrage nach Enter
If Uart_buffer <> Chr(8) Then 'Abfrage nach Backspace
Print Chr(uart_buffer); 'Eingehendes Zeichen ausgeben
Input_uart_overlay(bytes_recieved) = Uart_buffer
Incr Bytes_recieved
Else
Decr Bytes_recieved 'Zähler um eins verringern
Input_uart_overlay(bytes_recieved) = "" 'Zeichen löschen
End If
End If

Return


In Kombination mit einer Select-Case Abfrage klappt das auch schonmal recht gut.
Allerdings muss ich noch dafür sorgen, dass der Zähler nicht unter 0 fallen kann (ist mir beim löschen des Strings aufgefallen, dass er ja tiefer als 0 zählt wenn man zu oft Backspace drückt ;) ) und ich muss den Kram aus der ISR rausnehmen.....ich denke ich werde da auch mit einem Flag arbeiten....ähnlich wie bei der MCP2515 ISR, sprich wenn ein Zeichen reinkommt wird ein Flag gesetzt und dieses Flag wird im Hauptprogramm abgefragt.
Oder ist diese Idee nicht so prall?
Wüsste nicht wie man die ISR sonst schlanker gestalten könnte, weil sie soll auch nicht zu lang werden :)

for_ro
31.08.2012, 12:14
Hallo Daniel,
noch einige Anmerkungen zu deinem Code:
Die Sicherheitsabfrage zur Verhinderung < 0 würde ich so machen:
If Bytes_recieved > 0 Then Decr Bytes_recieved 'oder > 1, falls dein Array bei 1 anfängt
Du kannst dann sooft Backspace drücken wie du willst, das nächste gültige Zeichen kommt immer in die erste Position.

Ich würde auch noch eine Abfrage auf die Länge des Arrays machen, damit dir ein zu langer Input nicht die nachfolgenden Speicherzellen/Variablen überschreibt. Länge dazu in eine Konstante und die sowohl beim Dim als auch beim Incr benutzen.

Bascom setzt dies richtig um, ich finde es aber seltsam:
Input_uart_overlay(bytes_recieved) = ""
Wenn du willst, dass ein Zeichen gelöscht wird, würde ich es durch den Bytewert 0 (=Stringende) überschreiben.
Auch das kann man wieder in eine konstante setzen, damit man später noch direkt sieht, was da gemacht wird.

Denke daran, wenn du ein Zeichen abspeicherst, dass du auf die nachfolgende Position eine 0 schreibst, also das Stringende festlegst.

Rein von der Verarbeitungszeit musst du dir überlegen, ob du das Print in der ISR lässt. Dass dauert.
Evtl. ein Flag setzen und irgendwann im Hauptprogramm ausdrucken. Darf natürlich nicht länger dauern, als bis dein nächstes Zeichen eintrifft.

Kampi
31.08.2012, 12:31
Hey,

danke für die Anmerkungen.
Ich glaube in Bascom fangen Arrays immer bei 1 an. Ich meine mich erinnern zu können das Bascom meckert sobald man von einem Array die 0. Stelle ausgeben will.
Ich werde sie mal umsetzen :)

for_ro
31.08.2012, 13:16
Ich glaube in Bascom fangen Arrays immer bei 1 an. Ich meine mich erinnern zu können das Bascom meckert sobald man von einem Array die 0. Stelle ausgeben will.
Du kannst seit einigen Versionen mit dem Befehl
Config Base = 0
Arrays auch bei 0 anfangen lassen. Ist manchmal ganz angenehm.

Kampi
31.08.2012, 13:30
Also so funktioniert es mit dem Backslash nicht:





'ISR vom UART
Uart_recieved:

Uart_buffer = Udr 'Eingehendes Zeichen zwischenspeichern
If Uart_buffer <> Chr(13) Then 'Abfrage nach Enter

If Uart_buffer <> Chr(8) Then 'Abfrage nach Backspace

Print Chr(uart_buffer); 'Eingehendes Zeichen ausgeben
Input_uart_overlay(bytes_recieved) = Uart_buffer
Incr Bytes_recieved

Else

Input_uart_overlay(bytes_recieved) = 0 'Zeichen löschen
If Bytes_recieved > 0 Then Decr Bytes_recieved 'Zähler um Eins Verringern

End If

Else

End If

Return



Oder meinst du das die Zeile an einer anderen Stelle stehen muss?

for_ro
31.08.2012, 20:28
Hallo Daniel,
du meinst Backspace (also ein Zeichen löschen und zurückgehen) und nicht Backslash, oder?
Hiermit schreibst du anschließend aber evtl. in Input_uart_overlay(0), wenn Bytes_recieved vorher =1 war
If Bytes_recieved > 0 Then Decr Bytes_recieved
Ich bin mir jetzt nicht sicher, wie die restliche Logik deines Programms ist, würde aber vermuten, dass du im Moment, wo ein Zeichen eingelesen werden soll gerade auf dem Stringende (=0) stehst. Dazu müsstest du die Reihenfolge der beiden Befehl umdrehen, also so:
If Bytes_recieved > 0 Then Decr Bytes_recieved 'Zähler um Eins Verringern
Input_uart_overlay(bytes_recieved) = 0 'Zeichen löschen
Oder was meinst damit, dass es nicht funktioniert?

Kampi
31.08.2012, 21:10
Hey,

ja ich meinte Backspace! Hab die beiden verwechselt.
Mit nicht funktionieren meine ich, dass er anscheinend immernoch irgendwie falsch zählt.
Aber ich setze mich morgen nochmal damit auseinander.

MagicWSmoke
01.09.2012, 01:52
Es ist völlig zweckfrei das Zeichen zu löschen, denn entweder überschreibst Du es das nächste Mal mit einem anderen Zeichen, da der Index dekrementiert wird, oder Du empfängst ein CR, woraufhin Du den Stringterminator "0" schreibst.
Und mach' mal ein "received" draus, das schmerzt ja im Auge :D

Thomas E.
01.09.2012, 10:38
Du kannst seit einigen Versionen mit dem Befehl
Config Base = 0
Arrays auch bei 0 anfangen lassen. Ist manchmal ganz angenehm.
Echt jetzt? Seit welcher Version geht das? :eek:

MagicWSmoke
01.09.2012, 12:16
Seit welcher Version geht das?
Siehe history.txt, seit 2.0.0.0

Thomas E.
01.09.2012, 12:23
Siehe history.txt, seit 2.0.0.0
Danke, da hab ich anscheinend was verpasst.