Hi @all mal wieder ;),
ich versuche mich gerade in eine art Konfigurationsoberfläche per UART
zusammen zu basteln. Leider komme ich mit dem "Ringpuffer" Prinzip
zeichen zu empfangen nicht wirklich klar... und diese dann evtl.
Auszuwerten...
in meiner RX_ISR mache ich derzeitig folgendes:
********************************************************************
Uart_RX_Complete:
push temp1
push temp2
push temp3
in Save_SREG, SREG
sbr Flags, 1<<USART_RX
in temp1, udr
st x+,temp1
inc rs_puffer
cpi rs_puffer, 8
breq rs_count
out SREG, Save_SREG
pop temp3
pop temp2
pop temp1
reti
rs_count:
clr rs_puffer
ldi xl, low(rs232_rx_puffer)
ldi xh, high(rs232_rx_puffer)
reti
********************************************************************
Wenn ich jetzt davon ausgehe, das ich 8 Zeichen Empfangen habe (welche
ja nach und nach kommen) wie lese ich diese jetzt gescheit aus...
irgendwie bin ich durcheinander... bis jetzt hab ich das immer so
gemacht.
ISR setzt ein Flag das ein byte empfangen wurde, liest dieses aus und
stored es in den SRAM
Main schaut ob RX Flag gesetzt wurde und weis jetzt das ein Zeichen da
ist und springt zu einem RS232 Receive Handler welcher das eine Zeichen
verarbeitet...
nur komme ich im moment nicht mit dem kopf weiter als ein zeichen...
Gruß Dennis
P.S: @peter habe mir deinen Bootloader auch angeschaut nur verstanden
hab ich es immer noch nicht...
Hi du solltest, bei der Verwendung von mehreren reti's in einer ISR auch bei beiden deine Register wieder vom Stack holen. Matthias
@Matthias, uppss... hab ich noch gar nicht gesehen ;) Wird gleich gemacht ;) aber verstanden hab ich es leider dennoch nicht... (mit dem auswerten) Gruß Dennis
Oder einfach mal umstellen:
Uart_RX_Complete:
push temp1
push temp2
push temp3
in Save_SREG, SREG
sbr Flags, 1<<USART_RX
in temp1, udr
st x+,temp1
inc rs_puffer
cpi rs_puffer, 8
brne URXC1 ;Bei Zeichen < 8 zu URXC1
clr rs_puffer
ldi xl, low(rs232_rx_puffer)
ldi xh, high(rs232_rx_puffer)
URXC1: out SREG, Save_SREG
pop temp3
pop temp2
pop temp1
reti
Bei anderweitiger Verwendung von X XL und XH auch noch auf den Stack
pushen und poppen.
Dabei muß dann natürlich deren Inhalt im SRAM abgelegt werden.
Gruß
Andi
@Andi, ok auch danke für den Hinweis ;) spart ein wenig mehr Code ;) nur auch dies ist noch nicht wirklich die beantwortung / hilfestellung für meine Frage ;) Wie werte ich den Ringpuffer den ich mit jedem ISR mit einem Zeichen fülle wieder aus ? Gruß Dennis
Ich schätze, Du benötigst 2 mal einen Pointer für den Ring-Buffer. Zum einen einen für die ISR und zum anderen einen für das "Abholprogramm" in der Main. Das Abholprogramm vergleicht zuerst seine Zugriffsadresse mit der Zugriffsadresse der ISR. Sind beide Adressen identisch, sind keine neuen Bytes zum abholen da sind sie unterschiedlich ist mindestens 1 Byte zum abholen da. Genauso die ISR. Die prüft vorher auch ihre Zugriffsadresse mit der vom Abholprogramm als Kontrolle, ob vom vermeintlich noch vollem Buffer mindestens 1 Byte abgeholt wurde oder nicht. Ist die Zugriffsadresse von der ISR um 1 kleiner, mit Berücksichtigung des 8-ter Sprunges, als die vom Abholprogramm, ist der Buffer noch voll ansonsten kann die ISR ein weiteres Byte in den Buffer schreiben. Ich würde an Deiner Stelle erst mal die Zugriffsadressen im SRAM speichern, sonst hast Du irgend wann keinen X- und Y-Pointer mehr für andere Dinge zur Verfügung. Gruß Andi
Ach ja, die Zugriffsadressen sind ja nur 1 Byte groß (0 - 7). Mit 8Bit-Befehlen kannst Du einfache Vergleiche durchführen und wenn Du die konkrete Adresse benötigts einfach die Zugriffsadresse auf den Anfang des Buffers addieren: lds r16,ISRAdr .... .... ldi xl,low(RingBuffer) ldi xh,high(RingBuffer) add xl,r16 clr r16 adc xh,r16 .dseg ISRAdr: .byte 1 RingBuffer: .byte 8 Gruß Andi
@Andi, klingt als währst Du ein wenig skeptisch ;) Hab ich vieleicht das Prinzip allgemein mir selber "VerKompliziert"? Oder ist die Herangehensweise mit dem Flag setzen und in Main überprüfen schon der richtige weg ? Gruß Dennis
Ich bin überhaupt nicht skeptisch! Das mit dem Flag ist vielleicht nicht der falsche Weg. Aber mit für Beide Routinen, ISR-Empfang und Abholprogramm im Main, ist es glaube ich einfacher und effizienter es mit 2 Pointern zu machen. Man kann es auch wie folgt machen: Variablen: 1. Pointer für die ISR = BuffISRAdr (1 Byte) 2. Pointer für das Abholprogramm = BuffMainAdr (1 Byte) 3. Füllstandsanzeige für den Buffer = BuffFill (1 Byte) 4. Der Buffer = Buffer (8 Bytes) Die ISR prüft vor dem ablegen eines Byte BuffFill. Ist BuffFill kleiner als 8 kann das Byte an der Adresse BuffISRAdr + Buffer abgelegt werden, BuffFill um 1 erhöht werden und BuffISRAdr um 1 erhöht werden (bei 8 auf 0 setzen). Ansonsten ist der Buffer voll weil vom Abholprogramm noch nichts abgeholt wurde und das Byte verschwindet dann. Genauso überprüft das Abholprogramm BuffFill. Ist BuffFill ungleich 0 kann mindestens ein Byte abgeholt werden,BuffFill um 1 verringert werden und BuffMainAdr um 1 erhöht werden (bei 8 auf 0 setzen). Bei BuffFill = 0 ist kein Byte zum abholen da. Im Abholprogramm kann das dann in einer Schleife durchgeführt werden bis eine Kopie von BuffFill für die Schleife 0 ist. Die Variablen dafür kann man ja im SRAM speichern und mit LDS und STS darauf zugreifen: .dseg BuffISRAdr: .byte 1 BuffMainAdr: .byte 1 BuffFill: .byte 1 Buffer: .Byte 8 Gruß Andi
@Andi,
also das letzte hab ich nun wirklich nicht so ganz verstanden ... :(
habe jetzt erfolgreich geschaft Byte für Byte im SRAM abzulegen.
Mache in der RX_ISR jetzt folgendes:
ldi xl, low(RingBuffer) RingBuffer adresse laden
ldi xh, high(RingBuffer)
lds temp1, BufferAnzahl Anzahl der Zeichen
add xl, temp1 zur Adresse addieren
clr temp2
adc xh, temp2
in temp2, udr UDR einlesen
st x, temp2 und an aktuelle adr store
inc temp1 Dann +1 der Zeichen
cpi temp1, 8 vergleichen mit max
brne nochok1 Überlauf?
clr temp1 dann anzahl auf 0
sts BufferAnzahl, temp1 und storen
nochok1:
sts BufferAnzahl, temp1 ansonsten store akt. anz.
sbr Flags, 1<<USART_RX Flag setzen für Zeichen da
Das Speichern in den Ringpuffer funktioniert prinzipiell schon.
Ich habe mir mal Peter Danneger seinen Bootloader angeschaut, und finde
es sehr interessant wie er die empfangen "Strings" per Sprungtabelle
auswertet nur so richtig verstanden hab ich das ganze ganz und gar
nicht... war ne lange ratlose nacht gestern...
Warum ich das ganze immer noch nicht verstehe ? Keine Ahnung nach so
vielem Lesen und Schreiben meines Sources vollkommen durcheinander...
arggg... schrei
Gruß Dennis
P.S.: @Andi, vieleicht erbarmst Du dich noch einmal und versuchst es
mir anders zu erklären... thnks.
Hi Dennis, hast du allgemeine Verständnis-Probleme mit Ringspeichern? Guck dir mal diesen Link an, vielleicht hilft er dir: http://www.zeiner.at/c/ringbuffer.html Ich habe diesen jetzt mal mit Abwandlungen für mich übernommen, und es geht, soweit ich das beurteilen kann. Ich brauche ihn, genauso wie du, für meine serielle Schnittstelle. Dort habe ich das Problem, dass ich trotz Hardware-Handshake einige Bytes verliere (der PC reagiert nicht schnell genug auf das Sperren der Handshake-Leitungen), daher mache ich es nun über den Ringbuffer. Gruß Ralf
Hi Ralf, wie ich den Ringpuffer aufbauen muss um zu speichern, hab ich soweit verstanden... nur wie ich diesen dann auswerte, da hängt es bei mir... ein einzelnes Byte auswerten ist ja nicht wirklich ein Problem nur mehrere Bytes auswerten und vor allem den Puffer dann wieder richtig auszulesen. Diese Kombination zwischen auslesen und auswerten, da hängt es irgendwie, das ich einfach nicht weis wie ich es aufbauen soll. Nehmen wir einfach mal an, das ich sage ich möchte die Uhrzeit in einem DS1337 speichern. z.B. Byte C für Uhr einstellen und dann zeit übergeben. (Ist aber nur ein Beispiel für mein Problem) Gruß Dennis
Tja, das mit dem Auswerten ist halt so ne Sache. Kommt auch darauf an, was Du vor hast. Willst Du Deinem Kontroller Befehle in Klartext übergeben brauchst Du komplette Vergleiche von Zeichenketten. Willst Du Byte-Befehle übergeben ist das einfach mit Zahlenvergleichen zu machen und dann halt noch die Parameter (Uhrzeit) in entsprechend gleicher Reihenfolge zu übergeben. Der Buffer ist nur dazu da, damit der Prozessor bei extremer Beschäftigung anderswo möglichst keine Bytes verliert. Und wenn er das doch tut, kann man den Buffer vergrößern. Für die Auswertung von mehreren Bytes brauchst Du ein weiteres Zwischenlager im SRAM mit x Bytes wo das Abholprogramm (GET) die Bytes der Reihe nach hineinschreibt. Diese Größe hängt auch von dem ab, was Du anstellen willst. Gruß Andi
Hi Dennis, jetzt wird mir dein Problem klarer. Wenn ich es richtig verstanden habe, brauchst du das gleiche, was ich für meinen ISP-Programmer realisiert habe: Ich würde erstens mal das AUSLESEN vom AUSWERTEN komplett trennen. Der Ringbuffer ist nur für deine Schnittstelle da, sonst für NIX. Ich habe mir Gedanken zum Datensatz-Aufbau gemacht, den ich für den Programmer verwende. Das sieht bei mir wie folgt aus: Das erste Byte ist immer ein darstellbares Zeichen aus dem ASCII-Satz und dient als "Befehlskennung" Alle weiteren Zeichen des Datensatzes sind immer ASCII-Hex-Zeichen, also "0" - "9" und "A" - "F" und somit Daten. Jeder Befehl wird mit dem CR-Zeichen (Return) abgeschlossen. Ich empfange die Daten und setze sie in einen Ringbuffer, der nur für die serielle Schnittstelle da ist. Aus dem Ringbuffer lese ich die Daten in den "Befehlsbuffer" bis zum CR-Zeichen ein, wobei ich alle Zeichen bis auf das erste dahingehend prüfe, ob es wirklich ASCII-Hex-Zeichen sind. Dann wird aus dem Befehlsbuffer das erste Zeichen (-> Befehlszeichen) gelesen und geprüft, ob der Befehl bekannt ist. Wenn ja, übergebe ich den kompletten Befehls-Buffer der zum Befehl gehörenden Subroutine, die ihrerseits die Auswertung der restlichen Daten vornimmt (passt die Menge der Daten zum Befehl, sind die Daten unsinnig, usw.). Zum Beispiel eine Routine für das Lesen, wobei der Aufbau wie folgt ist: RAAAAMM R als Klartext (ASCII-Zeichen "R") für Read AAAA ASCII-Hex-Zeichen für die Adresse MM ASCII-Hex-Zeichen für die Menge der auszulesenden Datenbytes In deinem Fall heisst das dann wohl, das du beim Befehl "C" die SetClock-Routine aufrufst, die die restlichen Daten im Befehlsbuffer prüft, und wenn sie gültig sind, schreibst du die Uhrzeit in deinen Uhren-IC. Kannst du damit was anfangen? (Nebenbei, wenn es sich einfacher lösen lässt, bin ich für Vorschläge dankbar ;-)) Falls du den entsprechenden Code-Schnipsel haben möchtest, sag Bescheid. Gruß Ralf
@Dennis,
der Trick bei meinem Empfangspuffer ist, daß er komplette 256 Byte
belegt.
Wenn man dann das High-Byte der Adresse konstant hält:
; Receive Interrupt
;------------------------------------------------
URXCint:
ldi xh, high(rx_buff) ;set high address byte
in ia0, UDR
st x+, ia0
reti
ergibt sich ein automatisches Umlaufen ohne irgendwelche Tests.
Der X-Pointer ist dabei für den Interrupt reserviert, da man ja im
Hauptprogramm bequem mit 2 Pointern (Y,Z) auskommt.
Und bei der Auswertung wird dann auch immer nur das Low-Byte
hochgezählt:
exec_command:
ldi zl, low( command_table * 2 - 3 )
ldi zh, high( command_table * 2 - 3 )
mov a1, yl ;save command start
_coi1: adiw zl, 3 ;skip rjmp + odd zero byte
andi zl, 0xFE ;adjust to next word
mov yl, a1
clt
rjmp _coi3
_coi2: ld a0, y
inc yl ;increment low address only !
eor a0, r0
andi a0, 0x5F ;ignore case
breq _coi3
set ;not matching
_coi3: lpm r0, z+
tst r0
brne _coi2
brts _coi1 ;test next command
lsr zh ;byte to word address
ror zl
ijmp ;jump after command string
Somit ist kein extra Umkopieren notwendig.
In der command_table stehen die einzelnen Kommandos gefolgt von einem
RJMP zu der entsprechenden Funktion.
Frag ruhig, wenn Du etwas nicht verstehst.
Peter
Aber man könnte es auch noch mittels AND auf eine 2-er-Potenz
verkleinern (für andere Zwecke):
Z. B. für einen Buffer mit 64 Byte:
URXCint:
ldi xh, high(rx_buff) ;set high address byte
in ia0, UDR
st x+, ia0
andi xl,0x3F ;0b00111111
reti
Das AND sorgt dafür, das sich der Pointer nur von 0 bis 63 (relative
Adresse) bewegt.
Bei 32 oder 16 Byte dann "and xl,0x1F" oder "and xl,0x0F"
Natürlich muß man auch da aufpassen, das der Buffer keine 256
Byte-Schwelle überschreitet was man mit .org Sicherstellen kann.
Gruß
Andi
@Andi,
URXCint:
ldi xh, high(rx_buff) ;set high address byte
in ia0, UDR
st x+, ia0
andi xl,0x3F ;0b00111111
reti
Oh oh oh oh, ganz böses Foul !
"andi xl,0x3F" ändert das SREG, also schleunigst sichern !!!
Peter
Ups... ... ist schon klar. War ja nur mal als Beispiel eingeworfen. OK, kostet wieder Rechenzeit, und wie es bei Deiner Routine aussieht, ändert kein Befehl das SREG was wiederum Zeit spart wenn man es nicht sichern (und andere Register) muß. Gruß Andi
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.