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