PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PIC Eieruhr asm



Schlapfi
25.04.2015, 12:06
30094

Mit nur 2 CALL befehle habe ich ein lernprogramm geschrieben.
Ich hoffe es hilft auch jemanden.

lg
Schlapfi




;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++
; Dateiname: Eieruhr ( Lernprogramm )
; LED mit 3 x 7 Segment Multiplex betrieb
; Autor: Schlapfi
; Datum: Dez. 2013
; PIC 16F631
;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++
; Funktionsbeschreibung des Programms:
; Prozessor-Takt 8 MHz Intern
; Mit PAUSE wird gewartet bis Timer-0 überleuft, über ZAEHL wird diese Zeit erweitert
; auf 1,000999 Sec.( für Eierkochen ausreichend, Fehlmessung 0,6 Sec. in 10 Min.)
; und "einer" wird mit diesem Takt hochgezählt.
; Anoden sind RB6 = hundert , RB5 = zehner, RB4 = einer
; einer = Sekunde-einer
; zehner = Sekunde-zehner
; hundert = Minute
; 7-Segment mit gemeinsamer Anode.
; Katoden ausgänge sind: PORT-C
;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++

list p=16F631
#include <p16f631.inc>

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _CPD_OFF

ERRORLEVEL -302 ; Unterdrücken BANK SELECTION MESSAGES

;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++
; Deklaration der Variablen und Konstanten
;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++

einer EQU H'0022' ; Variable für die Einerstelle der Zahl
zehner EQU H'0023' ; Variable für die Zehnerstelle der Zahl
hundert EQU H'0024' ; Variable für die Hunderterstelle der Zahl
ZAEHL EQU H'0025' ; Zähler für Sekunde

;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++

; Masken für das Einschalten der LEDs
; Masken sind wegen besserer beschaltung so gwählt
; und können leicht verändert werden.

; gfedcba
#define D0 B'01000000' ; 0
#define D1 B'01111001' ; 1
#define D2 B'00100100' ; 2
#define D3 B'00110000' ; 3
#define D4 B'00011001' ; 4
#define D5 B'00010010' ; 5
#define D6 B'00000010' ; 6
#define D7 B'01111000' ; 7
#define D8 B'00000000' ; 8
#define D9 B'00010000' ; 9

;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++
; Startadresse des PIC-Controllers nach einem RESET oder Neustart
;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++

org 0x000 ; Startadresse des PICs

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
; PIC Einstellungen
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
;
START
bsf STATUS,RP0 ; Auswahl Bank 1

movlw B'01110000' ; Int-Oszilator 8 MHz,
movwf OSCCON

movlw b'00000100' ; TMR0 vorbereiten (intern, Vorteiler 32,)
movwf OPTION_REG

movlw b'00000000' ; TRISC = Ausgang
movwf TRISC ; damit ist Port C = Ausgang
movwf TRISB ; und Port B
movwf TRISA ; und Port A

bcf STATUS,RP0 ; Auswahl Bank 0

; Interrupt
movlw b'10000000' ; alle
movwf INTCON ; Interrupts freigeben

movlw .250
movwf ZAEHL

clrf PORTA ; Löschen aller Ausgänge
clrf PORTB
clrf PORTC ; Löschen aller Ausgänge

clrf einer
clrf zehner ; Löschen
clrf hundert

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
; Hauptprogramm: LED Anzeigen 3x7 Segmente
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
LEDAnz
clrf PORTB ; Löschen aller Ausgänge
clrf PORTC
; führende null unterdrücken. ZEROPIT abfragen ob gesetzt.
movlw d'0' ; 0 nach " W " laden
addwf hundert,w ; inhalt von hundert dazu addieren, schreibe in " W "
btfsc STATUS,Z ; ist nicht null ? : überspringe nächste Zeile
goto $+9 ; wenn null, 9 Zeilen nach unten
movlw B'01000000' ; hunderter Katode
movwf PORTB ; und an PORTB ausgeben
movf hundert,w ; Laden der Variable "hundert" in das W-Register
call LED ; Sprung zum Laden der LED-Variablen
movwf PORTC ; und an PORTC ausgeben
call PAUSE

clrf PORTB ; Löschen aller Ausgänge
clrf PORTC
movlw B'00100000'
movwf PORTB
movf zehner,w ; Laden der Variablen "zehner" in das W-Register
call LED ; Sprung zum Laden der LED-Variablen
movwf PORTC
call PAUSE

clrf PORTB ; Löschen aller Ausgänge
clrf PORTC
movlw B'00010000'
movwf PORTB
movf einer,w ; Laden der Variablen "einer" in das W-Register
call LED ; Sprung zum Laden der LED-Variablen
movwf PORTC
call PAUSE
goto LEDAnz ; und wieder zu Anzeige

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
; Ab hier folgen die Unterprogramme
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
; PAUSE:
; wartet bis Timer0 überleuft ( alle 4 mSec. ) und verringert ZAEHL bis null,
; 4 mSec ( Timer0 ) X 250 ( ZAEHL ) = Eine Sekunde
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++

PAUSE
btfss INTCON,T0IF ; Pause von 4 ms Dauer
goto $-1 ; warten bis Interruptflag gesetzt ist
movlw 06 ; Timer-0 auf 250 stellen
movwf TMR0 ; W -> Timer-0
bcf INTCON,T0IF ; TMR0-Flag löschen

decfsz ZAEHL ; ZAEHL minus 1 bis 0
return ; ZAEHL > 0
movlw .250 ; ZAEHL = 0
movwf ZAEHL ; und wird neu geladen

; erhöht "einer" um 1 und alle weiteren stellen bei überlauf

incf einer,1 ; Erhöhen der Variablen "einer" um +1

movfw einer ; Laden der Variablen "einer" ins W-Register
sublw D'10' ; 10 in W laden und abziehen.
btfss STATUS,Z ; ergebnis = 0 : überspringe nächste Zeile
return ; rechenergebnis nicht 0 = return
; Ja, dann
incf zehner,1 ; Zählervariable für die zweite Stelle erhöhen
clrf einer ; einer löschen

movfw zehner ; Laden der Variablen "zehner" ins W-Register
sublw D'6' ; Maximaler Zählwert erreicht?
btfss STATUS,Z ; erreicht : überspringe nächste Zeile
return ; nicht 0 = return
; Ja, dann
incf hundert,1 ; Zhundert um 1 erhöhen
clrf zehner ; zehner löschen

movfw hundert ; Laden der Variablen "hundert" ins W-Register
sublw D'10' ; Maximaler Zählwert erreicht?
btfss STATUS,Z ; erreicht : überspringe nächste Zeile
return ; nicht 0 = return

clrf hundert ; Zählervariable löschen
return ; Rücksprung



;+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++

LED
addwf PCL,1
retlw D0
retlw D1
retlw D2
retlw D3
retlw D4
retlw D5
retlw D6
retlw D7
retlw D8
retlw D9


END

RoboHolIC
25.04.2015, 21:59
Hallo Schlapfi.

Zunächst willkommen im Forum.
Ist das dein Microcontroller-Erstlingswerk? Ein kleines feines Projekt!

Zunächst ein ehrliches Lob: Du hast deine Source recht gut dokumentiert, sowohl die Zusammenhänge des Codes in sich selbst, als auch die Zuordnung zur Hardware.
Man kann ein paar gute Anregungen aus deinem Code mitnehmen.

Es sind aber auch einige Fallstricke eingebaut, die ich gerne einem anderen Anfänger ersparen würde.
Ich liste ein paar auf, ohne Anspruch auf Wichtigkeit und Vollständigkeit:

- Interrupts global freizugeben, ohne tatsächlich eine Interruptroutine zu implementieren ist nicht nötig und ein wenig riskant. Du verwendest das TMR0-Überlaufflag. Dieses wird unabhängig von einer Interruptfreigabe erzeugt, allein durch den Überlauf von TMR0.
- Der Programmstart sollte mit einem Sprung zu einem 'sprechenden' Label beginnen, das den Startpunkt des Hauptprogramms erkennen lässt, also z.B. 'main' oder 'start'. Sprung deswegen, weil im Falle eines Interrupts das Hauptprogramm unterbrochen und die Befehlsausführung beim Programmspeicherplatz Nr. 4 fortgesetzt wird. Es ist sicherer, gleich von Anfang an diese Speicherzuordnung im Programm anzulegen, damit man nicht später beim Einstig in die Interruprprogrammierung genau deswegen auf die Nase fällt. Ein Rücksprung RETFIE an dieser Stelle schafft dann zusätzliche 'Sicherheit' gegen unliebsame Überraschungen bei weiteren Experimenten in der ASM-Programmierung. Die von Microchip mitgelieferten Programmrohlinge (Templates) bilden genau dieses sichere Grundgerüst auch ab.
- Ein Sprung GOTO $-1 ist legitim, wenn es sich um ein quasi 'atomares' Programmelement zum Warten auf ein Flag handelt; da wird man nicht versehentlich einen weiteren Befehl einschieben und erspart sich das Erfinden eines Labelnamens. Anders dagegen ein GOTO $+9: Das birgt das Risiko, bei der Änderung des übersprungenen Codeabschnitts zu ungewollten Sprungzielen zu führen; hierfür gibt es benannte Sprungmarken (Labels). Die sollte man auch verwenden.
- ganz böse Falle:

LED
addwf PCL,1
retlw D0
retlw ...Dieses Konstrukt funktioniert nur so lange, bis bei der Manipulation von PCL ein Überlauf geschieht. Entweder muss dieser Fall abgefangen werden oder man platziert das Unterprogramm LED per org-Zeile so, dass es unverrückbar an sicherer Stelle im Programmspeicher steht, dann aber am besten gaaaaaanz weit hinten am Ende eines Codesegments! Jetzt funktioniert dein Programm zwar, aber jede Erweiterung birgt das Risiko, dass das UP 'LED' an eine gefährliche Stelle innerhalb des Codespeichers rutscht und der Programmablauf sich im Nirvana verirrt. Genaueres über die Table Read-Methodik findet sich in der App Note AN556 bei Microchip.

Gruß
Christian.

Schlapfi
26.04.2015, 18:13
Hallo RoboHolIc
Möchte mich vorstellen:
Bin Baujahr 1955 habe 2 handwerkliche Berufe erlernt,
1987 eine Elektriker ausbildung absolviert, ( aber nie ausgeübt )
kam vor einigen Jahren zu PIC-Controller, da ich eine Spezial-IC
wollte und mein Elektroniker meinte "Die gibt es nicht mehr"
er könne mir einen Mikrokontroller verkaufen.
Daher kaufte ich mir ein PICkit 1 und wollte mir diese IC "selbermachen".
Da ich der englischen Sprache nicht mächtig bin, fällt es mir seeeehr schwer
diverse MICROCHIP Datenplätter zu Identifizieren,
und habe mich mühhhhsam über deutschsprachige Bücher zu meinen wissen durchgekämpt.
Da im Buch "PICs für Einsteiger" von Thorsten Mumm die Tabellen so beschrieben sind.
Die Programmsprünge mit $+x habe ich in div anderen Bücher so gelesen zb.
Franzis Buch "Programieren vom PIC-Mikrocontrollern" von Dieter Kohtz
das dieses Fehlerbehaftet ist habe ich eventuell überlesen.
Aber ich glaube ich bin nicht der einzige der mit PIC so seine anfängerproblemchen hat
deswegen habe ich dieses Programm eingestellt , ohne 1000 call wo sich der anfänger
nicht mehr durchblickt ( Sprut )
Grüsse:
Schlapfi

RoboHolIC
26.04.2015, 23:24
Hallo Schlapfi.

Da hast du in Sachen Microcontroller anscheinend eine echte Ochsentour durchgemacht! Leider hast du dabei auch eher schlechte Lehrbücher erwischt.

Keinesfalls will ich dein Programm schlecht machen. Schließlich tut es ja, was es soll. Die Sache mit den Tabellen ist aber ein 'gutes' Beispiel für schlechte Lernbeispiele; das Springen mittels GOTO $+x ist gleich das nächste! Beides funktioniert so wie es dasteht, ist aber nicht zur Nachahmung zu empfehlen.

Dass du aus verschiedenen Quellen Material verwendet hast, habe ich schon anhand der verschiedenen Schreibweisen für Konstanten vermutet (z.B. der konstante Wert '06' ist oktal, also auf der Basis von 8 zu interpretieren; diese Darstellungsform wird selten verwendet, hat kaum Nutzwert und kann leicht als dezimal missverstanden werden).

Also nochmals: Ich will dich nicht abbürsten und dein Projekt schlechtmachen, sonden die Probleme aufzeigen, die dort angelegt sind.
Microchip liefert - gut versteckt auf dem PC - für (nahezu) jeden Controller eine Muster-Programmdatei, mit der ich von Anfang an sehr gut gefahren bin.
Das für deinen Controller passende Musterprogramm habe ich gleich hier reingestellt. Als Übung könntest du mal versuchen, dein Projekt in diesen Rahmen einzubauen.



;************************************************* *********************
; This file is a basic code template for assembly code generation *
; on the PIC16F631. This file contains the basic code *
; building blocks to build upon. *
; *
; Refer to the MPASM User's Guide for additional information on *
; features of the assembler (Document DS33014). *
; *
; Refer to the respective PIC data sheet for additional *
; information on the instruction set. *
; *
;************************************************* *********************
; *
; Filename: xxx.asm *
; Date: *
; File Version: *
; *
; Author: *
; Company: *
; *
; *
;************************************************* *********************
; *
; Files Required: P16F631.INC *
; *
;************************************************* *********************
; *
; Notes: *
; *
;************************************************* *********************


list p=16f631 ; list directive to define processor
#include <p16f631.inc> ; processor specific variable definitions


; '__CONFIG' directive is used to embed configuration data within .asm file.
; The labels following the directive are located in the respective .inc file.
; See respective data sheet for additional information on configuration word.

__CONFIG _FCMEN_ON & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT



;***** VARIABLE DEFINITIONS
w_temp EQU 0x7D ; variable used for context saving
status_temp EQU 0x7E ; variable used for context saving
pclath_temp EQU 0x7F ; variable used for context saving


;************************************************* *********************
ORG 0x000 ; processor reset vector

nop
goto main ; go to beginning of program


ORG 0x004 ; interrupt vector location

movwf w_temp ; save off current W register contents
movf STATUS,w ; move status register into W register
movwf status_temp ; save off contents of STATUS register
movf PCLATH,w ; move pclath register into w register
movwf pclath_temp ; save off contents of PCLATH register

; isr code can go here or be located as a call subroutine elsewhere

movf pclath_temp,w ; retrieve copy of PCLATH register
movwf PCLATH ; restore pre-isr PCLATH register contents
movf status_temp,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf w_temp,f
swapf w_temp,w ; restore pre-isr W register contents
retfie ; return from interrupt



main

; remaining code goes here




; example of preloading EEPROM locations

ORG 0x2100
DE 5, 4, 3, 2, 1

END ; directive 'end of program'



Frag einfach, wenn du dort etwas nicht verstehst.

Gruß
Christian.

Schlapfi
27.04.2015, 18:03
Hallo RoboHolIc

Danke für deine Antwort.
Das hier war mein erstes richtiges Programm.

Voltmeter bis 15 Volt


;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Dateiname: Voltmeter ( Lernprogramm )
; Autor: Schlapfi
; Datum: Sept. 2013
; Kontroller: 16F676
; Das Programm kann mit wenigen veränderungen auch für andere PIC's verwendet werden.
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Funktionsbeschreibung des Programms:
; Spannungsmessung bis 15.0 Volt. 3 stellige LED-anzeige mit Multiplex betrieb.
; An RA0 wird eine spannung gemessen und an RC0 - RC3 im BCD code ausgegeben
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; 10K 10K 10K
; - 0 V ____ ____ ____ + 15V
; ------|____|-----+-------|____|------------|____|------
; |
; ----------- || --+--- RA0 ->
; C 1µF
;
; Bordspsnnung ( Auto ) wird durch 3 geteilt,
; durch 3 Widerstände a,10 K Ohm.
; Von RA0 ( AD-Eingang ) einen Kondensator 1µF nach Masse stabilisiert
; die Anzeige. ( verhindert laufen )
; Der 5 Volt Messbereich des PIC entspricht dann 15 Volt.
; Das Ergebnis der Messung wird mit dem Faktor 3 Multipliziert
; und an RC1 bis RC4 im BCD code ausgegeben.
; Katoden sind RA5 = hundert ( 10 V ) ,
; RA1 / RA4 = zehner ( einerstelle ) und Dp.,
; RA2 = einer.( nachkommastelle )
; Die Katoden werden über Transistoren gesteuert der Dp. kann direkt gesteuert werden.
; BCD ausgänge sind: RC4 = 1 , RC1 = 2 , RC2 = 4 , RC3 = 8
; diese steuern einen CMOS-4511 7-Segment-Dekoder.
; Es können auch andere BCD zu 7 Segment decoder verwendet werden.( zb. 4513 oä.)
; Die Pinbelegung des PIC wurde wegen der Pinanordnung des 4511 so gewählt.
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
list p=16F676
#include <p16f676.inc>

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _CPD_OFF

ERRORLEVEL -302 ; Unterdrücken BANK SELECTION MESSAGES
;
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Deklaration der Variablen und Konstanten
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
;
schleife EQU H'0020' ; Anzahl der "schleifen"
help EQU H'0021' ; Hilfsvariable für verschiedene Aufgaben.
einer EQU H'0022' ; Variable für die Einerstelle der Zahl
zehner EQU H'0023' ; Variable für die Zehnerstelle der Zahl
hundert EQU H'0024' ; Variable für die Hunderterstelle der Zahl
ADC_L EQU H'0026' ; -----------------""---------------------
ADC_H EQU H'0027' ; Variablen
ADC_L1 EQU H'0028' ; weden für rechenoperationen gebraucht
ADC_H1 EQU H'0029' ; -----------------""---------------------
count EQU H'002C' ; -----------------""---------------------

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

; Masken für das Einschalten der LEDs
; Masken sind wegen der Eingänge des C-MOS 4511 so gwählt,
; und können für andere zwecke leicht verändert werden.

#define D0_AN B'00000000' ; 0
#define D1_AN B'00010000' ; 1
#define D2_AN B'00000010' ; 2
#define D3_AN B'00010010' ; 3
#define D4_AN B'00000100' ; 4
#define D5_AN B'00010100' ; 5
#define D6_AN B'00000110' ; 6
#define D7_AN B'00010110' ; 7
#define D8_AN B'00001000' ; 8
#define D9_AN B'00011000' ; 9

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

w equ 0 ; w ist Zielregister
f equ 1 ; f ist Zielregister

org 0 ; Start bei Adresse 0

goto START

org 04 ; feste Interrupt-Service-Adresse

START
bsf STATUS,RP0 ; Auswahl Bank 1
call 0x3FF ; Laden des Kalibrierungswertes
movwf OSCCAL ; Schreiben des Kalibrierungswertes für den internen Takt

movlw B'00010001' ; FOCS 8 und AN0
movwf ANSEL

movlw b'00000001' ;setze RA0 als Eingang und den Rest als Ausgang
movwf TRISA

movlw b'00000000' ;alle Pins von Port C sind Ausgänge
movwf TRISC

movlw B'00000011' ; Pull-ups abschalten, interne clock, Teiler 1:16
movwf OPTION_REG ; Schreiben des OPTION-Registers

bcf STATUS,RP0 ; Zurück zu Bank 0

movlw B'10000001' ; Rechtsbündig, AN 0, AD an
movwf ADCON0

clrf PORTA ; Löschen aller Ausgänge
clrf PORTC ; Löschen aller Ausgänge

clrf einer
clrf zehner
clrf hundert

movlw d'20'
movwf count


;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Hauptprogramm
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Hier wird der AD-Wandler 25 X abgefragt und zu ADC_L und ADC_H addiert.
; Entspricht einer multiplikation mit 25

Haupt
clrf ADC_L
clrf ADC_L1
clrf ADC_H
clrf ADC_H1
clrf help
movlw d'25' ; 25 in W laden
movwf help ; W in help laden
Prog call ADMess ; gehe zu unterprogramm ADMess
decfsz help,f ; 1 von help abziehen, prüfen ob null
goto Prog ; nicht null gehe zu Prog
bcf STATUS,C ; null erreicht lösche C bit
movfw ADC_L ; ADC_L in W laden
movwf ADC_L1 ; W in ADC_L1 kopieren
movfw ADC_H ; -"-
movwf ADC_H1
goto ADAD
ADMess
bsf STATUS,RP0 ; Auswahl Bank 1
clrf ADRESL ; niederwertigen Teil des AD-Ergebnisses löschen
bcf STATUS,RP0 ; Auswahl Bank 0

clrf ADRESH ; höherwertigen Teil des AD-Ergebnisses löschen
bsf ADCON0,GO ; A/D-Wandler starten

btfsc ADCON0,GO ; Ist der A/D-Wandler fertig?
goto $-1 ; Nein, weiter warten ( eine Zeile zurück )

movf ADRESH,w ; Dezimalwert aus ADH-Register in W schreiben
; movlw d'3' ; wird mit " NOP " überschrieben dient nur zur überprüfung
addwf ADC_H,f ; addiere W zu ADC_H und schreibe in F
bsf STATUS,RP0 ; Auswahl Bank 1
movf ADRESL,w ; Dezimalwert aus ADL-Register in W schreiben
bcf STATUS,RP0 ; Auswahl Bank 0
; movlw d'70' ; wird mit " NOP " überschrieben dient nur zur überprüfung
; ADC_H + ADC_L ergeben 12,3 Volt
bcf STATUS,C ; C löschen
addwf ADC_L,f ; addiere W zu ADC_L und schreibe in F
btfsc STATUS,C ; übertrag erfolgt ?
incf ADC_H,f ; addiere eins
return

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; ADC_L und ADC_H Werden mit 3 multipliziert
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

ADAD clrf schleife ; lösche schleife
movlw d'2' ; 2 in W laden, einmal steht es schon im Speicher
movwf schleife ; wert aus W in schleife

ADD_3 bcf STATUS,C ; C löschen
movf ADC_L1,w ; ADC_L1 in W laden
addwf ADC_L,f ; addiere W zu ADC_L
btfsc STATUS,C ; überlauf erfolgt ? kein überlauf überspringe
incf ADC_H,F ; bei überlauf addiere 1 zu ADC_H
movf ADC_H1,w ; lade ADC_H1 in W
addwf ADC_H,f ; addiere W zu ADC_H und schreibe nach F
decfsz schleife,f ; eins von schleife abziehen bis null,
; wenn null überspringe nächsten befehl.
goto ADD_3 ; gehe zu

; normalerweise wird ADC_H und ADC_L jetzt durch 256 dividiert = 9 X durch 2 dividieren
; ADC_H ein mahl rechts ADC_L wird nicht mehr gebraucht.Das Messergebnis steht in ADC_H

rrf ADC_H,w ; dividiere durch 2 und schreibe nach W
; in ADC_H steht jetzt das endergebnis
movwf help ; gemessene Spannung ( ADC_H ) in help schreiben

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; auswertung des endergebnis von Hexa in Dezimahl
; help wird zerlegt in einer , zehner und hunderter
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

clrf schleife
bcf STATUS,C
movlw d'100' ; lade 100 in W Register
sub_H
subwf help,F ; 100 von help abziehen und in F schreiben
btfsc STATUS,C ; übertrag erfolgt ?
goto ergeb_Pos_H ; kein übertrag gehe zu ergeb_Pos_H
addwf help,f ; addiere W ( 100 ) zu help
movfw schleife ; schleife in W Register laden
movwf hundert ; anzahl der durchläufe ist jetzt hundert
clrf schleife ; lösche schleife
bcf STATUS,C ; Carryflag löschen
movlw d'10' ; lade 10 in W Register
goto sub_Z
ergeb_Pos_H
incf schleife,F ; schleife + 1
goto sub_H ; und noch einmal
sub_Z
subwf help,F ; 10 von help abziehen
btfsc STATUS,C ; übertrag erfolgt ?
goto ergeb_Pos_Z ; kein übertrag gehe zu ergeb_Pos_Z
addwf help,f ; addiere W ( 10 ) zu help
movfw schleife ; schleife in W Register laden
movwf zehner ; anzahl der durchläufe ist jetzt zehner
movfw help ; der rest
movwf einer ; ist einer
bcf STATUS,C
goto Anz

ergeb_Pos_Z
incf schleife,F ; schleife + 1
goto sub_Z
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Ansteuerung einer 3 X 7 Segment Anzeige ( Multiplex )
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

Anz
clrf PORTA ; Löschen aller Ausgänge
clrf PORTC
; führende null unterdrücken. ZEROPIT abfragen ob gesetzt.
movlw d'0' ; 0 nach " W " laden
addwf hundert,w ; inhalt von hundert dazu addieren, schreibe in " W "
btfsc STATUS,Z ; ist nicht null ? : überspringe nächste Zeile
goto An2 ; wenn null, 6 Zeilen nach unten
movlw B'00100000' ; hunderter Katode
movwf PORTA ; und auf PORTA ausgeben
movf hundert,w ; Laden der Variable "hundert" in das W-Register
call LED ; Sprung zum Laden der LED-Variablen
movwf PORTC ; und auf PORTC ausgeben
call PAUSE ; Pause 4 mSek.
An2
clrf PORTA ; Löschen aller Ausgänge
clrf PORTC
movlw B'00010010' ; zehner Katode
movwf PORTA
movf zehner,w ; Laden der Variablen "zehner" in das W-Register
call LED ; Sprung zum Laden der LED-Variablen
movwf PORTC
call PAUSE

clrf PORTA ; Löschen aller Ausgänge
clrf PORTC
movlw B'00000100'
movwf PORTA
movf einer,w ; Laden der Variablen "einer" in das W-Register
call LED ; Sprung zum Laden der LED-Variablen
movwf PORTC
call PAUSE
decfsz count ; count minus 1 bis 0
goto Anz ; count nicht 0
movlw d'20'
movwf count
goto Haupt ; count = 0, neue AD-Messung

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
; Ab hier folgen die Unterprogramme
;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
;Warteschleife Pause

PAUSE ; Label, wo die Standardwarteschleife beginnt
movlw d'199' ; Dezimalwert für die Zeitkonstante damit "PAUSE" = 1ms ist
movwf schleife ; Schieben des Wertes 199 in die Variable "schleife"
; Ende der Start-Eeinstellungen für "PAUSE1ms"

PAUSEE ; Einsprungstelle für Pausenschleife
nop
nop
decfsz schleife,1 ; Dekrementiere die Variable "schleife" um 1 und schreibe das Ergebnis zurück in "schleife"
goto PAUSEE ; Ist die Zeit verstrichen? Nein, dann springe zurück zu "PAUSEE"
; Neuer Durchlauf bis "schleife"= 0
return





;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

LED
addwf PCL,1
retlw D0_AN
retlw D1_AN
retlw D2_AN
retlw D3_AN
retlw D4_AN
retlw D5_AN
retlw D6_AN
retlw D7_AN
retlw D8_AN
retlw D9_AN

;+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++

END


Dieses arbeitet seit über einem Jahr zuverlässig.
Ich dachte ich brauch org 0x00 und org 0x04 nur dann,
wenn ich die ISR verwende. Möchte meine Programme
nicht unnötig verkomplizieren.
Der PIC fängt ja immer bei 0x00 an,
oder sehe ich da etwas falsch.
Wie kann ich das mit den Tabellen besser machen ?

Grüsse Schlapfi

30101

RoboHolIC
27.04.2015, 22:56
Hallo Schlapfi.


Ich dachte ich brauch org 0x00 und org 0x04 nur dann, wenn ich die ISR verwende.
'org 0x00' ist beim PIC16F676 für den ersten ausführbaren Befehl implizit gegeben. 'org 0x04' wird nur in Verbindung mit Interrupts wirklich benötigt. Alles so richtig.
Dann sollte man aber konsequenterweise auch das GIE-Bit nicht setzen!


Möchte meine Programme nicht unnötig verkomplizieren.
Bei sehr kleinen Programmen (=am Anfang) wirkt die Strukturierung oft überflüssig, aufblähend. Das ändert sich mit steigender Codegröße sehr bald. Ein Gerücht sagt, dieser Effekt ziehe sich durch praktisch alle Programmiersprachen hindurch! Strukturierung macht Programme i.d.R. robuster gegenüber Änderungen, besser lesbar, pflegeleichter ... Der effektive Code im Controller wird dabei nicht oder nur unwesentlich größer, lediglich der Sourcecode nimmt an Umfang zu.


Der PIC fängt ja immer bei 0x00 an, oder sehe ich da etwas falsch.
Ja, dieser PIC macht das so. Es gibt (gab) bei den PICs aber auch ganz andere Konzepte. Es ist gut und hilfreich, mittels org-Anweisungen das jeweilige Programmspeicherkonzept im ASM-Code abzubilden. So kann man beispielsweise die Codesegmente eines PIC16F876 mittels org 0x0000, org 0x0800, org 0x1000 und org 0x1800 im ASM-Programm bekannt machen und verschiedene Unterprogramme dort gezielt platzieren. Das hilft sehr im effizienten Umgang mit der Codesegmentierung und der Assembler hat die Möglichkeit, Adressüberschneidungen zu erkennen.


Wie kann ich das mit den Tabellen besser machen ?
Ich kann das nicht besser erklären als die genannte App Note AN556. Grob und aus der Erinnerung heraus gesagt besteht das Problem darin, dass ein simples 'addwf PCL' nur die acht niederwertigsten der (maximal) dreizehn Adressbits beeinflusst! Tritt bei der Addition ein Überlauf von PCL auf, dann muss PCH inkrementiert werden. So ist die Tabellenfunktion wasserdicht programmiert. Insgesamt sind das etwa fünf oder sechs Codezeilen, die dir im Fall des Falles viele Haarbüschel auf deinem Kopf retten werden!

Gruß
Christian.

Schlapfi
28.04.2015, 19:23
Hallo RoboHolIC.

Danke für deine Tips, werde mir das genauer ansehen.

Grüsse Schlapfi