include p16c771.inc PROCESSOR p16c771 RADIX dec FIRMWARE_VERSION EQU 080 ; vN.NN ; Fridge - ; ; Control software for PIC16c771 based peltier refrigerator. ; Written by Jesse Donaldson. ; Configuration Bits: ; =================== ; Chip = 16C771 ; Code Protection: OFF ; Watchdog Timer: OFF ; Internal RC, w/o ClkOut ; BrownOutV =4.2v ; BrownOut disabled ; PowerUp timer disabled ; Reset pin disabled ; CPU Speed = 4MHz (nominal; 3.55 - 4.31 MHz, based on internal RC) ; Instruction cycle time = 1uS (4 clock cycles) ; Pin Usage: ; ========== ; # - Name Function: ; 1 - RA0: Input: Knob center button, active low. ; 2 - RA1: Analog input: heat sink temp ; 7 - RA2: Analog input: inside (cold) temp ; 8 - RA3: Output/Unused - Control for fan? ; 3 - RA4: Input: Knob encoder A ; 4 - RA5: Input: Knob encoder B ; 17 - RA6: Output/Unused - Unused, input w/ external pullup??? ; 18 - RA7: Output/Unused - Unused, input w/ external pullup??? ; 9 - RB0: Output: LCD RS ; 10 - RB1: Output: LCD RW ; 19 - RB2: Output: LCD E ; 20 - RB3: Output: 10KHz PWM for Peltier device - NEEDS EXTERNAL PULLDOWN to keep peltier off during initialization ; 11 - RB4: Output: LCD data 4 ; 12 - RB5: Output: LCD data 5 ; 13 - RB6: Output: LCD data 6 ; 14 - RB7: Output: LCD data 7 ; 5 - Vss ; 6 - AVss ; 16 - Vdd ; 15 - AVdd ; build commands: ; gpasm -I ~/gputils-0.11.6/header/ Fridge.asm ; picp /dev/tty.USA19191P1.1 16c771 -bpc -wp Fridge.hex ; DOLATER: ; * Add fan control when heatsink gets too hot ; * Design & Implement more view screens (simple, complicated) ; * Support left justified numbers by having the WriteASCIINumber call return the length in W ; * Make Peltier PWM active low (for internal pullup), or use external pulldown resistor to keep device off during initialization ; * Use BANKSEL for bank selection & PAGESEL for jump table stuff (ala BANKSEL TRISA) ; * Get back extra CG_char by using 8-16 for drawing CGRam rather than 0-7 ; * 10 KHz PWM is interfering with temp measurements... ; * Fix temp initialization, and prevent boot-cancel until it's set up. ; * Freezes up sometimes? On animated screen, when warming? Suspect this was when looking up "255" readings from ADC ; * Test actual use... push fridge and see what it can maintain. - test shutdown. ; * Do we adjust the peltier duty cycle downwards too quickly? ; Default configuration constants: ; =============================== DEFAULT_SET_POINT EQU 50 ; start up with set point = 50¡F SET_POINT_MIN EQU 20 SET_POINT_MAX EQU 80 ADJUST_ITERATIONS EQU 10 ; 10 iterations (3 seconds) between adjustments to peltier duty cycle ; DOLATER - raise this? SHUTDOWN_TEMP EQU 125 ; Heatsink temp > 125¡F will cause emergency shutdown. ; General Purpose Register Usage: ; =============================== ; Bank 0: 0x20-0x6F ; Bank 1: 0xA0-0xEF ; Bank 2: 0x120-0x16F PREV_COLD_1 EQU 0x20 ; Past measurements for cold-side temp sensor PREV_COLD_2 EQU 0x21 PREV_COLD_3 EQU 0x22 PREV_COLD_4 EQU 0x23 PREV_HOT_1 EQU 0x24 ; Past measurements for hot-side temp sensor PREV_HOT_2 EQU 0x25 PREV_HOT_3 EQU 0x26 PREV_HOT_4 EQU 0x27 PROGRESS_COUNT EQU 0x28 PROGRESS_FILL EQU 0x29 BOOT_COUNTER EQU 0x2A ADJUST_COUNTER EQU 0x2B ; counter used to slow down adjustment of peltier duty cycle, see ADJUST_ITERATIONS SAVED_DUTY_CYCLE EQU 0x2C ; saved copy of current duty cycle... used when temporarily disabling the peltier. BOOT_PROGRESS EQU 0x2D ; 0-42... state of the progress bar. ; Registers 0x70-0x7f are mirrored in each bank, available anytime: LCD_TEMP_REG EQU 0x70 UI_DELAY_COUNT EQU 0x71 ; Counter used for UIDelay routine BCD_100 EQU 0x72 BCD_10 EQU 0x73 BCD_1 EQU 0x74 DebounceCounter EQU 0x75 ; Counter for keyboard debounce timing; could probably share UI_DELAY_COUNT CUR_DISP_SCREEN EQU 0x76 HEAT_SINK_TEMP EQU 0x77 COLD_BOX_TEMP EQU 0x78 SET_POINT_TEMP EQU 0x79 KEY_STATE EQU 0x7A PREV_KEY_STATE EQU 0x7B TEMP_REG EQU 0x7C ; Temporary register, may be used by any subroutine. BOOT_STR_ADRL EQU 0x7D ; Holds current low byte of boot string address. Used for scrolling the text. BOOT_STR_ADRH EQU 0x7E ; Holds current high byte of boot string address. Used for scrolling the text. SCROLL_COUNTER EQU 0x7F ; Counter used by DrawStringUpTo16Chars. __CONFIG _WDT_OFF & _CP_OFF & _VBOR_42 & _BODEN_OFF & _PWRTE_OFF & _INTRC_OSC_NOCLKOUT & _MCLRE_OFF ; ********************************** ; FOR MACPIC, COPY STUFF BELOW HERE ; ********************************** ; #include "MacPICStuff.h" ; Configuration that works in MacPIC too: ; Constants for IO Pin Usage: ; =========================== ; Port B LCD_RS EQU 0 LCD_RW EQU 1 LCD_E EQU 2 PWM_OUT EQU 3 LCD_D0 EQU 4 LCD_D1 EQU 5 LCD_D2 EQU 6 LCD_D3 EQU 7 ; Port A KB_CENTER EQU 0 ; input for diddle pin KB_ENCODER_A EQU 4 KB_ENCODER_B EQU 5 PORTA_KEY_MASK EQU 00110001b ; Generic LCD constants: LINE_1_ADDR EQU 0x80 LINE_2_ADDR EQU 0xC0 LINE_3_ADDR EQU 0x90 LINE_4_ADDR EQU 0xD0 ; CGRAM_CHAR0_ADDR EQU 0x40 ; we don't use this character, since we can't draw it using strings CGRAM_CHAR1_ADDR EQU 0x48 CGRAM_CHAR2_ADDR EQU 0x50 CGRAM_CHAR3_ADDR EQU 0x58 CGRAM_CHAR4_ADDR EQU 0x60 CGRAM_CHAR5_ADDR EQU 0x68 CGRAM_CHAR6_ADDR EQU 0x70 CGRAM_CHAR7_ADDR EQU 0x78 ; CGRAM Character constants: FILL1_PROGRESS_CHAR EQU 1 FILL2_PROGRESS_CHAR EQU 2 FILL3_PROGRESS_CHAR EQU 3 EMPTY_PROGRESS_CHAR EQU 4 RTEND_PROGRESS_CHAR EQU 5 LTEND_PROGRESS_CHAR EQU 6 ; Data display screen constants (see SetDisplayScreen) DISP_SCREEN_DIAG EQU 0 DISP_SCREEN_ANIMATED EQU 1 DISP_SCREEN_DETAIL EQU 2 DISP_SCREEN_SIMPLE EQU 3 DISP_SCREEN_MAX EQU 1 ; Addresses for the diagnostics screen: DIAG_HS_TEMP_ADDR EQU (LINE_1_ADDR+3) DIAG_COLD_TEMP_ADDR EQU (LINE_1_ADDR+13) DIAG_SET_POINT_ADDR EQU (LINE_2_ADDR+11) DIAG_DUTY_CYCLE_ADDR EQU (LINE_3_ADDR+12) ; Addresses for the animated screen: ANIM_TEMP_ADDR EQU (LINE_1_ADDR+8) ANIM_SETPT_ADDR EQU (LINE_2_ADDR+8) ; Character constants: DEGREE_SYMBOL EQU 11011111b NUMERIC_SPACE EQU (' ' - '0') ; used to replace leading 0's when displaying numbers. ; We add '0' to it before displaying, so this number will then become a space. ; ***************** ; * Macros ; ***************** ; DrawString - ; This MACRO sets up the program memory read address registers to point to ; the given label, and then calls DrawStringDoWork() to draw the string to ; the LCD. This allows us to easily draw constant strings stored very compactly ; (2 chars in each instruction word). DrawString MACRO addr BANKSEL PMADRL ERRORLEVEL -302 movlw (addr & 0xFF) movwf PMADRL movlw (addr >> 8) movwf PMADRH ERRORLEVEL +302 call DrawStringDoWork ; returns us to bank 0 ENDM DrawRawData MACRO addr BANKSEL PMADRL ERRORLEVEL -302 movlw (addr & 0xFF) movwf PMADRL movlw (addr >> 8) movwf PMADRH ERRORLEVEL +302 call DrawDataDoWork ENDM ; ***************** ; * Code ; ***************** ORG 0x0000 ; reset vector goto start ORG 0x0004 ; interrupt vector nop start call InitPIC call InitLCD ; initialize default set point movlw DEFAULT_SET_POINT movwf SET_POINT_TEMP ; Initialize keyboard state call DebounceKeyboard movf KEY_STATE, W movwf PREV_KEY_STATE ; Initialize other vars movlw ADJUST_ITERATIONS movwf ADJUST_COUNTER call BootDisplay movlw DISP_SCREEN_DIAG ; movlw DISP_SCREEN_ANIMATED call SetDisplayScreen ;***************************** ; All done with initialization, enter main loop. ; We do two things here: ; 1) Poll the ADC, output the result on the LCD ; 2) Adjust the PWM duty cycle so we can see it on a scope ;***************************** ; set PWM duty cycle to 1%... it will slowly rise. movlw 1 movwf CCPR1L loop_forever ; Check heatsink temp for emergency shutdown: movlw SHUTDOWN_TEMP subwf HEAT_SINK_TEMP, W btfss STATUS, C goto Dont_Shutdown call EmergencyShutdown Dont_Shutdown ; Handle Keyboard events: call HandleKeyboard ; Monitor temperatures & set point, ; and adjust peltier duty cycle accordingly call AdjustPeltier ; Update LCD: call UpdateDisplay ; Update the PWM duty cycle: ; This code cycles it slowly from 0-100% repeatedly. ; incf CCPR1L, F ; movlw 100 ; subwf CCPR1L, W ; btfsc STATUS, Z ; clrf CCPR1L movlw 250 ; wait 100ms, intersperse calls to KandleKeyboard to increase KB responsiveness call delay call HandleKeyboard movlw 250 call delay call HandleKeyboard movlw 250 call delay call HandleKeyboard movlw 250 call delay call HandleKeyboard movlw 250 call delay call HandleKeyboard movlw 250 call delay call HandleKeyboard movlw 250 call delay call HandleKeyboard movlw 250 call delay ; This is at the end, since the first call is actually before the beginning of the loop. call UpdateTempMeasurements goto loop_forever nop ;************************************************* ;************************************************* ;************************************************* ;* Level 5 routines - only call level 0, 1, 2, 3, & 4 ;************************************************* ;************************************************* ;************************************************* HandleKeyboard ; Checks the keyboard, & handles all timing associated with buttons. ; Responds to button presses. ; If the keyboard state has not changed, just return. movf PORTA, W xorwf PREV_KEY_STATE, W andlw PORTA_KEY_MASK btfsc STATUS, Z return ; If kb state has changed, debounce it. ; This just checks all of port A until it's steady for a while. call DebounceKeyboard ; Now check debounced kb state to make sure its really different: movf KEY_STATE, W xorwf PREV_KEY_STATE, W andlw PORTA_KEY_MASK btfsc STATUS, Z return ; Check for center-button press, ; (triggered on falling edge, the push) btfss PREV_KEY_STATE, KB_CENTER ; if it was low before, then we already handled it. goto FinishHandleKeyboard ; ... and in that case, we're done. No twisting the knob while pressed! btfsc KEY_STATE, KB_CENTER ; if it's high, button is not "active". goto CheckKnobDirection ; So look for knob-twisting. ; Center button was pressed - ; Increment the current display screen, and roll over if necessary. incf CUR_DISP_SCREEN, F movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_MAX ; check if its >max, and roll over if so. btfss STATUS, C clrf CUR_DISP_SCREEN movf CUR_DISP_SCREEN, W call SetDisplayScreen goto FinishHandleKeyboard CheckKnobDirection ; Knob inputs work like this, for clockwise rotation: ; Pos | A | B | ;-----+---+---+ If you want a function to determine which direction ; 1 | L | L | the knob has been rotating, quick use of a k-map and ; 2 | H | L | some squinting yields this: ; 3 | H | H | Ac xor Bp => 1 for clockwise, 0 for counter-clockwise. ; 4 | L | H | (the c and p indicate current and previous values). ; if Ac and Bp are different, we're going clockwise. btfsc KEY_STATE, KB_ENCODER_A ; If Ac is set, check if Bp is clear. goto CheckBpClear goto CheckBpSet ; ... otherwise, Ac is clear. Check if Bp is set. CheckBpClear btfss PREV_KEY_STATE, KB_ENCODER_B ; If Bp is clear, succeed: increment set point. goto DecrementSetPoint goto IncrementSetPoint ; ... otherwise, we fail: decrement set point. CheckBpSet btfsc PREV_KEY_STATE, KB_ENCODER_B ; If Bp is set, succeed: increment set point. goto DecrementSetPoint goto IncrementSetPoint ; ... otherwise, we fail: decrement set point. DecrementSetPoint decf SET_POINT_TEMP, F movf SET_POINT_TEMP, W addlw (- (SET_POINT_MIN - 1)) btfss STATUS, Z goto FinishHandleKeyboard movlw SET_POINT_MAX ; We went below min, roll it over to the max. movwf SET_POINT_TEMP goto FinishHandleKeyboard IncrementSetPoint incf SET_POINT_TEMP, F ; increment set point movf SET_POINT_TEMP, W sublw SET_POINT_MAX ; check if its >max, and roll over if so. btfsc STATUS, C goto FinishHandleKeyboard movlw SET_POINT_MIN ; We went over max, roll it over to the min. movwf SET_POINT_TEMP FinishHandleKeyboard ; Update the prev keystate variable. movf KEY_STATE, W movwf PREV_KEY_STATE return ;************************************************* ;************************************************* ;************************************************* ;* Level 4 routines - only call level 0, 1, 2, & 3 ;************************************************* ;************************************************* ;************************************************* SetDisplayScreen ; Set the current display screen ; Eventually you will be able to change between several screens of data to ; display on the LCD. For now, we only support screen 0. ; Pass the desired screen in W. ; DISP_SCREEN_DIAG (0) - For development use, displays all critical data. ; DISP_SCREEN_ANIMATED (1) - Screen includes fancy animation, fun to look at. ; DISP_SCREEN_DETAIL (2) - For end use, displays more detailed data. ; DISP_SCREEN_SIMPLE (3) - for end use, displays only temperature and set point. movwf CUR_DISP_SCREEN ; save new display mode ; Clear display in preparation for displaying the new screen. ; This lengthy instruction requires additional delay movlw 00000001b call LCDWriteInstr movlw 16 ; wait >1.53ms (was 15.2ms) call delay; ; Check display mode, dispatch to appropriate handler movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_DIAG btfsc STATUS, Z goto SetDiagnosticScreen movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_ANIMATED btfsc STATUS, Z goto SetAnimatedScreen movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_DETAIL btfsc STATUS, Z goto SetDetailScreen movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_SIMPLE btfsc STATUS, Z goto SetSimpleScreen ; ERROR case - invalid display mode DrawString(Error_String) return ; Animated display screen looks liks this: ; 1234567890123456 ; Current: nnn¡F ; Target: nnn¡F ; --Cooling------- ; [*******-------] SetAnimatedScreen DrawString(Anim_String_1) movlw LINE_2_ADDR call LCDWriteInstr DrawString(Anim_String_2) movlw LINE_3_ADDR call LCDWriteInstr DrawString(Anim_Cooling) call SetupProgressBar return SetDetailScreen DrawString(Detail_String) movlw LINE_4_ADDR call LCDWriteInstr return SetSimpleScreen DrawString(Simple_String) movlw LINE_4_ADDR call LCDWriteInstr return SetDiagnosticScreen ; Implementation for DISP_SCREEN_DIAG. ; It will look like this: ; 1234567890123456 ; HS:nnn¡ Cold:nnn¡ ; Set Point: nnn¡ ; Duty cycle: nnn% ; Fan: ON v0.nn DrawString(Diag_String_1) movlw LINE_2_ADDR call LCDWriteInstr DrawString(Diag_String_2) movlw LINE_3_ADDR call LCDWriteInstr DrawString(Diag_String_3) movlw LINE_4_ADDR call LCDWriteInstr DrawString(Diag_String_4) ; Display the version in the lower right movlw FIRMWARE_VERSION call Convert2BCD movf BCD_100, W ; and write it to the lcd addlw '0' ; convert to ASCII call LCDWriteData movlw '.' call LCDWriteData movf BCD_10, W addlw '0' ; convert to ASCII call LCDWriteData movf BCD_1, W addlw '0' ; convert to ASCII call LCDWriteData return EmergencyShutdown ; Turn peltier off & cease all regular operation. ; Clear LCD and blink obvious warning shutdown message. ; Shutdown Peltier Device movlw 0 movwf CCPR1L ; Prepare screen for message: movlw LINE_1_ADDR call LCDWriteInstr DrawString(Asterisk_String) movlw LINE_4_ADDR call LCDWriteInstr DrawString(Asterisk_String) ; Enter main shutdown loop: Shutdown_Loop movlw LINE_2_ADDR call LCDWriteInstr DrawString(Overheat_String) movlw LINE_3_ADDR call LCDWriteInstr DrawString(Shutdown_String) movlw 5 call UIDelay movlw LINE_2_ADDR call LCDWriteInstr DrawString(Blank_Shutdown_String) movlw LINE_3_ADDR call LCDWriteInstr DrawString(Blank_Shutdown_String) movlw 3 call UIDelay goto Shutdown_Loop return BootDisplay ; Make pretty boot stuff on screen ; We've got a progress bar at the bottom, which goes across the screen. ; It says "Booting...." at the top. ; And on the 3rd line, there's scrolling text indicating the nifty things happening while we boot. ; The scrolling algorithm works like this: ; We get the address of the boot string, save it off, and draw the first 16 chars ; of the string there. Then we increment our saved address, and repeat, etc... ; In between iterations, we also occassionally increment the progress bar. ; NOTE: this actually does something useful now - initializes the initial temperature display. ; Cancelation (via center button push) is disabled until sensors are "calibrated". movlw LINE_1_ADDR call LCDWriteInstr DrawString(Booting_String) movlw LINE_2_ADDR call LCDWriteInstr DrawString(Divider_Line_String) call SetupProgressBar clrf BOOT_COUNTER clrf BOOT_PROGRESS ; Set up boot string address low & high. movlw (Boot_Status_String & 0xFF) movwf BOOT_STR_ADRL movlw (Boot_Status_String >> 8) movwf BOOT_STR_ADRH ; Clear temp measurements prior to initialization: clrf HEAT_SINK_TEMP clrf COLD_BOX_TEMP clrf SAVED_DUTY_CYCLE ; so temp measuring doesn't accidentally turn it on... BootDisplayIteration call FilterInitialTempMeasurements ; It's not actually useful to disable the cancel based on whether the current temps ; have been set - see note in FilterInitialTempMeasurements about the bug I don't ; understand. ; addlw 0 ; btfsc STATUS, Z ; goto DisableBootCancel ; Check for center button press - skip boot animation. btfss PORTA, KB_CENTER ; if it's low, it's pressed. goto ContinueBoot DisableBootCancel ; Move to line 3: movlw LINE_3_ADDR ; set lcd location call LCDWriteInstr ; Draw the current portion of the scrolling string... BANKSEL PMADRL ERRORLEVEL -302 movf BOOT_STR_ADRL, W movwf PMADRL movf BOOT_STR_ADRH, W movwf PMADRH ERRORLEVEL +302 call DrawStringUpTo16Chars ; returns us to bank 0 ; Wait a moment... call BootWaitAndUpdateTemps ; Move back to line 3: movlw LINE_3_ADDR ; set lcd location call LCDWriteInstr ; Skip the 1st char, and update it again. ; This is necessary since each word holds 2 characters... ; Without it, we just increment the address each time, and scroll by *2* chars instead of 1. BANKSEL PMADRL ERRORLEVEL -302 movf BOOT_STR_ADRL, W movwf PMADRL movf BOOT_STR_ADRH, W movwf PMADRH ERRORLEVEL +302 call DrawUpTo16CharsOffset ; returns us to bank 0 ; Wait again... call BootWaitAndUpdateTemps ; Increment boot string address registers. ; Don't worry about the "high" address, since strings can't currently cross a 256 word boundary anyways. incf BOOT_STR_ADRL, F ; Check to see if this is the end of the string, by checking if the address ; is equal to the "end of string" address constant we have set up. movlw (Boot_Status_End & 0xFF) ; Does low byte match? subwf BOOT_STR_ADRL, W btfss STATUS, Z goto ContinueCandy movlw (Boot_Status_End >> 8) ; Does high byte match? subwf BOOT_STR_ADRH, W btfss STATUS, Z goto ContinueCandy goto ContinueBoot ContinueCandy incf BOOT_COUNTER, F ; Increment the boot counter... movf BOOT_COUNTER, W ; ... and if this is a 4th iteration... andlw 0x03 btfss STATUS, Z goto BootDisplayIteration incf BOOT_PROGRESS, F ; ... increment & update the progress bar too. movf BOOT_PROGRESS, W call FillProgressBar ; DOLATER - we could probably eliminate BOOT_PROGRESS by dividing BOOT_COUNTER by 4. ; Remember to clear STATUS,C before rotating if we do this. goto BootDisplayIteration ContinueBoot movlw 10 call UIDelay return ;************************************************* ;************************************************* ;************************************************* ;* Level 3 routines - only call level 0, 1, & 2 ;************************************************* ;************************************************* ;************************************************* BootWaitAndUpdateTemps ; A ui delay routine used during boot which also works on filtering ; initial measurements from temperature sensors. movlw 125 call delay call FilterInitialTempMeasurements movlw 125 call delay call FilterInitialTempMeasurements movlw 125 call delay call FilterInitialTempMeasurements movlw 125 call delay call FilterInitialTempMeasurements movlw 125 call delay call FilterInitialTempMeasurements movlw 125 call delay return; Write4Spaces ; Writes 4 spaces to the LCD movlw ' ' call LCDWriteData movlw ' ' call LCDWriteData movlw ' ' call LCDWriteData movlw ' ' call LCDWriteData return UpdateDisplay ; Updates the currently displayed screen of data on the LCD ; Called frequently, takes the current display mode into account ; (though we only support DISP_SCREEN_DIAG currently). ; Check display mode, dispatch to appropriate handler movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_DIAG btfsc STATUS, Z goto UpdDiagnosticScreen movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_ANIMATED btfsc STATUS, Z goto UpdAnimatedScreen movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_DETAIL btfsc STATUS, Z goto UpdDetailScreen movf CUR_DISP_SCREEN, W sublw DISP_SCREEN_SIMPLE btfsc STATUS, Z goto UpdSimpleScreen ; ERROR case - invalid display mode return UpdAnimatedScreen ; Write current temp movlw ANIM_TEMP_ADDR ; set lcd location call LCDWriteInstr movf COLD_BOX_TEMP, W ; Display on screen call WriteASCIINumber ; Append ¡F movlw DEGREE_SYMBOL call LCDWriteData movlw 'F' call LCDWriteData ; Write set point movlw ANIM_SETPT_ADDR ; set lcd location call LCDWriteInstr movf SET_POINT_TEMP, W ; Display on screen call WriteASCIINumber ; Append ¡F movlw DEGREE_SYMBOL call LCDWriteData movlw 'F' call LCDWriteData ; Update the mode line: movlw LINE_3_ADDR ; set lcd location call LCDWriteInstr ; Check temperature, print "Warming", "Cooling", or "Standby". movf COLD_BOX_TEMP, W subwf SET_POINT_TEMP, W btfsc STATUS,Z goto Anim_PrintStandby btfss STATUS, C goto Anim_PrintCooling Anim_PrintWarming DrawString(Anim_Warming) goto Anim_UpdStatusBar Anim_PrintStandby DrawString(Anim_Standby) goto Anim_UpdStatusBar Anim_PrintCooling DrawString(Anim_Cooling) Anim_UpdStatusBar bcf STATUS, C ; rrf rotates thru carry, so we'd better clear it first. rrf CCPR1L, W ; Cheap trick... duty cycle is 0-100, progress bar is 0-42, so we divide by 2... call FillProgressBar return UpdDetailScreen movlw LINE_3_ADDR ; set lcd location call LCDWriteInstr movlw 'S' call LCDWriteData movlw 'P' call LCDWriteData movlw ':' call LCDWriteData movf SET_POINT_TEMP, W ; Display on screen call WriteASCIINumber movlw DEGREE_SYMBOL call LCDWriteData movlw 'F' call LCDWriteData ; Do NOT return - fall through and display simple screen data too. ; return UpdSimpleScreen movlw LINE_2_ADDR ; set lcd location call LCDWriteInstr movf COLD_BOX_TEMP, W ; Display on screen call WriteASCIINumber movlw DEGREE_SYMBOL call LCDWriteData movlw 'F' call LCDWriteData return UpdDiagnosticScreen movlw DIAG_HS_TEMP_ADDR ; set lcd location call LCDWriteInstr movf HEAT_SINK_TEMP, W ; Display on screen call WriteASCIINumber movlw DEGREE_SYMBOL call LCDWriteData movlw ' ' call LCDWriteData movlw DIAG_COLD_TEMP_ADDR ; set lcd location call LCDWriteInstr movf COLD_BOX_TEMP, W ; Display on screen call WriteASCIINumber movlw DIAG_SET_POINT_ADDR ; set lcd location call LCDWriteInstr movf SET_POINT_TEMP, W ; Display on screen call WriteASCIINumber movlw DEGREE_SYMBOL call LCDWriteData movlw 'F' call LCDWriteData movlw DIAG_DUTY_CYCLE_ADDR ; set lcd location call LCDWriteInstr movf CCPR1L, W ; Display on screen call WriteASCIINumber movlw '%' call LCDWriteData return ; Software initialization of LCD panel InitLCD bcf PORTB, LCD_RS ; configure for writes to instruction reg bcf PORTB, LCD_RW movlw 170 ; wait >15ms. call delay ; Function Set - 8 bit interface, but the low nybble is "don't cares" movlw 00110000b call LCDWriteHighNybble movlw 51 ; wait 5.1ms call delay ; Function Set - 8 bit interface, but the low nybble is "don't cares" movlw 00110000b call LCDWriteHighNybble ; Delays necessary after LCDWriteHighNybble since we can't ; count on the one in the LCDWriteInstr/Data subroutine movlw 10 ; wait 100us call delay ; Function Set - 8 bit interface, but the low nybble is "don't cares" movlw 00110000b call LCDWriteHighNybble movlw 10 ; wait 100us call delay ; Function Set - switch to 4 bit interface movlw 00100000b call LCDWriteHighNybble movlw 10 ; wait 100us call delay ; Function Set - 2 lines, 5x7 font movlw 00101000b ; try 0100 1100 0000 call LCDWriteInstr ; Display Off movlw 00001000b call LCDWriteInstr ; Clear Display - lengthy instruction requires additional delay movlw 00000001b call LCDWriteInstr movlw 16 ; wait >1.53ms (was 15.2ms) call delay; ; Entry Mode Set - Increment on write, shift disabled movlw 00000110b call LCDWriteInstr ; Display On movlw 00001100b call LCDWriteInstr return ; Converts number in W to BCD, and writes the digits to the LCD ; Replaces leading 0's with spaces, so the output is right justified ; and is always 3 characters long. NOTE that this is destructive to the ; BCD_100 register - it may be modified on return. WriteASCIINumber call Convert2BCD movf BCD_100, W ; Check hundreds digit, replace with ' ' if its 0. btfsc STATUS, Z movlw NUMERIC_SPACE addlw '0' ; convert to ASCII & write to LCD call LCDWriteData movf BCD_10, W ; Ditto for tens digit, except we only replace it if addwf BCD_100, F ; the 100's digit is *also* 0. btfsc STATUS, Z movlw NUMERIC_SPACE addlw '0' call LCDWriteData movf BCD_1, W ; 1's digit is always displayed, even if 0. addlw '0' call LCDWriteData return DrawStringUpTo16Chars ; Like DrawStringDoWork, except it stops after 16 characters... ; Used for scrolling text during the boot sequence. movlw 16 movwf SCROLL_COUNTER ; we use this for a counter, so we know when we hit 16 chars. goto Draw16CharsContinue DrawUpTo16CharsOffset ; As above, but skips the 1st char. Used for more smooth scrolling, since each address has 2 chars. movlw 16 movwf SCROLL_COUNTER ; we use this for a counter, so we know when we hit 16 chars. ERRORLEVEL -302 ; Initiate program memory read: BANKSEL PMCON1 bsf PMCON1, RD ERRORLEVEL +302 goto Draw16CharsSkipOne Draw16CharsContinue ERRORLEVEL -302 ; Initiate program memory read: BANKSEL PMCON1 bsf PMCON1, RD ; Read 1st char (high byte) and write it out: BANKSEL PMDATL rlf PMDATL, W ; Rotate high bit into C rlf PMDATH, W ; Rotate left, taking low bit from C andlw 0x7F ; Mask off high bit (mostly just to test/set Z, since it should already be 0) BANKSEL PORTA btfsc STATUS, Z ; Return if its 0 return call LCDWriteData ; Write it to LCD ; Update the character count, and exit if we hit 0. decf SCROLL_COUNTER, F btfsc STATUS, Z return Draw16CharsSkipOne ; Read 2nd char (low byte) and write it out: BANKSEL PMDATL movf PMDATL, W andlw 0x7F ; Mask off high bit BANKSEL PORTA btfsc STATUS, Z ; Return if its 0 return call LCDWriteData ; Write it to LCD ; Update the character count, and exit if we hit 0. decf SCROLL_COUNTER, F btfsc STATUS, Z return BANKSEL PMADRL incf PMADRL, F ; Increment Program Memory read address... ERRORLEVEL +302 goto Draw16CharsContinue ; ... & recurse on rest of string. DrawStringDoWork ; Read string from program memory registers, and write characters to the LCD. ; Characters are packed, 2 7-bit ASCII characters in each 14-bit word. ; Strings are null-terminated, and the PMADR* registers are required to be set up ; before calling (to point us to the right string). ; Does a lot of weird stuff with bank select bits, so its careful to always return to bank 0 before returning. ERRORLEVEL -302 ; Initiate program memory read: BANKSEL PMCON1 bsf PMCON1, RD ; Read 1st char (high byte) and write it out: BANKSEL PMDATL rlf PMDATL, W ; Rotate high bit into C rlf PMDATH, W ; Rotate left, taking low bit from C andlw 0x7F ; Mask off high bit (mostly just to test/set Z, since it should already be 0) BANKSEL PORTA btfsc STATUS, Z ; Return if its 0 return call LCDWriteData ; Write it to LCD ; Read 2nd char (low byte) and write it out: BANKSEL PMDATL movf PMDATL, W andlw 0x7F ; Mask off high bit BANKSEL PORTA btfsc STATUS, Z ; Return if its 0 return call LCDWriteData ; Write it to LCD BANKSEL PMADRL incf PMADRL, F ; Increment Program Memory read address... ERRORLEVEL +302 goto DrawStringDoWork ; ... & recurse on rest of string. DrawDataDoWork ; Debug version displays decimal equivalents instead... ERRORLEVEL -302 ; Initiate program memory read: BANKSEL PMCON1 bsf PMCON1, RD ; Read 1st char (high byte) and write it out: BANKSEL PMDATL movf PMDATH, W BANKSEL PORTA btfsc STATUS, Z ; Return if its 0 return call LCDWriteData ; Write it to LCD ; Read 2nd char (low byte) and write it out: BANKSEL PMDATL movf PMDATL, W BANKSEL PORTA btfsc STATUS, Z ; Return if its 0 return call LCDWriteData ; Write it to LCD BANKSEL PMADRL incf PMADRL, F ; Increment Program Memory read address... ERRORLEVEL +302 goto DrawDataDoWork ; ... & recurse on rest of string. SetupProgressBar ; Initializes the progress bar characters in CG-RAM, ; & draws an empty progress bar on the bottom row. movlw CGRAM_CHAR1_ADDR call LCDWriteInstr DrawRawData(CGRAM_Progress_Data) movlw LINE_4_ADDR call LCDWriteInstr DrawRawData(Empty_Progress_Data) return FillProgressBar ; Draws a progress bar filled up to W out of 42. ; Only refreshes the progress bar up to the amount filled. ; Save current progress amount & pin it to 1 minimum - cheapo bug fix. movwf PROGRESS_FILL ; movf PROGRESS_FILL, W ; btfsc STATUS, Z ; incf PROGRESS_FILL, F clrf PROGRESS_COUNT ; Check if it's >42, and pin it if so. movlw 42 subwf PROGRESS_FILL, W btfss STATUS, C goto Continue_SB_Fill movlw 42 movwf PROGRESS_FILL Continue_SB_Fill movlw LINE_4_ADDR call LCDWriteInstr movlw LTEND_PROGRESS_CHAR call LCDWriteData Prog_Stage_1 ; All done? Possibly write last blank char... movf PROGRESS_FILL, W btfsc STATUS, Z goto FinishProgBar ; While the fill count > 3, write filled characters movlw 3 subwf PROGRESS_FILL, F btfss STATUS, C goto WritePartialProgChar movlw FILL3_PROGRESS_CHAR call LCDWriteData incf PROGRESS_COUNT, F goto Prog_Stage_1 WritePartialProgChar movlw 3 addwf PROGRESS_FILL, W call LCDWriteData incf PROGRESS_COUNT, F FinishProgBar ; If we wrote less than the full # of chars, write a blank one. movlw 14 subwf PROGRESS_COUNT, W btfsc STATUS, C return movlw 4 call LCDWriteData return ;************************************************* ;************************************************* ;************************************************* ;* Level 2 routines - only call level 0 & 1 ;************************************************* ;************************************************* ;************************************************* ; Takes a byte to write in the W register, and writes it out to the LCD. ; Includes a 100us delay after the write to give the LCD time to execute the instruction. ; Additional delays will be necessary for some instructions. LCDWriteData bsf PORTB, LCD_RS bcf PORTB, LCD_RW goto LCDWriteStuff LCDWriteInstr bcf PORTB, LCD_RS bcf PORTB, LCD_RW LCDWriteStuff movwf LCD_TEMP_REG ; save write data: ; Write the high nybble: call LCDWriteHighNybble ; Write the low nybble: swapf LCD_TEMP_REG, W call LCDWriteHighNybble movlw 1 ; wait 50us call delay return FilterInitialTempMeasurements ; Does one iteration of temperature measuring & possibly sets up the initial measurements. ; Returns 1 in W if temperatures have been initialized, 0 otherwise. ; If we've set the cold-box temp, we're done filtering the temp measurements. ; Just return. movf COLD_BOX_TEMP, W ; Leave this code disabled - there's a bug I don't understand, where ; the last four values *frequently* match, and only slowly make it up to ; the actual current value... so we can't disable this routine after the 1st ; time they match, unfortunately. It's a hack, but we use the boot screen ; to allow time for the temp measurements to settle. ; btfss STATUS, Z ; retlw 10 ; Update our measurement variables: call UpdateTempMeasurementsNoDelay ; Examine previous measurements... if they all match, then ; we initialize the "current temperature" vars to the values. movf PREV_HOT_1, W subwf PREV_HOT_2, W btfss STATUS, Z retlw 0 movf PREV_HOT_1, W subwf PREV_HOT_3, W btfss STATUS, Z retlw 0 movf PREV_HOT_1, W subwf PREV_HOT_4, W btfss STATUS, Z retlw 0 movf PREV_COLD_1, W subwf PREV_COLD_2, W btfss STATUS, Z retlw 0 movf PREV_COLD_1, W subwf PREV_COLD_3, W btfss STATUS, Z retlw 0 movf PREV_COLD_1, W subwf PREV_COLD_4, W btfss STATUS, Z retlw 0 ; Copy temp measurements into the variables: movf PREV_HOT_1, W movwf HEAT_SINK_TEMP movf PREV_COLD_1, W movwf COLD_BOX_TEMP retlw 1 ;************************************************* ;************************************************* ;************************************************* ;* Level 1 routines - only call level 0 ;************************************************* ;************************************************* ;************************************************* InitPIC ; Initialize IO pins bcf STATUS, RP0 ; select bank 0 bcf STATUS, RP1 ; select bank 0 CLRF PORTA ; clear ports, output 0 CLRF PORTB ; ***** Select Memory Bank #1 bsf STATUS, RP0 ; select bank 1 ERRORLEVEL -302 movlw 00001000b ; Port B is all outputs, but the PWM is configured as an input until it has completed 1 cycle movwf TRISB movlw 00110111b ; Port A is all unused outputs, except RA0, RA1, RA2, 4, 5 movwf TRISA movlw 00000110b ; 1 analog input: AN1 (RA1, RA2) movwf ANSEL ;*************************** ; Initialize PWM (and TMR2) movlw 101 ; given a 1MHz counter, this should yield a 10KHz period. movwf PR2 ; This period counter allows us to adjust the duty cycle from 0-100. ; ***** Return to Memory Bank #0 ERRORLEVEL +302 bcf STATUS, RP0 ; re-select bank 0 movlw 00000100b ; Turn TMR2 on, with no pre/post scaling (1:1, 1MHz freq) movwf T2CON movlw 0x00 ; Leave PWM off for now: duty cycle = 0%. We can adjust it movwf CCPR1L ; later by modifying this register, and it will be all set up. movlw 00001100b ; single output PWM mode, turn PWM on movwf CCP1CON movlw 2 ; Delay one PWM period call delay bsf STATUS, RP0 ; select bank 1 ERRORLEVEL -302 bcf TRISB, PWM_OUT ; reconfigure PB3 as an output ERRORLEVEL +302 bcf STATUS, RP0 ; re-select bank 0 ;*************** ; Initialize ADC movlw 11001001b ; clk=Frc, src=RA1, ADC ON movwf ADCON0 return ; Write high nybble of instruction in W. ; Assumes LCD_RS and LCD_RW are already set correctly. LCDWriteHighNybble movwf LCD_TEMP_REG bcf PORTB, LCD_RW ; Begin write cycle: bsf PORTB, LCD_E ; Set E signal nop ; Copy high nybble of instruction in W over high nybble of output port movf LCD_TEMP_REG, W xorwf PORTB, W andlw 11110000b xorwf PORTB, F nop ; Finish write cycle: bcf PORTB, LCD_E ; clear E signal return UIDelay ; Delays for W * 100ms ; Used for extra long delays, as needed for UI stuff movwf UI_DELAY_COUNT movlw 250 call delay movlw 250 call delay movlw 250 call delay movlw 250 call delay decfsz UI_DELAY_COUNT, W goto UIDelay return UpdateTempMeasurements ; Reads both temperature sensors, keeping track of the last 4 measurements, ; and adjusts the actual "temp" registers by 1 depending on the values. ; If all 4 previous measurements are greater than the current value, then we increment it. ; If they're all less, then we decrement. Otherwise, it is left unchanged. ; Thus, the final temperature will only ever change when all 4 measurements agree ; that it needs to move in one direction or the other. ; Temporarily turn off the PWM. ; Cutting power to the peltier should make our temperature measurements more accurate. movf CCPR1L, W movwf SAVED_DUTY_CYCLE movlw 0 movwf CCPR1L movlw 150 ; 15ms delay call delay ; Vdd settling time since we turned off the peltier. UpdateTempMeasurementsNoDelay ; Just as above, but doesn't temporarily disable th epeltier device & wait for settling. ; Note that peltier operation is still restored to SAVED_DUTY_CYCLE afterwards... ; Measure ADC Channel 1, the heat sink temp: ; First, roll over previous measurements: movf PREV_HOT_3, W movwf PREV_HOT_4 movf PREV_HOT_2, W movwf PREV_HOT_3 movf PREV_HOT_1, W movwf PREV_HOT_2 ; Make new measurement & save it. movlw 11001001b ; clk=Frc, src=RA1, ACD ON movwf ADCON0 call MeasureADC call ConvertADC2F movwf PREV_HOT_1 ; Now, if they are all > HEAT_SINK_TEMP, increment it: ; DOLATER - could make this stuff take a lot less space using INDF... ; Or, actually, assembler macros. movf PREV_HOT_1, W subwf HEAT_SINK_TEMP, W btfsc STATUS, C goto DecrementHot movf PREV_HOT_2, W subwf HEAT_SINK_TEMP, W btfsc STATUS, C goto DecrementHot movf PREV_HOT_3, W subwf HEAT_SINK_TEMP, W btfsc STATUS, C goto DecrementHot movf PREV_HOT_4, W subwf HEAT_SINK_TEMP, W btfsc STATUS, C goto DecrementHot incf HEAT_SINK_TEMP, W ; Increment heat sink temp... and clear past measurements. movwf HEAT_SINK_TEMP movwf PREV_HOT_1 movwf PREV_HOT_2 movwf PREV_HOT_3 movwf PREV_HOT_4 goto UpdateCold DecrementHot ; Now, if they are all < HEAT_SINK_TEMP, increment it: movf HEAT_SINK_TEMP, W subwf PREV_HOT_1, W btfsc STATUS, C goto UpdateCold movf HEAT_SINK_TEMP, W subwf PREV_HOT_2, W btfsc STATUS, C goto UpdateCold movf HEAT_SINK_TEMP, W subwf PREV_HOT_3, W btfsc STATUS, C goto UpdateCold movf HEAT_SINK_TEMP, W subwf PREV_HOT_4, W btfsc STATUS, C goto UpdateCold decf HEAT_SINK_TEMP, W movwf HEAT_SINK_TEMP movwf PREV_HOT_1 movwf PREV_HOT_2 movwf PREV_HOT_3 movwf PREV_HOT_4 UpdateCold: ; Measure ADC Channel 2, the cold box temp: ; First, roll over previous measurements: movf PREV_COLD_3, W movwf PREV_COLD_4 movf PREV_COLD_2, W movwf PREV_COLD_3 movf PREV_COLD_1, W movwf PREV_COLD_2 movlw 11010001b ; clk=Frc, src=RA2, ACD ON movwf ADCON0 call MeasureADC call ConvertADC2F movwf PREV_COLD_1 ; Now, if they are all > COLD_BOX_TEMP, increment it: ; DOLATER - could make this stuff take a lot less space using INDF... ; Or, actually, assembler macros. movf PREV_COLD_1, W subwf COLD_BOX_TEMP, W btfsc STATUS, C goto DecrementCOLD movf PREV_COLD_2, W subwf COLD_BOX_TEMP, W btfsc STATUS, C goto DecrementCOLD movf PREV_COLD_3, W subwf COLD_BOX_TEMP, W btfsc STATUS, C goto DecrementCOLD movf PREV_COLD_4, W subwf COLD_BOX_TEMP, W btfsc STATUS, C goto DecrementCOLD incf COLD_BOX_TEMP, W ; Increment cooling chamber temp & clear prev measurements movwf COLD_BOX_TEMP movwf PREV_COLD_1 movwf PREV_COLD_2 movwf PREV_COLD_3 movwf PREV_COLD_4 goto ReturnFromUpdateTemp DecrementCOLD ; Now, if they are all < COLD_BOX_TEMP, increment it: movf COLD_BOX_TEMP, W subwf PREV_COLD_1, W btfsc STATUS, C goto ReturnFromUpdateTemp movf COLD_BOX_TEMP, W subwf PREV_COLD_2, W btfsc STATUS, C goto ReturnFromUpdateTemp movf COLD_BOX_TEMP, W subwf PREV_COLD_3, W btfsc STATUS, C goto ReturnFromUpdateTemp movf COLD_BOX_TEMP, W subwf PREV_COLD_4, W btfsc STATUS, C goto ReturnFromUpdateTemp decf COLD_BOX_TEMP, W movwf COLD_BOX_TEMP movwf PREV_COLD_1 movwf PREV_COLD_2 movwf PREV_COLD_3 movwf PREV_COLD_4 ReturnFromUpdateTemp ; Re-enable the peltier movf SAVED_DUTY_CYCLE, W movwf CCPR1L return ;************************************************* ;************************************************* ;************************************************* ;* Level 0 - Leaf routines - Do not make subroutine calls ;************************************************* ;************************************************* ;************************************************* AdjustPeltier ; Monitors the hot & cold side temperature sensors, and adjusts the peltier ; duty cycle accordingly. We use a simple algorithm where the duty cycle can ; only be adjusted every 10 seconds or so... when we adjust it up, it's always 1%. ; but when we adjst it down, it's by the number of degrees we need to move. ; This is a concervative technique to avoid putting the peltier device under undue thermal stress. ; First we increment our adjustment counter - every 100 calls will be about 10 seconds, ; and that's the only time we actually adjust the power to the peltier decfsz ADJUST_COUNTER, F return ; Reset our iteration counter movlw ADJUST_ITERATIONS movwf ADJUST_COUNTER ; Subtract the cold box temp from our setpoint... ; If they're the same, just return. movf COLD_BOX_TEMP, W subwf SET_POINT_TEMP, W btfsc STATUS,Z ; Are we correct temperature? return ; Then just return. btfss STATUS, C ; If it rolls over, our cold box is too warm, so increase power to peltier. goto AdjustUp ; Otherwise, adjust it down. ; clrf CCPR1L ; return AdjustDown ; Okay, adjust the peltier power down a bit... ; 'W' contains number of degrees we need to move. ; Since it's fairly safe to decrease power, we decrease ; by the same # of percentage points of duty cycle. subwf CCPR1L, F btfss STATUS, C ; If we rolled over, pin the duty cycle at 0. clrf CCPR1L return AdjustUp ; If it's less than 100, then increment it. movlw 100 subwf CCPR1L, W btfss STATUS, Z incf CCPR1L, F return ; Delays for W * .1 ms. delay movwf TEMP_REG movlw 35 ; for each W, do 35 iterations of inner loop (>100us) ; This is enough over to account for error tolerance in the internal RC oscillator. ; Inner loop takes 4 cycles/iteration ; (4us since 4MHz clock = 1MHz instruction cycle) delay_loop addlw 0xFF ; add -1 btfss STATUS, Z goto delay_loop ; Decrement TEMP_REG, and spin for another 100us. ; If its 0, return! decfsz TEMP_REG, W goto delay return DebounceKeyboard ; Waits until keyboard state has not changed for a reasonable amount of time. movlw 0x00 movwf DebounceCounter movf PORTA, W andlw PORTA_KEY_MASK movwf KEY_STATE ContinueKBDebounce movlw 1 call delay movf PORTA, W andlw PORTA_KEY_MASK subwf KEY_STATE, W btfss STATUS, Z goto DebounceKeyboard incf DebounceCounter, F btfss STATUS, Z ; if counter wrapped around, continue goto ContinueKBDebounce return ; Convert integer in W into 3 digit binary coded decimal. ; Digits are returned in the registers BCD_100, BCD_10, and BCD_1. Convert2BCD ; First we separate out the high digit from the remainder: movwf BCD_10 clrf BCD_100 conv_loop_1 movlw 100 subwf BCD_10, W btfss STATUS, C goto overflow movwf BCD_10 incf BCD_100, F goto conv_loop_1 overflow ; Now its time to look at the low two digits: movf BCD_10, W movwf BCD_1 clrf BCD_10 conv_loop_2 movlw 10 subwf BCD_1, W btfss STATUS, C goto overflow_2 movwf BCD_1 incf BCD_10, F goto conv_loop_2 overflow_2 return MeasureADC ; measure the ADC, using the currently configured channel, return result in W. movlw 1 call delay ; cap charge time bsf ADCON0, 2 ; start conversion ADC_POLL: btfsc ADCON0, 2 goto ADC_POLL movlw 1 call delay ; Allow some reset time? movf ADRESH, w return ConvertADC2F ; Converts output from ADC into degrees ferenheit ; Input is taken from ADC registers, result is returned in W. ; Each 10-bit step is .48¡C, .48¡C * (9/5) = .87¡F movf ADRESH, w addlw -128 ; 128 8-bit steps is 2.5v or -23¡C ; So, -23¡c is now our baseline instead of -273¡C movwf TEMP_REG bcf STATUS, C rlf TEMP_REG, F rlf TEMP_REG, F ; Get next 2 lower bits: ; access the ADRESL register, and shift off the low 6 bits. ; ***** Select Memory Bank #1 ERRORLEVEL -302 bsf STATUS, RP0 ; select bank 1 rrf ADRESL, F ; DOLATER - replace with 3 left-rotates? rrf ADRESL, F rrf ADRESL, F rrf ADRESL, F rrf ADRESL, F rrf ADRESL, F movlw 00000011b andwf ADRESL, W ; ***** Return to Memory Bank #0 ERRORLEVEL +302 bcf STATUS, RP0 ; re-select bank 0 ; Or 2 lower bits into the rest of the result, ; and convert to ¡F using our lookup table: iorwf TEMP_REG, F movlw 7 movwf PCLATH ; Check TEMP_REG for 255, and decrement it... ; (we crash if we try to look up 255). movlw 0xFF xorwf TEMP_REG, W btfsc STATUS, Z decf TEMP_REG, F movf TEMP_REG, W goto TempLookupTable ; will return directly to our caller... return ORG 0x500 ; String constants! ; These are stored in program memory as 7-bit ASCII, 2-per-instruction. ; So, you can only use 7-bit ascii characters. They must also be manually ; null-terminated, and must not span a 256-word boundary (because the string ; drawing code will only increment PMADRL and assumes PMADRH remains constant) Boot_Status_String DA " ...calibrating heatsink... ...reticulating sensors... ...determining cooling coefficients... ...initializing heat shield... ...injecting insulation compound... ...initiating feedback registers... ...modulating duty cycle... ...activating peltier device... ...commencing cooling operation... \0" Boot_Status_End ; Used as constant address symbol to tell when we reach the end of the string. Error_String DA "Error!\0" Detail_String DA "Detail\0" Simple_String DA "Simple\0" Booting_String DA "Booting...\0" Anim_String_1 DA "Current:\0" Anim_String_2 DA " Target:\0" Anim_Cooling DA "--Cooling-------\0" Anim_Standby DA "--Standby\0" Anim_Warming DA "--Warming\0" Diag_String_1 DA "HS: Cold: \0" Diag_String_2 DA "Set Point: \0" Diag_String_3 DA "Duty Cycle: \0" Diag_String_4 DA "Fan: ON v\0" ORG 0x600 Empty_Progress_Data DB 6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,0 ; String data for an empty progress bar ; CGRAM data, stored as strings. ; We use 0x20 as a non-zero placeholder for a clear row in CGRAM. ; The value must be non-zero, or the string drawing code will stop drawing. CGRAM_Progress_Data DB 10101b, 10000b, 10000b, 10000b, 10000b, 10000b, 10101b, 0x20 ; stage-1 progress character. DB 10101b, 10100b, 10100b, 10100b, 10100b, 10100b, 10101b, 0x20 ; stage-2 progress character (2 vertical lines filled). DB 10101b, 10101b, 10101b, 10101b, 10101b, 10101b, 10101b, 0x20 ; stage-3 progress character (full - 3 vertical lines filled). DB 10101b, 0x20, 0x20, 0x20, 0x20, 0x20, 10101b, 0x20 ; Empty progress character - gray line across top & bottom DB 10000b, 10000b, 11000b, 11000b, 11000b, 10000b, 10000b, 0x20 ; Right-end character - left facing end-cap DB 00001b, 00001b, 00011b, 00011b, 00011b, 00001b, 00001b, 0x20, 0x00 ; Left-end character - right facing end-cap Asterisk_String DA "****************\0" Overheat_String DA "* *Overheat* *\0" Shutdown_String DA "* *Shutdown* *\0" Blank_Shutdown_String DA "* *\0" Divider_Line_String DA "----------------\0" ORG 0x700 ; Lookup table will be stored from 0x700-0x7FF ; We have to make sure that it is within a single 256-word page so that ; PCLATH won't need to be updated - we can do the lookup by modifying PCL only. TempLookupTable ; Lookup table converts from ADC steps (0-254) to degrees F. ; The Celsius to Ferenheit conversion is included, as well as the ADC to Celsius. ; Because we have to fit it all within a single 256 word page, we can't carry quite ; the entire range on inputs. ; Formula: -9.4¡F + (n * .87¡F) addwf PCL, F retlw 0 ; 0: Baseline is at -23¡C, or -9.4¡F retlw 0 ; 1: -8.53¡F retlw 0 ; 2: -7.66¡F retlw 0 ; 3: -6.79¡F retlw 0 ; 4: -5.92¡F retlw 0 ; 5: -5.05¡F retlw 0 ; 6: -4.18¡F retlw 0 ; 7: -3.31¡F retlw 0 ; 8: -2.44¡F retlw 0 ; 9: -1.57¡F retlw 0 ; 10: -0.7¡F retlw 0 ; 11: .17¡F retlw 1 ; 12: 1.04¡F retlw 2 ; 13: 1.91¡F retlw 3 ; 14: 2.78¡F retlw 4 ; 15: 3.65¡F retlw 5 ; 16: 4.52¡F retlw 5 ; 17: 5.39¡F retlw 6 ; 18: 6.26¡F retlw 7 ; 19: 7.13¡F retlw 8 ; 20: 8¡F retlw 9 ; 21: 8.87¡F retlw 10 ; 22: 9.74¡F retlw 11 ; 23: 10.61¡F retlw 11 ; 24: 11.48¡F retlw 12 ; 25: 12.35¡F retlw 13 ; 26: 13.22¡F retlw 14 ; 27: 14.09¡F retlw 15 ; 28: 14.96¡F retlw 16 ; 29: 15.83¡F retlw 17 ; 30: 16.7¡F retlw 18 ; 31: 17.57¡F retlw 18 ; 32: 18.44¡F retlw 19 ; 33: 19.31¡F retlw 20 ; 34: 20.18¡F retlw 21 ; 35: 21.05¡F retlw 22 ; 36: 21.92¡F retlw 23 ; 37: 22.79¡F retlw 24 ; 38: 23.66¡F retlw 25 ; 39: 24.53¡F retlw 25 ; 40: 25.4¡F retlw 26 ; 41: 26.27¡F retlw 27 ; 42: 27.14¡F retlw 28 ; 43: 28.01¡F retlw 29 ; 44: 28.88¡F retlw 30 ; 45: 29.75¡F retlw 31 ; 46: 30.62¡F retlw 31 ; 47: 31.49¡F retlw 32 ; 48: 32.36¡F retlw 33 ; 49: 33.23¡F retlw 34 ; 50: 34.1¡F retlw 35 ; 51: 34.97¡F retlw 36 ; 52: 35.84¡F retlw 37 ; 53: 36.71¡F retlw 38 ; 54: 37.58¡F retlw 38 ; 55: 38.45¡F retlw 39 ; 56: 39.32¡F retlw 49 ; 57: 40.19¡F retlw 41 ; 58: 41.06¡F retlw 42 ; 59: 41.93¡F retlw 43 ; 60: 42.8¡F retlw 44 ; 61: 43.67¡F retlw 45 ; 62: 44.54¡F retlw 45 ; 63: 45.41¡F retlw 46 ; 64: 46.28¡F retlw 47 ; 65: 47.15¡F retlw 48 ; 66: 48.02¡F retlw 49 ; 67: 48.89¡F retlw 50 ; 68: 49.76¡F retlw 51 ; 69: 50.63¡F retlw 52 ; 70: 51.5¡F retlw 52 ; 71: 52.37¡F retlw 53 ; 72: 53.24¡F retlw 54 ; 73: 54.11¡F retlw 55 ; 74: 54.98¡F retlw 56 ; 75: 55.85¡F retlw 57 ; 76: 56.72¡F retlw 58 ; 77: 57.59¡F retlw 58 ; 78: 58.46¡F retlw 59 ; 79: 59.33¡F retlw 60 ; 80: 60.2¡F retlw 61 ; 81: 61.07¡F retlw 62 ; 82: 61.94¡F retlw 63 ; 83: 62.81¡F retlw 64 ; 84: 63.68¡F retlw 65 ; 85: 64.55¡F retlw 65 ; 86: 65.42¡F retlw 66 ; 87: 66.29¡F retlw 67 ; 88: 67.16¡F retlw 68 ; 89: 68.03¡F retlw 69 ; 90: 68.9¡F retlw 70 ; 91: 69.77¡F retlw 71 ; 92: 70.64¡F retlw 72 ; 93: 71.51¡F retlw 72 ; 94: 72.38¡F retlw 73 ; 95: 73.25¡F retlw 74 ; 96: 74.12¡F retlw 75 ; 97: 74.99¡F retlw 76 ; 98: 75.86¡F retlw 77 ; 99: 76.73¡F retlw 78 ; 100: 77.6¡F retlw 78 ; 101: 78.47¡F retlw 89 ; 102: 79.34¡F retlw 80 ; 103: 80.21¡F retlw 81 ; 104: 81.08¡F retlw 82 ; 105: 81.95¡F retlw 83 ; 106: 82.82¡F retlw 84 ; 107: 83.69¡F retlw 85 ; 108: 84.56¡F retlw 85 ; 109: 85.43¡F retlw 86 ; 110: 86.3¡F retlw 87 ; 111: 87.17¡F retlw 88 ; 112: 88.04¡F retlw 89 ; 113: 88.91¡F retlw 90 ; 114: 89.78¡F retlw 91 ; 115: 90.65¡F retlw 92 ; 116: 91.52¡F retlw 92 ; 117: 92.39¡F retlw 93 ; 118: 93.26¡F retlw 94 ; 119: 94.13¡F retlw 95 ; 120: 95¡F retlw 96 ; 121: 95.87¡F retlw 97 ; 122: 96.74¡F retlw 98 ; 123: 97.61¡F retlw 98 ; 124: 98.48¡F retlw 99 ; 125: 99.35¡F retlw 100 ; 126: 100.22¡F retlw 101 ; 127: 101.09¡F retlw 102 ; 128: 101.96¡F retlw 103 ; 129: 102.83¡F retlw 104 ; 130: 103.7¡F retlw 105 ; 131: 104.57¡F retlw 105 ; 132: 105.44¡F retlw 106 ; 133: 106.31¡F retlw 107 ; 134: 107.18¡F retlw 108 ; 135: 108.05¡F retlw 109 ; 136: 108.92¡F retlw 110 ; 137: 109.79¡F retlw 111 ; 138: 110.66¡F retlw 112 ; 139: 111.53¡F retlw 112 ; 140: 112.4¡F retlw 113 ; 141: 113.27¡F retlw 114 ; 142: 114.14¡F retlw 115 ; 143: 115.01¡F retlw 116 ; 144: 115.88¡F retlw 117 ; 145: 116.75¡F retlw 118 ; 146: 117.62¡F retlw 118 ; 147: 118.49¡F retlw 119 ; 148: 119.36¡F retlw 120 ; 149: 120.23¡F retlw 121 ; 150: 121.1¡F retlw 122 ; 151: 121.97¡F retlw 123 ; 152: 122.84¡F retlw 124 ; 153: 123.71¡F retlw 125 ; 154: 124.58¡F retlw 125 ; 155: 125.45¡F retlw 126 ; 156: 126.32¡F retlw 127 ; 157: 127.19¡F retlw 128 ; 158: 128.06¡F retlw 129 ; 159: 128.93¡F retlw 130 ; 160: 129.8¡F retlw 131 ; 161: 130.67¡F retlw 132 ; 162: 131.54¡F retlw 132 ; 163: 132.41¡F retlw 133 ; 164: 133.28¡F retlw 134 ; 165: 134.15¡F retlw 135 ; 166: 135.02¡F retlw 136 ; 167: 135.89¡F retlw 137 ; 168: 136.76¡F retlw 138 ; 169: 137.63¡F retlw 139 ; 170: 138.5¡F retlw 139 ; 171: 139.37¡F retlw 140 ; 172: 140.24¡F retlw 141 ; 173: 141.11¡F retlw 142 ; 174: 141.98¡F retlw 143 ; 175: 142.85¡F retlw 144 ; 176: 143.72¡F retlw 145 ; 177: 144.59¡F retlw 145 ; 178: 145.46¡F retlw 146 ; 179: 146.33¡F retlw 147 ; 180: 147.2¡F retlw 148 ; 181: 148.07¡F retlw 149 ; 182: 148.94¡F retlw 150 ; 183: 149.81¡F retlw 151 ; 184: 150.68¡F retlw 152 ; 185: 151.55¡F retlw 152 ; 186: 152.42¡F retlw 153 ; 187: 153.29¡F retlw 154 ; 188: 154.16¡F retlw 155 ; 189: 155.03¡F retlw 156 ; 190: 155.9¡F retlw 157 ; 191: 156.77¡F retlw 158 ; 192: 157.64¡F retlw 159 ; 193: 158.51¡F retlw 159 ; 194: 159.38¡F retlw 160 ; 195: 160.25¡F retlw 161 ; 196: 161.12¡F retlw 162 ; 197: 161.99¡F retlw 163 ; 198: 162.86¡F retlw 164 ; 199: 163.73¡F retlw 165 ; 200: 164.6¡F retlw 165 ; 201: 165.47¡F retlw 166 ; 202: 166.34¡F retlw 167 ; 203: 167.21¡F retlw 168 ; 204: 168.08¡F retlw 169 ; 205: 168.95¡F retlw 170 ; 206: 169.82¡F retlw 171 ; 207: 170.69¡F retlw 172 ; 208: 171.56¡F retlw 172 ; 209: 172.43¡F retlw 173 ; 210: 173.3¡F retlw 174 ; 211: 174.17¡F retlw 175 ; 212: 175.04¡F retlw 176 ; 213: 175.91¡F retlw 177 ; 214: 176.78¡F retlw 178 ; 215: 177.65¡F retlw 179 ; 216: 178.52¡F retlw 180 ; 217: 179.39¡F retlw 180 ; 218: 180.26¡F retlw 181 ; 219: 181.13¡F retlw 182 ; 220: 182¡F retlw 183 ; 221: 182.87¡F retlw 184 ; 222: 183.74¡F retlw 185 ; 223: 184.61¡F retlw 185 ; 224: 185.48¡F retlw 186 ; 225: 186.35¡F retlw 187 ; 226: 187.22¡F retlw 188 ; 227: 188.09¡F retlw 189 ; 228: 188.96¡F retlw 190 ; 229: 189.83¡F retlw 191 ; 230: 190.7¡F retlw 192 ; 231: 191.57¡F retlw 192 ; 232: 192.44¡F retlw 193 ; 233: 193.31¡F retlw 194 ; 234: 194.18¡F retlw 195 ; 235: 195.05¡F retlw 196 ; 236: 195.92¡F retlw 197 ; 237: 196.79¡F retlw 198 ; 238: 197.66¡F retlw 199 ; 239: 198.53¡F retlw 199 ; 240: 199.4¡F retlw 200 ; 241: 200.27¡F retlw 201 ; 242: 201.14¡F retlw 202 ; 243: 202.01¡F retlw 203 ; 244: 202.88¡F retlw 204 ; 245: 203.75¡F retlw 205 ; 246: 204.62¡F retlw 205 ; 247: 205.49¡F retlw 206 ; 248: 206.36¡F retlw 207 ; 249: 207.23¡F retlw 208 ; 250: 208.1¡F retlw 209 ; 251: 208.97¡F retlw 210 ; 252: 209.84¡F retlw 211 ; 253: 210.71¡F retlw 212 ; 254: 211.58¡F ; retlw 213 ; 255: ¡F ; no room for this b/c of the addlw at the beginning. END