;*******************************************************************
;                  Motordrehzahlsteuerung.ASM
;	            mit Pulsbreitenmodulation
;                     fr Elektrobuggy
;                PIC16C84 Voggeneder Andreas
;*******************************************************************

; Oszillator Frequenz 4 MHz

; Hebel oben  ... ca. 512
; Hebel unten ... ca. 1024

; PWM ... PA0
; DIR ... PA1
; FERN .. PB0
; SAVE_O ... PB1    Obere Hebelstellung speichern
; SAVE_U ... PB2    Untere Hebelstellung speichern
; SAVE TZ .. PB3    Tote Zohne speichern
; RESTORE .. PB4    Defaultwerte spichern
; MODE ..... PB5    =1 -> 2 Richtungen


     include         "picreg.equ"
     LIST    p=16C84,w=1 ; PIC16C84 is the target processor
     __CONFIG  _CP_OFF & _WDT_ON & _XT_OSC & _PWRTE_ON


minimum equ .512                ; Impulsl„nge obere Hebelstellung
maximum equ .1024               ; Impulsl„nge untere Hebelstellung
min_d equ .250                  ; Impulsmindestl„nge = 500 us
max_d equ .1250                 ; Impulsh”chstdauer = 1500 us
Rcal equ .10000                 ; Rcal = 100 kOhm
Roff equ .600                   ; Abschaltpunkt Rntc = 6 kOhm
Ron equ .1200                   ; Wiedereinschaltpunkt Rntc = 12 kOhm
Mot equ 7                       ; Motorausgang
Motn equ 6                      ; invertierter Motorausgang


intcon equ 0Bh
eeadr equ 9h
eedata equ 8h
speed equ 0ch                   ; errechnete Geschwindigkeit
count equ 0dh                   ; Periodenz„hler fr PWM
on equ 0eh                      ; Zyklenz„hler fr PWM
flag equ 0fh                    ; diverse flags

ACCaHI EQU 10h                  ; Akkumulamtoren fr Arithmetikroutinen
ACCaLO EQU 11h
ACCbHI EQU 12h
ACCbLO EQU 13h
ACCcHI EQU 14h
ACCcLO EQU 15h
ACCdHI EQU 16h
ACCdLO EQU 17h
rtch equ 18h                    ; High- Byte Timer
imp_h equ 19h                   ; gemessene Impulsbreite high
imp_l equ 1Ah                   ; gemessene Impulsbreite low
save equ 1Bh                    ; Zwischenspeicher fr W
un_h equ 1Ch                    ; untere Impulsbreite high
un_l equ 1Dh                    ;        - " -        low
ob_h equ 1Eh                    ; obere Impulsbreite high
ob_l equ 1Fh                    ;        - " -       low
tz equ 20h                      ; tote Zohne
divisor_l equ 21h               ; Umrechnungsfaktor
save_s equ 22h                  ; Zwichenspeicher fr Status
kal_lo equ 23h                  ; gemessene Ladezeit 100k Widerstand
kal_hi equ 24h
temp equ 25h                    ; Temor„res Arbeitsregister

        org 0
        goto start              ; RESET Vektor
        org 4                   ; Interrupt Vektor
int     btfsc intcon,2          ; rtc ?
	goto int_rtc
        movwf save              ; W sichern
        movlw 7                 ; RTCC anhalten
        option
        swapf STATUS,w          ; Swapf ver„ndert Status- Register nicht
        movwf save_s            ; Status sichern
exint   btfss Port_B,0          ; Externer Interrupt
	goto h_l
l_h     clrf RTCC               ; Low- High- Flanke
        clrf rtch               ; Timer l”schen
;        bcf Port_A,2           ; Testfunktion
        clrw
        option                  ; interrupt bei h-l
	goto exiend
h_l     movf RTCC,w             ; High- Low- Flanke
        clrf RTCC
        movwf imp_l             ; Z„hlerstand sichern
	movf rtch,w
        movwf imp_h
        btfsc intcon,2          ; Timer overflow ?
        incf imp_h              ; ja, dann high- Byte incrementieren
        bsf flag,0              ; merken, daá neue Daten da
;        bsf Port_A,2           ; Testfunktion
        movlw 40H
        option                  ; Interrupt bei l-h
exiend  bcf intcon,1            ; Interrupt Flags l”schen
        bcf intcon,2            ;
        swapf save_s,w          ; Status wiederherstellen
        movwf STATUS
        swapf save
        swapf save,w            ; W- Register wiederherstellen
        retfie                  ; und zurck zum Hauptprogramm
;
int_rtc bcf intcon,2            ; Timer- Interrupt, Timer- high incrementieren
        incfsz rtch             ; beeinflusst Z- Flag nicht (incf schon)
	retfie
        retfie

; ****************************************************************************
; division macro
;
divMac MACRO
	LOCAL NOCHK
	LOCAL NOGO

	bcf STATUS,CARRY
	rlf ACCdLO
	rlf ACCdHI
	rlf ACCcLO
	rlf ACCcHI
	movf ACCaHI,w
	subwf ACCcHI,w		;check if a>c
	btfss STATUS,Z_bit

	goto NOCHK
	movf ACCaLO,w
	subwf ACCcLO,w		;if msb ecual then check lsb
NOCHK   btfss STATUS,CARRY      ;carry set if c>a
	goto NOGO
	movf ACCaLO,w		; c-a into c
	subwf ACCcLO
	btfss STATUS,CARRY
	decf ACCcHI
	movf ACCaHI,w
	subwf ACCcHI
        bsf STATUS,CARRY        ;shift a 1 into b (result)
NOGO	rlf ACCbLO
	rlf ACCbHI

;
        ENDM
;
;*****************************************************************************
; Double Precision Divide ( 16/16 -> 16 )

; ( ACCb/ACCa -> ACCb with remainder in ACCc ): ~ 6 bit output ,with Quotiont in 
; ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO) .

; NOTE: Before calling this routine, the user should make sure that the Numerator(ACCb) 
; is greater than Denominator (ACCa) . If the case is not true, the user should 
; scale either Numerator or Denominator or both such that Numerator is greater than 
; the Denominator.


setup	movlw .16	;for 16 shifts
	movwf temp
	movf ACCbHI,w	;move ACCb to ACCd
	movwf ACCdHI
	movf ACCbLO,w
	movwf ACCdLO
	clrf ACCbHI
	clrf ACCbLO
        return

; **********************************************************************
;
neg_A   comf ACCaLO	;negate ACCa ( -ACCa -> AC( a )
        incf ACCaLO
        btfsc STATUS,Z_bit
        decf ACCaHI
        comf ACCaHI
        retlw 0

D_divF                  ; 16 bit Division

        call setup
        clrf ACCcHI
        clrf ACCcLO

;use the Macro 16 times

	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
	divMac
        return

;************************************************;



MADD    movf ACCaLO,w           ; 16 bit Addition
        addwf ACCbLO
        btfsc STATUS,CARRY
        incf ACCbHI
        movf ACCaHI,w
        addwf ACCbHI
        return

MPY     call setup              ; 16 bit Multiplikation
MLOOP   rrf ACCdHI
        rrf ACCdLO
        SKPNC
        call MADD
        rrf ACCbHI
        rrf ACCbLO
        rrf ACCcHI
        rrf ACCcLO
        decfsz temp
        goto MLOOP
        return

start   clrf RTCC               ; Reset
        movlw 40H               ; Vorteiler auf Timer 1:2
	option			; und Int bei l_h
	clrf speed
	clrf flag
        movlw 0CH
;
	tris Port_A
        movlw 03fH
        tris Port_B             ; Port A.1 ... Ausgang DIR (1=Rckw„rts)
        bsf Port_B,Motn
        bcf Port_B,Mot          ; Motor ausschalten
        bcf Port_A,1            ; Relais ausschalten
        btfss Port_B,4          ; EEProm mit standardwerten vorbelegen
        goto restore
        call crc                ; Prfsumme berechnen
	movf ACCaHI
	bz m14
	clrwdt
        movlw 6                 ; Prfsumme lesen
	call ee_rd
        movf ACCaLO,w
        subwf ACCaLO,w          ; gespeicherte und berechnete Prfsumme gleich
        bz m11
m14     movlw .16               ; nein, dann Standardkonfiguration
	movwf divisor_l
	movlw (minimum >> 8)
	movwf un_h
        movlw (minimum & 0FFH)
	movwf un_l
	movlw (maximum >> 8)
	movwf ob_h
        movlw (maximum & 0FFH)
	movwf ob_l
        movlw 2                 ; tote zohne = 2
	movwf tz
	goto m12
;
m11     movlw tz                ; Konfigruation ist OK
        movwf FSR               ; Pointer auf Speicherbereich
	movlw 5			; 5 bytes zu lesen
        movwf count
	bcf STATUS,7
m13     movf count,w
	call ee_rd
        movf ACCaLO,w           ; Byte aus EEProm lesen
        movwf 0                 ; und im Konfigurationsspeicher ablegen
        decf FSR                ; Pointer auf n„chste Speicherzelle
	decfsz count
	goto m13
;
m12	movf un_l,w
	subwf ob_l,0
	movwf ACCbLO
	movf un_h,w
	btfss STATUS,CARRY
	incf un_h,0		; Ergebnis in W
	subwf ob_h,0
	movwf ACCbHI
	clrf ACCaHI
	movlw .31
	movwf ACCaLO
	call D_divF
        movf ACCbLO,w           ; (ob-un)/31
        movwf divisor_l         ; Divisionsfaktor berechen
;
        movlw 0b0H
        movwf intcon            ; Interrupts zulassen
m19     clrwdt
        movlw .135
        movwf kal_lo            ; Defaultwert
        clrf kal_hi
        bcf flag,0
        movlw 04
        tris Port_A
        bsf Port_A,3            ; Vergleichswid. messen
        bsf Port_A,4            ; Eingang freigeben, Kond. wird nun geladen
        clrf RTCC
        clrf rtch
m18     movlw .5                ; Timeout 500H erreicht ?
        subwf rtch,w
        bc m18a

        btfss Port_A,4          ; warten bis C geladen
        goto m18
        call get_mes            ; Messwerte speichern
        btfsc flag,0
        goto m19
        btfsc Port_B,0
        goto m19
        movlw (Rcal & 0FFH)     ; Widerstandswert nach ACCb
        movwf ACCbLO
        movlw (Rcal >> 8)
        movwf ACCbHI
        call D_divF
        movf ACCbLO,w
        movwf kal_lo            ; RC/tc speichern
        movf ACCbHI,w
        movwf kal_hi
m18a
;        movlw .1
;        movwf coun
        movlw .25
        movwf count
wd1     clrwdt
        btfss flag,0
        goto wd1
        bcf flag,0
        decfsz count
        goto wd1

        movlw 08
        tris Port_A             ; Vergleichswid. abschalten, NTC freigeben
        bcf flag,2              ; Messung l„uft
        movlw 40H
        option                  ; Interrupt bei l-h
        bsf Port_A,2            ; NTC messen
        clrf RTCC
        clrf rtch
        clrf eeadr
wait_d  clrwdt
        btfsc Port_A,4          ; C geladen ?
        call get_mes            ; Messwert speichern
        btfss flag,0            ; auf 1. Datensatz warten
	goto wait_d
;
m7      bcf flag,0              ; merker rcksetzen

        btfss flag,2            ; Messung erfolgreich ?
        goto m15                ; nein, dann keine Berechnung
        movf kal_lo,w
        movwf ACCbLO
        movf kal_hi,w           ; Rc/tc laden
        movwf ACCbHI
        call MPY                ; und mit tx multiplizieren
        movf ACCcLO
        btfss Port_A,0          ; šbertemperatursicherung angesprochen ?
        goto m21
        sublw (Ron & 0FF)
        movf ACCcHI,w
        btfss STATUS,CARRY
        incf ACCcHI,w
        sublw (Ron >> 8)       ; Ron- Rx negativ
        bnc m15                ; ja, dann Motor freigeben
        clrw
        clrf speed
        goto m10

m21     sublw (Roff & 0FF)
        movf ACCcHI,w
        btfss STATUS,CARRY
        incf ACCcHI,w
        sublw (Roff >> 8)       ; Roff- Rx negativ
        bnc m15
        bsf Port_A,0            ; Alarm zu hohe Temp.
        bcf Port_A,1            ; Relais ausschalten
        clrw                    ; Speed = 0
        clrf speed
        goto m10                ; Messung starten

m15     bcf Port_A,0            ; Alarmausgang abschalten
        btfss Port_B,1
        goto save_u
        btfss Port_B,2
        goto save_o

        movf imp_l,w
        sublw (min_d & 0FF)
        movf imp_h,w
        btfss STATUS,CARRY
        incf imp_h,w
        sublw (min_d >> 8)      ; Ist mindestdauer - impulsl„nge >0 ?
        bc m20                  ; ja, dann Fehler

        movf imp_l,w
        sublw (max_d & 0FF)
        movf imp_h,w
        btfss STATUS,CARRY
        incf imp_h,w
        sublw (max_d >> 8)      ; Ist h”chstdauer - impulsl„nge < 0 ?
        bnc m20                 ; ja, dann Fehler

        movf un_l,w
        subwf imp_l,w           ; Impulsdauer - untere Grenze
	movwf ACCbLO
	movf un_h,w
	btfss STATUS,CARRY
        incf un_h,w
        subwf imp_h,w
	movwf ACCbHI
	bc m5
	clrf ACCbHI		; Ergebnis negativ
	clrf ACCbLO		; Signal am minimum
	goto m6
;
m5	movf imp_l,w
        subwf ob_l,w            ; Obere Grenze - Impulsdauer
        movf imp_h,w
	btfss STATUS,CARRY
        incf imp_h,w
        subwf ob_h,w
	bc m6
        movf un_l,w             ; Ergebnis negativ,
        subwf ob_l,w            ; d.h. Hebel am unteren Anschlag,
        movwf ACCbLO            ; unteren Grenzwert- oberen Grenzwert
        movf un_h,w             ; berechnen
	btfss STATUS,CARRY
        incf un_h,w
        subwf ob_h,w
	movwf ACCbHI
;
m6      clrf ACCaHI             ; Wert nun durch Schrittweite dividieren
	movf divisor_l,w
	movwf ACCaLO
	call D_divF
;	
        movlw .31
        subwf ACCbLO,w          ; max. 32 Stufen zul„ssig
        bnc m17
        movlw .31
        movwf ACCbLO

m17     btfss Port_B,5          ; Mode ?
        goto m16

        btfsc ACCbLO,4          ; Zweirichtungsbetrieb, Bit 4 ist Richtung
	goto m9
        bcf Port_A,1            ; Umpolrelais ausschalten
        btfsc flag,1            ; testen ob vorher umgepolt war
        call warte              ; umschaltzeit abwarten
        bcf flag,1              ; Direction =0  
        comf ACCbLO             ; Wert negieren
        goto m10a

m9      bsf Port_A,1            ; Umpolrelais einschalten
        btfss flag,1            ; testen ob vorher umgepolt war
        call warte              ; umschaltzeit abwarten
        bsf flag,1              ; Direction=1
m10a    bcf STATUS,CARRY
        rlf ACCbLO,w
        andlw 1f
        movwf speed             ; neue Geschwindigkeit
        sublw 1EH
        movlw 1FH
        btfsc STATUS,Z
        movwf speed
        movf speed,w
        goto m10

m16   ;  bcf STATUS,CARRY
;        rrf ACCbLO
        comf ACCbLO,w
;        movwf speed
;        bcf Port_A,1            ; Direction immer 0
        andlw 1f
        movwf speed             ; neue Geschwindigkeit

m10     btfss Port_B,3          ; Soll tote Zohne kalibriert werden ?
        goto save_tz            ; ja, dann Speichern
        subwf tz,w              ; prfen ob Speed innerhalb Toter Zohne ist
	btfsc STATUS,CARRY
        clrf speed              ; ja, dann Speed = 0
        movlw 1f
        xorwf speed

m20     
        bcf flag,2              ; neue Messung

        movlw 1f
        xorwf speed,w
        bnz m8                 ; Speed <> 0, dann PWM
xx      
        bcf Port_B,Mot
        bsf Port_B,Motn
        btfss Port_B,5          ; Flugmodus ?
        bsf Port_A,1            ; ja, Bremse einschalten
        bsf Port_A,4            ; Eingang freigeben, Kond. wird nun geladen
        clrf RTCC
        clrf rtch
        goto wait_d             ; Motor ausgeschaltet


m8      btfss Port_B,5          ; Flugmodus ?
        bcf Port_A,1            ; Bremse ausschalten
        bsf Port_A,4            ; Eingang freigeben, Kond. wird nun geladen
        clrf RTCC
        clrf rtch
m8a     movlw .16               ; 16 Perioden ausgeben
	movwf count
m3      movlw .32               ; pro Periode 32 Stufen
	movwf on
        bsf Port_B,Mot          ; Ausgang einschalten
        bcf Port_B,Motn
        btfsc flag,0            ; neue Daten eingetroffen ?
        goto m7                 ; ja, dann neue Berechnung
;
cycle   movf on,w
        subwf speed,w           ; Speed = Zyklus
        movlw 0C0
        btfsc STATUS,Z
        xorwf Port_B            ; Ausgang ausschalten
        btfsc Port_A,4
        call get_mes            ; Messwerte lesen
m4      decfsz on
	goto cycle
        clrwdt                  ; Watchdog rcksetzen
        decfsz count
	goto m3
        goto m8a

;
restore clrf intcon             ; Konfigurationsspeicher mit Standardwerten
        movlw .16               ; belegen
	movwf divisor_l
        movlw 1
        movwf eeadr
        movlw (minimum >> 8)    ; OBERE Hebelstellung
	movwf un_h
	call ee_wr
	incf eeadr
        movlw (minimum & 0FFH)
	movwf un_l
	call ee_wr
	incf eeadr
        movlw (maximum >> 8)    ; untere Hebelstellung
	movwf ob_h
	call ee_wr
	incf eeadr
        movlw (maximum & 0FFH)
	movwf ob_l
	call ee_wr
	incf eeadr
        movlw 1                 ; tote zohne = 1
	movwf tz
	call ee_wr
	call crc
        movlw 6
	movwf eeadr
	movf ACCaLO,w
	call ee_wr
rest1	clrwdt
	btfss Port_B,4
	goto rest1
	goto start
;
ee_rd	movwf eeadr
	bsf STATUS,5
	bsf 8,0			; EEprom lesezyklus starten
	bcf STATUS,5
	movf eedata,w
        movwf ACCaLO
	return

ee_wr	movwf eedata		; Adresse muá schon in EEadr stehen
	bsf STATUS,5
	bsf 8,2			; WREN setzen
	movlw 55
	movwf 9
	movlw 0AA
	movwf 9
	bsf 8,1			; schreibzyklus starten
wr1	clrwdt
	btfss 8,4
	goto wr1		; schreibzyklus abwarten
	clrf 8
	bcf STATUS,5
	return
	
save_u  bcf intcon,7            ; Obere Hebelstellung verewigen
        bcf Port_B,Mot
        bsf Port_B,Motn
        bcf Port_A,1            ; Relais ausschalten
        movlw 1
        movwf eeadr
        movf imp_h,w
	call ee_wr
        incf eeadr
	movf imp_l,w
	call ee_wr
	call crc
        movlw 6
	movwf eeadr
	movf ACCaLO,w
	call ee_wr
su1	clrwdt
	btfss Port_B,1
	goto su1
	goto start
;
save_o  bcf intcon,7            ; Untere Hebelstellung verewigen
        bcf Port_B,Mot
        bsf Port_B,Motn
        bcf Port_A,1            ; Relais ausschalten
        movlw 3
	movwf eeadr
	movf imp_h,w
	call ee_wr
        incf eeadr
	movf imp_l,w
	call ee_wr
	call crc
        movlw 6
	movwf eeadr
	movf ACCaLO,w
	call ee_wr
so1	clrwdt
	btfss Port_B,2
	goto so1
	goto start
;
save_tz bcf intcon,7            ; Tote Zohne im EEProm verewigen
        bcf Port_B,Mot
        bsf Port_B,Motn
        bcf Port_A,1            ; Relais ausschalten
        movlw 5
	movwf eeadr
	movf speed,w
	call ee_wr
	call crc
        movlw 6
	movwf eeadr
	movf ACCaLO,w
	call ee_wr
tz1	clrwdt
	btfss Port_B,3
	goto tz1
        goto start
;
crc     movlw 5                 ; Prfsummenberechnung
        movwf count             ; 5 Bytes zu lesen
        clrf ACCaLO
	clrf ACCaHI
crc1    movf count,w
        call ee_rd              ; Byte aus EEProm lesen
	iorwf ACCaHI
        addwf ACCaLO            ; Aufaddieren
        decfsz count            ; na„chstes
	goto crc1
        return

warte   movlw .255              ; ca. 100ms Verz”gerung
        movwf count
        movlw .130
        movwf on
w1      decfsz count
        goto w1
        clrwdt
        decfsz on
        goto w1
        return

get_mes movlw 47H               ; Messwert abspeichern
        option
        bcf Port_A,4            ; C entladen
        movf RTCC,w
        movwf ACCaLO            ; Messwert nach ACCa
        movf rtch,w
        movwf ACCaHI
        bsf flag,2              ; Flag Messwert eingelesen setzen
        return
     END


