list p=16f876 ;************************************************************** ;* Pinbelegung ;* ---------------------------------- ;* PORTA: 0 < Spannung U ;* 1 - ;* 2 - ;* 3 - ;* 4 - ;* 5 - ;* 6 - ;* 7 - ;* ;* PORTB: 0 LCD Display E ;* 1 - ;* 2 LCD Display RS ;* 3 LCD Display R/W ;* 4 LCD Display D4 ;* 5 LCD Display D5 ;* 6 LCD Display D6 ;* 7 LCD Display D7 ;* ;* PORTC: 0 - ;* 1 - ;* 2 - ;* 3 - ;* 4 - ;* 5 - ;* 6 - ;* 7 - ;* ;************************************************************** ; ;sprut (zero) Bredendiek 01/2003 ; ; Lehrbeispiel: ADC mit LCD ; ; Prozessor 16F876 ; ; Prozessor-Takt 10 MHz ; ; LCD am PortB ;********************************************************** ; Includedatei für den 16F876 einbinden #include ERRORLEVEL -302 ;SUPPRESS BANK SELECTION MESSAGES ; Configuration festlegen: ; Power on Timer, kein Watchdog, HS-Oscillator, kein Brown out, kein LV-programming __CONFIG _PWRTE_ON & _WDT_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF ;******************************************************** ; Variablen festlegen ;16 Bit Rechenregister xw0 equ 0x22 ; xw1 equ 0x23 ; f0 equ 0x24 ; f1 equ 0x25 ; counter equ 0x26 ; Fehler equ 0x27 ; ST equ 0x28 ; SZ equ 0x29 ; SH equ 0x2A ; SE equ 0x2B ; loops equ 0x2C ; timer für wait loops2 equ 0x2D ; timer für wait LcdStatus equ 0x2E ; LcdDaten equ 0x2F ; Ini_con Equ B'00000000' ; TMR0 -> Interupt disable Ini_opt Equ B'00000010' ; pull-up ; für LCD-Pins #define LcdE PORTB,0 ; enable Lcd #define LcdRw PORTB,3 ; read Lcd #define LcdRs PORTB,2 ; Daten Lcd (nicht control) #define LcdPort PORTB ; Datenbus des LCD (obere 4 Bit) ;******************************************************** ; Das Programm beginnt mit der Initialisierung Init bsf STATUS, RP0 ; Bank 1 movlw Ini_opt ; pull-up on movwf OPTION_REG movlw B'00000000' ; PortB alle outputs movwf TRISB bcf STATUS, RP0 ; Bank 0 clrf PORTB movlw Ini_con ; Interupt disable movwf INTCON ; ADC initialisieren ; ADC einschalten BSF ADCON0, 0 ; ADON=1 ; ADC-Eingang AN0 auswählen BCF ADCON0, 5 ; ADCHS2=0 BCF ADCON0, 4 ; ADCHS1=0 BCF ADCON0, 3 ; ADCHS0=0 ; ADC speed für 5 ... 20 MHz einstellen BSF ADCON0, 7 ; ADCS1=1 BCF ADCON0, 6 ; ADCS0=0 ; Daten rechtsbündig BSF STATUS,RP0 ; Bank1 clrf ADCON1 BSF ADCON1, 7 ; ADFM=1 BCF STATUS,RP0 ; Bank0 ;Display initialisieren call InitLcd ;Ausgabe der Begrüßung movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw 'G' call OutLcdDaten movlw 'U' call OutLcdDaten movlw 'T' call OutLcdDaten movlw 'E' call OutLcdDaten movlw 'N' call OutLcdDaten movlw ' ' call OutLcdDaten movlw 'T' call OutLcdDaten movlw 'A' call OutLcdDaten movlw 'G' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT call WAIT Mainloop call ADC ; Spannung messen nach f1,f0 call mV ; Wandlung in Millivolt nach f1,f0 call B2D ; Wandlung in dezimal nach ST,SH,SH,SE call Ausgabe ; anzeigen am LCD goto Mainloop ;***************************************************** ; Spannung mit ADC messen ; Ergebnis nach F1,f0 ADC BSF ADCON0, 2 ; ADC starten ADCloop BTFSC ADCON0, 2 ; ist der ADC fertig? GOTO ADCloop ; nein, weiter warten movfw ADRESH ; obere 2 Bit auslesen movwf f1 ; obere 2-Bit nach U1H bsf STATUS,RP0 ; Bank1 movfw ADRESL ; untere 8 Bit auslesen bcf STATUS,RP0 ; Bank0 movwf f0 ; untere 8-Bit nach U1L return ;********************************************************************* ;16 bit Adition, C-Flag bei Überlauf gesetzt Add16 ; 16-bit add: f := f + xw movf xw0,W ; xw0 nach W addwf f0,F ; f0 := f0 + xw0 movf xw1,W ; xw1 nach W btfsc STATUS,C ; fall ein Überlauf auftrat: incfsz xw1,W ; xw1+1 nach W addwf f1,F ; f1 := f1 + xw1 return ; fertig ;***************************************************** ; 16 Bit Subtraktion, bei Überlauf (neg. Ergebnis) ist C gesetzt Sub16 ; 16 bit f:=f-xw clrf Fehler ; extraflags löschen movf xw0, w ; f0:=f0-xw0 subwf f0, f btfsc STATUS,C goto Sub16a movlw 0x01 ; borgen von f1 subwf f1, f btfss STATUS,C bsf Fehler, C ; Unterlauf Sub16a movf xw1,w ; f1:=f1-xw1 subwf f1 ,f btfss STATUS,C bsf Fehler, C ; Unterlauf bcf STATUS, C ; C-Flag invertieren btfsc Fehler, C bsf STATUS, C return ;***************************************************** ; Division durch 2 wird w-mal ausgeführt ; die zu dividierende Zahl steht in xw Div2 movwf counter ; Anzahl der Divisionen speichern Div2a ; 16 bit xw:=xw/2 bcf STATUS, C ; carry löschen rrf xw1, f rrf xw0, f decfsz counter, f ; fertig? goto Div2a ; nein: noch mal return ;***************************************************** ; Wandlung des ADC-Wert in Millivolt (binär) ; Der ADC-Wert steht in f1,f0 ; Ergebnis steht in f1,f0 mV ; zunächst die Multiplikation mal 5 movfw f0 movwf xw0 movfw f1 movwf xw1 call Add16 ; f := 2xADC call Add16 ; f := 3xADC call Add16 ; f := 4xADC call Add16 ; f := 5xADC ; ADC * 5 nach xw kopieren movfw f0 movwf xw0 movfw f1 movwf xw1 ; xw := 5xADC ; xw durch 64 dividieren (6 mal durch 2) ; dann ist xw = 5xADC/64 movlw 6 call Div2 call Sub16 ; f := 5xADC - 5xADC/64 ; xw auf 5xADC/128 verringern movlw 1 call Div2 call Sub16 ; f := 5xADC - 5xADC/64 - 5xADC/128 return ; fertig ;***************************************************** ; Wandlung einer Binärzahl (< 10000) in eine Dezimalzahl ; Die Binärzahl steht in f1,f0 ; die Dezimalstellen werden in ST (tausender), SH (hunderter), ; SZ (zehner) und SE (einer) gespeichert im BCD-Code B2D ; Test auf tausender 1000d = 0x03E8 movlw 0x03 movwf xw1 movlw 0xE8 movwf xw0 call B2Da movwf ST ; Test auf hunderter 100d = 0x0064 clrf xw1 movlw 0x64 movwf xw0 call B2Da movwf SH ; Test auf zehner 10d = 0x000A clrf xw1 movlw 0x0A movwf xw0 call B2Da movwf SZ movfw f0 movwf SE return B2Da clrf counter B2Sb incf counter, f ; wie oft abgezogen? call Sub16 ; f:=f-xw btfss STATUS, C ; zu oft abgezogen? goto B2Sb ; nein: noch einmal call Add16 ; f:=f+xw decf counter, w ; weil immer 1 zuviel gezählt wird return ;***************************************************** ;Anzeige der dezimalzahl am LCD mit 'mV' ; input: ST, SH, SZ, SE dezimalstellen im BCD-Code Ausgabe movlw B'10000000' ; 1. Zeile call OutLcdControl movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw '0' ; 30h = '0011 0000' iorwf ST, w ; BCD -> ASCII call OutLcdDaten ; zum LCD movlw '.' call OutLcdDaten movlw '0' iorwf SH, w call OutLcdDaten movlw '0' iorwf SZ, w call OutLcdDaten movlw '0' iorwf SE, w call OutLcdDaten ; movlw 'm' ; 'mA' anhängen ; call OutLcdDaten movlw 'V' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten movlw ' ' call OutLcdDaten ;movlw B'01000000' ;call OutLcdControl return ;***************************************************** ;+++LCD-Routinen************************************** ;***************************************************** ;LCD initialisieren, Begrüßung ausgeben InitLcd movlw D'255' ; 250 ms Pause nach dem Einschalten movwf loops call WAIT movlw B'00110000' ; 1 movwf LcdPort bsf LcdE nop bcf LcdE movlw D'50' ; 50 ms Pause movwf loops call WAIT movlw B'00110000' ; 2 call Control8Bit movlw B'00110000' ; 3 call Control8Bit movlw B'00100000' ; 4 call Control8Bit movlw B'00000001' ; löschen und cusor home call OutLcdControl movlw B'00101000' ; 5 function set, 4-bit 2-zeilig, 5x7 call OutLcdControl movlw B'00001000' ; 6 display off call OutLcdControl movlw B'00000110' ; 7 entry mode, increment, disable display-shift call OutLcdControl movlw B'00000011' ; 8 cursor home, cursor home call OutLcdControl movlw B'00001100' ; 9 display on, Kursor aus , Blinken aus call OutLcdControl return ;***************************************************** ; ein Steuerbyte 8-bittig übertragen Control8Bit movwf LcdPort bsf LcdE nop bcf LcdE movlw D'10' movwf loops call WAIT return ;***************************************************** ; darauf warten, daß das Display bereit zur Datenannahme ist LcdBusy bsf STATUS, RP0 ; make Port B4..7 input movlw B'11110000' iorwf TRISB, f bcf STATUS, RP0 BusyLoop bcf LcdRs bsf LcdRw ; Lesen bsf LcdE nop movf LcdPort, w movwf LcdStatus bcf LcdE nop bsf LcdE ; Enable nop bcf LcdE btfsc LcdStatus, 7 ; teste bit 7 goto BusyLoop bcf LcdRw bsf STATUS, RP0 ; make Port B4..7 output movlw B'00001111' andwf TRISB, f bcf STATUS, RP0 return ;***************************************************** ; aus W ein Byte mit Steuerdaten zum Display übertragen OutLcdControl movwf LcdDaten call LcdBusy movf LcdDaten, w andlw H'F0' movwf LcdPort ; Hi-teil Daten schreiben bsf LcdE nop bcf LcdE ; Disable LcdBus swapf LcdDaten, w andlw H'F0' movwf LcdPort ; Lo-teil Daten schreiben bsf LcdE nop bcf LcdE ; Disable LcdBus return ;***************************************************** ; aus W ein Datenbyte zum Display übertragen OutLcdDaten movwf LcdDaten call LcdBusy movf LcdDaten, w andlw H'F0' movwf LcdPort ; Hi-teil Daten schreiben bsf LcdRs ; Daten bsf LcdE ; Enable LcdBus nop bcf LcdE ; Disable LcdBus swapf LcdDaten, w andlw H'F0' movwf LcdPort ; Lo-teil Daten schreiben bsf LcdRs ; Daten bsf LcdE nop bcf LcdE ; Disable LcdBus bcf LcdRs ; return ;***************************************************** ;Zeitverzögerung um loops * 1 ms ; 10 MHz externer Takt bedeutet 2,5 MHz interner Takt ; also dauert 1 ms genau 2500 Befehle ; 250 Schleifen a 10 Befehle sind 2500 Befehle = 1 ms WAIT top movlw .250 ; timing adjustment variable (1ms) movwf loops2 top2 nop ; sit and wait nop nop nop nop nop nop decfsz loops2, F ; inner loops complete? goto top2 ; no, go again ; decfsz loops, F ; outer loops complete? goto top ; no, go again retlw 0 ; yes, return from subWAIT end