;*******************************************************************
;  TEMPALM4.ASM
;  Temperature Alarm/Thermostat  Celsius and Fahrenheit Display
;
;  Use with Philips 2322-640-63103 NTC Thermistor R/r=9.0, 10K@25C
;
;  Valid range of -50C to +125C.  Basic accuracy 2% within the
;  range -30C to +80C.
;*******************************************************************
;   Target: PIC16C84 or PIC16F84
;
;   Hardware:
;   Port B <> LTN211 Display and Keypad
;   RB7       E  : Enable (H->L transition latches data)
;   RB6       KeyPad Input
;   RB5       Piezo Beeper Output
;   RB4       Register Select for LCD
;   RB0 - RB3 LCD Data Lines and 4 keys
;
;   Port A <> ADC0831 Serial ADC
;   RA0       ADC Data (Input)
;   RA1       ADC Clock (Output)
;   RA2       ADC CS (Output)
;   RA3       Thermostat Output (LED/Opto Driver)
;
;*******************************************************************
;
	LIST    p=16F84       ;PIC16F84 is the target processor
	INCLUDE "P16F84.INC"  ;Include file with register defines
;
;********************************************************************
;               Test Program
;*********************************************************************

;Register Defines
Temp      equ    2E        ;temp storage during minus sign functions
ADCresult equ    2D        ;stores ADC reading
ADCCtr    equ    2C        ;stores count for reading 8 bits from serial ADC
MaxTemp   equ    2B        ;Temp. locations for Min and Max temp
MinTemp   equ    2A
HighAlarm equ    29        ;Current high/low alarm values
LowAlarm  equ    28        ;Stored as ADC values, not actual temps.
AlarmOn   equ    27 
Temp2     equ    26        ;temp storage

;Registers for B2_BCD
H_Byte    equ     19       ;Input (Binary) values
L_Byte    equ     1A
R0        equ     1B       ;Output (BCD) values
R1        equ     1C
R2        equ     1D
count     equ     1E       ;Temp. registers for function
B2temp    equ     1F

;Registers for Misc. Functions
DispChar   equ  10         ;Character to display on LCD
DelayTemp  equ  11         ;Temp Counter for time delay
DelayT2    equ  12         ;Counter for LONG time delay
TempKey    equ  13         ;Temporary key press value
AlarmFlag  equ  14         ;Bit1=ALARM_ACTIVE
CharCounter equ 15         ;Counter for String printing
DegreesC   equ  16         ;Temperature in degrees Celsius
DegreesSign equ 17         ;Sign Flag for degrees
mulplr     equ  18         ;Multiplier for 8*8 (C->F conversion)

;PORT defines
ADCCLK    equ    1
ADCDAT    equ    0
ADCCS     equ    2
LED       equ    3      ;Alarm/Thermostat Output LED
BEEPER    equ    5      ;Beeper on Port B

;EEPROM addresses
EEADR_HI_LIM    equ     1       ;High temperature alarm/limit
EEADR_LO_LIM    equ     2       ;Low temperature alarm/limit
EEADR_MAX_TEMP  equ     3       ;Maximum temperature reached
EEADR_MIN_TEMP  equ     4       ;Minimum temperature reached
EEADR_ALARM_ON  equ     5       ;Alarm On/Off Status

;PIC Register Location Defines
EEDATA    equ     8   ;EEPROM register defines
EEADR     equ     9
EECON1    equ    88
EECON2    equ    89

;Keys
KEY_MODE  equ   0       ;MODE key used to set parameters
KEY_UP    equ   1       ;Increment key
KEY_DN    equ   2       ;Decrement key
KEY_ENTER equ   3       ;ENTER key

;Flags
ALARM     equ   1       ;Flag to Signify that Alarm is currently ON
ON        equ   1       ;Flag to enable Audible Alarm

;MAIN Program Begins
       goto    main

;Celsius Conversion Table
;  Parameters: ADC value in w
;  Returns   : Celsius temperature (signed via bit 7) in w
CTable addwf   PCL,f       ;calculate jump table location
       retlw   .50+80      ;location 0 - i.e. ADC reading of 0
       retlw   .50+80      ;Set BIT 7 for a negative quantity
       retlw   .50+80
       retlw   .50+80
       retlw   .49+80
       retlw   .46+80
       retlw   .43+80
       retlw   .40+80
       retlw   .39+80
       retlw   .37+80
       retlw   .35+80
       retlw   .34+80
       retlw   .32+80
       retlw   .31+80
       retlw   .30+80
       retlw   .29+80
       retlw   .27+80         ;location 10h
       retlw   .26+80
       retlw   .25+80
       retlw   .24+80
       retlw   .24+80
       retlw   .23+80
       retlw   .22+80
       retlw   .21+80
       retlw   .20+80
       retlw   .19+80
       retlw   .19+80
       retlw   .18+80
       retlw   .17+80
       retlw   .16+80
       retlw   .16+80
       retlw   .15+80
       retlw   .15+80         ;location 20h
       retlw   .14+80
       retlw   .13+80
       retlw   .13+80
       retlw   .12+80
       retlw   .12+80
       retlw   .11+80
       retlw   .11+80
       retlw   .10+80
       retlw   .9+80
       retlw   .9+80
       retlw   .8+80
       retlw   .8+80
       retlw   .7+80
       retlw   .7+80
       retlw   .6+80
       retlw   .6+80         ;location 30
       retlw   .5+80
       retlw   .5+80
       retlw   .4+80
       retlw   .4+80
       retlw   .3+80
       retlw   .3+80
       retlw   .2+80
       retlw   .2+80
       retlw   .2+80
       retlw   .1+80
       retlw   .1+80
       retlw   .0
       retlw   .0
       retlw   .1
       retlw   .1
       retlw   .1          ;location 40
       retlw   .2
       retlw   .2
       retlw   .2
       retlw   .3
       retlw   .3
       retlw   .4
       retlw   .4
       retlw   .4
       retlw   .5
       retlw   .5
       retlw   .6
       retlw   .6
       retlw   .6
       retlw   .7
       retlw   .7
       retlw   .7          ;location 50
       retlw   .8
       retlw   .8
       retlw   .9
       retlw   .9
       retlw   .9
       retlw   .10
       retlw   .10
       retlw   .10
       retlw   .11
       retlw   .11
       retlw   .12
       retlw   .12
       retlw   .12
       retlw   .13
       retlw   .13
       retlw   .13         ;location 60
       retlw   .14
       retlw   .14
       retlw   .14
       retlw   .15
       retlw   .15
       retlw   .15
       retlw   .16
       retlw   .16
       retlw   .16
       retlw   .17
       retlw   .17
       retlw   .17
       retlw   .18
       retlw   .18
       retlw   .18
       retlw   .19         ;location 70
       retlw   .19
       retlw   .20
       retlw   .20
       retlw   .20
       retlw   .21
       retlw   .21
       retlw   .21
       retlw   .22
       retlw   .22
       retlw   .22
       retlw   .23
       retlw   .23
       retlw   .23
       retlw   .24
       retlw   .24
       retlw   .25         ;location 80
       retlw   .25
       retlw   .25
       retlw   .26
       retlw   .26
       retlw   .26
       retlw   .27
       retlw   .27
       retlw   .27
       retlw   .28
       retlw   .28
       retlw   .28
       retlw   .29
       retlw   .29
       retlw   .30
       retlw   .30
       retlw   .31        ;location 90
       retlw   .31
       retlw   .31
       retlw   .32
       retlw   .32
       retlw   .33
       retlw   .33
       retlw   .33
       retlw   .34
       retlw   .34
       retlw   .34
       retlw   .35
       retlw   .35
       retlw   .35
       retlw   .36
       retlw   .36
       retlw   .37         ;location A0
       retlw   .37
       retlw   .38
       retlw   .38
       retlw   .38
       retlw   .39
       retlw   .40
       retlw   .40
       retlw   .40
       retlw   .41
       retlw   .41
       retlw   .42
       retlw   .42
       retlw   .42
       retlw   .43
       retlw   .43
       retlw   .44         ;location B0
       retlw   .45
       retlw   .45
       retlw   .46
       retlw   .46
       retlw   .46
       retlw   .47
       retlw   .47
       retlw   .48
       retlw   .48
       retlw   .49
       retlw   .50
       retlw   .50
       retlw   .51
       retlw   .51
       retlw   .52
       retlw   .52         ;location C0
       retlw   .53
       retlw   .53
       retlw   .54
       retlw   .55
       retlw   .55
       retlw   .56
       retlw   .56
       retlw   .57
       retlw   .57
       retlw   .58
       retlw   .59
       retlw   .60
       retlw   .61
       retlw   .61
       retlw   .62
       retlw   .62         ;location D0
       retlw   .63
       retlw   .64
       retlw   .65
       retlw   .66
       retlw   .66
       retlw   .67
       retlw   .68
       retlw   .69
       retlw   .70
       retlw   .71
       retlw   .72
       retlw   .73
       retlw   .73
       retlw   .74
       retlw   .76
       retlw   .77         ;location E0
       retlw   .78
       retlw   .80
       retlw   .81
       retlw   .82
       retlw   .83
       retlw   .85
       retlw   .86
       retlw   .87
       retlw   .89
       retlw   .91
       retlw   .92
       retlw   .94
       retlw   .96
       retlw   .98
       retlw   .100
       retlw   .105         ;location F0
       retlw   .107
       retlw   .110
       retlw   .112
       retlw   .115
       retlw   .120
       retlw   .125
       retlw   .125
       retlw   .126        ;End of Table (F8)

	org     100     ;Put all string tables on Page 2

StringSetup
	addwf   PCL,f   ;Calculated jump for all strings
StringInit
	retlw   'T'
	retlw   'e'
	retlw   'm'
	retlw   'p'
	retlw   'A'
	retlw   'l'
	retlw   'm'
	retlw   '2'
	retlw   ' '
	retlw   'V'
	retlw   '2'
	retlw   '.'
	retlw   '1'
	retlw   80
StringInit2
	retlw   'M'
	retlw   '.'
	retlw   'C'
	retlw   's'
	retlw   'e'
	retlw   'l'
	retlw   'e'
	retlw   ' '
	retlw   '9'
	retlw   '9'
	retlw   '/'
	retlw   '0'
	retlw   '9'
	retlw   80
StringTemp
   retlw  '_'
	retlw  'T'
	retlw  'e'
	retlw  'm'
	retlw  'p'
	retlw  '='
	retlw  80       ;End-of-string terminator
StringMin
	retlw  'M'
	retlw  'i'
	retlw  'n'
	retlw  ' '
	retlw  'T'
	retlw  '='
	retlw  80       ;End-of-string terminator
StringMax
	retlw  'M'
	retlw  'a'
	retlw  'x'
	retlw  ' '
	retlw  'T'
	retlw  '='
	retlw  80       ;End-of-string terminator
StringHi
	retlw  'H'
	retlw  'i'
	retlw  ' '
	retlw  'L'
	retlw  'i'
	retlw  'm'
	retlw  '='
	retlw  80       ;End-of-string terminator
StringLow
	retlw  'L'
	retlw  'o'
	retlw  ' '
	retlw  'L'
	retlw  'i'
	retlw  'm'
	retlw  '='
	retlw  80       ;End-of-string terminator
StringSetLowAlarm
	retlw  'S'
	retlw  'e'
	retlw  't'
	retlw  ' '
	retlw  'L'
	retlw  'o'
	retlw  'w'
	retlw  ' '
	retlw  'L'
	retlw  'i'
	retlw  'm'
	retlw  'i'
	retlw  't'
	retlw  80
StringSetHighAlarm
	retlw  'S'
	retlw  'e'
	retlw  't'
	retlw  ' '
	retlw  'H'
	retlw  'i'
	retlw  'g'
	retlw  'h'
	retlw  ' '
	retlw  'L'
	retlw  'i'
	retlw  'm'
	retlw  'i'
	retlw  't'
	retlw  80       ;End-of-string terminator
StringClearMinMax
	retlw  'C'
	retlw  'l'
	retlw  'e'
	retlw  'a'
	retlw  'r'
	retlw  ' '
	retlw  'M'
	retlw  'i'
	retlw  'n'
	retlw  '/'
	retlw  'M'
	retlw  'a'
	retlw  'x'
	retlw  '?'
	retlw  80       ;End-of-string terminator
StringPressClr
	retlw  'P'
	retlw  'r'
	retlw  'e'
	retlw  's'
	retlw  's'
	retlw  ' '
	retlw  '+'
	retlw  ' '
	retlw  't'
	retlw  'o'
	retlw  ' '
	retlw  'C'
	retlw  'l'
	retlw  'r'
	retlw  80       ;End-of-string terminator
;>>> Future Option: Alarm/Thermostat function
StringSetAlarm
	retlw  'A'
	retlw  'l'
	retlw  'a'
	retlw  'r'
	retlw  'm'
	retlw  ' '
	retlw  'O'
	retlw  'n'
	retlw  '/'
	retlw  'O'
	retlw  'f'
	retlw  'f'
	retlw  '?'
	retlw  80       ;End-of-string terminator
StringPressOn
	retlw  'P'
	retlw  'r'
	retlw  'e'
	retlw  's'
	retlw  's'
	retlw  ' '
	retlw  '+'
	retlw  ' '
	retlw  'f'
	retlw  'o'
	retlw  'r'
	retlw  ' '
	retlw  'O'
	retlw  'N'
	retlw  ' '
	retlw  80       ;End-of-string terminator
StringPressOff
	retlw  'P'
	retlw  'r'
	retlw  'e'
	retlw  's'
	retlw  's'
	retlw  ' '
	retlw  '+'
	retlw  ' '
	retlw  'f'
	retlw  'o'
	retlw  'r'
	retlw  ' '
	retlw  'O'
	retlw  'F'
	retlw  'F'
	retlw  80       ;End-of-string terminator
StringAlmDisabled
	retlw  'A'
	retlw  'l'
	retlw  'a'
	retlw  'r'
	retlw  'm'
	retlw  ' '
	retlw  'D'
	retlw  'i'
	retlw  's'
	retlw  'a'
	retlw  'b'
	retlw  'l'
	retlw  'e'
	retlw  'd'
	retlw  80       ;End-of-string terminator

main
	clrf    PORTB         ;Make All Outputs LOW
	movlw   b'01000000'   ;Port B all outputs except Keypad input
	bsf     STATUS,RP0    ;select Page 1
	movwf   TRISB
	bcf     STATUS,RP0    ;select Page 0
	clrf    PORTB         ;Make All Outputs LOW

	clrf    PORTA         ;Make All Outputs LOW
	movlw   b'00000001'   ;Port A all outputs except ADC Data inputs
	bsf     STATUS,RP0    ;select Page 1
	movwf   TRISA
	bcf     STATUS,RP0    ;select Page 0
	movlw   b'00000100'   ;All Outputs LOW except ADC /CS high
	movwf   PORTA

	bsf     PORTB,BEEPER  ;Wait for LCD to power-up and beep
	call    Delay
	bcf     PORTB,BEEPER
	call    LCD_Init

	movlw   StringInit    ;Display Initialization Message
	call    OutString
	call    LCD_L2
	movlw   StringInit2
	call    OutString
        movlw   .16
	movwf   Temp
InitDly
	call    LongDelay
	decfsz  Temp,f
	goto    InitDly

;Initialize parameters from on-chip EEPROM storage
	movlw   EEADR_HI_LIM    ;Initialize HIGH and LOW alarms from EEPROM
	call    ReadEE          ;W now contains High limit
	movwf   HighAlarm
	movlw   EEADR_LO_LIM
	call    ReadEE
	movwf   LowAlarm
	movlw   EEADR_MIN_TEMP  ;Initialize HIGH and LOW alarms from EEPROM
	call    ReadEE
	movwf   MinTemp
	movlw   EEADR_MAX_TEMP
	call    ReadEE
	movwf   MaxTemp
	movlw   EEADR_ALARM_ON
	call    ReadEE
	movwf   AlarmOn

	clrf    AlarmFlag     ;Set Alarm OFF

mainloop
	call   LCD_Cls        ;Clear Display

	call   ReadADC

	;Update MIN and MAX temps if required

	movf    MaxTemp,w
	subwf   ADCresult,0   ;check if max temp exceeded
	btfss   STATUS,C
	goto    NotNewMax
	movf    ADCresult,w   ;ADC > Max temp ... update var as well as EEPROM
	movwf   MaxTemp
	movwf   Temp
	movlw   EEADR_MAX_TEMP
	call    WriteEE       ;Write Max Temp to EEPROM
NotNewMax
	movf    MinTemp,w
	subwf   ADCresult,0   ;check if under min temp
	btfsc   STATUS,C
	goto    NotNewMin
	movf    ADCresult,w   ;ADC < Min temp ... update var and EEPROM
	movwf   MinTemp
	movwf   Temp
	movlw   EEADR_MIN_TEMP
	call    WriteEE       ;Write Min Temp to EEPROM
NotNewMin

	;Display current temperature
	movlw  80           ;Top Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringTemp   ;Display "Temp="
	call   OutString

	movfw  ADCresult    ;Convert to degrees, display current temp and "C"
	call   DisplayTemp
	
	movlw  b'10001111'  ;Top Line, last char
	movwf  DispChar
	call   LCD_Cmd
	movlw  '_'
	movwf  DispChar
	call   LCD_Chr

	bcf     AlarmFlag,ALARM ;Turn off alarm flag
	movf    HighAlarm,w
	subwf   ADCresult,0     ;check if high alarm exceeded
	btfsc   STATUS,C
	goto    AlarmIsOn

	movf    LowAlarm,w
	subwf   ADCresult,0     ;check if low alarm exceeded
	btfsc   STATUS,C
	goto    NoAlarm

AlarmIsOn
	bsf    AlarmFlag,ALARM  ;set alarm flag ON

NoAlarm

;Set or Clear limit LED as required
	btfsc  AlarmFlag,1  ;Check if Alarm is ON, if so Light LED
	goto   AlmLedOn
	bcf    PORTA,LED    ;LED Off
	goto   AlmLedOff
AlmLedOn
	bsf    PORTA,LED
AlmLedOff

;Display min/max and hi/low limits alternately on bottom
	movlw  b'11000000'  ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringMin    ;Display "Min T="
	call   OutString
	movfw  MinTemp
	call   DisplayTemp

        call   BeepAndTest   ;Beep if alarm enabled and test keypad

	movlw  b'11000000'  ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringMax    ;Display "Max="
	call   OutString
	movfw  MaxTemp
	call   DisplayTemp

        call   BeepAndTest   ;Beep if alarm enabled and test keypad

	;Display High and Low Alarm Values
	movlw  b'11000000'  ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringHi      ;Display "Hi=" High Alarm message
	call   OutString
	movfw  HighAlarm
	call   DisplayTemp

        call   BeepAndTest   ;Beep if alarm enabled and test keypad

	movlw  b'11000000'  ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringLow     ;Display "Lo=" Low Alarm message
	call   OutString
	movfw  LowAlarm
	call   DisplayTemp

        call   BeepAndTest

;If alarm is disabled, display warning message
	btfsc  AlarmOn,ON       ;Display warning only if alarm is disabled
	goto   mainloop

	movlw  b'11000000'  ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringAlmDisabled	;Display "Alarm Disabled"
	call   OutString
	call   BeepAndTest
	goto   mainloop

;End Of Main Loop

;### Called Functions
;   BeepAndTest: If alarm is enabled, beep.  Also check keypad for keypress during delay
;   DisplayTemp: Converts an ADC value into degrees C and displays on LCD
;   ScanKey    : scans four keys, returns key pressed in w

;If alarm is enabled and Alarm limits are exceeded, beep.
;Also, check keypad for keys pressed
BeepAndTest
        btfss  AlarmOn,ON       ;Beep only if Alarm is Enabled
        goto   NoAudibleAlarm
        btfsc  AlarmFlag,ALARM  ;Check if Alarm is ON, if so, Beep 1 sec
        bsf    PORTB,BEEPER
NoAudibleAlarm
        movlw   .8              ;Call 125mS delay 8 times for 1 second delay
        movwf   Temp
DDelay  decfsz  Temp,f
        goto    DelayIt

        bcf    PORTB,BEEPER     ;Turn off beeper
        return

;Check Keypad for MODE key pressed.  If pressed, Jump to Parameter Setting
;routine.  Otherwise, just delay one second for display
DelayIt
	call    LongDelay       ;125mS delay
	call    ScanKey         ;Check if a key is pressed
	movwf   Temp2
	btfsc   Temp2,KEY_MODE  ;Go to Parameter Setting mode
	call    ModeSet
	goto    DDelay

;### KeyPad Scan Function
;Returns either 0 in w (no key pressed) or bits 0-3 set in w for key pressed
;Fully debounced, waits for key release
ScanKey
	call    TestKey
	movwf   Temp2
	btfss   Temp2,7       ;Is w still null? (i.e. is bit 7 still set)
	goto    WaitForRls
	movlw   0             ;Nothing found, clear to <space>
	return
WaitForRls
	movwf   TempKey
WaitKeyLoop
	call    TestKey
	movwf   Temp2
	btfss   Temp2,7       ;Is w still null? (i.e. is bit 7 still set)
	goto    WaitKeyLoop   ;Key still down .... wait for release
	movfw   TempKey       ;Put Key Return Value into w
	return

;### KeyPad Non-debounced Scan Function
;Returns either 0 in w (no key pressed) or 1-4 in w for key pressed
;No debounce, active on key press
ScanKey2
	call    TestKey
	movwf   Temp2
	btfss   Temp2,7       ;Is w still null? (i.e. is bit 7 still set)
	return                ;w must contain 1-4
	movlw   0             ;Nothing found, clear to <space>
	return

;Return bit 7 high if no key pressed or 1-4 in w for key pressed
TestKey
	clrf    PORTB        ;All data lines low
	movlw   80           ;Pre-Load w with bit 7 high
	bsf     PORTB,0      ;Make Key 1 row HIGH
	btfsc   PORTB,6      ;Check if key 1 pressed
	movlw   b'00000001'
	bcf     PORTB,0
	bsf     PORTB,1      ;Make Key 2 row HIGH
	btfsc   PORTB,6      ;Check if key 2 pressed
	movlw   b'00000010'
	bcf     PORTB,1
	bsf     PORTB,2      ;Make Key 3 row HIGH
	btfsc   PORTB,6      ;Check if key 3 pressed
	movlw   b'00000100'
	bcf     PORTB,2
	bsf     PORTB,3      ;Make Key 4 row HIGH
	btfsc   PORTB,6      ;Check if key 4 pressed
	movlw   b'00001000'
	bcf     PORTB,3
	return

;### SET key was pressed ... Allow user to set parameters
ModeSet

	call   LCD_Cls
	movlw  StringSetHighAlarm ;Display "SET HIGH ALARM"
	call   OutString
ModeSetHi2
	movlw  b'11000000'      ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringHi         ;Display "HI ="
	call   OutString
	movf   HighAlarm,w
	call   DisplayTemp
	call    ScanKey2      ;Check if a key is pressed
	movwf   Temp2
	btfsc   Temp2,KEY_MODE  ;If Key 1, go to next MODE (Set Low Alm)
	goto    ModeSetLow
	btfsc   Temp2,KEY_UP   ;If "+", Increment HIGH alarm Value
	incf    HighAlarm
	btfsc   Temp2,KEY_DN   ;If "-", Decrement HIGH alarm Value
	decf    HighAlarm
	call    ShortDelay
	goto    ModeSetHi2

ModeSetLow
	call    LCD_Cls
	movlw   StringSetLowAlarm       ;Display "SET LOW ALARM"
	call    OutString
ModeSetLow2
	movlw  b'11000000'      ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringLow        ;Display "LO ="
	call   OutString
	movfw  LowAlarm
	call   DisplayTemp
	call    ScanKey2      ;Check if a key is pressed
	movwf   Temp2
	btfsc   Temp2,KEY_MODE  ;If Key 1, go to next MODE (Clr Min/Max)
	goto    ModeSetClr
	btfsc   Temp2,KEY_UP  ;If "+", Increment LOW alarm Value
	incf    LowAlarm
	btfsc   Temp2,KEY_DN  ;If "-", Decrement LOW alarm Value
	decf    LowAlarm
	call    ShortDelay
	goto    ModeSetLow2

ModeSetClr
	call    LCD_Cls
	movlw   StringClearMinMax       ;Display "CLEAR MIN/MAX"
	call    OutString
ModeSetClr2
	movlw  b'11000000'      ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	movlw  StringPressClr   ;Display "PRESS + TO CLR"
	call   OutString
	call    ScanKey       ;Check if a key is pressed
	movwf   Temp2
	btfsc   Temp2,KEY_MODE  ;If Key 1, go to next MODE (Done)
	goto    ModeSetAlarmOn
	btfsc   Temp2,KEY_UP  ;If "+", Clear Min/Max value
	goto    ClrMinMax
	call    ShortDelay
	goto    ModeSetClr2

ClrMinMax
	call    ReadADC       ;Initialize Min and Max temps to current
	movfw   ADCresult
	movwf   MaxTemp
	movwf   MinTemp
	movwf   Temp
	movlw   EEADR_MIN_TEMP
	call    WriteEE         ;Update Min Temp in EEPROM
	movlw   EEADR_MAX_TEMP
	call    WriteEE         ;Update Max temp in EEPROM

ModeSetAlarmOn
	call    LCD_Cls
	movlw   StringSetAlarm  ;Display "Alarm ON/OFF?"
	call    OutString
ModeSetAlarmOn2
	movlw  b'11000000'      ;Bottom Line
	movwf  DispChar
	call   LCD_Cmd
	btfss  AlarmOn,ON       ;Display correct message
	goto   ModeAlarmOn
ModeAlarmOff
	;Alarm is ON, allow user to turn OFF
	movlw  StringPressOff   ;Display "PRESS + FOR OFF"
	call   OutString
	goto   ModeAlarmMessageDone
ModeAlarmOn
	;Alarm is OFF, allow user to turn ON
	movlw  StringPressOn    ;Display "PRESS + FOR ON"
	call   OutString

ModeAlarmMessageDone
	call    ScanKey       ;Check if a key is pressed
	movwf   Temp2
	btfsc   Temp2,KEY_MODE  ;If Key 1, go to next MODE (Done)
	goto    ModeSetDone
	btfsc   Temp2,KEY_UP  ;If "+", Toggle Alarm Status
	comf    AlarmOn,f     ;Complement to invert On/Off bit status
	call    ShortDelay
	goto    ModeSetAlarmOn2

ModeSetDone
	call    ShortDelay
	movf    HighAlarm,w   ;Save updated HIGH and LOW alarms and Alarm Enable to EEPROM
	movwf   Temp
	movlw   EEADR_HI_LIM
	call    WriteEE
	movf    LowAlarm,w
	movwf   Temp
	movlw   EEADR_LO_LIM
	call    WriteEE
	movf    AlarmOn,w
	movwf   Temp
	movlw   EEADR_ALARM_ON
	call    WriteEE

	return

;*******************************************************************
;  Internal functions
;*******************************************************************
;OutString: Routine to print a string to the LCD
;  Parameters: Pointer (address) of 0x80 terminated string
;  Returns:    Nothing
;  Calls:      LCD_Chr
;  Vars Used:  CharCounter, DispChar
OutString
	movwf   CharCounter
	decf    CharCounter,f	;Real starting address of string
NextChr
;Since all strings are in Page 1, load PCLATH now
	movlw   1
	movwf   PCLATH

	movf    CharCounter,w
	call    StringSetup
	movwf   DispChar
	btfsc   DispChar,7
	return
	call    LCD_Chr
	incf    CharCounter
	goto    NextChr

;ReadADC: Reads a byte from the ADC0831
;  Parameters: None
;  Returns:    One byte in ADCresult representing the ADC value

ReadADC
	clrf    ADCresult
	bcf     PORTA,ADCCS    ;Activate /CS line on the ADC to start
	movlw   8              ;Set number of bits to read in = 8 +1 start
	movwf   ADCCtr         ;Load this into the counter register
	bsf     PORTA,ADCCLK   ;Pulse the ADC clock line High then Low to
	nop                    ;  signal the start of a conversion
	bcf     PORTA,ADCCLK

Get_One_Bit                    ;This is the main loop, should execute 9 times
	bsf     PORTA,ADCCLK   ;Pulse the ADC clock line high then low
	nop                    ;  to get one data bit
	bcf     PORTA,ADCCLK
	bcf     STATUS,C       ;Clear Carry Bit
	btfsc   PORTA,ADCDAT   ;Check the value of the data bit
	bsf     STATUS,C       ;Bit is a one ... set carry bit and rotate in
	rlf     ADCresult      ;Shift bits left from carry into ADResult
	decfsz  ADCCtr,f       ;Decrement Bit Counter
	goto    Get_One_Bit    ;Do it 9 times, first bit is a START bit
	bsf     PORTA,ADCCS    ;When done, Disable the ADC

	;LIMIT max reading if necessary since table size < 255 bytes
	movlw  .248           ;Limit reading of ADC to F8 to prevent
	subwf  ADCresult,w    ;  going past last entry in jump table
	btfss  STATUS,C
	return                ;All is OK, just return with the value
	movlw  .248           ;Limit the ADC reading at end of jump table
	movwf  ADCresult
	return

;DisplayTemp: Converts an ADC reading to degrees C and degrees F
;             and displays on LCD
;  Parameters: ADC reading in W
;  Returns:    Nothing
DisplayTemp
        clrf   PCLATH        ;Conversion table is in 1st page
	call   CTable        ;Goto jump table for Celsius Temperature

	movwf  L_Byte        ;Load binary data for BCD conversion
        movwf  DegreesC      ;Store for later use in conversion to degrees F
        bcf    DegreesSign,0 ;Clear negative flag
        btfss  L_Byte,7      ;Test bit 7 (is it a negative number?)
        goto   NotNeg

	movlw  '-'           ;Negative number, display minus sign
	movwf  DispChar
	call   LCD_Chr
	bcf    L_Byte,7      ;Convert to non-negative number
        bsf    DegreesSign,0 ;Flag that this is a negative number for C->F
        bcf    DegreesC,7    ;Convert to unsigned number

NotNeg
	clrf   H_Byte
	call   B2_BCD        ;convert to BCD

	;Display R1 lo (LSB) as an ASCII digit
	movfw   R1
	andlw   b'00001111'
	btfsc   STATUS,Z     ;Remove leading zeroes
	goto    BlankOne
	addlw   30           ;Turn into an ASCII digit by adding 0x30
	movwf   DispChar
	call    LCD_Chr
BlankOne
	;Display R2 hi then R2 lo
	swapf   R2,0
	andlw   b'00001111'
	addlw   30            ;Turn into an ASCII digit
	movwf   DispChar
	call    LCD_Chr
	movfw   R2
	andlw   b'00001111'
	addlw   30            ;Turn into an ASCII digit
	movwf   DispChar
	call    LCD_Chr

        movlw   'C'           ;Display "C/"
	movwf   DispChar
	call    LCD_Chr
        movlw   '/'
	movwf   DispChar
	call    LCD_Chr

;Convert degrees C to degrees F by multiplying by 29/16 and adding 32
        movlw   .29
        movwf   mulplr

;Multiply DegreesC * 29.  16-bit Result is is H_Byte, L_Byte
mpy_S   clrf    H_Byte
        clrf    L_Byte
        movlw   8
        movwf   count
        movf    DegreesC,w
        bcf     STATUS,C        ; Clear the carry bit in the status Reg.
mloop   rrf     mulplr,f
        btfsc   STATUS,C
        addwf   H_Byte,f
        rrf     H_Byte,f
        rrf     L_Byte,f
        decfsz  count,f
        goto    mloop

;Divide by 16 (29/16 is almost = 9/5)
        movlw   4
        movwf   count
NextShift
        bcf     STATUS,C
        rrf     H_Byte,f
        rrf     L_Byte,f
        decfsz  count,f
        goto    NextShift

;Convert to a 2's complement signed number then add 32
        btfss   DegreesSign,0
        goto    PositiveTemp    ;Positive number, no need to convert

        comf    L_Byte,f        ;Complement and add 1 to convert to negative
        incf    L_Byte,f        ;an never exceed -.91 anyway
        comf    H_Byte,f

PositiveTemp
        movlw   .32
        addwf   L_Byte,f
        btfsc   STATUS,C        ;L_Byte oveflowed, increment H_Byte
        incf    H_Byte,f

;If Degrees F is a negative number, display it as such now
        btfss   H_Byte,7
        goto    FDegNotNeg
        movlw   '-'
        movwf   DispChar
        call    LCD_Chr
        clrf    H_Byte          ;Always zero since max=-50F
        comf    L_Byte,f
        incf    L_Byte,f

FDegNotNeg
	call   B2_BCD        ;convert to BCD

	;Display R1 lo (LSB) as an ASCII digit
	movfw   R1
	andlw   b'00001111'
	btfsc   STATUS,Z     ;Remove leading zeroes
   goto    BlankOneF
	addlw   30           ;Turn into an ASCII digit by adding 0x30
	movwf   DispChar
	call    LCD_Chr
BlankOneF
	;Display R2 hi then R2 lo
	swapf   R2,0
	andlw   b'00001111'
	addlw   30            ;Turn into an ASCII digit
	movwf   DispChar
	call    LCD_Chr
	movfw   R2
	andlw   b'00001111'
	addlw   30            ;Turn into an ASCII digit
	movwf   DispChar
	call    LCD_Chr

   movlw   'F'           ;Display "F"
	movwf   DispChar
	call    LCD_Chr

   movlw  ' '            ;Trailing blanks to clear display
	movwf  DispChar
	call   LCD_Chr
   movlw  ' '
	movwf  DispChar
	call   LCD_Chr
	return

;ReadEE: Reads a byte from the EEPROM of the PIC16C84/16F84
;  Parameters: location in EEPROM (0-63 dec) in W
;  Returns:    byte read in W register
ReadEE
	movwf   EEADR           ;Put desired address into EEADR
	bsf     STATUS,RP0      ;Select Register Page 1
	bsf     EECON1,0        ;RD operation flag
	bcf     STATUS,RP0      ;Select Register Page 0 again
	movf    EEDATA,W        ;Read data, put into W register
	return

;WriteEE: Writes a byte to the EEPROM of the PIC16C84/16F84
;  Parameters: location in EEPROM (0-63 dec) in W
;              data to be written in register 'Temp'
;  Note: Value in Temp is destroyed during write operation
;        Disables interrupts during write
WriteEE:
	movwf   EEADR           ;Put desired address into EEADR
	movf    Temp,W          ;Put data to be written into EEDATA
	movwf   EEDATA
	movf    INTCON,W        ;Store old interrupt status in Temp
	movwf   Temp
	bcf     INTCON,7        ;Turn off all interrupts
	bsf     STATUS,RP0      ;Select Register Page 1
	bsf     EECON1,2        ;Write Enable flag
	movlw   55h             ;Required sequence to write to EEPROM
	movwf   EECON2
	movlw   b'10101010'
	movwf   EECON2
	bsf     EECON1,1        ;WR operation flag
EEIsBusy
	btfsc   EECON1,1        ;Wait for BUSY flag to end
	goto    EEIsBusy
	bcf     EECON1,2        ;Disable Write Enable flag
	bcf     STATUS,RP0      ;Select Register Page 0 again
	movf    Temp,w          ;Reset interrupts to original state
	movwf   INTCON
	return


;*******************************************************************
;  LTN211 16 char by 2 LCD display functions for PICPROTO
;*******************************************************************
LCD_Init                      ;Initialize LTN211 LCD display
	movlw   33            ;Output 0x33 to LCD
	movwf   DispChar
	call    LCD_Cmd
	movlw   32            ;Output 0x32 to LCD
	movwf   DispChar
	call    LCD_Cmd
	movlw   28            ;Output 0x28 to LCD
	movwf   DispChar            ;  4 bit mode
	call    LCD_Cmd
	movlw   0C            ;Output 0x0C to LCD
	movwf   DispChar            ;  Display ON, Cursor&Blink Off
	call    LCD_Cmd
	movlw   06            ;Output 0x06 to LCD
	movwf   DispChar            ;  Entry Mode: AutoIncrement
	call    LCD_Cmd
	call    LCD_Cls
	return

LCD_Cls                       ;Clear LCD display screen, home cursor
	movlw   01            ;Output 0x01 to LCD
	movwf   DispChar            ;  Clear Display
	call    LCD_Cmd
	movlw   02            ;Output 0x02 to LCD
	movwf   DispChar            ;  Home Cursor
	call    LCD_Cmd
	call    Delay
	call    Delay
	return

LCD_L2  ;Set cursor to start of LCD line 2
	movlw   b'11000000'
	movwf   DispChar            ;  Set Display Cursor to Line 2
	call    LCD_Cmd
	call    Delay
	call    Delay
	call    Delay
	return

LCD_Cmd                       ;Send a command to the LCD display
	swapf   DispChar,0          ;Put upper nibble into lower of W
	andlw   b'00001111'   ;Mask off upper nibble
	movwf   PORTB
	bsf     PORTB,7      ;Toggle Enable HIGH then LOW again
	call    Delay
	call    Delay
	bcf     PORTB,7
	call    Delay
	call    Delay
	movfw   DispChar            ;send lower nibble to display
	andlw   b'00001111'   ;Mask off upper nibble
	movwf   PORTB
	bsf     PORTB,7      ;Toggle Enable HIGH then LOW again
	call    Delay
	call    Delay
	bcf     PORTB,7
	call    Delay
	call    Delay         ;?
	return

LCD_Chr                       ;Send a Character to the LCD display
	swapf   DispChar,0    ;Put upper nibble into lower of W
	andlw   b'00001111'   ;Mask off upper nibble
	movwf   PORTB
	bsf     PORTB,4      ;Set bit 4 (Register Select) HIGH
	bsf     PORTB,7      ;Toggle Enable HIGH then LOW again
	bcf     PORTB,7
	movfw   DispChar      ;send lower nibble to display
	andlw   b'00001111'   ;Mask off upper nibble
	movwf   PORTB
	bsf     PORTB,4      ;Set bit 4 (Register Select) HIGH
	bsf     PORTB,7      ;Toggle Enable HIGH then LOW again
	bcf     PORTB,7
	call    Delay
	call    Delay         ;?
	return

LongDelay                     ;Approx. 125 mS delay
	movlw   .255
	movwf   DelayT2
ldelaya call    Delay
	decfsz  DelayT2,1
	goto    ldelaya
	return

ShortDelay                     ;Approx. 25 mS delay
	movlw   .51
	movwf   DelayT2
sdelaya call    Delay
	decfsz  DelayT2,1
	goto    sdelaya
	return

Delay                         ;0.6ms delay for LCD initialization
	movlw   .250          ;Load Temp with constant for .3 ms
	movwf   DelayTemp
delaya  decfsz  DelayTemp,1   ;Dec until zero
	goto    delaya
	movlw   .250          ;Load Temp with constant for .3 ms
	movwf   DelayTemp
delayb  decfsz  DelayTemp,1   ;Dec until zero
	goto    delayb
	return

;*******************************************************************
;  Binary to BCD Converter
;
;  Input: Two bytes of RAM; L_Byte, H_Byte
;  Output: Three bytes of RAM; R0, R1, R2
;
;  Usage: Put value into L_Byte and H_Byte, call B2_BCD, result in
;         R2 is LSD and R1 is MSD: Four digits (nibbles) in 2 bytes
;         Lower 4 bits of LSD are least sig. digit, upper 4 are 2nd
;         digit.
;*******************************************************************

B2_BCD  bcf     STATUS,0        ;Clear carry bit
	movlw   .16
	movwf   count           ;Set counter to 16
	clrf    R0
	clrf    R1
	clrf    R2
loop16  rlf     L_Byte
	rlf     H_Byte
	rlf     R2
	rlf     R1
	rlf     R0
	decfsz  count
	goto    adjDEC
	RETLW   0

adjDEC  movlw   R2
	movwf   FSR
	call    adjBCD
	movlw   R1
	movwf   FSR
	call    adjBCD
	movlw   R0
	movwf   FSR
	call    adjBCD
	goto    loop16

adjBCD  movlw   3
	addwf   0,W
	movwf   B2temp
	btfsc   B2temp,3        ;Test if result > 7
	movwf   0
	movlw   30
	addwf   0,w
	movwf   B2temp
	btfsc   B2temp,7        ;Test if result > 7
	movwf   0             ;Save as MSD
	RETLW   0

;*******************************************************************

       END


