Elmaetare.asm

;Johnny Apell
;20120120
;ver 0
;ver 1  20120127  programmet trimmat för både 1000 och 10.000 pulsers mätare

;Grundprogrammet är hämtat på nätet...vilket styrde en lysdiod upp och ner i ramp...i undervisningssyfte!
;Därmed var PWM klar att användas och anpassas för mitt behov.
;I programmet räknas inte de enskilda instruktionerna... för att skapa 1mS pulsen
;utan allt har trimmats med hjälp av oscilloskopet via LED utgången!!

;Programmet omskrivet för att läsa av lysdiodens frekvens på elmätaren för effekten 0...10kW
;och presentera ett pwm-värde på utgången till ett visarinstrument.
;256 stegs upplösning på pwm utgången ger 10.000W/256=ca 39W per steg
;vid en 10.000 pulsers elmätare...max frekvens = 27,777 Hz pulslängd 36mS
;vid en 1.000 pulser elmätare ... max frekvens 2,7777Hz pulslängd 360mS
;Kretsen är inte avsedd för grund till debitering av ström...eller att kontrollera
;om elmätaren visar rätt! Utan jag vill ha en hum om hur mycket ström som för
;tillfället förbrukas. Och det visas på ett nytt/surplus vridspoleinstrument 0,1...1mA
;
;PWM-frekvensen beräknas genom att kolla hur många interna 1mS pulser som hinner räknas mellan pulserna från elmätaren
;Sedan divideras 36*256=9216 med antalet räknade pulser för en 10000 pulsers elmätare
;Och 92160/antalet pulser för en 1000 pulsers elmätare
;Då får vi ett linjärt pwm-värde mellan 0...255!!
;
;Beräkningen startar på den positiva flanken av pulsen tills
;nästa positiva flank.
;Vid den positiva flanken lägges pwm:en ut, och divisionen tar ca 0,5mS, medveten att det inkräktar på räkneperioden...

;
;Genom att bygla ingången GPIO,0 VAL, till 0V väljes 10.000/1.000 pulsers el-mätare.
;
;
;Genom att bygla GPIO,1 TEST till 0V läggs högsta PWM frekvensen ut... 
;hjälp för att justera instrumentet till max. Det är all justering som behövs
;
;
;Intern 4MHz klocka  (ger 1µS instruktionstid)


;==================================================================================
           list     P=12F683, ST=OFF   	; Turnoff Symbol Table in List file.

           errorlevel  -302        	; Ignore error message when storing to Bank 1.
;==================================================================================

        #include   <p12f683.inc>

   __config _INTOSCIO & _WDT_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_ON & _CPD_OFF & _BOD_NSLEEP & _IESO_OFF & _FCMEN_OFF

; ------------------------------------
; Configuration Bit Settings Explained
; ------------------------------------
; Internal RC Oscillator, No Clock Out
; Watch Dog Timer = OFF
; Power Up Timer = ON
; MCLR = INTERNAL
; Code Protect = ON
; Data EE Read Protect = OFF
; Brown Out Detect = Enabled in Run, Disabled in Sleep, SBOREN Disabled
; Internal External Switch Over Mode (2-speed Startup) = OFF
; Monitor Clock Fail-safe = OFF

;==========================================================================

;*****[ Data Storage Reg's ]*****
        CBLOCK  0x20        	; Tilldela registerna olika namn från adresser 0x20
            count1              ; Olika register till delayfunktionerna
            count2              
            count3
            count4
            count5
            counter				;PWM loop-räknaren
	    	counter1
	    	slask				;diverse hjälpflaggor
			slask1
			slask2
	    	DIVID0				;divisionen
	   		DIVID1
 	    	DIVID2
	    	DIVIS0
	    	DIVIS1
	    	REMB0				;resten i divisionen...användes ej
	    	REMB1
	    	LOOPCOUNT
            ENDC                ; 

					;tilldela enskilda bitar egna namn, lättare att läsa
#define 	VAL		GPIO,0		;val av el-mätare
#define 	TEST	GPIO,1		;testa max utgång för pwm
#define		PULS	GPIO,5		;inkommande el-mätarepuls
#define 	LED		GPIO,4		;lysdioden
#define		FLAG	slask,0		;flankflaffan
#define		FLAG1	slask,1
#define		Low0	CCP1CON,4	;de låga bitarna i pwm....användes inte
#define		Low1	CCP1CON,5
#define		_C		STATUS,0	;om beräkningarna blev med "Carry"
#define		_Z		STATUS,2	;om beräkningar blev "Zero"


;==================================================================================
    ORG     0           	; Programmet startar från adress 0
	goto	Initialize		;Starta med att initiera kretsens olika register.. längst ner i programmet

;==========================================================================================
main						;hit kommer programmet efter power on
	
	btfss	TEST			;kolla om testpluggen sitter i...för att justera instrumentet.		
	call	Test			;gå då till testmode
	btfss	PULS			;kolla om det har kommit en mätarpulspuls
	goto	$-1				;vänta annars...
	movlw	.125			;skriv in ett slaskvärde till samplingstiden...för att komma igång.
	movwf	count1
	movlw	.255			;fyll på lite höga värden  i divisorn så att visaren inte hoppar till
	movwf	DIVIS0			;vid power om
	movwf	DIVIS1
							;gå och jobba....

					;Här kommer programmet att loopa runt och vid varje mS 
					;räkna upp divisorn till divisionen senare
					;när positiv flank från elmätaren kommer, anropas divisionsrutinen
					;beräkning, divisionen sker och nytt pwm-värde lägges ut till instrumentet

					;hur många 1mS pulser hinnes det med att räknas internt mellan inkommande el-mätarpulser??
					;det är lika med T=1/F  36mS=10kW  9216ms=40W  255.....0 pwm-värde
loop1
	btfsc	PULS			;kolla om ny el-mätarepuls...  den positiva flanken vill vi ha
	goto	$+3				;hoppa 3 steg om el-mätarepulsen är hög
	bcf		FLAG			;reset flankflaggan vid låg el-mätare-inpuls
	goto	$+3				;hoppa till räknaren om el-mätarepulsen är låg
	btfss	FLAG			;flankflaggan... sätts i divisionsrutinen, vid satt flagga.. hoppa över till mS-räknaren
	call	DIV				;vid den positiva flanken...flaggan låg, lägg ut pwm-värdet i divisionsrutinen

	decfsz	count1,f		;För att skapa 1mS intern räknepuls
	goto	loop1			;loopa runt tills räknaren är nere till "0" = 1mS

							;här räknar vi upp registret "divisorn" ett steg varje gång vi passerar, efter varje mS
	incf	DIVIS0,f		;öka höga registret med ett,  ett knep att inca två register...
	incfsz	DIVIS1,f		;öka låga registret med ett och kolla om det slog över??
	decf	DIVIS0,f		;Det slog inte över... minska tillbaka höga registret med ett.
	movlw	.125			;ett lämpligt värde att skapa 1mS pulser, mätt med oscilloskopet!
	movwf	count1			;laddas in i mS-räknaren
	bcf		LED				;släck monitor LED-dioden, ger ca 1mS blink i LED:en
;	call	Toggla			;för att trimma med oscilloskopet...
	goto 	loop1			;tebaks till ny samplingsrunda...

;=============================================================================================

					;En divisions-snutt taget från nätet
					;Det blir över 500 instruktioner i divisionen, vilket kommer att ta ca 0,5mS i tid

;Inputs:
;   Dividend - DIVID0:DIVID1:DIVID2 (0 - most significant!)
;   Divisor  - DIVIS0:DIVIS1
;Temporary:
;   Counter  - LOOPCOUNT
;   Remainder- REMB0:REMB1
;Output:
;   Quotient - DIVID0:DIVID1:DIVID2
					;Vi skall dividera 9216/med räknade mS-pulser för 10.000 pulser elmätare
					;eller 92160/med räknade mS-pulser för 1.000 pulsers elmätare
					;vi bryr oss inte om resten i divisionen, utan använder endast heltalen.
					;så allt under 1 i divisionen blir "0"
					;vi förväntar oss ett svar mellan 0...255. Vi använder 256 stegs PWM
 
DIV
	bsf		LED				;tänd monitor LED-dioden... en blink varje inkommande puls
							;den släcks i loopen ovan, efter ca 1mS

							;först gör vi ett val av el-mätare 1.000/10.000 pulser/kWh
	btfsc	VAL				;val om det är 1.000/10.000 pulsers el-mätare från jumpern
	goto	p10k			;gå till 10.000 pulsers el-mätare

p1k							;1.000 pulsers el-mätare
	movlw	0x00 			;360mS = 2,777Hz = 10kW... högsta effekten
	movwf	DIVID2			;360*256=92160 (16800h)lika med dividenden
	movlw	0x68
    movwf	DIVID1 			;0 + 104*256 + 1*65536 = 92160
	movlw	.1
	movwf	DIVID0			;då har vi 92160 i dividenden
	goto	divid		

p10k						;10.000 pulsers el-mätare
	movlw	0x00	 		;lägg in divisionsvärdet 9216 (2400h)i dividenden  36*256
	movwf	DIVID2			;36mS = 27,77Hz = 10kW... högsta effekten
	movlw	0x24
    movwf	DIVID1 
	clrf	DIVID0			;då har vi 9216 i dividenden


								;Så har vi divisionen här
FXD2416U:
divid   CLRF 	REMB0			;programsnutt från Nikolai Golovchenko 
        CLRF 	REMB1			;24bitar/16 bitar division
        MOVLW 	.24
        MOVWF 	LOOPCOUNT
LOOPU2416
        RLF 	DIVID2,w         ;shift dividend left to move next bit to remainder
        RLF 	DIVID1,f         ;
        RLF 	DIVID0,f         ;

        RLF 	REMB1,f          ;shift carry (next dividend bit) into remainder
        RLF 	REMB0,f

        RLF 	DIVID2,f         ;finish shifting the dividend and save  carry in AARGB2.0,
        MOVF 	DIVIS1,w         ;substract divisor from 16-bit remainder
        SUBWF 	REMB1,f          ;
        MOVF 	DIVIS0,w         ;
        BTFSS 	STATUS,C         ;
        INCFSZ 	DIVIS0,w         ;
        SUBWF 	REMB0,f          ;
        SKPNC                    ;if no borrow after 16 bit subtraction
        BSF 	DIVID2,0         ;then there is no borrow in result. Overwrite
        BTFSC 	DIVID2,0         ;if no borrow after 17-bit subtraction
        GOTO 	UOK46LL          ;skip remainder restoration.
        ADDWF	REMB0,f          ;restore higher byte of remainder. (w 
        MOVF 	DIVIS1,w         ;restore lower byte of remainder
        ADDWF 	REMB1,f          ;

UOK46LL
        DECFSZ 	LOOPCOUNT,f      ;decrement counter
        GOTO 	LOOPU2416        ;and repeat the loop if not zero.

					;nu har vi svaret från divisionen i DIVIDENDEN

							;kolla först om svaret är över 255, genom att kolla att de höga byten inte är noll
	bcf		_Z				;reset Zero-flaggan
	movlw	0x00			;ladda in 00
	xorwf	DIVID1,w		;XOR:a med andra byten
	btfsc	_Z				;kolla om det blev zero
	goto	out				;ja.. zero hoppa vidare
	movlw	.255			;ej zero... skriv in 255, max till pwm
	movwf	DIVID2
	bcf		_Z				;reset Zero-flaggan
	movlw	0x00			;ladda in 00
	xorwf	DIVID0,w		;XOR:a med högsta byten
	btfsc	_Z				;kolla om det blev zero
	goto	out				;ja.. zero hoppa vidare
	movlw	.255			;ej zero... skriv in 255, max till pwm
	movwf	DIVID2

out
	movfw	DIVID2			;svaret ligger i DIVID2, vi förväntar oss max 255
	movwf	CCPR1L			;flytta över till PWM-utgången

	clrf	DIVIS0			;rensa räkneregistren till nästa vända
	clrf	DIVIS1
	bsf		FLAG			;sätt flankflaggan, så att inte denna funktion körs flera ggr medans el-mätarepulsen är hög
	movlw	.50				;skriv in ett slaskvärde för att fylla ut till första mS pulsen i loopningen ovan
	movwf	count1
    return


;=========================================================================================
Test				;Genom att lägga ut maxfrekvensen på PWM så kan, endast vid power on.
					;vridspoleinstrumentet justeras till max med trimpotten.
					;samt se om instrumentet förljer 75%, 50%, 25% 10% 0% ??
					;Lysdioden togglas oxo
	movlw	.255
	movwf	CCPR1L			;och maxställ pwm 100%
	call	delay
	call	delay
	movlw	.192			;75%
	movwf	CCPR1L
	call	delay
	movlw	.128			;50%
	movwf	CCPR1L
	call	delay
	movlw	.64				;25%
	movwf	CCPR1L
	call	delay
	movlw	.26				;10%
	movwf	CCPR1L
	call	delay
	clrf	CCPR1L			;0%
	call 	delay
	btfss	TEST			;kolla om testpluggen sitter i...för att justera instrumentet.		
	goto	Test			;kolla om pluggen är kvar
	clrf	CCPR1L			;nollställ pwm
	return

delay
	
	movlw	.25
	movwf	slask2
	call	Toggla
	decfsz	slask,f
	goto	$-1
	decfsz  slask1,f
	goto	$-3
	decfsz	slask2,f
	goto	$-6
	return
;,============================================================================================
Toggla					;toggla utgången varannan gång. användes vid testning mot oscilloskopet.
	btfss	LED			;kolla om LED är tänd??
	goto	$+5			;den är inte tänd... hoppa ner och tänd den
	bcf		LED			;släck istället...
	nop					;lika många instruktioner
	nop
	return
	bsf		LED			;tänd
	return


;----------------------------------------------------------------------------------
					;Denna rutin anropas vid power on och ställer de önskade registren i processorn
					;så att den fungerar som vi vill
Initialize				
    BANKSEL OSCCON          ;Switch to Bank 1.
    movlw   b'01100001'     ;4MHz Clk, IntOsc, SysClk via IntOsc
    movwf   OSCCON
;
    BANKSEL CMCON0          ;Switch to Bank 0.
    movlw   b'00000111'     ;Turn off Comparator.
    movwf   CMCON0 
;   
    BANKSEL ANSEL           ;Switch to Bank 1.
    clrf    ANSEL           ;Set I/O pins to Digital.
;
    movlw   b'0101011'     	;efine inputs & outputs.IN=1 OUT=0
    movwf   TRISIO
    movlw   b'01000011'     ;Aktivera pull-ups, Prescaler -> TMR0
    movwf   OPTION_REG      ;(TMR0 prescaler = 16).
;
	movlw	b'00101011'		;välj vilka pinnar som skall ha weak pullup	
	movwf	WPU				;placera i WPU registret
    BANKSEL GPIO            ;Switch to Bank 0.
;
							;Set CCP1 as PWM
    movlw   B'00001111'
    movwf   CCP1CON         ;Set bits 4-5 (2 LSB's of Duty Cycle) to 00.
;
					;Set highest PWM value (resolution),
					;10-bit but only 256 discrete Duty Cycles.
    BANKSEL PR2             ;Bank 1
    movlw   d'255'          ;10-bit res. but 256 Duty Cycles (Duty Cycle LSB = 00)
    movwf   PR2             ;Establishes Timer2 Period.
;
					;Set prescaler to 16 for a PWM freq. of 244Hz (based on 4MHz clock).
					;Since the human eye cannot perceive flicker above 120Hz, 244Hz is OK.
    BANKSEL T2CON           ;Bank 0
    movlw   B'00000010'
    movwf   T2CON           ;Timer2 is now OFF & Prescaler is 16.
       
    clrf    CCPR1L          ;Clear PWM Reg.1 LO Byte (sets PWM Duty Cycle to zero)
;                           ;(CCPR1L contains the 8 MSB's of the Duty Cycle.)
    bsf     T2CON, TMR2ON   ;Turn on Timer2.
;
	goto	main 			;nu är vi färdiga med initiering av kretsen...dax att jobba!


	end