; Thermometer / clock ; Jan 2001 ; LCD hookup on RB3 - RB0 ; RS to ground ; RW on RB4 ; Enable on RB5 ; RA0 input on A/D ; RA1 output on A/D ; RA2 input for setting time ; RA3 input for entering time ; RA4 input for detecting charging ; RA7 output high to turn on LCD/temp ; INCLUDE 'M16F62x.inc' DEVICE PROTECT_OFF, WDT_OFF, PWRT_ON, BOD_OFF, MCLR_ON, LVP_OFF DEVICE INTRC_OSC_NOCLKOUT DEVICE PIC16F628 porta_buf equ 0x20 portb_buf equ 0x21 trisa_buf equ 0x22 trisb_buf equ 0x23 W_s equ 0x24 STATUS_s equ 0x25 LCD_data equ 0x26 LCD_Enable equ 5 LCD_RS equ 4 ;LCD_RW equ tied to ground TimeH equ 0x27 TimeM equ 0x28 TimeS equ 0x29 TimePos equ 7 ;position on screen where time is drawn tempF equ 0x30 tempC equ 0x31 i equ 0x32 x equ 0x33 ;16 bit registers. high comes lower in ram xlow equ 0x34 y equ 0x35 ylow equ 0x37 t equ 0x38 tlow equ 0x39 tempF_f equ 0x3a ;fractional portion of temperatures tempC_f equ 0x3b TempPos equ 0x40 + 4 TempPin equ 1 ;pin on port A to sample waitcounter equ 0x3c ;file register for wait DispTempEn equ 0x40 ;non-zero if enabled org 0x0000 BCF INTCON, GIE goto start MOVLF macro LITERAL, FILE movlw LITERAL movwf FILE ENDM BSW macro LITERAL iorlw (1< 1) movf FILE, W ;move to working MOD LITERAL movwf FILE ;move back to register ENDM MOD macro LITERAL ; W mod LITERAL addlw (256 - LITERAL) ;subtract literal BTFSS STATUS, C ;if negative, add back addlw LITERAL ENDM WAITMACRO MACRO LITERAL movlw LITERAL call wait ENDM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; interrupt handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0004 ;interrupt handler movwf W_s ;save context swapf W_s, F swapf STATUS, W movwf STATUS_s ;main interrupt routine movlw 0x80 addwf TMR1H ;use half of cycle ;handle time incf TimeS MODF TimeS, 60 BTFSS STATUS, Z goto updatetime ;second overflow incf TimeM MODF TimeM, 60 BTFSS STATUS, Z goto updatetime ;minutes overflow incf TimeH MODF TimeH, 24 updatetime call CheckPower ;bsf disptempen,7 btfss DispTempEn, 7 ;if zero, don't update goto exit_int movlf TimePos, LCD_Data call MoveLCD call DrawTime movlw 11101111b BSF STATUS, RP0 andwf TRISA BCF STATUS, RP0 movlw 11101111b andwf porta movf timeS, w andlw 1 addlw 1111b andlw 10000b iorwf PORTA ;handle temperature movlf TempPos, LCD_Data call MoveLCD call sampleTemp call DrawTempF ;exit interrupt exit_int BCF PIR1,TMR1IF ;clear timer overflow swapf STATUS_s, W movf STATUS swapf W_s, W retfie ;;;;;;;;; start of program start MOVLW 0x07 MOVWF CMCON movlf 00000000b,porta bsf STATUS, rp0 movlf 11111111b, trisa bcf STATUS, rp0 clrf DispTempEn waitforpower WAITMACRO 20 ;call CheckPower call InitLCD ;btfss DispTempEn, 7 ; goto waitforpower ;configure the LCD ;call InitLCD ;done in checkpower ;set display mode ;movlf 00000100b, LCD_Data ;call ModeLCD ;call set time routine clrf DispTempEn bsf DispTempEn, 4 clrf TimeH clrf TimeM clrf TimeS clrf TempF clrf TempF_f clrf TempC clrf TempC_f ;call SetTime call ClearLCD ;configure timer interrupt call InitTimer1 ;set up timer call CheckPower ;see if it should stay powered up stable sleep goto stable ;;;;;;;;;;;;;;;;;;;;; ; subroutines ;;;;;;;;;;;;;;;;;;;;; InitTimer1 movlw 0x80 ;only use half of cycle. otherwise would be too long of loop movwf TMR1H ; clrf TMR1L ;set initial value movlw 00001110b ; 1:1 prescale, oscillator enabled, not synchronized, external clock, not enabled yet movwf T1CON ;set timer1 control register BSF INTCON, PEIE ; open up interrupts bsf STATUS, rp0 BSF PIE1, TMR1IE bcf STATUS, rp0 BSF INTCON, GIE BSF T1CON, 0 ; start timer return Wait clrf waitcounter incf waitcounterincf btfss STATUS, Z goto $ - 2 addlw 0xff ;ms to wait in W (approx) btfss STATUS, Z goto wait return LATCHLCD MACRO movf portb_buf, w BSW LCD_Enable movwf portb nop clrw addlw 1 btfss STATUS,Z goto $ - 2 nop BCW LCD_Enable movwf portb nop nop ENDM ;;; initiate LCD display InitLCD BCF STATUS, RP1 BSF STATUS, RP0 ;bank 1 movlf 11000000b, TRISB ;bit mask for LCD output BCF STATUS, RP0 ;bank 0 WAITMACRO 20 movlf 00000011b, portb_buf LATCHLCD WAITMACRO 5 movlf 00000011b, portb_buf LATCHLCD WAITMACRO 5 movlf 00000011b, portb_buf LATCHLCD WAITMACRO 5 movlf 00000010b, portb_buf LATCHLCD WAITMACRO 5 ;set function byte 1 movlf 00000010b, portb_buf LATCHLCD WAITMACRO 2 ;set function byte 0 movlf 00001000b, portb_buf LATCHLCD WAITMACRO 2 ;clear display movlf 00000000b, portb_buf LATCHLCD WAITMACRO 2 movlf 00001000b, portb_buf LATCHLCD WAITMACRO 1 ;reset display movlf 00000000b, portb_buf LATCHLCD WAITMACRO 2 movlf 00000001b, portb_buf LATCHLCD WAITMACRO 2 return ;;; clear display ClearLCD movlf 00000000b, portb_buf LATCHLCD WAITMACRO 1 movlf 00000001b, portb_buf LATCHLCD WAITMACRO 1 return ;;;; set display mode bits ; bit 2 - on/off ; bit 1 - cursor ; bit 0 - blink ModeLCD movlf 00000000b, portb_buf LATCHLCD WAITMACRO 1 movlw 00000111b andwf LCD_Data, W iorlw 00001000b movwf portb_buf LATCHLCD WAITMACRO 1 clrf portb_buf clrf portb return ;;; LCD_Data is sent as data DataLCD swapf LCD_Data, W andlw 00001111b BSW LCD_RS movwf portb_buf LATCHLCD WAITMACRO 1 movf LCD_Data, W andlw 00001111b BSW LCD_RS movwf portb_buf LATCHLCD WAITMACRO 1 clrf portb_buf clrf portb return ;;;; Move to location MoveLCD swapf LCD_Data, W andlw 00000111b iorlw 00001000b movwf portb_buf LATCHLCD WAITMACRO 1 movf LCD_Data, W andlw 00001111b movwf portb_buf LATCHLCD WAITMACRO 1 clrf portb_buf clrf portb return ;;; draw temp at current location on LCD DrawTempF movf TempF, W movwf X movlw 100 movwf Y call Divide ;find 100s movf X, w btfsc STATUS, Z movlw ( ' ' - '0' ) addlw '0' movwf LCD_Data Call DataLCD ;draw 100s digit movf T, W ;use remander (10s and 1s movwf X movlw 10 movwf Y Call Divide movf X, W ;tens addlw '0' movwf LCD_Data Call DataLCD movf T, W addlw '0' ;ones movwf LCD_DATA call DataLCD movlw '.' movwf LCD_DATA Call DataLCD movf TempF_f, W addlw '0' movwf LCD_Data Call DataLCD movlw 0xDF ;degree symbol movwf LCD_Data Call DataLCD movlw 'F' movwf LCD_Data Call DataLCD return ;;; draw time at current location on LCD DrawTime ;draw hours movf TimeH, W MOD 12 btfsc STATUS, Z movlw 12 ;w contains hour number movwf X movlw 10 movwf Y Call Divide movf X, W ;move 10s to w btfsc STATUS, Z ;if 0, then set to space movlw (' ' - '0') addlw '0' ;set to correct digit movwf LCD_Data Call DataLCD movf T, W addlw '0' movwf LCD_Data Call DataLCD movlw ':' movwf LCD_Data Call DataLCD ;minutes movf TimeM, W movwf X Call Divide ;divide by ten movf X, W addlw '0' movwf LCD_Data Call DataLCD movf T, W addlw '0' movwf LCD_Data Call DataLCD movlw ':' movwf LCD_Data Call DataLCD ;seconds movf TimeS, W movwf X Call Divide ;divide by ten movf X, W addlw '0' movwf LCD_Data Call DataLCD movf T, W addlw '0' movwf LCD_Data Call DataLCD movlw ' ' movwf LCD_Data Call DataLCD movf TimeH, W movwf X movlw 12 movwf Y Call Divide ;find am/pm movlw 'a' btfsc X, 0 movlw 'p' movwf LCD_Data call DataLCD return ;;; get user input for time SetTime clrf TimeS ;clear time clrf TimeM clrf TimeH movlf 00000101b, LCD_Data Call ModeLCD clrf LCD_Data Call MoveLCD movlf TIMEMESSAGE, LCD_Data Call MessageLCD setH movlf TimePos + 0x40, LCD_Data Call MoveLCD Call DrawTime movlf ( TimePos + 0x40 + 1 ), LCD_Data Call MoveLCD Call getIncrementSetTime movwf X addwf TimeH, W MOD 24 movwf TimeH Call WaitSecond BTFSC X, 0 goto setH setMh movlf TimePos + 0x40, LCD_Data Call MoveLCD Call DrawTime movlf ( TimePos + 0x40 + 3 ), LCD_Data Call MoveLCD Call getIncrementSetTime movwf X BTFSC X, 0 movlw 10 ; add ten if was 1 addwf TimeM, W MOD 60 movwf TimeM Call WaitSecond BTFSC X, 0 goto setMh setM movlf TimePos + 0x40, LCD_Data Call MoveLCD Call DrawTime movlf ( TimePos + 0x40 + 4 ), LCD_Data Call MoveLCD Call getIncrementSetTime movwf X addwf TimeM, W MOD 60 movwf TimeM Call WaitSecond BTFSC X, 0 goto setM setSh movlf TimePos + 0x40, LCD_Data Call MoveLCD Call DrawTime movlf ( TimePos + 0x40 + 6 ), LCD_Data Call MoveLCD Call getIncrementSetTime movwf X BTFSC X, 0 movlw 10 ; add ten if was 1 addwf TimeS, W MOD 60 movwf TimeS Call WaitSecond BTFSC X, 0 goto setSh setS movlf TimePos + 0x40, LCD_Data Call MoveLCD Call DrawTime movlf ( TimePos + 0x40 + 7 ), LCD_Data Call MoveLCD Call getIncrementSetTime movwf X addwf TimeS, W MOD 60 movwf TimeS Call WaitSecond BTFSC X, 0 goto setS movlf 00000100b, LCD_Data Call ModeLCD return ;;; wait one second WaitSecond WAITMACRO 250 WAITMACRO 250 WAITMACRO 250 WAITMACRO 250 return ;;; return 1 if pressed + button, and 0 if pressed enter getIncrementSetTime btfsc PORTA, 2 goto onehigh btfsc PORTA, 3 goto zerohigh goto getIncrementSetTime onehigh WAITMACRO 50 btfsc PORTA, 2 retlw 1 goto getIncrementSetTime zerohigh WAITMACRO 50 btfsc PORTA, 3 retlw 0 goto getIncrementSetTime ;;;; divide x by y into register x, remander in T Divide clrf T movlw 8 movwf i divtop bcf STATUS, C rlf X, F rlf T, F movf Y, W subwf T, W btfsc STATUS, C movwf T btfsc STATUS, C bsf X, 0 decfsz i goto divtop return ;;; more math ;;; add X:Xl + Y:Yl -> X:Xl add16 clrf T ;hold carry if needed movf Y+1, W addwf X+1, F btfsc STATUS, C incf X btfsc STATUS, C incf T movf Y, W addwf X, F movf T, W iorwf STATUS ;status has C as 1 if carried. return add16m MACRO Xm, Ym movf Ym+1, W addwf Xm+1, F btfsc STATUS, C incf Xm movf Ym, W addwf Xm, F ENDM sub16 clrf T movf Y+1, W subwf X+1, F btfss STATUS, C ;check for borrow decf X ;was a borrow btfss STATUS, C ;again incf T ;T = 1 if borrow movf Y, W subwf X, F movf T, W xorwf STATUS ; if borrow occured once, C will be 0. else, 1. borrow can't happen twice return sub16m MACRO Xm, Ym movf Ym+1, W subwf Xm+1, F btfsc STATUS, C decf Xm movf Ym, W subwf Xm, F ENDM ; x:x1 by y:y1 into t:t1:x:x1 mult16 clrf T ;32 bit result is T:Tl:X:Xl clrf T+1 movlw 16 ;16 rounds movwf i loopmult16 btfss X+1, 0 goto noadd add16m T, Y noadd bcf STATUS, C rrf T rrf T+1 rrf X rrf X+1 decfsz i goto loopmult16 return MOV16 MACRO Xm,Ym ;Xm <- Ym movf Ym, W movwf Xm movf Ym+1, W movwf Xm+1 ENDM ;;; get temperature sampleTemp movlw 11111101b ;1 is output, 0 is input BSF STATUS, rp0 andwf TRISA BCF STATUS, rp0 clrf T+1 movlf 16, T clrf T+1 presampleloop movf porta, W andlw 1 btfss STATUS, Z bcf porta, 1 ;skip if 0 btfsc STATUS, Z bsf porta, 1 ;skip if 1 nop nop nop decfsz T+1 goto presampleloop decfsz T goto presampleloop clrf X clrf X+1 movlf 32, T ; 8192 samples clrf T+1 ;bcf porta, 1 ;output 0 ;btfsc porta, 0 ;wait until 0 ;goto $ - 1 ;bsf porta, 1 ;output 1 ;btfss porta, 0 ;wait until 1 ;goto $ - 1 sampleloop movf porta, W andlw 1 btfss STATUS, Z bcf porta, 1 ;skip if 0 btfsc STATUS, Z bsf porta, 1 ;skip if 1 addwf X+1, F btfsc STATUS, C incf X decfsz T+1 goto sampleloop decfsz T goto sampleloop ;sensor measurement in x,x+1 movf X+1, W movwf TempF movlf 4, Y+1 clrf Y call mult16 ;multiply by 4, so upper is 128 - 0 range. ;correction math movlf 0, Y ; f(x) = 1.6445 * X - 48.402 movlf 156, Y + 1 call mult16 movf X, w movwf X+1 movf T+1, w movwf X movlf 28, Y ; 29 and 84/256 movlf 229, Y + 1 call Add16 ;now format movf X, W movwf TempF movf x+1,w movwf X ;range of 0 - 256, 16 interval. movlf 26, Y ;divide lower by 26 to get 0-9 interval call Divide movf x,w movwf TempF_f ;BSF STATUS, rp0 ;clrf TRISA ;turn off output ;BCF STATUS, rp0 bcf PORTA, 1 return CheckPower movf PORTA, W andlw 10000000b xorwf DispTempEn, w btfsc STATUS, Z return ;if zero, no change xorwf DispTempEn, w btfss STATUS, Z ;if not set... goto displayon ;... turn on display... displayoff movlw 10111111b andwf PORTA movlw 10111111b bsf STATUS, RP0 andwf TRISA, F movlw 11111111b iorwf TRISB, F bcf STATUS, RP0 clrf DispTempEn return displayon movlw 01000000b iorwf PORTA, F movlw 10111111b bsf STATUS, RP0 andwf TRISA, F bcf STATUS, RP0 movlw 01000000b iorwf PORTA, F call InitLCD movlf 00000100b, LCD_Data call ModeLCD movlf 10000000b, DispTempEn return ;;; displayable messages TIMEMESSAGE equ 0 org 0x0400 getbyte addwf PCL, F ;message data follows db "Enter current time:", 0 ;;; draw message subroutine. message index in LCD_Data MessageLCD clrf i movlf 03h, PCLATH movf LCD_Data, W movwf i ;location to fetch from messageloop movf i, W incf i call getbyte addlw 0 btfsc STATUS, Z ;return when 0 in W goto exitmessage movwf LCD_DATA ; otherwise, display character Call DataLCD WAITMACRO 1 goto messageloop exitmessage movlf 00h, PCLATH return