Assemberhilfe PIC 12LF1822

Gast #3420085
Lesenswert?

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:
1
 PAGE ;===================================================================================
2
; RAM VARIABLES
3
;=========================================================================================
4

5
;-----------------------------------------------------------------------------------------
6
; all Banks
7
;-----------------------------------------------------------------------------------------
8

9
cblock  0x070 ; von jeder bank zugriff?
10
; hier tabelle anlegen:
11
DAC_TABLE addwf PCL,1
12
          retlw d'1'
13
          retlw d'3'
14
              .
15
              .
16
              . ; für alle 16 Werte  
17

18
endc

Ist das so korrekt?

Zum Aufruf der Tabelle:
1
  movlw    0x070
2
  movwf    PCLATH
3
  movf     TWert,0
4
  call     DAC_TABLE

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!
Gast #3420254
Lesenswert?

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.
#3420295
Lesenswert?

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.
Gast #3420301
Lesenswert?

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!!!
#3420306
Lesenswert?

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.
Gast #3420333
Lesenswert?

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.

     ......................
Gast #3420362
Lesenswert?

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?
Gast #3420366
Lesenswert?

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?
Gast #3420420
Lesenswert?

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?
Gast #3420475
Lesenswert?

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
15
  MOVWF DACCON0
16
  GOTO MAIN

Kann das so funktionieren?

Danke, lG
Gast #3420540
Lesenswert?

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
 ...
#3420575
Lesenswert?

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.
Gast #3420612
Lesenswert?

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?
Gast #3420917
Lesenswert?

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
Gast #3420926
Lesenswert?

Anbei jetz der ausgebesserte (fertige?) code (Teile davon):
1
.
2
.
3
.PAGE ;===================================================================================
4
; I/O PORTS
5
;=========================================================================================
6

7

8
; PORT A
9
#define  D0  RA3  ; DI  rotary switch input 0
10
#define  D1  RA2  ; DI  rotary switch input 1
11
#define  D2  RA5  ; DI  rotary switch input 2
12
#define  D3  RA4  ; DI  rotary switch input 3
13

14
.
15
.
16
.
17

18
 SUBTITLE " ---> Interrupt"
19
 PAGE ;===================================================================================
20
; RESET
21
;=========================================================================================
22
  org  _RESET
23
  ERRORLEVEL  -306  ; disable page boundary warning
24
  GOTO  INIT  ; Go to initialization procedure
25
  ERRORLEVEL  +306  ; disable page boundary warning
26

27
;=========================================================================================
28
; INTERRUPT service routine
29
;=========================================================================================
30
  org  _INTERRUPT
31
ISR      ; interrupt service routine
32
  RETFIE
33
;=========================================================================================
34
; End of Interrupt service routine
35
;=========================================================================================
36

37
;=========================================================================================
38
; Table for DAC
39
;=========================================================================================
40

41
DAC_TABLE   addwf PCL,F ; add current value in W to PCL
42
      retlw d'0'
43
      retlw d'2'
44
      retlw d'4'
45
      retlw d'5'
46
      retlw d'6'
47
      retlw d'7'
48
      retlw d'8'
49
      retlw d'10'
50
      retlw d'12'
51
      retlw d'14'
52
      retlw d'16'
53
      retlw d'19'
54
      retlw d'22'
55
      retlw d'25'
56
      retlw d'28'
57
      retlw d'31'
58

59
;=========================================================================================
60
; main program
61
;=========================================================================================
62

63
MAIN  
64
  CRLW ; clear working reg
65
  BTFSC D0
66
  IORLW 1
67
  BTFSC D1
68
  IORLW 2
69
  BTFSC D2
70
  IORLW 4
71
  BTFSC D3
72
  IORLW 8
73

74
  CALL DAC_TABLE
75
  MOVWF DACCON0
76

77
  BRA  MAIN
78
  
79
  
80
;=========================================================================================
81
; Initialization
82
;=========================================================================================
83

84
INIT
85
  BANKSEL  OSCCON
86
  MOVLW  b'01101000'  ; 4MHz HF-Oscillator
87
  MOVWF  OSCCON
88
;-----------------------------------------------------------------------------------------
89
; initialize PCLATH storage
90
;-----------------------------------------------------------------------------------------
91
  MOVLW  high(M_PCLATH-1)
92
  MOVWF  FSR1H
93
  MOVLW  low(M_PCLATH-1)
94
  MOVWF  FSR1L
95
;-----------------------------------------------------------------------------------------
96
; initialize IOs
97
;-----------------------------------------------------------------------------------------
98
  BANKSEL  WPUA  ; disable all individual pullups
99
  CLRF  WPUA
100
  BANKSEL  OPTION_REG  ; global enable pullups
101
  BCF  NOT_WPUEN
102
  
103
  SET_D_IN_PU  D0  ; digital input with pullup
104
  SET_D_IN_PU  D1  ; digital input with pullup
105
  SET_D_IN_PU  D2  ; digital input with pullup
106
  SET_D_IN_PU  D3  ; digital input with pullup
107

108

109
;-----------------------------------------------------------------------------------------
110
; initialize DAC
111
;-----------------------------------------------------------------------------------------
112
  BSF    DACCON0,7  ; DAC Enable
113
  BSF    DACCON0,5  ; use DACOUT for DAC output  
114
  BCF    DACCON0,2  ; select Vdd as reference
115
  BCF    DACCON0,3
116

117
;-----------------------------------------------------------------------------------------
118
; jump to main program
119
;-----------------------------------------------------------------------------------------
120
  
121
  GOTO  MAIN
122

123
;=========================================================================================
124
; end of main program
125
;=========================================================================================
126

127
 END
128
;=========================================================================================

set_d_in_pu ist ein makro, das den pin als digitalen eingang definiert 
und die pullups aktiviert.

Könnte das so funktionieren?
Gast #3420982
Lesenswert?

Dank für den hinweis,

neuer Auschnitt aus der Init:
1
;-----------------------------------------------------------------------------------------
2
; initialize DAC
3
;-----------------------------------------------------------------------------------------
4
  BSF    DACCON0,7  ; DAC Enable
5
  BSF    DACCON0,5  ; use DACOUT for DAC output  
6
  BCF    DACCON0,2  ; select Vdd as reference
7
  BCF    DACCON0,3
8

9
;-----------------------------------------------------------------------------------------
10
; clear status reg
11
;-----------------------------------------------------------------------------------------
12

13
  CLRF STATUS
14

15

16
;-----------------------------------------------------------------------------------------
17
; jump to main program
18
;-----------------------------------------------------------------------------------------
19
  
20
  GOTO  MAIN
21

22
;=========================================================================================
23
; end of main program
24
;=========================================================================================

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?
Gast #3420985
Lesenswert?

oder viel besser:
;----------------------------------------------------------------------- 
------------------
; 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 DACCON0
  MOVWF DACCON0
Gast #3420992
Lesenswert?

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
#3421006
Lesenswert?

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.
Gast #3421009
Lesenswert?

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.
Gast #3421080
Lesenswert?

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...
Gast #3421133
Lesenswert?

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.
Gast #3421601
Lesenswert?

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?
Gast #3421686
Lesenswert?

Das nächste Projekt werde ich sicher auch in C machen...

Dieses würde ich gerne in Assembler fertig machen, hab meine Main auch 
schon umgeschrieben:
1
MAIN
2
  BANKSEL  PORTA
3
  MOVLW   10
4
  MOVWF  counter
5

6
  LOOP
7
                  ;HIER Verzögerung 10ms einfügen
8
    MOVWF  TEMP_REG
9
    CLRW  
10

11
    BTFSS  PIN_D0
12
    IORLW  1
13
    BTFSS  PIN_D1
14
    IORLW  2
15
    BTFSS  PIN_D2
16
    IORLW  4
17
    BTFSS  PIN_D3
18
    IORLW  8
19

20
    SUBWF  TEMP_REG,1  ; if equal -> zero bit set
21

22
    BTFSC  STATUS,Z  ;is zero flag not set?
23
    DECFSZ counter
24

25
                  //Hier: wenn zero bit nicht gesetzt- counter zurück auf 10 setzen, damit wirklich 10 AUFEINANDERFOLGENDE, gleiche werte erkannt werden
26
    BRA LOOP
27

28
  CALL DAC_TABLE
29
  BANKSEL DACCON1
30
  MOVWF DACCON1
31

32
  BRA  MAIN

Kann da bitte mal ein Assembler experte drüber schaun?
#3421720
Lesenswert?

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.
Gast #3421785
Lesenswert?

MAIN
  BANKSEL  PORTA
  MOVLW   10
  MOVWF  counter

  LOOP

    call dly_10ms
                  ;HIER Verzögerung 10ms einfügen
    MOVWF  TEMP_REG
    CLRW

    BTFSS  PIN_D0
    IORLW  1
    BTFSS  PIN_D1
    IORLW  2
    BTFSS  PIN_D2
    IORLW  4
    BTFSS  PIN_D3
    IORLW  8

    xorwf  TEMP_REG,0  ; if equal -> zero bit set
    bz     MAIN
    DECFSZ counter
    goto   LOOP

    xorwf  TEMP_REG,1
    movf   TEMP_REG,0

  CALL DAC_TABLE
  BANKSEL DACCON1
  MOVWF DACCON1

  BRA  MAIN

dly_10ms
        movlw 1

delay10ms ; W * 10ms delay
  movwf dc3
dly2
  movlw .13
  movwf dc2
  clrf  dc1
dly1
  decfsz dc1,f
  goto  dly1
  decfsz  dc2,f
  goto  dly1
  decfsz  dc3,f
  goto  dly2
  retlw  0
Gast #3421811
Lesenswert?

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.
Gast #3421829
Lesenswert?

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
Gast #3421859
Lesenswert?

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?
Gast #3421993
Lesenswert?

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
>
Gast #3422123
Lesenswert?

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?
#3422132
Lesenswert?

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.
#3422175
Lesenswert?

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
Gast #3422495
Lesenswert?

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 :)
Gast #3422644
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren