PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Optimieren von UART-Komunikation, bitte um Meinungen



JoeM1978
03.03.2013, 01:16
Ich habe für mein aktuelles Projekt Datenpakete per UART zu empfangen.

Vorgabe ist das empfangen von 16Byte für die weiterverarbeitung im Hauptprogramm.
Aktuell ist es so gelöst, das die Pakete jeweils mit einem vorgegebenen Startzeichen beginnen müssen.
Diese sind dann entweder bis 16Byte begrenzt oder werden durch ein vorgegebenes Endzeichen abgeschlossen.

Ich möchte mir damit eine art Universelle Übertragung schaffen, die ich für weitere Projekte einfach anpassen kann.

Der Code sieht so aus:

'-------------------------------------------------------------------------------
'Variablen
'-------------------------------------------------------------------------------
Dim Temp_byte As Byte
Dim Datenblock(16) As Byte
Dim Indexposition As Byte
Dim Startzeichen As String * 1
Startzeichen = "{"
Dim Endzeichen As String * 1
Endzeichen = "}"
Dim Datenblock_komplett As Bit
Dim Datenblock_laenge As Byte
Datenblock_laenge = 16

Dim Datenblock_string As String * 16
'-------------------------------------------------------------------------------
'Hauptprogramm
'-------------------------------------------------------------------------------

Do

If Datenblock_komplett = 1 Then
For Indexposition = 1 To Datenblock_laenge Step 1
Insertchar Datenblock_string , Indexposition , Datenblock(indexposition)
Next
Print Datenblock_string
Datenblock_string = ""
Datenblock_komplett = 0
End If

Loop

End


'-------------------------------------------------------------------------------
'Interrupt
'-------------------------------------------------------------------------------

Serial0charmatch:

While Ischarwaiting() = 1
Temp_byte = Inkey()
waitus 2000
If Temp_byte = Asc(startzeichen) Then
Indexposition = 0
Datenblock_komplett = 0
Elseif Temp_byte = Asc(endzeichen) Then
Datenblock_komplett = 1
Else
Indexposition = Indexposition + 1
Datenblock(indexposition) = Temp_byte
End If
If Indexposition = Datenblock_laenge Then
Datenblock_komplett = 1
Clear Serialin
End If
Wend

If Indexposition < Datenblock_laenge Then
Do
Indexposition = Indexposition + 1
Datenblock(indexposition) = 0
Loop Until Indexposition = Datenblock_laenge
Datenblock_komplett = 1
End If

Return


Funktioniert soweit auch zufriedenstellend.
Kurz noch Beispiele...

{1234567890123456} ergibt 1234567890123456
{123456 ergibt 123456
{123456} ergibt 123456
123456 ergibt "nix" (weil kein Startzeichen)

Allerdings möchte ich gerne eure Meinung hören, inwieweit es da zu Problemem kommen kann,
oder ob man das noch optimieren könnte (speziell wegen der Abarbeitungszeit im Interupt).

Derzeit wird per Kabel übertragen... soll aber die nächsten Tage durch ein Funkmodul (APC220) ersetzt werden.

P.S. ... sorry, das es nicht auskommentiert ist... sollte aber trotzdem leicht überschaubar sein.

peterfido
03.03.2013, 13:46
Start, Endzeichen und Datenblocklänge würde ich als Konstante hinterlegen. Spart Laufzeit.

JoeM1978
03.03.2013, 18:36
Joa... hast du recht. werd ich noch anpassen.

Generell geht es mir aber mehr um das einlesen der Bytes in das Array... und späther um das Entschlüsseln
in einzelne "Komandos" ... bzw was ich da jetzt schon für Probleme vermeiden könnte.

In die Gegenrichtung (also senden) werde ich ebenso ein System einfügen mit vermutlich16byte-paketen.
Damit werden dann sensordaten zum pc (zur auswertung) gesendet.

EDIT:
Nachdem gestern das Programm im test funktionierte... heute auf einmal nicht... musste ich fehlersuche betreiben.
Anscheinend ist die Routine für meine derzeitige Baudrate (9600) ZU SCHNELL.
Ich hatte im test hier 2 printbefehle drinn. Dann hats funktioniert.

Also... Ich habs getestet... es werden rund 1500 bis 2000 us Wartezeit benötigt bis überhaupt die Zeichen aus dem
Serial-buff verwendbar sind. Dann geht es wieder ;-)

peterfido
03.03.2013, 20:57
Das ist mir noch nicht passiert. Allerdings arbeite ich mit ischarwaiting() in der Hauptschleife. Die Interruptroutine nutze ich nur bei Softuart. Pausen brauchte ich bisher aber nie einbauen.

MagicWSmoke
03.03.2013, 21:56
inwieweit es da zu Problemem kommen kann
Zitat Bascom Hilfe:

When using the BYTEMATCH option, you must preserve the registers you alter.

oder ob man das noch optimieren könnte
Ich versteh nicht so recht, was das werden soll, aber 2ms in einem Interrupt zu warten, funktioniert gar nicht.
Ist da vielleicht ein BYTEMATCH = ALL in der Config drin ? Dann führt der Code in Verbindung mit dem gepufferte Serialin in's Leere, kurz macht die ganze Sache sinnlos.
Da baut man sich besser selbst 'ne Routine auf Basis des URXC-Interrupts auf.

Anscheinend ist die Routine für meine derzeitige Baudrate (9600) ZU SCHNELL.
Halte ich für ein Gerücht, wenn der Code nur mit Delay funktioniert, ist der Wurm drin.
Mein Gefühl von der ganzen Konstruktion ist, die ist reif für die Tonne :D

Hambone
04.03.2013, 07:52
Ich kann dir nur empfehlen, die Lib von P.Fleury zu benutzen. Das spart dir viel Ärger und Sucherei und die Lib funktioniert.

http://homepage.hispeed.ch/peterfleury/avr-software.html

Gruss Hambone

Michael
04.03.2013, 08:39
Hallo Hambone,

Ich kann dir nur empfehlen, die Lib von P.Fleury zu benutzen.
kannst du bitte noch kurz erklären, wie man die in Bascom einbindet?

@Joe:

Ist da vielleicht ein BYTEMATCH = ALL in der Config drin ?
wenn du deinen ganzen Code posten würdest, bräuchte man nichts vermuten.

Gruß, Michael

Hambone
04.03.2013, 16:56
Oh, das weiss ich nicht, da ich Bascom nicht benutze. Ich arbeite mit GCC. GCC ist auch nicht schwerer als Basic. Ich habe früher mit verschiedenen Basic-dialekten gearbeitet und finde C auf Dauer besser, wenn auch an manchen Stellen gewöhnungsbedürftig (wie Kurzschreibwesen i++ --> i=i+1, wobei das noch harmlos ist.) . Aber es müsste im Forum etwas ähnliches geben. Der Vorteil der Fleury Lib ist, daß die von vielen Usern getestet ist und funktioniert.
Ich kann mich erinnern, dass es früher bei Turbo-Pascal die Möglichkeit gab, C- und / oder Assembler Routinen in den Code einzubinden möglicherweise auch in Bascom?

Gruss Hambone

peterfido
04.03.2013, 17:28
Inline ASM geht. C-Code lässt sich nicht so einfach einbinden. Ich habe unter Bascom mit dem UART keinerlei Probleme. Das hier beschriebene Problem kann überall liegen, da der Code nicht komplett da steht. Auch möglich, dass der AVR ins Nirvana läuft und dann neustartet und daher die Ausgabe passt.

Ich nutze wie gesagt die IsCharWaiting() Methode ohne Interrupts. Evlt. noch buffered Empfang einstellen. Dann geht nichts verloren. Mein X300T Programm lässt nach dem Starten erstmal eine Meldung mehrere Sekunden übers Display laufen bevor es in die Mainloop geht und trotzdem kann ich einfach schon Daten losschicken. Da geht nichts verloren.

JoeM1978
04.03.2013, 17:43
So... der komplette code wäre dann:
Es ist ohne diese Verzögerung in serial0charmatch.

Das Funkmodul ist an sich schon in betrieb und ich kann dann wahlweise über Kabel oder Funk die Verbindung testen.

Der Hinweis auf die Register ist von mir noch nicht geprüft...
Da muss ich mich erst noch bissl einarbeiten... hab mich mit den Registern noch nicht wirklich beschäftigt. :rolleyes:

Was ich auch etwas seltsam finde...
Wenn ich vom pc aus den port öffne und sende... ok. kommt an wie gedacht"... wenn ich den port aber trenne und
ein zweites mal öffne sind irgendwelche "zeichen" im buffer, die mir das ganze array zerschiessen.
wird beim trennen des ports auf der tx/rx-leitung noch was gesendet ??



'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'Programmname: UART-Schnittstelle.bas
'Letzte Änderung: 03.03.2013
'Funktion: Empfang von gesamt 18Byte,
' ausgabe als echo über seriell und lcd zur kontrolle
'Mikrocontroller: Mega8
'
'Input:
'Output:
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'Den Atmega mit den passenden Daten füttern.
'-------------------------------------------------------------------------------
$regfile = "m8def.dat" 'eingesetzter Mikrocontroller
$crystal = 8000000 'eingestellte Taktfrequenz (8MHz)
$hwstack = 100 'Standardwert
$swstack = 100 'Standardwert
$framesize = 100 'Standardwert
$baud = 9600

'-------------------------------------------------------------------------------
'Ports/Pins/Configs
'-------------------------------------------------------------------------------

'-------------------------------------------------------------------------------
'Timer/OCR/PWM/ISR usw. setzen
'-------------------------------------------------------------------------------
Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Config Serialin = Buffered , Size = 18 , Bytematch = 123 '123={
Enable Interrupts

'-------------------------------------------------------------------------------
'Variablen
'-------------------------------------------------------------------------------
Dim Temp_byte As Byte
Dim Datenblock(16) As Byte
Dim Indexposition As Byte
Dim Startzeichen As String * 1
Startzeichen = "{"
Dim Endzeichen As String * 1
Endzeichen = "}"
Dim Datenblock_komplett As Bit
Dim Datenblock_laenge As Byte
Datenblock_laenge = 16
Dim Lcd_position As Byte
Dim Datenblock_string As String * 16

'-------------------------------------------------------------------------------
'Hauptprogramm
'-------------------------------------------------------------------------------
Cls
Waitms 100

Do
If Datenblock_komplett = 1 Then
'UART-Datenblock in String füllen
For Indexposition = 1 To Datenblock_laenge Step 1
Insertchar Datenblock_string , Indexposition , Datenblock(indexposition)
Next
Disable Interrupts
'Ausgabe/Abarbeiten des UART-Datenblock
Cls
Waitms 100
For Indexposition = 1 To 3 Step 1
Lcd_position = Indexposition
Locate 1 , Lcd_position
Lcd Chr(datenblock(indexposition))
Next
For Indexposition = 3 To Datenblock_laenge Step 1
Lcd_position = Indexposition - 8
Locate 2 , Lcd_position
Lcd Chr(datenblock(indexposition))
Next
'Echo zurücksenden zur Kontrolle
Print Datenblock_string
Datenblock_string = ""
Datenblock_komplett = 0
Enable Interrupts
End If
Loop
End

'-------------------------------------------------------------------------------
'Interrupt
'-------------------------------------------------------------------------------

Serial0charmatch:

While Ischarwaiting() = 1
Temp_byte = Inkey()
Waitus 2000

Locate 2 , 13
Lcd Chr(temp_byte)
Wait 1

If Temp_byte = Asc(startzeichen) Then
Indexposition = 0
Datenblock_komplett = 0
Elseif Temp_byte = Asc(endzeichen) Then
Datenblock_komplett = 1
Else
Indexposition = Indexposition + 1
Datenblock(indexposition) = Temp_byte
End If
If Indexposition = Datenblock_laenge Then
Datenblock_komplett = 1
Clear Serialin
End If
Wend

If Indexposition < Datenblock_laenge Then
Do
Indexposition = Indexposition + 1
Datenblock(indexposition) = 22
Loop Until Indexposition = Datenblock_laenge
Datenblock_komplett = 1
Clear Serialin
End If

Return

Das bytematch ist 123 (also wie das Startzeichen { )

Nachtrag: ... bei dem jetzigen code... wenn ich die verzögerung rausnehm...

Ich sende {xyz1234567890123}
es kommt die ersten 2 vorgänge nur "leerstellen" ins array... ab dem dritten dritten mal ist dann
"y} " im Array

Besserwessi
04.03.2013, 18:32
Eine Wartezeit wie ein Waitus 2000 sollte nicht im Interrupts sein. Auch die Ausgabe auf das LCD ist ggf. ein Problem, auch wenn das wohl eher eine HIlfe zur Fehlersuche ist. So wie es jetzt gemacht ist, wird die Wartezeit gebraucht damit in der Zeit die zur Verarbeitung von 1 byte schon das Nächste byte ankommt. Entsprechend sollte die Wartezeit zusammen mit der Laufzeit auf die Bautrate abgestimmt sein. Dabei kann der recht langsame LCD Befehl ggf. die extra Wartezeit auch ganz ersetzen, was vor allem Zeigt das man LCD(...) in der ISR vermeiden sollte.

Die Interrupt Routine sollte so schnell sein, das immer nur 1 Byte verarbeitet wird - die while Schleife ist also eigentlich überflüssig. Damit muss natürlich dann auch die Abfrage auf das vorzeitige Ende eines Datenblocks anders gelöst werden, z.B. in die IF Abfrage mit ASC(endzeichen).

JoeM1978
04.03.2013, 18:42
Was an sich meine erste Vermutung dann bestätigen würde...
nämlich das die abarbeitung zu schnell abläuft und das nächste zeichen noch garnicht im Buff vorliegt.

Wenn das dann so stimmt weiss ich ja, wo ich ansetzen muss...

peterfido
04.03.2013, 19:42
Da ist auch ein Wait 1 in der isr...

So würde ich es wohl probieren: (ungetestet, da nur Laptop hier und keine Hardware)


'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'Programmname: UART-Schnittstelle.bas
'Letzte Änderung: 03.03.2013
'Funktion: Empfang von gesamt 18Byte,
' ausgabe als echo über seriell und lcd zur kontrolle
'Mikrocontroller: Mega8
'
'Input:
'Output:
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'Den Atmega mit den passenden Daten füttern.
'-------------------------------------------------------------------------------
$regfile = "m8def.dat" 'eingesetzter Mikrocontroller
$crystal = 8000000 'eingestellte Taktfrequenz (8MHz)
$hwstack = 40 'Standardwert
$swstack = 40 'Standardwert
$framesize = 20 'Standardwert
$baud = 9600


'-------------------------------------------------------------------------------
'Ports/Pins/Configs
'-------------------------------------------------------------------------------

'-------------------------------------------------------------------------------
'Timer/OCR/PWM/ISR usw. setzen
'-------------------------------------------------------------------------------
Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Config Serialin = Buffered , Size = 18
Enable Interrupts

'-------------------------------------------------------------------------------
'Variablen
'-------------------------------------------------------------------------------
Dim Temp_byte As Byte
Dim Datenblock(16) As Byte
Dim Indexposition As Byte
Const Startzeichen = 123
Const Endzeichen = 125
Const Datenblock_laenge = 16
Dim Datenblock_komplett As Bit
Dim Lcd_position As Byte
Dim Datenblock_string As String * 16
Dim Datenblock_byte(16) As Byte At Datenblock_string Overlay

'-------------------------------------------------------------------------------
'Hauptprogramm
'-------------------------------------------------------------------------------
Cls
Waitms 100

Do
If Ischarwaiting() = 1 Then
Gosub Empfangen
End If
If Datenblock_komplett = 1 Then
Reset Datenblock_komplett
Gosub Anzeige
End If
Loop
End

Anzeige:
Cls
Waitms 100
For Indexposition = 1 To 3 Step 1
Lcd_position = Indexposition
Locate 1 , Lcd_position
Lcd Chr(datenblock(indexposition))
Next
For Indexposition = 3 To Datenblock_laenge Step 1
Lcd_position = Indexposition - 8
Locate 2 , Lcd_position
Lcd Chr(datenblock(indexposition))
Next
'Echo zurücksenden zur Kontrolle
Print Datenblock_string


Return


Empfangen:
Temp_byte = Inkey()
If Temp_byte > 12 Then 'unter 13 sind nur Sonderzeichen
If Temp_byte = Startzeichen Then
Indexposition = 0
Elseif Temp_byte = Endzeichen Then
Gosub Auswerten
Else
Incr Indexposition
Datenblock(indexposition) = Temp_byte
End If
If Indexposition = Datenblock_laenge Then
Gosub Auswerten
End If
End If

Return

Auswerten:
Dim Dummy As Byte
Datenblock_string = String(datenblock_laenge , 0)
Dummy = Memcopy(datenblock(1) , Datenblock_byte(1) , Indexposition)
Datenblock(1) = String(datenblock_laenge , 22) 'Wozu?
Indexposition = 0
Set Datenblock_komplett
Return

JoeM1978
04.03.2013, 21:13
Als erstes danke ich euch mal für eure Mühe...
und natürlich auch für den vorgelegten Code.

das...

Datenblock(1) = String(datenblock_laenge , 22) 'Wozu?

War dazu gedacht, das ich auch kurze Messages senden kann und der rest mit "lückenfüller" neutralisiert wird.

Hab den Code von dir versucht... bis auf die Ausgabe auf lcd ist das anscheinend funktionierend.
Wobei das LCD eh nur für Testzwecke dranhängt bis die Komunikation fehlerlos steht.
Das LCD ist aber schnell angepasst.

Was mir gedanken macht daran... sollte aus irgendeinem Grund 2 oder mehr Datenblöcke kurz nacheinander gesendet werden
und das hauptprogramm hatte noch keine zeit den Buffer auszulesen...
Wird dann alles, was zuviel gesendet wird einfach verworfen ?

Dann sollte ich mir sozusagen ein "manuelles handshake" einbauen... das die pc-software weiss,
wann der client wieder empfangsbereit ist.

Das war der ursprüngliche grund, weshalb ich den Buffer "so schnell wie möglich" auslesen wollte mit der ISR.

MagicWSmoke
04.03.2013, 21:15
Was an sich meine erste Vermutung dann bestätigen würde...
nämlich das die abarbeitung zu schnell abläuft und das nächste zeichen noch garnicht im Buff vorliegt.
Wie schon gesagt, das ganze Konzept ist kompletter Käse, Du hast das Prinzip Interrupt-gesteuerter Verarbeitung überhaupt nicht verstanden.
Der BYTEMATCH ist normalerweise dafür vorgesehen, das Ende einer Übertragung zu erkennen. Es ist nicht dafür gedacht den Anfang zu erkennen und dann solange in der ISR rumzurödeln und alles andere zu blockieren, bis man die Zeichenkette empfangen hat. Wofür soll dann der gepufferte Empfang überhaupt noch gut sein?
Das hier ist jetzt in ein paar Minuten entstanden und sollte eigentlich funktionieren. "Eigentlich", weil nicht auf Hardware getestet, aber vielleicht siehst Du die Idee.

$regfile = "m8def.dat"
$crystal = 8000000
$hwstack = 100
$swstack = 100
$framesize = 100
$baud = 9600

Const block_wait = 0
Const block_receive = 1
Const block_length = 16
Const False = 0
Const True = 1

Dim rx_state As Byte
Dim buff(block_length) As Byte
Dim data_block(block_length) As Byte
Dim db_index As Byte
Dim rcvd_char As Byte
Dim rcv_complete As Bit
Dim tmp As Byte

rx_state = block_wait
db_index = 0
rcv_complete = False

On URXC UART_Get_Char
Enable URXC
Enable Interrupts

Do
If rcv_complete = True Then
Printbin data_block(1)
rcv_complete = False
End If
Loop

UART_Get_Char:
rcvd_char = UDR
Select Case rx_state
Case block_wait:
If rcvd_char = "{" Then rx_state = block_receive
Case block_receive:
If rcvd_char <> "}" Then
If db_index < block_length Then
Incr db_index
buff(db_index) = rcvd_char
Else
db_index = 0
rx_state = block_wait
End If
Else
If db_index > 0 Then
tmp = memcopy(buff(1) , data_block(1) , db_index)
rcv_complete = True
End If
db_index = 0
rx_state = block_wait
End If
End Select
Return

peterfido
05.03.2013, 14:45
Was mir gedanken macht daran... sollte aus irgendeinem Grund 2 oder mehr Datenblöcke kurz nacheinander gesendet werden
und das hauptprogramm hatte noch keine zeit den Buffer auszulesen...
Wird dann alles, was zuviel gesendet wird einfach verworfen ?

Bei 9600 Baud und 8 MHz langweilt der sich zwischendurch. Da geht normal nichts verloren. Wenn du aus der Hauptschleife Subs aufrufst, welche lange Pausen beinhalten, oder die Pausen gar in die Hauptschleife legst, dann besteht die Gefahr, dass der Buffer überläuft. Ich teste die Geschwindigleit meiner Programme gern mit einem blinkenden Herz auf dem LCD. Alle 1000 Durchläufe wird das Herz dargestellt oder ausgeblendet. Selbst meine umfangreichen Projekte kommen da gefühlt auf 1000 Durchläufe die Sekunde. Handshaking nutze ich selbstgemacht. Wenn der Raspberry was zum AVR schickt, dann wird eine Antwort passend zum gesendeten erwartet. Kommt diese, aus welchen Gründen auch immer, nicht, dann wird das zuletzt gesendete wiederholt. Da beißt sich auch nichts. Mein Protokoll berücksichtigt auch bereits gestartete Übertragungen, z.B. wenn die Fernbedienung oder eine Taste am Gerät "was zu melden hat".

Ich sende dafür nicht aus jeder Sub direkt per print, sondern schreibe die Daten in einen Sendepuffer. Jeder Durchgang der Hauptschleife ruft eine Sub auf, welche prüft, ob es was zu senden gibt und sendet dann chronologisch nach FIFO art. Der Raspberry macht es ähnlich. Da nutze ich zum Senden ein eigenes C-Programm, welches prüft, ob es evtl. schon in einer anderen Instanz läuft und falls ja, wird eine Pause eingelegt. Meine Tests mit 100 "gleichzeitigen" Aufrufen liefen fehlerfrei. Soviel kommen in der Realität aber gar nicht vor.

JoeM1978
05.03.2013, 18:50
Bei 9600 Baud und 8 MHz langweilt der sich zwischendurch

... und genau das hatte ich anfangs komplett falsch eingeschätzt.
ich war erst der meinung, "die paar" Zeichen liegen schon im Buff, bis die erste schleife der isr durch ist. *blöderweise
Ist aber ne bekannte schwäche bei mir. *lach

Wenn man dann das Geschwindigkeitsproblem betrachtet ist auch ganz klar das Bytematch
(wie MagicWSmoke sagte) genau in eine andere Richtung "korrekt" verwendet werden muss.

Der Ansatz, es über Bytematch zu lösen ist an sich nicht schlecht... aber ich muss erst den Buffer füllen lassen...
und danach erst das Auslesen beginnen.

Ich finds aber immerwieder schön was zu lernen. Am besten passiert das natürlich aus eigenen Fehlern :D

Wo ich gerade bei Bytematch bin...
Was ist der Sinn dahinter, das man Register per Pushall behandeln muss bei Verwendung von Bytematch ?
Ich konnte das aus der Bascom-hilfe nicht wirklich erkennen.
Auch konnte ich nicht finden, welche register denn wirklich dafür verwendet werden.

Wäre schön, wenn da jemand knappe Infos oder nen Link hätte damit ich das nachlesen kann.

MagicWSmoke
05.03.2013, 19:04
Was ist der Sinn dahinter, das man Register per Pushall behandeln muss bei Verwendung von Bytematch ?
Die Serialcharmatch ist nichts anderes als die Verlängerung des URXC-Interrupts. Zuerst schreibt Bascom das angekommene Byte in den Puffer, danach wird Serialcharmatch ausgeführt. Bascom sichert für den URXC seine im Interrupt verwendeten Register, aber nicht solche aus Usercode. Wird ASM benutzt, dann ist's sehr einfach, die Register sind dann bekannt, bei Basic-Code eher weniger, dann müsste man alle verwendeten Register in Erfahrung bringen und diese sichern, das Erkennen ist eher schwierig. Deshalb der Rat zu Pushall/Popall.

JoeM1978
05.03.2013, 19:19
OK. das ist also...

Empfang (urxc) -> Buffer -> auf Byte/Char-match kontrollieren durch den MC (interne ISR) -> erst bei Treffer in die "User"-ISR springen

... aber ist mir noch nicht ganz verständlich aus welchem Grund die Register dazu gesichert werden müssen.
Würde es sonst zu Überschneidungen kommen beim Empfangsbuffer während intern die routine abläuft ?

MagicWSmoke
05.03.2013, 19:33
Weil jeder Interrupt, auch der URXC, mitten im normalen Code unterbrechen kann, müssen alle von einer Interruptroutine veränderten Register gesichert werden, sonst gibt's Verhau.
Der Code für den gepufferten Empfang macht das für die von ihm veränderten Register. Sobald aber User-Code in diese URXC mit "eingehängt" wird, können weitere Register verändert werden.

JoeM1978
05.03.2013, 19:55
@MagicWSmoke
Hab mir gerade deinen code genauer angeschaut ... das "rcvd_char = UDR" muss ich glaub ersetzen durch "rcvd_char = inkey()"
und
"Incr db_index" ... soweit ich mich erinner gibts kein "Incr (Var)" in Bascom. Da fall ich selber immerwieder drauf rein.

OK muss mich selber berichtigen... Incr (Var) ... gibt es doch... es war i++ was nicht geht.

MagicWSmoke
05.03.2013, 20:01
Hab mir gerade deinen code genauer angeschaut ... das "rcvd_char = UDR" muss ich glaub ersetzen durch "rcvd_char = inkey()"
Wie kommst Du darauf?

JoeM1978
05.03.2013, 20:13
OK. habs in der Bascom-hilfe dann letztendlich doch gefunden UDR ist das Data-Register vür UART.
Also lese ich damit direkt aus dem Register.

Was mich stutzig macht... mit "On Urxc Uart_get_char" im Header spuckt er mir ne fehlermeldung aus,
wenn ich serialin configurieren will...
liest er dann also jedes einzelne zeichen sofort über die isr aus ?
Dann ist also hier nix mit Buffer verwenden drinn wenn ich das recht sehe.

MagicWSmoke
05.03.2013, 20:21
OK. habs in der Bascom-hilfe dann letztendlich doch gefunden UDR ist das Data-Register vür UART.
Das würdest Du auch im Datenblatt finden.

Was mich stutzig macht... mit "On Urxc Uart_get_char" im Header spuckt er mir ne fehlermeldung aus,
wenn ich serialin configurieren will...
Klar, verwendet ja auch den selben Interrupt.

liest er dann also jedes einzelne zeichen sofort über die isr aus ?
Dann ist also hier nix mit Buffer verwenden drinn wenn ich das recht sehe.
Sobald ein Zeichen eintrifft, wird die ISR aufgerufen, das UDR gelesen und passend verarbeitet, gepuffert wird's in buff().
Das ist genauso eine gepufferte Empfangsroutine, wie sie Bascom per gepufferten Serialin aufbaut, nur eben darauf spezialisiert, einen bestimmten Datenblock zu empfangen.

JoeM1978
05.03.2013, 20:26
ok... also ist das sozusagen wie der gepufferte Empfang... nur eben "manuell".

Dann muss ich aber nochmal was hinterfragen...
bei 9600baud, meinst da könnte es passieren, das während dieser "manuellen Bufferroutine" ein zweites Zeichen schon ankommt ?
Würde das in einer "warteschleife hängen oder einfach verworfen werden ?

MagicWSmoke
05.03.2013, 20:41
ok... also ist das sozusagen wie der gepufferte Empfang... nur eben "manuell".
Ich würd' eher sagen eine andere Version des gepufferten Empfangs.

bei 9600baud, meinst da könnte es passieren, das während dieser "manuellen Bufferroutine" ein zweites Zeichen schon ankommt ?
Keine Chance :D

9600 Baud sind 9600 Bits pro Sekunde, ein Zeichen sind mit Start- und Stopbit rund 10 Bits, also sind's rund 960 Zeichen pro Sekunde, das sind etwas mehr als 1 mS für ein Zeichen.
In dieser Zeit hat der µC ca. 8000 Takte zur Verfügung, meine gepufferte Routine braucht so ca. 140-200 Takte pro Aufruf.

Bei 400000 Baud würdest Du an Grenzen stoßen.

Würde das in einer "warteschleife hängen oder einfach verworfen werden ?
Ohne Handshake gehen Zeichen verloren, wenn der UART-Puffer voll ist und nichts mehr aufnehmen kann.

JoeM1978
05.03.2013, 20:46
joa... und da die routine eh sofort bei jedem zeichen aufgeht sollte das kein problem sein.

Jetzt muss ich nur aufpassen, das ich bei anderen ISR keine gravierenden Verzögerungen reinbekomm.
Denn dann könnte es doch seltsame auswirkungen haben, je nachdem welche ISR höherer priorität hat.

Betreffs deinem Code... die Array buff() und data_block() ...
Gibts eine schnellere möglichkeit ein Array zu löschen als eine For_Next-Schleife ?
Denn wenn ich unterschiedlich lange Messages Empfange wäre es schon störend, wenn der Inhalt der vorherigen noch drinn steht.

MagicWSmoke
05.03.2013, 20:57
Denn dann könnte es doch seltsame auswirkungen haben, je nachdem welche ISR höherer priorität hat.
Sobald eine ISR aktiv ist, sind alle anderen gesperrt, d.h. eine Endlosschleife in einer anderen ISR kann alles blockieren.
Eine Priorität gibt's nur dann, wenn mehrere Interruptanforderungen anstehen und Interrupts erlaubt sind, dann wird derjenige mit höchster Priorität zuerst ausgeführt.

Gibts eine schnellere möglichkeit ein Array zu löschen als eine For_Next-Schleife ?
Memcopy() mit entsprechender Option ist recht schnell. Aber das ist unnötig, stört doch nicht, wenn da was drinsteht. Du musst nur wissen, wie lange die Nutzdaten tatsächlich sind.
Wenn Du hier die Länge in eine zusätzliche Variable kopierst, weißt Du das immer.

tmp = memcopy(buff(1) , data_block(1) , db_index)
data_block_len = db_index

JoeM1978
05.03.2013, 21:15
Eine Priorität gibt's nur dann, wenn mehrere Interruptanforderungen anstehen und Interrupts erlaubt sind, dann wird derjenige mit höchster Priorität zuerst ausgeführt.
Dann ist also innerhalb der ISR ein "Disable Interupts"... und
ind der letzten Zeile "Enable Interupts" sowieso nutzlos, da das automatisch gesperrt ist.

Die aktuelle Datenblock-Länge zu speichern ist eine Möglichkeit. Stimmt.

In die Gegenrichtung... also Daten senden mach ich am einfachsten über
Print"{irgendwas}"; ?
oder wäre es sinnvoll das ebenfalls über das Register zu schicken ?

Ich hab gelesen, das das UDR das Register ist für I/O ... also sprich für beide Richtungen.
Ist das Problematisch, wenn Während dem Senden daten empfangen werden ?
Das gibt doch dann nur durcheinander vermute ich mal.

MagicWSmoke
05.03.2013, 21:34
Man kann auch Interrupts in einer ISR extra erlauben, "nested interrupts", aber das ist nicht ungefährlich und hat 'ne Menge Tücken.

Print ist problemlos benutzbar, nur mit allem wie Input usw. würdest Du Probleme haben, weil sich die URXC sofort die Daten greift. Geht aber trotzdem, einfach den Interrupt verbieten.

Nein, ist kein Problem. Schreibt z.B. ein Print auf das UDR, so hat das keinen Einfluss auf daraus gelesene Werte, das wird wie zwei unterschiedliche Register unter einem Namen behandelt.

JoeM1978
05.03.2013, 21:41
Fein. gut zu wissen, das es dann doch unterschiedliche Register für RX und TX sind.

Immerhin sollen schon einige Daten zum PC Wandern also Sensoren, odometrie, evtl. Statusmeldungen...
Das wäre käse, wenn das alles warten müsste.

Deshalb möcht ich ja auch lieber vorher die Komunikation genau hinterfragen... nich das es
späther schon probleme gibt, wenn z.b. Radsensoren/Drehgeber paar mal in der Sekunde Werte senden sollen.

Naja... ich frag einfach was ich nicht versteh... irgendwann machts peng und es bleibt im Hirn hängen
(oder bringt mich zur verzweiflung)*lach

MagicWSmoke
05.03.2013, 21:54
Deine Anfangslösung mit 2mS Waits hätte solch eine Blockade verursacht, da dort für den Empfang die ISR nicht verlassen worden wäre, hätte also für max 32mS blockiert. Interruptgesteuert dagegen wird nix blockiert, man könnte noch mit gepuffterem Senden bei dieser niedrigen Baudrate Rechenleistung freimachen.

JoeM1978
05.03.2013, 22:10
Ja... diese 2ms delay waren ursprünglich auch nicht geplant.
Ich hatte mich in der Geschwindigkeit vertahn und dachte der buffer würde schneller gefüllt, als die ISR das auslesen kann.

... ich kann ja alternativ den atmega mit Handbremse betreiben und nur paar Herz takt geben. *lach
Wobei mir grade so ein Bild in den Sinn kommt von den ersten "Computern" die aus tausenden Relais und Schützen ganze Stockwerke
belegt hatten.