Forum: Mikrocontroller und Digitale Elektronik Assemberhilfe PIC 12LF1822


von Hast (Gast)


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!

von Hast (Gast)


Lesenswert?

Ist die Frage so blöd gestellt oder kann mir dabei wirklich keiner 
helfen?

von chris (Gast)


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.

von Max H. (hartl192)


Lesenswert?

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.

von John (Gast)


Lesenswert?

Hier ist auch noch eine Erklärung, wie eine Tabelle funktioniert (in 
Flash)
http://www.pictutorials.com/Lookup_Table_Example.swf

von Chris B. (dekatz)


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.

von Hast (Gast)


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!!!

von Hast (Gast)


Lesenswert?

Seite 26 im Datenblatt sollte das heisen...

von Chris B. (dekatz)


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.

von chris (Gast)


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.

     ......................

von Hast (Gast)


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?

von Hast (Gast)


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?

von hmm (Gast)


Lesenswert?

"Assemberhilfe"?

von chris (Gast)


Lesenswert?

genau

von Hast (Gast)


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?

von Hast (Gast)


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

von chris (Gast)


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
 ...

von Max H. (hartl192)


Lesenswert?

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.

von chris (Gast)


Lesenswert?

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

von Hast (Gast)


Lesenswert?

Vielen, vielen Dank für Eure Hilfe! Ich bekomm morgen die Hardware und 
werde die Software gleich testen!

von chris (Gast)


Lesenswert?

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.

von Max H. (hartl192)


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.

: Bearbeitet durch User
von Hast (Gast)


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?

von Max H. (hartl192)


Lesenswert?

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.

von Hast (Gast)


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

von Hast (Gast)


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?

von chris (Gast)


Lesenswert?

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.

von Hast (Gast)


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?

von chris (Gast)


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

von chris (Gast)


Lesenswert?

Wurde aber nicht bereits gesagt, DAC Bits sollten in DACCON1 ?

von Hast (Gast)


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

von Hast (Gast)


Lesenswert?

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:
1
  CALL DAC_TABLE
2
  BANKSEL DACCON1
3
  MOVWF DACCON1

von Chris B. (dekatz)


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.

von chris (Gast)


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.

von Hast (Gast)


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...

von chris (Gast)


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.

von Hast (Gast)


Lesenswert?

Vielen vielen Dank, ihr seid echt spitze!!!

von Hast (Gast)


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?

von Erich (Gast)


Lesenswert?

Mein Ansatz:
Den Kram in C programmieren.
Dann braucht man sich auch nicht mehr "händisch" um diese unsäglichen 
Bankregister kümmern.
Gruss

von Erich (Gast)


Angehängte Dateien:

Lesenswert?

Achja:
Ein Beispiel nachtragen für diesen Prozessor.

http://chitose6thplant.web.fc2.com/n_4digit/config_pikapika.html

Datei anbei mit Nachträgen für Compiler PICC 9.83

Gruss

von Hast (Gast)


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?

von Max H. (hartl192)


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.

: Bearbeitet durch User
von chris (Gast)


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

von chris (Gast)


Lesenswert?

JALv2 ist eine Alternative zu C und ist auch im Kommerziellen Umfeld 
kostenlos.

von Max H. (hartl192)


Lesenswert?

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!

von chris (Gast)


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.

von Hast (Gast)


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

von Hast (Gast)


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?

von chris (Gast)


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
>

von Hast (Gast)


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?

von Max H. (hartl192)


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.

: Bearbeitet durch User
von Max H. (hartl192)


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

: Bearbeitet durch User
von Hast (Gast)


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 :)

von Max H. (hartl192)


Lesenswert?

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:
1
 delay10ms
2
   movlw .156
3
   movwf del_reg
4
5
 delay
6
   movlw .20
7
   decfsz WREG,f
8
   goto $-1
9
   decfsz,del_reg,f
10
   goto delay
11
   return

von Hast (Gast)


Lesenswert?

Ok, dachte ich mir...

Also habe ich in diesem Fall mit 24 eine Verzögerung von ca 11,8 ms. 
Richtig?

von Max H. (hartl192)


Lesenswert?

Hast schrieb:
> Also habe ich in diesem Fall mit 24 eine Verzögerung von ca 11,8 ms.
> Richtig?

Ja. Genauer sinds 11.862ms.

von Hast (Gast)


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

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
Noch kein Account? Hier anmelden.