Hallo!
Ich bin Anfänger in Assembler und würde dringend Hilfe benötigen.
Verwende den PIC12LF1822:
http://ww1.microchip.com/downloads/en/DeviceDoc/41413C.pdf
Ich hab noch nie Assembler programmiert, bitte um Hilfe... Ich werde
versuchen einen Ansatz zu präsentieren...
Ich will den DAC verwenden, um 4 digitale Eingänge einzulesen (16 Werte)
und an Hand des eingelesenen Wertes AM DAC Ausgang eine analoge Spannung
ausgeben. Ich hab ein Programmgerüst für diesen PIC bekommen, die
Initialisierung der Ein-/Ausgänge, DAC usw.. sollte laufen.
Meine Idee wäre nun, in der Hauptschleife die digitalen Eingänge
einzulesen. Der DAC hat 5 Bit. Der Zusammenhang zwischen digitalem
Eingangswert ist nicht linear, deswegen würde ich gerne eine Tabelle
verwenden, die mir zum eingelesenen digitalen Wert einen analogen Wert
von 0-31 (5Bit) zuordnet, den ich dann in das DACCONO1 Register
schreibe.
Die initialierung laut datenblatt hab ich durchgeführt, es geht nur noch
um das Anlegen der Tabelle und um das Hauptprogramm, das die 4 Eingänge
einliest, entsprechend dem Wert in der Tabelle nachschaut und den Wert
aus der Tabelle in das ADCCON1 Register schreibt.
Erstellen der Tabellle:
Diesen Abschnitt hab ich von RN-Wissen. Kann mir jemand bitte erklären,
was in diesen 4 Zeilen genau passiert? Ich komm von C und mir ist nicht
ganz klar, wann welcher Wert in der Tabelle genommen wird?!
Vielen Dank für die Hilfe!
Nein, der Code ist so blòd.
Mach die Tabelle gleich nach dem Interrupt, wenn du den ueberhaupt hast,
und dann genuegt ein call auf die Tabelle.
Grund, PCLath wird mit 0 iniziert, und wenn die Tabelle im Codebereich
kleiner 256 ist, dann braucht man PClath nicht anfassen, und PCLath
braucht
man praktisch nur fuer solche Tabellen.
Hast du schon probiert ober nur der DAC funktioniert.
Das könntest du Probieren indem du verschieden Konstanten ausgibst.
Hier wird das mit der Tabelle erkärt:
http://www.sprut.de/electronic/pic/programm/led.htm
Und die Tabelle legst du nicht in CBLOCK an, sie ist wie normaler Code
zu behandeln.
Könntest du das gesamte Programm posten, dann kann ich besser helfen.
Hast schrieb:> Hallo!>>> cblock 0x070 ; von jeder bank zugriff?> ; hier tabelle anlegen:> DAC_TABLE addwf PCL,1> retlw d'1'> retlw d'3'> .> .> . ; für alle 16 Werte>> endc
Also die Tabelle liegt sicher nicht im RAM (cblock...endc) da der PIC im
RAM kein programm ausführen kann, sondern normal im Codebereich - also
Flash!!
btw: und man schreibt besser <DAC_TABLE addwf PCL,F> an Stelle von
<DAC_TABLE addwf PCL,1> - dann muss man nicht rätseln ob als Ziel das
W-Register oder ein Fileregister gemeint ist .... also 0 = W und 1 = F.
> Ist das so korrekt?>> Zum Aufruf der Tabelle:> [avrasm]> movlw 0x070> movwf PCLATH> movf TWert,0> call DAC_TABLE>
Dadurch stimmt der Aufruf der Tabelle auch nicht mehr, sondern:
movlw HIGH DAC_TABLE
movwf PCLATH
Damit wird die Adressbits 11 und 12 (= Page N´0|1|2|3) der
Tabellenadresse geladen. Genaugenommen funkteoniert das so: die beiden
Adressbits liegen im PCLATH auf Bit 3 und 4 und diese sind mit den Bits
11 und 12 des PCH (ProgramCounterHigh) verknüpft
Die Adressbits 0...10 werden ja im CALL-Befehl codiert.
movf TWert,0
Hier wird der in Variable TWert gespeicherte Offset in die Tabelle in
das W-Register geladen (auch hier besser movf TWert,W schreiben).
call DAC_TABLE
Dürfte klar sein.
In DAC_TABLE wird dann mit
addwf PCL,1 (besser addwf PCL,F)
W zum PCL (ProgrammCounterLow) addiert.
Dadurch hast du mit PCH:PCL die Adresse für den nächsten Befehl
vorliegen und das ist eben der entsprechende retlw xx.
Achtung: bei dieser Methode muss deine gesamte Tabelle innerhalb einer
256-Byte Grenze liegen. Denn ein addwf PCL,F macht beim Overflow (>0xff)
KEINEN Übetrag auf PCH!
Wenn du das noch detailierter haben willst, dann lädst du dir am besten
die AN556 "Implementing a Table Read" bei MICROCHIP runter.
Danke zunächst für eure Hilfe...
@all: leider habe ich jetz keinen Zugriff auf den gesamten Code, weil
ich nicht mehr in der firma bin. Hab aber bewusst nur Teile daraus
gepostet, weil das gesamte Programm sehr groß ist. Ich will eigentlich
nur die Tabelle anlegen und dann vom Hauptprogramm darauf zugreifen.
Vielleicht könnt ihr mir mit meinen folgenden Beschreibungen trotzdem
helfen (bitte :), ist mein ersten Assembler Programm... :()
@chris: ich verwende keine interrupts.die vordefinierte Struktur des
asm-files hat da einen Bereich (siehe Code) wo RAM Variablen definiert
werden. dort ist mit cblock ein Speicherbereich definiert, wo alle Bank
drauf zugreifen können (Adresse 0x070, siehe auch Datenblatt Seite ) Ich
dachte dort könnte ich die Tabelle anlegen, dann brauch ich nicht
zwischen den Banks hin und herschalten. Ist das keine gute Idee?
Was bedeutet, wenn die Tabelle im Codebereich kleiner als 256 ist?
@hartl: nein hab noch nicht probiert ob der DAC funktioniert. zwecks
CBLOCK siehe Beitrag zu Chris...
Werde mir dein Beispiel mal durchdenken, dann poste ich meinen Ansatz...
Vielen Dank!!!
Hast schrieb:> wo RAM Variablen definiert> werden. dort ist mit cblock ein Speicherbereich definiert, wo alle Bank> drauf zugreifen können (Adresse 0x070, siehe auch Datenblatt Seite ) Ich> dachte dort könnte ich die Tabelle anlegen, dann brauch ich nicht> zwischen den Banks hin und herschalten. Ist das keine gute Idee?
Nein, funkteoniert nicht. Das ist RAM-Speicher, bei Ausschalten sin
ddort die Daten weg und beim Einschalten steht nur "Müll" drinnen.
Du verwechselst die Speichersegmentierungen "Banking" (RAM) mit "Paging"
(FLASH) ;-)
Du kannst deine TWert-Variable im Common-RAM 0x70....0x7f ablegen, dann
brauchst du dich nicht um die Speicherbank kümmern.
DAs im Cblock usw war eine blòde idee, da die Codetabelle im Code
(Flash)
und nicht im Ram ist.
Mach die Tabelle gleich am Anfang, und spring mit deinem Code darueber.
Hier ein Beispiel mit Anmerkung, wo die Tabelle hinkommen sollte.
LIST p=12LF1822 ; Processor selection
#INCLUDE P16LF1822.INC ; Processor specific definitions
; Processor configuration
__CONFIG _WDTE_SWDTEN & _PWRTE_ON & _CP_OFF
; Reset vector
org 0x00
goto start
; Interrupt vector
org 0x04
retfie ; No interrupts used
; Tabellne kommen hier rein //// dann brauchst du dich um PCLATH nicht
kuemmern und ein call reicht.
; Main program entry point
start
SETUP
bsf BSR, BSR0 ; select bank 1
movlw .0
movwf TRISA ; TRIS make all of port a outputs
movlw .0 ;
movwf OSCCON ;configure oscillator for internal
31khz
movlw 0x19
movwf WDTCON ;configure watchdog timer for 4 seconds
and enable
movlw .0
movwf INTCON ;configure interrupts, disable external
interrupts, WDT starts after last instruction
bcf BSR, BSR0 ;select bank 0
movlw .0
movwf LATA ; make all i/o pins low
BEGIN
bsf LATA, RA5 ; RA5, power up transceiver
nop
bsf LATA, RA4 ; RA4, select transmit
nop ; wait for transceiver to initialize
nop
nop
nop
nop
nop
nop
nop
nop
nop
bsf LATA, RA2 ;make RA2 High, RA4 GOES LOW HERE WITH
RA2!!
bcf LATA, RA2 ;make RA2 low
bsf LATA, RA2 ;make RA2 High
bcf LATA, RA2 ;make RA2 low
bsf LATA, RA2 ;make RA2 High
bcf LATA, RA2 ;make RA2 low; ....etc.
......................
OK, das heißt, ich sollte den Code für die Tabelle in INIT anlegen (wird
vor der Hauptfunktion MAIN aufgerufen), wo auch der DAC, IOs usw
initialisiert werden? d.h. ich würde dort einfach folgenden Code
schreiben:
[asmavr]
INIT ; wird einmal vor der MAIN ausgeführt
;...... diverse initialisierungen, IOs, DAC usw...
DAC_TABLE addwf PCL,F
retlw d'1'
retlw d'3'
.
.
.
[/asmavr]
Muss ich da jetzt vorher einen Speicher mit org definieren?
Hallo Chris, hab deinen Beitrag erst jetz gelesen... Müsste dann soweit
passen mit der Tabelle oder?
Das heißt, ich brauch dann im MAIN nur mit movf den entsprechenden
Offset in Arbeitsregister laden und dann den Call auf die Tabelle
machen, dann ist der gesuchte Wert im Arbeitsregister?
Danke!! dann wäre das mit der Tabelle geklärt..
Ich will nun an 4 Pins einen digitalen Wert einlesen (RA2-RA5). Dieser
Wert ist dann der Offset für die Tabelle. D.h. ich schreib den
eingelesenen Wert einfach mit movf ins Arbeitsregister und rufe dann mit
CALL die tabelle auf.
Die Initialisierung ist getan, die entsprechenden pins sind als digitale
eingänge definiert und die Pullups sind aktiviert. Aber wie kann ich nun
den digitalen wert einlesen?
Ich hätte folgende Idee in PSEUDO Code:
MAIN
CLEAR arbeitsreg
PIN RA 2 einlesen
IF RA2 HIGH
BIT 1 in arbeitsreg setzen
PIN RA 3 einlesen
IF RA3 HIGH
BIT 2 in arbeitsreg setzen
.
.
. das gleiche für RA4 und RA5
CALL Tabelle
ZURÜCK zu MAIN
es sollen ständig die 4 pins eingelesen werden, mit dem digitalen wert
wird die tabelle aufgerufen. Mein Ansatz in Assembler:
1
MAIN
2
CLRW
3
BTFSC ??? 2 ;??? INputregister RA???
4
BSF W,1
5
BTFSC ??? 3
6
BSF W,2
7
.
8
.
9
.
10
.
11
12
; dann steht hier der digitalwert im arbeitsregister
13
CALL DAC_TABLE
14
; nun steht der wert im arbeotsreg, den ich in DACCON0 schreiben muss
15
MOVWF DACCON0
16
GOTO MAIN
könnte das so funktionieren? ist das letzte statement mit GOTO MAIN für
einen endlosschleife geeignet oder macht man das anders?
Vorher wusste ich nicht, wie ich einen Pin einlesen (PORTA), dieses ist
nämlich bei den ATmel µCs das Register für die Ausgänge (ich komme von
der ATmel C-Programmierung und muss nun PIC in Assembler programmieren
:), deswegen mögen meine Fragen manchmal wirklich lächerlich
erscheinen)...
aktueller Code:
1
MAIN
2
CLRW
3
BTFSC PORTA 2 ;
4
BSF W,1
5
BTFSC PORTA 3
6
BSF W,2
7
.
8
.
9
.
10
.
11
12
; dann steht hier der digitalwert im arbeitsregister
13
CALL DAC_TABLE
14
; nun steht der wert im arbeotsreg, den ich in DACCON0 schreiben muss
W ist was spezielles, und nicht ueber Register erreichbar, man kann
da keine Bits setzen, bzw nur ueber arithmetische Operationen.
Porta2-5
rrf porta,w ; schiebt porta um ein bit nach rechts und speichert es in
W
movwf tmp ; speichert den Wert zwischen
rrf porta,w ; schiebt nochmals ...
andlw 0xf ; diese Zeile sollte/kònnte auch im Unterprg. vor movwf pcl
stehen
call ...
oder wie du es gemacht hast:
clrw ; loescht w
btfsc porta,2
iorlw 1
btfsc porta,3
iorlw 2
...
chris schrieb:> W ist was spezielles, und nicht ueber Register erreichbar, man kann> da keine Bits setzen, bzw nur ueber arithmetische Operationen.
Stimmt nicht ganz. Bei PIC16 ist es nicht über Register erreichbar, beim
PIC12 hingegen kann man das W z.B. über decfsz WREG,f erreichen. Es ist
in jeder Bank.
movwf tmp ; speichert den Wert zwischen
rrf porta,w ; schiebt nochmals ...
ist falsch,
movwf tmp ; speichert den Wert zwischen
rrf tmp,w ; schiebt nochmals ...
ist richtig
M. H. schrieb:> beim> PIC12 hingegen
Ja stimmt, es ist ein Pic18 welcher da als pic12 vermarktet wird.
Ich habe deren ASM nicht so verinnerlicht, da ich bei pic18 meistens
nicht
ASM mache, bzw nur kurze optimierungen.
chris schrieb:> bcf BSR, BSR0 ;select bank 0
Dafür würde ich den Befehl "mowlb 0x00" nehmen, der löscht sich alle
bits, nicht nur das erste.
Und den DAC wirst du noch irgendwie initialisieren und enablen müssen.
so z.B.
bsf DACCON0, DACEN ;Enable DAC
bsf DACCON0, DACOE ;DAC voltage level is also an output on the DACOUT
pin
bcf DACCON0, DACPSS ;
bcf DACCON0, DACPSS ;Positiv reference: Vdd
Und den Wert den der DAC ausgeben soll musst du in DACCON1 schreiben.
Hallo,
die Initialisierung habe ich so ähnlich gemacht...
M. H. schrieb:> bsf DACCON0, DACOE ;DAC voltage level is also an output on the DACOUT> pin
das habe ich so gemacht, weiß aber nicht genau was das bedeutet. Für
mich bedeutet das, dass das Ergebnis der DA-Wandlung am DACOUT pin
ausgegeben wird, stimmt das? warum dann das "also" im Kommentar?
M. H. schrieb:> bcf DACCON0, DACPSS ;Positiv reference: Vdd
das hab ich nicht gemacht; wird in diesem Fall einfach Vdd als Referenz
verwendet?
Hast schrieb:> weiß aber nicht genau was das bedeutet.
Das bedeutet dass die DAC-Spannung an den DACOUT-Pin ausgegeben wird.
Hast schrieb:> warum dann das "also" im Kommentar?
Weil die DAC-Spannung auch Intern am Comparator, CPS und ADC anliegt.
Hast schrieb:> wird in diesem Fall einfach Vdd als Referenz> verwendet?
Ja.
Guten Morgen,
M. H. schrieb:> Und den DAC wirst du noch irgendwie initialisieren und enablen müssen.>> so z.B.>> bsf DACCON0, DACEN ;Enable DAC
ich hab jetz noch mal nachgeschaut: im include file des µC steht
1
#define DACEN DACCON0,7
reicht es da nicht, wenn man einfach nur
1
bsf DACEN
schreibt??
Das selbe gilt dann auch für DACOE...
Danke, lG
In Main, oder am Ende fon Init musst du noch ein clrf STATUS machen, da
du Bankselekts verwendest, und so im aktuellen Code anstelle von den
Eingangspins die
Tris Pin liests.
So, oder?
Angenommen, es liegt nun nur an D0 ein High-Pegel, der digitale Wert ist
also 1. Dann wird in der Main eine 1 ins Arbeitsregister geschrieben und
nach aAufruf der Tabelle steht der Wert 2 im Arbeitsregister, richtig?
Danke, ausgeführt...
Hast schrieb:> Angenommen, es liegt nun nur an D0 ein High-Pegel, der digitale Wert ist> also 1. Dann wird in der Main eine 1 ins Arbeitsregister geschrieben und> nach aAufruf der Tabelle steht der Wert 2 im Arbeitsregister, richtig?
kannst du da zustimmen?
Und kann ich BRA MAIN am Ende der Main verwenden? Oder sollte ich da
besser GOTO MAIN verwenden?
lG
chris schrieb:> Wurde aber nicht bereits gesagt, DAC Bits sollten in DACCON1 ?
Ahhh, richtig, danke für den Hinweis, ist natürlich falsch!
es ändert sich als in der Main:
Hast schrieb:> Danke, ausgeführt...> Und kann ich BRA MAIN am Ende der Main verwenden? Oder sollte ich da> besser GOTO MAIN verwenden?>> lG
In dem konkreten Fall reicht BRA.
Auf Nummer sicher gehst du mit GOTO - da erspart man sich bei einer
Programmerweiterung so manche Änderung von BRA in GOTO weil plötzlich
das Sprungzeiel bei BRA zu weit entfernt ist.
kann ich zustimmen,
Bra und Goto sind hier in diesem Falle gleichwertig.
Du hast das Banksel in der Init uebersehen, und weiters das Ruecksetzen
der Banksel in der Main.
Mit Banksel Daccon1 wechselst du zu Bank2, dort ist anstelle von PORTA
LATA also die Ausgangs Latch der PortA Treiber, kein Input.
Wenn du da nicht zurueckwechselst, dann funktioniert dir das ganze nicht
mehr.
Danke,
die beiden Bankselects hatte ich nicht übersehen, hab ich so übernommen
von deinem Code.
chris schrieb:> ;-----------------------------------------------------------------------> ------------------> ; initialize DAC> ;-----------------------------------------------------------------------> ------------------> BANKSEL DACCON0> BSF DACCON0,7 ; DAC Enable> BSF DACCON0,5 ; use DACOUT for DAC output> BCF DACCON0,2 ; select Vdd as reference> BCF DACCON0,3>> MAIN> BANKSEL PORTA> CRLW ; clear working reg> BTFSC D0> IORLW 1> BTFSC D1> IORLW 2> BTFSC D2> IORLW 4> BTFSC D3> IORLW 8>> CALL DAC_TABLE> BANKSEL DACCON1> MOVWF DACCON1> BRA MAIN
das ist der aktuelle code..
Die Frage mit der Tabelle ist noch offen:
Hast schrieb:> Hast schrieb:>> Angenommen, es liegt nun nur an D0 ein High-Pegel, der digitale Wert ist>> also 1. Dann wird in der Main eine 1 ins Arbeitsregister geschrieben und>> nach aAufruf der Tabelle steht der Wert 2 im Arbeitsregister, richtig?
Vielen Dank für eure Hilfe, hätte ich alleine nie geschafft! Hardware
ist da, bin gespannt obs funktioniert...
ja richtig, ich hatte schon weiter oben auf deine Frage geschrieben,
dass
ich auf der zustimmen kann, hatte diese jedoch nicht expliziet zitiert,
deshalt ist es warscheinlich untergegangen.
Solltest du weitere Tabellen haben, z.B. Stringausgaben, sei es auch
durch
Macro, wo das Register PCLATH veràndert wird,
dann solltest du ein "clrf PCLATH" vor dem "addwf PCL" hinzufuegen.
Dies kann auch durch das Macro Pagesel passieren, welches zum analog zum
banksel ist, aber nur fuer call. Kònnte im Code vorkommen, obwohl hier
eigentlich nicht notwendig.
Hab die Software soeben mit der Hardware getestet, funktioniert dank
euch!
Nun würde ich noch eine kleine Erweiterung brauchen:
Es geht nur um die Main, die sieht wie folgt aus:
1
MAIN
2
BANKSEL PORTA
3
CLRW ; clear working reg
4
BTFSS PIN_D0
5
IORLW 1
6
BTFSS PIN_D1
7
IORLW 2
8
BTFSS PIN_D2
9
IORLW 4
10
BTFSS PIN_D3
11
IORLW 8
12
13
CALL DAC_TABLE
14
BANKSEL DACCON1
15
MOVWF DACCON1
16
17
BRA MAIN
Diese main läuft ja nun immer sehr schnell durch, ich will nun eine
Verzögerung von 10ms einbaun und den Wert erst dann in DACCON1
schreiben, wenn 10 mal in Folge der gleiche Digitalwert anlag.
Mein Ansatz: eine Schleife über den ersten block legen, die so lange
durchlaufen wird, bis eine variable auf 10 ist. In der schleife wird der
aktuelle digitalwert eingelesen und abgespeichert. wenn sich dieser von
einem Schleifendurchgang zum nächsten ändert, wird die Variable zurück
auf null gesetzt, sonst um 1 erhöht. Und wie gesagt, wenn die Variable
10 ist wird die Schleife verlassen und dann der Rest der MAIN (ab Call
DAC_TABLE) ausgeführt.
Wie kann ich das in Assembler realisieren?
Bei der Verzögerung wirst du vermutlich das W verändern, also musst du
den Wert wo anders zwischenspeichern.
z.B.:
SUBWF TEMP_REG,f ; if equal -> zero bit set
MOVWF TEMP_REG
BTFSC STATUS,Z ;is zero flag not set?
DECFSZ counter,f
Das kannst du machen, weil movwf kein Flag im Status beeinflusst.
Und ich würde empfehlen wegen besserer lesilichkeit des Codes
nich SUBWF TEMP_REG,1 oder SUBWF TEMP_REG,0
sondern SUBWF TEMP_REG,f oder SUBWF TEMP_REG,w zu schreiben.
M. H. schrieb:> Bei der Verzögerung wirst du vermutlich das W verändern, also musst du> den Wert wo anders zwischenspeichern.
Das ist jetzt sicher und nicht nur eine Vermutung, du musst das
MOVWF TEMP_REG versicheben!
Sorry, hatte ich uebersehen, danke M. H.
Diese Zeilen muss raus, nicht verschieben sondern loeschen.
MOVWF TEMP_REG
TEMP_REG wird durch das Xor geupdated.
CLRW braucht es nicht umbedingt, der Code ist aber besser lesbar wenn
diese Zeile bleibt.
Ok, hab mir das jetzt versucht durchzuüberlegen:
Ist das XORWF nach dem IORLW 8 wirklich richtig? Ich interpretiere das
so:
Wenn W und TEMP_REG gleich sind, dann kommt bei dieser Operation Null
heraus und das ZeroBit wird gesetzt. Wenn das aber gesetzt ist, hüpft er
zurück zu MAIN, das soll doch nur der Fall sein, wenns ungleich ist,
oder? Denn wenns gleich ist sollte der Counter dekrementiert werden...
Oder hab ich da was übersehen/falsch verstanden?
Den Code ab dly_10ms verstehe ich nicht, könntest du mir den ev.
stichwortartig erklären?
Danke!!!
lG
Beim CALL von dly_10ms wird 1 ins W geschrieben, aber wie komm ich dann
zu delay10ms, dly2 und dly1?
Was bedeutet dc1,2 und 3, das konnte auch google mir nicht
beantworten...
und was bedeutet ein Punkt vor einer Zahl, wie in movlw .13?
Hast schrieb:> Beim CALL von dly_10ms wird 1 ins W geschrieben, aber wie komm ich> dann> zu delay10ms, dly2 und dly1?
Da kein return, geht der Code in delay10ms weiter.
>> Was bedeutet dc1,2 und 3, das konnte auch google mir nicht> beantworten...
dc1 , dc2 , dc3 sind Variablen, und als solche zu declarieren.
>> und was bedeutet ein Punkt vor einer Zahl, wie in movlw .13?
das bedeutet, 13 ist dezimal, ich habs nicht so mit der Basic
Schreibweise.
> Den Code ab dly_10ms verstehe ich nicht, könntest du mir den ev.> stichwortartig erklären?>> dly_10ms> movlw 1 ; lade 1 in W und gehe zu delay10ms weiter>> delay10ms ; W * 10ms delay> movwf dc3 ; speichere W in Wariable dc3> dly2> movlw .13 ; lade 13 in variable dc2> movwf dc2> clrf dc1 ; und 0 in variable dc1> dly1> decfsz dc1,f ; decrementiere dc1 und wenn 0 uberspringe naechsten opc.> goto dly1 ; springe zu dly1 , also 3 clk je loop> decfsz dc2,f; decrementiere ....> goto dly1 ; 2 clk alle 256 loops zusaetlich> decfsz dc3,f; ....> goto dly2 ; 2+3clk von dly2-dly1 zusatlich.> retlw 0 ; macht ca 13*(3*256+2+5-2) = 10.045ms *W, return W = 0> Ist das XORWF nach dem IORLW 8 wirklich richtig? Ich interpretiere das> Oder hab ich da was übersehen/falsch verstanden?>
Hast recht, hatte da warscheinlich was missverstanden oder zu schnell
durchgelesen. Weiter Unter die korrigierte Version:
chris schrieb:> MAIN> BANKSEL PORTA> MOVLW 10> MOVWF counter>> LOOP>> call dly_10ms> ;HIER Verzögerung 10ms einfügen>> BTFSS PIN_D0> IORLW 1> BTFSS PIN_D1> IORLW 2> BTFSS PIN_D2> IORLW 4> BTFSS PIN_D3> IORLW 8>> xorwf TEMP_REG,f ; if equal -> zero bit set , destroy TEMP_REG
movwf TEMP_REG ; alway save new value to TEMP_REG
> bnz MAIN ; else branch to main> DECFSZ counter ; count down and until counter is zero> goto LOOP ; go to loop
; else
> CALL DAC_TABLE> BANKSEL DACCON1> MOVWF DACCON1>> BRA MAIN>
OK,
bis zu dly_10ms scheint mir jetz alles klar zu sein.
Mir dem delay komme ich noch immer nicht ganz zu recht:
chris schrieb:>> dly_10ms>> movlw 1 ; lade 1 in W und gehe zu delay10ms weiter>>>> delay10ms ; W * 10ms delay>> movwf dc3 ; speichere W in Wariable dc3>> dly2>> movlw .13 ; lade 13 in variable dc2>> movwf dc2>> clrf dc1 ; und 0 in variable dc1
Bis hier stehen folgende Werte in den Variablen:
dc3 = 1
dc2 = 13
dc1 = 0
W = 13
richtig?
chris schrieb:>> dly1>> decfsz dc1,f ; decrementiere dc1 und wenn 0 uberspringe naechsten opc.
wenn ich Null dekrementiere, kommt null oder -1 raus?
>> goto dly1 ; springe zu dly1 , also 3 clk je loop
Warum 3 Clock je loop, wird mit jeder clockflake ein befehl ausgeführt,
verstehe ich das richtig?
Hast schrieb:> Warum 3 Clock je loop, wird mit jeder clockflake ein befehl ausgeführt,> verstehe ich das richtig?
Eein Befehl dauert 4 Clock Perioden, ein Sprungbefehl Dauer 2 Befehle
lang.
Hast schrieb:
> wenn ich Null dekrementiere, kommt null oder -1 raus?
Binar: 11111111 --> -1 oder 255 Je nachdem ob man die Zahl als
unsigened oder als 2er-Komplement sieht.
Hast schrieb:
> Bis hier stehen folgende Werte in den Variablen:> dc3 = 1> dc2 = 13> dc1 = 0> W = 13>> richtig?
Ja
Bei einer Frequenz von 31kHz dauert ein Befehl 129.032µs. Du musst also
77.5 Befehlszyklen lang nicht tun. Das könnte man theoretisch mit 77
Nops lösen, das macht man aber nicht, da es unnötig viel
Programmspeicher brauch.
Ich würde da eine einfache Delay-Schleife empfehlen und keine doppelt
verschachtelte:
delay10ms
movlw .24
decfsz WREG,f
goto $-1
return
Die schleife dauert dann:
2 Zyklen fürs Call
+ 1 Zyklus für movlw
+ 24*3 Zyklen für decfsz WREG,f goto $-1
+ 2 Zyklen für Retun
= 77
goto $-1 ist ein Sprung in die Zeile darüber. decfsz WREG,f wird also so
lange widerhohlt bis die Zahl im W null ist, also 24 Mal. Und jede
Wiederholung dauert 3 Befehlszyklen. Decfsz brauch einen und goto zwei.
Ich habe in einem neueren Quellcode
Hast schrieb:> BANKSEL OSCCON> MOVLW b'01101000' ; 4MHz HF-Oscillator> MOVWF OSCCON
Entdeckt also jetzt die Schleife für 4MHz:
Bei einer Frequenz von 4MHz dauert ein Befehl 1µs. Du musst also
10000 Befehlszyklen lang nicht tun.
Ich würde da eine verschachtelte Delay-Schleife empfehlen und keine
doppelt
verschachtelte:
delay10ms
movlw .156
movwf del_reg
delay
movlw .24
decfsz WREG,f
goto $-1
decfsz,del_reg,f
goto delay
return
Die innere Schleife dauert dann:
+ 1 Zyklus für movlw
+ 20*3 Zyklen für decfsz WREG,f goto $-1
= 61
Die gesamte Schleife dauert dann:
2 Zyklen fürs Call
+ 1 Zyklus für movlw
+ 1 Zyklus fürs movwf
+ 156*(3+ 61[Innere Schleife]) Zyklen
+ 2 Zyklen für Retun
= 9990 Zyklen
Das sind 10µs zu wenig. Wenn es genauer sein muss müsste man mit den
Schleifenzählern rumprobieren oder 10*Nop oder 5* goto $+1 einfügen
goto $-1 ist ein Sprung in die Zeile darüber. decfsz WREG,f wird also so
lange wiederholt bis die Zahl im W null ist, also 20 Mal in der inneren
Schleife.
Und jede Wiederholung dauert 3 Befehlszyklen. Decfsz braucht einen und
goto zwei.
Bei der äußeren Schleife funktioniert so ähnlich, nur dass immer die
ganze innere Schleife mit wiederholt wir und sie wird 156* ausgeführt
Morgen :)
Danke für die Hilfe, habs ausprobiert, funktioniert!
Eine Frage dazu:
M. H. schrieb:> delay10ms> movlw .156> movwf del_reg>> delay> movlw .24> decfsz WREG,f> goto $-1> decfsz,del_reg,f> goto delay> return>> Die innere Schleife dauert dann:> + 1 Zyklus für movlw> + 20*3 Zyklen für decfsz WREG,f goto $-1> = 61
Warum 20*3 und nicht 24*3?
Ist für dei Funktionalität nicht ausschlaggebend, da die Verzögerung
nicht genau sein muss, nur ca 10ms... Nur dem Interesse halber :)
Hast schrieb:> Warum 20*3 und nicht 24*3?
ICh habe die innere schleife kopiert und eingefürt, aer vergessen den
wert zu änder.
Es sollte movlw .20 stehen:
Perfekt,
Danke euch nochmals, vor allem Chris und M.H. ! Ich denke die beste
Methode, Assembler zu lernen ist (wie natürlich bei jeder
Programmiersprache), es selber an einem Projekt zu tun. Und wenn es
dabei Leute wie euch gibt, fällt es einem noch viel leichter! DANKE!
lG Hast