Sida 10 av 26

Postat: 8 februari 2008, 13:09:38
av Stewal
Då styrs allt från loopen med andra ord.

Skall ta och labba lite nu, skapa en räknare som räknar upp vid varje spar av data och när den är full skicka datat till Pc´n.

Postat: 8 februari 2008, 13:42:35
av sodjan
Jo, med rätt definition av "styrs" så kan man kanske säga det... :-)

Dock så skulle jag säga att ISR'en "styrs" från USART'en genom att det
ju är den som triggar själva interruptet...

Notera att många tror att en "räknare" är något speciellt. Man får
frågor som "hur skapar man en räknare". Frågan är felstäld. En
räknare är bara en variabel som vilken som helst, det är hur man
*hanterar* den som gör den till en "räknare". D.v.s att man t.ex
använder INCF och DECF för att öka/minska variablen/räknaren.

Alla variabler (oavsett om de används som "flaggor", "räknare" eller
något annat) allokeras precis som vanligt med RES.

> skapa en räknare som räknar upp vid varje spar av data och när den är full

Det är bufferten som blir "full", inte räknaren... :-)

Ett tips angående räknaren. Det är enklare att låta den räkna från
4 -> 0 (än t.ex från 1 -> 5) eftersom det är enklare i koden att testa
en variabel för "=0" än för "=5". Kolla instruktionen DECFSZ så ser du
varför.

Postat: 8 februari 2008, 14:08:24
av Stewal
Här är räknaren jag gjorde. Det är inte all kod men du kanske förstår.

Kod: Markera allt

Counter	RES	1

movwf	RX_BUFFER	; Kopiera w till RX_BUFFER
incf	Counter, 1	; Öka räknaren med 1
btfss	Counter, 5	; Om räknaren är 5 skippa nästa inst.
goto	if_esc
goto	done         

EDIT:
Så ditt svar nu att det är enklare att räkna ner och kolla om den är =0

Postat: 8 februari 2008, 14:37:02
av Stewal
Så här?

Kod: Markera allt

Counter	RES	1
;********************
movlw	d'4'        ;Sätt räknare till 4
movwf	Counter

if_esc
decf	Counter, 1	; EDIT: minska räknaren med 1
btfsc	Counter	; Om räknaren är 0 skippa nästa inst.
goto	if_esc
goto	done

Postat: 8 februari 2008, 15:06:19
av sodjan
Precis, ungefär så. Ett par saker.

DECF och BTFSC kan kombineras i DECFSZ, kolla den !

Alltså något i stil med :

Kod: Markera allt

if_esc                     ; tecknet var ett <ESC> !
    movlw    d'4'          ;Sätt räknare till 4
    movwf    Counter
    clrf     buffer_full   ; Buffer är *inte* full...
    goto     done          ; Avsluta ISR'en...

    <här ska det vara kod för att hantera bufferten...>

    decfsz   Counter, f    ; Minska räknaren med 1 och hoppa
                           ; över nästa instruktion om den blev = 0.
    goto     done          ; Räknaren är <> 0, avsluta ISR'en...

    ; Räknaren är alltså = 0 om vi hamnar här !
    incf     buffer_full   ; Sätt buffer_full = 1 (den var ju = 0 innan...).
    goto     done          ; Avsluta ISR'en...

done
    retfie
I main-loopen får man sedan kolla om buffer_full blir <> 0...

Postat: 8 februari 2008, 15:44:52
av Stewal
Så nu är filen uppdaterad, tror att det här bör funka ej testat.
http://rodel.se/resultat_display.asm

Postat: 8 februari 2008, 16:00:58
av sodjan
Nej, det fungerar inte.
Det finns bl.a en "goto datain" som inte ska vara där.
Jag ser inte heller hur du hanterar andra tecken än <ESC>,
d.v.s de som ska läggas in i bufferten. Det saknas en pekare
till bufferten. Och det är säkert mer... :-)

Postat: 8 februari 2008, 16:17:29
av Stewal
Är det den här du tänker på?

Kod: Markera allt

    sublw	h'1B'	; Om RX_DATA = 1B, så blir Z nu = 0
	btfsc	STATUS, Z	; D.v.s skippa nästa inst om Z = 0
	goto	datain		; Z=1, hoppa till datain.
Så är det för att om det inte är 1B=ESC, så skall den läsa in igen och kolla.

>Jag ser inte heller hur du hanterar andra tecken än <ESC>,
Det har du rätt i tur att det finns fler ögon.

>d.v.s de som ska läggas in i bufferten.
Då måste buffern ökas från 5 till 6, eftersom det är 6 st. tecken som skall skickas till displayen.

Det saknas en pekare till bufferten?
Nu är inte jag me.

Postat: 8 februari 2008, 17:04:11
av sodjan
OK, det här med interruptstyrd kod, är lite av en AHA-upplevelse.
Du har inte riktigt haft den än... :-) :-)

> Så är det för att om det inte är 1B=ESC, så skall den läsa in igen och kolla.

Ja, men *nästa* gång som ISR'en anropas (d.v.s USART'en triggar ett interrupt).
Kom ihåg, ISR'en körs varje gång det kommer in ett tecken, du ska *inte*
ligga kvar där och vänta på nästa tecken ! Det är själva kärnan i en
interrupt styrd kod !

En gång till...
ISR'en hanterar *ETT* tecken i taget !! D.v.s varje gång den körs!

Så kolla vad du har fått in :

- Är det en <ESC>, sätt räknaren och gör inget mer (d.v.s hoppa till retfie).
(Visst, du bör också sätta pekaren till bufferten rätt...)

- Är det något annat *och* du håller på med ett paket, skriv till bufferten.
*Om* bufferten är full, sätt "full" flaggan.

Det kan alltså behövas en extra flagga som talar om att du har ett
aktivt paket "på gång". Men du kan kanske också klara dig utan den...

> Då måste buffern ökas från 5 till 6, eftersom det är 6 st. tecken som skall skickas till displayen.

Nej, den buffert vi talar om här, lagrar bara det som kommer på RS232 linjen.
Vad som ska till displayen är en helt annan sak, och det har ingenting med ISR'en att göra !

> > Det saknas en pekare till bufferten?
> Nu är inte jag me.

Alltså, när det kommer in ett tecken och du befinner dig i ett "paket", så
måste du veta var nästa tecken ska skrivas i bufferten.

Detta löser man enklast genom att sätta en variabel ("pekare") = RX_BUFFER.
Denna variabel/pekare ökas med ett för varje tecken. Den används
tillsammans med de två registren INDF och FSR. Se kap 2.4 i databladet
för exempel, "Indirect Addressing".

När du ska läsa och analysera bufferten senare så kan det vara enklare att
helt enkelt skriva RX_BUFFER, RX_BUFFER + 1, RX_BUFFER + 2 o.s.v.

Postat: 9 februari 2008, 14:20:43
av Stewal
Ha nu läst om "Indirect Addressing", men jag får inte ihop det med det här projektet. Var koden för pekaren skall in.

EDIT:

Om man tittar på Icecap´s kod, så finns en buffer Rx_Buffer på 5 byte.
Sen Rx_Input som jag mistänker är pekaren.

Postat: 9 februari 2008, 14:36:31
av sodjan
Du har en buffert med 5 (eller 4) positioner.
Du får in ett tecken via USART'en.
Detta ska in på "nästa" position i RX_BUFFER.

Så långt OK ?

För att veta var "nästa" position är så måste du ha något som
håller reda på var du är i RX_BUFFER (d.v.s från en körning av ISR'en
till nästa). Det är det pekaren gör.

När du har fått en <ESC> så sätter du pekaren = RX_BUFFER
(d.v.s den pekar på fösta positionen i RX_BUFFER).
För varje tecken som läggs in ökas pekaren med 1.
När pekaren är lika med RX_BUFFER+4 (eller RX_BUFFER+3 om du inte lagrar
<ESC>) så är bufferten slut (det ersätter den där första räknaren vi hade,
eller så kör du med båda, gör hur du vill...)

Rent praktiskt i koden så använder man INDF och FSR för att adressera
RX_BUFFER "indirekt".

Under inläsning av ett paket till RX_BUFFER så behöver du inte indirekt
adressering till något annat, så värder i FSR kan ligga kvar mellan ISR
anropen.

På vilken plats i koden ? Tja... Då du ska skriva till RX_BUFFER, sannolikt.

> Om man tittar på Icecap´s kod, så finns en buffer Rx_Buffer på 5 byte.
> Sen Rx_Input som jag mistänker är pekaren.

Precis, du gör i stort sätt likadant (rent logiskt, det skrivs naturligtsivs
lite annorlunda...)

Postat: 9 februari 2008, 15:58:18
av Stewal
Får nog ta och bena ut vad dom olika registrena är.

Kan man säga att Rx_buffer är samma som FSR?

Får inte ihop det hur man sparar i Rx_buffer och samtidigt sätter pekaren.
Är det så här?

Vi säger att vi sparar allt som börjar på 1B för att klar göra det här.
Om det är 1B som kommit in från RCREG och vi vill spara den i Rx_buffer skrivar man så här då, varje gång det skall sparas i buffern?

Kod: Markera allt

      		movf	rcreg, w
 		movwf	rx_buffer
 		clrf	indf
 		incf	rx_buffer
 		btfss	rx_buffer,5
 		goto	done
 		incf	buffer_full
done
	retfie

Postat: 9 februari 2008, 18:31:37
av Stewal
Glöm inlägget innan.
Nu är en AHA, mycket nära skall bara verifiera.
Kan erkänna att jag trodde att "0x20" var ett värde, men det är ju en adress i FSR minnet. :doh:

Kod: Markera allt

MOVLW 0x20 ;initialize pointer
Tog en stund att klura ut det.

Postat: 9 februari 2008, 19:13:33
av Icecap
Om vi utgår ifrån att du har följande definierad:

Kod: Markera allt

BufLen equ 4 ; ESC sparas ju inte, den nollställer
ESC equ d'27'
Rx_Buffer res BufLen ; 4 bytes
Rx_Counter res 1
Incoming res 1

Sedan i ISR-delen där det är konstaterat att det har kommit en byte i RCREG:
  movf RCREG,w ; Get incoming byte
  movwf Incoming ; Save for later use
  sublw ESC ; Compare with ESC
  btfsc STATUS,Z ; Test if equal
  goto ESC_Found ; Jump if it was an ESC
; Was not ESC, put it in buffer
  movf Rx_Counter,f ; Get status
  btfsc STATUS,Z ; Test if Zero-flag is set
  goto Exit_ISR ; Leave, no more room in buffer
  movf Rx_Counter,w ; Get the index
  addlw Rx_Buffer,w ; Add address offset
  movwf FSR ; Save result in FSR to index
  movf Incoming,w ; Get the input
  movwf INDF ; Save it in buffer
  incf Rx_Counter,f ; Point on next location
  movf Rx_Counter,f ; Check value of counter
  btfss STATUS,Z ; Is Rx_Counter = 0?
  goto Exit_ISR ; Jump if not...
  ...
  här har 5 bytes blivit tagit emot, kolla om det är rätt data och flytt dom på plats om det är.
  ...
  goto Exit_ISR ; Jump if not...
ESC_Found
  movlw BufLen ; Reset count
  movwf Rx_Counter ; Save it in correct location
Exit_ISR
Om din Main-loop då kollar om Rx_Input är noll kan den också kolla de andra data. OBS: datan som kommer in sparas på följande sätt: först inkommen står sist i buffern.

Edit: skrivit med en flasksugande 2½ månaders tjej på ena armen, garanti för rätt funktion ges inte!

Postat: 9 februari 2008, 19:44:44
av sodjan
OK, vi tar enbart själva FSR/INDF hanteringen. (Tillkommer resten
i ISR'en som kollar efter <ESC> o.s.v. men det hade ganska bra
koll på, tror jag... :-) ).

Först någonstans i början koden har du en RES som allokerar minne till bufferten :

Kod: Markera allt

RX_BUFFER     RES     4
(4'an kan lika vara en definierat konstant som i Icecap's exempel, det
är att föradra, speciellt om man vill använda "längden" senare i koden,
då är det bara ett ställe att ändra på om länden ska ändras...)

Detta allokerar (reserverer) 4 bytes i sekvens någonstans i minnet (GPR)
och ger symbolen RX_BUFFER ett värde som motsvarar adressen till *första*
positionen i RX_BUFFER.

(PS: Jag rekomenderar varm att du har MAP filen påslagen (MPLINK i
Project Setup-någonstans) så att du även kan se hur olika variabler är
allokerade i minnet. Inte för att man behöver veta adresserna, men det
ger lite mer förståelse för vad RES (tillsammans med MPLINK) gör).

Sen så ska vi låta registret FSR "peka" på första positionen i RX_BUFFER :

Kod: Markera allt

movlw     RX_BUFFER 
    movwf     FSR
Notera, move *literal* to W, d.v.s själva värdet av *symbolen* "RX_BUFFER" !
*Inte* det som RX_BUFFER eventuellt innehåller !

Detta görs alltså någonstans då vi ska börja på ett nytt datablock efter en <ESC>...

Så där, nu "pekar" FSR mot första positionen av RX_BUFFER.

Nu, för varje tecken som du mottar (efter en <ESC>) så gör du i princip :

Kod: Markera allt

movf      RCREG, w     ; Hämta tecknet från USART'en till WREG
    movwf     INDF         ; Skriv tecknet till aktuell position i RX_BUFFER.
    incf      FSR          ; Öka "pekaren" med ett för nästa tecken...
Notera alltså att då man skriver/läser INDF, så är det igentligen adressen
i FSR som man skriver/läser !

Nu är FSR ökad med ett och förberätt för nästa tecken som kommer in
til USART'en. På så sätt kommer varje tecken att hamna i "nästa"
position i RX_BUFFER.

Icecap har en lite annorlunda hantering där FSR värdet beräknas varje
gång. Det fungerar det också, men i detta fall räcker det med att
bara räkna upp det med 1 vid varje varv genom ISR'en. (Icecap's
metod är dock lite mer generell. Ofta har man antingen flera buffrar eller
har behov att t.ex skriva och läsa från olika positioner i samma buffert,
och då måste man spara undan FSR värdet mellan de olika accesserna...)

Tillkommer lite check av om/när 4 tecken är mottaget, o.s.v.
Men det har diskuterats i tidigare inlägg.