; DEVICE = 16F628A ; Mains grid frequency indicator, 12 LEDs indicate the line frequency in 20 mHz increments from 49.90 to 50.10 Hz ; US 60 Hz version should be trivial ; Filename: netzlupe.asm ; v0.0 170723 Start project ; v0.1 170724 Skip display counter to smoothen readout ; v0.2 170724 Add moving average (N=4) ; v1.0 170724 Final release ; v1.1 170724 Moved lamp test so display is up to date after it has run ; v1.2 170730 Fixed Lamp Test not run because GIE enabled too early LIST P=16F628A, F=INHX8M #include <p16f628a.inc> __CONFIG _XT_OSC & _WDT_OFF & _LVP_OFF & _CP_OFF ; Equates RESET_V EQU 0x00 ; Address of RESET Vector OSC_FREQ EQU D'4000000' ; Oscillator Frequency is 4.0 MHz ; Registers FLAGS EQU 0x20 ; Only used for lamp test MAN EQU 0x21 ; Moving Average * N MA EQU 0x22 ; Moving Average which gets displayed MAPREV EQU 0x23 ; Previous Moving Average MAINT EQU 0x24 ; Intermediate value for moving average calculation (SUBWF) XI EQU 0x25 ; Measurement to be averaged SKIP EQU 0x26 ; Skip display counter to make readout less jittery ; Flags #define LAMPTST FLAGS,0 ; Ports #define N6 PORTB,1 ; RED, <49.90 Hz (>20,040 us) #define N5 PORTB,2 ; RED, 49.90-49.92 Hz (+32 us) #define N4 PORTB,3 ; YEL, 49.92-49.94 Hz (+24 us) #define N3 PORTB,4 ; YEL, 49.94-49.96 Hz (+16 us) #define N2 PORTB,5 ; GRN, 49.96-49.98 Hz (+8 us) #define N1 PORTB,6 ; GRN, 49.98-50.00 Hz (0 us) #define P1 PORTB,7 ; GRN, 50.00-50.02 Hz (-8 us) #define P2 PORTA,0 ; GRN, 50.02-50.04 Hz (-16 us) #define P3 PORTA,1 ; YEL, 50.04-50.06 Hz (-24 us) #define P4 PORTA,2 ; YEL, 50.06-50.08 Hz (-32 us) #define P5 PORTA,3 ; RED, 50.08-50.10 Hz (-40 us) #define P6 PORTA,4 ; RED, >50.00 Hz (< -40 us) ; Port RB0 is INT for 50 Hz input ORG 0x00 GOTO START ORG 0x04 ; Interrupt handlers BCF INTCON,INTF ; Clear the interrupt caused by RB0 INT (there are no others) GOTO UPDATE RETFIE ; Just in case LAMPTEST BTFSS N6 GOTO LTN5 BTFSS N5 GOTO LTN4 BTFSS N4 GOTO LTN3 BTFSS N3 GOTO LTN2 BTFSS N2 GOTO LTN1 BTFSS N1 GOTO LTP1 BTFSS P1 GOTO LTP2 BTFSS P2 GOTO LTP3 BTFSS P3 GOTO LTP4 BTFSS P4 GOTO LTP5 BTFSS P5 GOTO LTP6 BSF P6 BCF LAMPTST RETFIE LTN5 BSF N6 BCF N5 RETFIE LTN4 BSF N5 BCF N4 RETFIE LTN3 BSF N4 BCF N3 RETFIE LTN2 BSF N3 BCF N2 RETFIE LTN1 BSF N2 BCF N1 RETFIE LTP1 BSF N1 BCF P1 RETFIE LTP2 BSF P1 BCF P2 RETFIE LTP3 BSF P2 BCF P3 RETFIE LTP4 BSF P3 BCF P4 RETFIE LTP5 BSF P4 BCF P5 RETFIE LTP6 BSF P5 BCF P6 RETFIE UPDATE ; Calculate moving average: MA*[i]=MA*[i-1]+X[i]-MA*[i-1]/N (MA*=MA*N, N=4) MOVFW TMR1L ; Get timer1 LSB, 0x4E20 is 20,000 us ADDLW 0x27 ; Offset and correction -> 0x40 MOVWF XI ; Store into measurement register CLRF TMR1L ; Need to clear Timer1 as soon as possible CLRF TMR1H BCF STATUS,C ; Clear carry RRF XI,1 ; divide by two (->0x20) MOVFW MAN ; Get previous moving average*N MOVWF MAPREV ; Store previous moving average*N ADDWF XI,0 ; Add X[i] MOVWF MAINT ; Intermediate calculation MA BCF STATUS,C ; Clear carry RRF MAPREV,1 ; Calculate previous moving average/N BCF STATUS,C ; Clear carry RRF MAPREV,0 ; Divide by 4 and store in W SUBWF MAINT,0 ; and subtract MA MOVWF MAN ; Store new moving average *N MOVWF MA ; and store in MA (temp) BCF STATUS,C ; Clear carry RRF MA,1 ; Calculate moving average (MA*/N) BCF STATUS,C ; Clear carry RRF MA,1 ; Divide by 4 and keep in MA BTFSC LAMPTST GOTO LAMPTEST DISPLAY ; The final value gets decoded and displayed DECFSZ SKIP RETFIE MOVLW 0x19 ; Every 500 ms (25*20ms) update display MOVWF SKIP MOVLW 0xFF MOVWF PORTA MOVWF PORTB ; Switch off all LEDs ; Test for P6, <0x0C MOVFW MA SUBLW 0x0C BTFSC STATUS,C ; less than 0x0C, goto P6 GOTO ONP6 ; Test for P5, <0x10 MOVFW MA SUBLW 0x10 BTFSC STATUS,C GOTO ONP5 ; Test for P4, <0x14 MOVFW MA SUBLW 0x14 BTFSC STATUS,C GOTO ONP4 ; Test for P3, <0x18 MOVFW MA SUBLW 0x18 BTFSC STATUS,C GOTO ONP3 ; Test for P2, <0x1C MOVFW MA SUBLW 0x1C BTFSC STATUS,C GOTO ONP2 ; Test for P1, <0x20 MOVFW MA SUBLW 0x20 BTFSC STATUS,C GOTO ONP1 ; Test for N1, <0x24 MOVFW MA SUBLW 0x24 BTFSC STATUS,C GOTO ONN1 ; Test for N2, <0x28 MOVFW MA SUBLW 0x28 BTFSC STATUS,C GOTO ONN2 ; Test for N3, <0x2C MOVFW MA SUBLW 0x2C BTFSC STATUS,C GOTO ONN3 ; Test for N4, <0x30 MOVFW MA SUBLW 0x30 BTFSC STATUS,C GOTO ONN4 ; Test for N5, <0x34 MOVFW MA SUBLW 0x34 BTFSC STATUS,C GOTO ONN5 ; No test for N6, >=0x34 BCF N6 RETFIE ONN5 BCF N5 RETFIE ONN4 BCF N4 RETFIE ONN3 BCF N3 RETFIE ONN2 BCF N2 RETFIE ONN1 BCF N1 RETFIE ONP1 BCF P1 RETFIE ONP2 BCF P2 RETFIE ONP3 BCF P3 RETFIE ONP4 BCF P4 RETFIE ONP5 BCF P5 RETFIE ONP6 BCF P6 RETFIE ; End interrupt handlers START CLRF STATUS ; Do initialization, Select bank 0 CLRF INTCON ; Clear int-flags, Disable interrupts CLRF PCLATH ; Keep in lower 2KByte MOVLW B'00000111' ; Disable comparators (damn you Microchip! Should have been 0x00!) MOVWF CMCON BSF STATUS, RP0 ; Select bank 1 CLRF TRISA ; RA7-0 Outputs CLRF TRISB ; RB7-0 Outputs BSF TRISB,0 ; Except RB0 (INT) MOVLW B'11000000' ; Disable weak pullups, INT rising edge MOVWF OPTION_REG BCF STATUS, RP0 ; Select bank 0 MOVLW 0xFF MOVWF PORTA ; Make all PORT A outputs high (LEDs OFF) MOVWF PORTB ; Make all PORT B outputs high ; Clear/init registers CLRF FLAGS MOVLW 0x01 ; To make sure the first readout gets displayed after lamp test MOVWF SKIP ; Set up interrupts CLRF PIR1 ; Clear peripheral stuff BSF STATUS,RP0 ; Go to bank 1 (BANKSEL PIE1) CLRF PIE1 ; Clear more peripheral stuff BCF STATUS,RP0 ; Go to bank 0 BSF INTCON,INTE ; Enable external interrupt on RB0 ; Setup Timer1 BCF T1CON,T1CKPS1 ; These set the TIMER1 prescaler. Values: BCF T1CON,T1CKPS0 ; 00=1:1 01=1:2 10=1:4 11=1:8 BCF T1CON,T1OSCEN ; Turn off the TIMER1 oscillator (we don't need it because we're using the internal oscillator) BCF T1CON,TMR1CS ; Select the internal oscillator for TIMER1 BSF T1CON,TMR1ON ; Enable TIMER1 CLRF TMR1H CLRF TMR1L BSF LAMPTST BCF N6 ; Switch on LED N6 BSF INTCON,GIE ; Enable global interrupts MAIN GOTO MAIN ; Loop forever ; Subroutines? END