; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; Panteltje hcs-0.2 ; copyright Jan Panteltje 2007-always ; integer and floating point math copyright Microchip, some other routines copyright others, my part is GPL. ; ; ; I have written a small temperature controller for PIC16F690 ; It uses LM135 or similar Kelvin sensors. ; It is quite universal as it also displays voltage. ; It has a real time clock (as long as it has power), runs ; from the PIC internal 8MHz oscillator, and seems quite accurate, ; The soft has a serial 19200 Baud, 8 bits no parity interface, and accepts ; the following commands: ; ; unnENTER sets hour ; mnnENTER sets minute ; tnnnENTER sets temperature set point in Celsius, is saved in EEPROM ; annENTER displays AD channel nn count ; vnnENTER displays AD channel nn converted to voltage ; cnnENTER displays AD channel nn converted to Celsius ; s displays temp set point ; GnnnENTER sets clock calibration, timer1 reload low byte, use 175 for nominal, is saved in EEPROM ; g displays clock calibration (timer1 reload low byte) ; I calibrate inside temp sensor, use 17 as default, 16 is one degree Celcius higher, 18 one lower, value is saved in EEPROM ; i print inside calibration value ; O calibrate outside temp sensor, use 17 as default, 16 is one degree Celcius higher, 18 one lower, value is saved in EEPROM ; o print outside calibration value ; * AFTER FIRST POWER ON type: G175ENTER, I17ENTER, O17ENTER, t21ENTER to program the EEPROM, then H to start the control loop again * ; The above commands exit the control loop (control loop is on by default on power on). ; ; ; H starts control loop (again) ; h help message tells you to read README, there is no README yet :-) ; F loops as fast as ADC can be read (for test). ; f ADC is read once per minute, this is the default. ; ; Very precise control of room temperature is achieved by doing an test against set point ; every 60 seconds, this is ONLY intended to switch an electric heater (via a triac optocoupler), ; probably too fast for something that runs on oil or gas. ; ; There is a LED output 'frost alarm' (so you can tap the water if needed). ; ; This control is a bit more pleasant then the normal bi-metal thermostats in the electric ; heaters, those have an hysteresis of several degrees C, this less then one. ; I have enabled 2 analog channels here only, AN5 and AN7. ; If you need more analog inputs you need to set the related bits in TRISA / TRISB / TRISC, ; and ANSEL and ANSELH (about line 902 in the code). ; There are too many banksel instructions, and I likely forgot some. ; The math is done in integer, the floating point lib (copyright Microchip) is defined out. ; CODE IS FOR GPASM, may work on MPLAB too. ; set this to your supply voltage (use LM317 for example, ADC uses supply as reference). ; Vdd PIC measured 5.06V ; #define VREF 5060 ; mV #define VREF_H D'19' ; high byte #define VREF_L D'196' ; low byte TRUE equ 1 FALSE equ 0 MSB equ 7 Mode16 equ TRUE ; Change this to FALSE if a 32 bit product is desired for F_mpy routine. SIGNED equ FALSE ; Set This To 'TRUE' if the routines ; ; for Multiplication & Division needs ; ; to be assembled as Signed Integer ; ; Routines. If 'FALSE' the above two ; ; routines ( D_mpy & D_div ) use ; ; unsigned arithmetic. ; NOTE : If one needs to get a 32 bit product( & an 8 bit exponent ), ; re assemble the program after changing the line " Mode16 equ TRUE" ; to " Mode16 equ FALSE ". ; If this option is chosen, then the 32 bit result is returned in ; ( ACCbHI, ACCbLO, ACCcHI, ACCcLO ) and the 8 bit exponent in EXPb. ; This method ( with " Mode16 equ FALSE " ) is NOT Recommended. ; for second jump table, commands with numeric arguments. #define COMMAND_OFF D'0' #define COMMAND_READ_ADC D'1' #define COMMAND_TEMP_SETPOINT D'2' #define COMMAND_PRINT_SETPOINT D'3' #define COMMAND_SET_PWM D'4' #define COMMAND_PRINT_MILLIVOLTS D'5' #define COMMAND_PRINT_TEMPERATURE D'6' #define COMMAND_HCS D'7' #define COMMAND_SET_MINUTES D'8' #define COMMAND_SET_HOURS D'9' #define COMMAND_FAST_MODE D'10' #define COMMAND_SLOW_MODE D'11' #define COMMAND_CALIBRATE_CLOCK D'12' #define COMMAND_PRINT_CLOCK_CALIBRATION D'13' #define COMMAND_OUTSIDE_TEMP_CALIBRATION D'14' #define COMMAND_PRINT_OUTSIDE_TEMP_CALIBRATION D'15' #define COMMAND_INSIDE_TEMP_CALIBRATION D'16' #define COMMAND_PRINT_INSIDE_TEMP_CALIBRATION D'17' #define MODE_OFF D'0' #define MODE_READ_ADC D'1' #define MODE_PRINT_SETPOINT D'2' #define MODE_PRINT_MILLIVOLTS D'3' #define MODE_PRINT_TEMPERATURE D'4' #define MODE_HCS D'5' #define MODE_CALIBRATE_CLOCK D'6' #define MODE_PRINT_CLOCK_CALIBRATION D'7' #define MODE_PRINT_OUTSIDE_TEMP_CALIBRATION D'8' #define MODE_PRINT_INSIDE_TEMP_CALIBRATION D'9' ; ** NOTE: ** ; define config fuses ; IF YOU WANT TO RUN VERIFY, BETTER HAVE COPY PROTECTION OFF ;-) ; Internal OSC, OSC clock output on pin 3 (RA4,AN3,T1G,OSC2,CLKOUT). __CONFIG _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTOSC ; when !MRCLRE is asserted in INTOSC or RC mode, the internal clock oscillator is disabled. ; include PIC register definitions, and some macros PROCESSOR p16f690 include ; include "picmac.h" ; IO pin bit assignment PORTA, PORTB, PORTC ; PORTA ; PORTB ; PORTC HEATER_OUT equ 0 ; PORTC RC0 pin 16 heater control, 0 is active OUTSIDE_TEMP equ 1 ; PORTC RC1 pin 15 AN5 outside temperature LM135 FROST_ALARM equ 2 ; PORTC RC2 pin 14 frost alarm LED, 0 is on INSIDE_TEMP equ 3 ; PORTC RC3 pin 7 AN7 inside temperature LM135 ; equ 4 ; PORTC RC4 pin 6 ; equ 5 ; PORTC RC5 pin 5 ; equ 6 ; PORTC RC6 pin 8 ; equ 7 ; PORTC RC7 pin 9 ; define register file variables ; start at 0x20 hex, 32 dec, total 256 bytes ; 0x20 - 0x7f in bank 0 ; 32 - 127 ; 0x70 - 0x7f in each bank the same ; 112 - 127 ; 0xa0 - 0xef in bank 1 ; 160 - 239 ; 0x120 - 0x16f in bank 2 ; 288 - 376 flags equ D'32' ; flags delay_count1 equ D'33' ; 1ms delay delay_count2 equ D'34' RESULTLO equ D'35' RESULTHI equ D'36' tx_timeout equ D'37' flags1 equ D'38' temp1 equ D'39' count equ D'40' temp_h equ D'41' temp_l equ D'42' temp equ D'43' ; temp for binary to BCD bin equ D'44' H_byte equ D'44' ; bin 16 in for binary to BCD L_byte equ D'45' ; bin 16 in for binary to BCD bcd equ D'46' R0 equ D'46' ; BCD digit 5 right justified R1 equ D'47' ; BCD digit 4 and 3 R2 equ D'48' ; BCD digit 2 and 1 ii equ D'49' timer1_reload_l equ D'50' ; saved in EEPROM clock calibration outside_temp_calibration equ D'51' inside_temp_calibration equ D'52' temp_correction equ D'53' ;pti equ D'50' ;pto equ D'51' ;temp_w equ D'52' ;temp_s equ D'53' ;temp2 equ D'54' status_display_timer equ D'55' command equ D'56' digit_in equ D'57' digit_cnt equ D'58' temp3 equ D'59' value equ D'60' flags2 equ D'61' flags3 equ D'62' pwm_pulse_width equ D'63' analog_select equ D'64' minutes equ D'65' seconds_5 equ D'66' seconds equ D'67' ana0_h equ D'68' ana0_l equ D'69' ana1_h equ D'70' ana1_l equ D'71' ana2_h equ D'72' ana2_l equ D'73' ana3_h equ D'74' ana3_l equ D'75' ana4_h equ D'76' ana4_l equ D'77' ana5_h equ D'78' ana5_l equ D'79' ana6_h equ D'80' ana6_l equ D'81' ana7_h equ D'82' ana7_l equ D'83' ana8_h equ D'84' ana8_l equ D'85' ana9_h equ D'86' ana9_l equ D'87' ana10_h equ D'88' ana10_l equ D'89' ana11_h equ D'90' ana11_l equ D'91' pri_h equ D'92' pri_l equ D'93' pri_cnt equ D'94' fsr_save equ D'95' fsr_save2 equ D'96' mode equ D'97' ad_h equ D'98' ad_l equ D'99' celcius_h equ D'100' celcius_l equ D'101' temp_s_h equ D'102' temp_s_l equ D'103' vref_h equ D'104' ; to be saved in EEPROM vref_l equ D'105' ; to be saved in EEPROM millivolt_h equ D'106' millivolt_l equ D'107' kelvin_h equ D'108' kelvin_l equ D'109' temp_int equ D'110' hours equ D'111' ; SAME IN ALL BANKS 112 - 127 temp_setpoint equ D'112' ; to be saved in EEPROM eeprom_address equ D'113' eeprom_data equ D'114' ACCaLO equ D'115' ACCaHI equ D'116' ACCbLO equ D'117' ACCbHI equ D'118' ACCcLO equ D'119' ACCcHI equ D'120' ACCdLO equ D'121' ACCdHI equ D'122' sign equ D'123' s_pclath equ D'124' ;EXPa equ D'124' ;EXPb equ D'125' offset equ D'125' temp_w equ D'126' temp_s equ D'127' ; define flags FAST_MODE_FLAG equ D'1' ; define flags1 ONE_MINUTE_FLAG equ D'0' NEGATIVE_TEMPERATURE_FLAG equ D'1' FIRST_ZERO_SUPPRESSED_FLAG equ D'2' HAVE_INTERRUPT_FLAG equ D'4' EEPROM_WRITE_ERROR_FLAG equ D'5' TEST_LED_ON_FLAG equ D'6' ONE_SECOND_FLAG equ D'7' ; define flags ; macros to save and restore W and status register in interrupt. save_w_stat macro movwf temp_w swapf STATUS,W clrf STATUS ; extra force bank 0 clears IRP, RP1, RP0 movwf temp_s movfw PCLATH movwf s_pclath endm restore_w_stat macro movfw s_pclath movwf PCLATH swapf temp_s,W movwf STATUS swapf temp_w,F swapf temp_w,W endm ; code start org 0 goto reset_entry org 4 ; interrupt entry point ; 8 MHz internal OSC ; OPTION_REG is 0xff at power up! ; do interrupt processing here save_w_stat ; save W and status banksel 0 ; what interrupt? ; test for timer 1 interrupt test_timer1_interrupt: ; banksel PIR1 ; bank 0 btfss PIR1, TMR1IF goto test_rx_interrupt ; decrement seconds / 5, if 0 reload 5, decrement seconds. bcf flags1, ONE_SECOND_FLAG decfsz seconds_5 goto in_5 bsf flags1, ONE_SECOND_FLAG movlw D'5' movwf seconds_5 ; increment seconds incf seconds ; test 60 seconds movlw D'60' subwf seconds, w btfss STATUS, Z goto in_5 ; one minute #ifdef TEST_LED ; test led toggle btfsc flags1, TEST_LED_ON_FLAG goto test_led_off bcf PORTC, FROST_ALARM ; test LED on bsf flags1, TEST_LED_ON_FLAG goto one_minute test_led_off: bsf PORTC, FROST_ALARM ; test LED off bcf flags1, TEST_LED_ON_FLAG ; end test led toggle #endif ; TEST_LED one_minute: clrf seconds incf minutes bsf flags1, ONE_MINUTE_FLAG ; to be checked and cleared in main ; test 60 minutes movlw D'60' subwf minutes, w btfss STATUS, Z goto in_5 clrf minutes incf hours ; test 24 hours movlw D'24' subwf hours, w btfss STATUS, Z goto in_5 clrf hours in_5: ; reload 65535 - 50000 makes 5Hz = 200mS ; Calibrate clock speed here ; banksel TMR1H ; bank 0 65535 - 50000 = 15535 reload (upcounter int on overflow), makes 60 175 movlw D'60' movwf TMR1H ; movlw D'175' movfw timer1_reload_l movwf TMR1L ; banksel T1CON ; in bank 0 bsf T1CON, T1CKPS1 ; 11 = 1:8 prescaler bsf T1CON, T1CKPS0 ; ; banksel 0 ; goto int_end test_rx_interrupt: ; banksel PIR1 ; btfss PIR1, RCIF ; goto int_end ; read rx status ; test framing error banksel RCSTA btfss RCSTA, FERR goto test_overrun ; framing error goto reset_rx_circuit test_overrun: btfss RCSTA, OERR goto get_rx_char ; overrun error reset_rx_circuit: ; clear error flags bcf RCSTA, FERR bcf RCSTA, OERR ; restart rx bcf RCSTA, CREN bsf RCSTA, CREN ; goto int_end ; discard character ; send Bell audio signal immediatly movlw D'7' ; ring bell call tx_w goto int_end get_rx_char: ; test_serial_port_interrupt btfss PIR1, RCIF ; test if serial port interrupt goto int_end ; have serial char in RCREG ; movfw RCREG ; TEST echo for TEST ; call tx_w movlw D'13' subwf RCREG, W btfsc STATUS, Z goto cr_command movlw 'a' subwf RCREG, W btfsc STATUS, Z goto read_adc_command movlw 'c' subwf RCREG, W btfsc STATUS, Z goto print_temperature_command movlw 'F' subwf RCREG, W btfsc STATUS, Z goto fast_mode_command movlw 'f' subwf RCREG, W btfsc STATUS, Z goto slow_mode_command movlw 'g' subwf RCREG, W btfsc STATUS, Z goto print_clock_calibration_command movlw 'G' subwf RCREG, W btfsc STATUS, Z goto calibrate_clock_command movlw 'H' subwf RCREG, W btfsc STATUS, Z goto hcs_command movlw 'h' subwf RCREG, W btfsc STATUS, Z goto print_help movlw 'I' subwf RCREG, W btfsc STATUS, Z goto inside_temp_calibration_command movlw 'i' subwf RCREG, W btfsc STATUS, Z goto print_inside_temp_calibration movlw 'm' subwf RCREG, W btfsc STATUS, Z goto set_minutes_command movlw 'O' subwf RCREG, W btfsc STATUS, Z goto outside_temp_calibration_command movlw 'o' subwf RCREG, W btfsc STATUS, Z goto print_outside_temp_calibration movlw 'p' subwf RCREG, W btfsc STATUS, Z goto set_pwm_command movlw 's' subwf RCREG, W btfsc STATUS, Z goto print_setpoint_command movlw 't' subwf RCREG, W btfsc STATUS, Z goto temp_setpoint_command movlw 'u' subwf RCREG, W btfsc STATUS, Z goto set_hours_command movlw 'v' subwf RCREG, W btfsc STATUS, Z goto print_millivolts_command movlw '0' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '1' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '2' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '3' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '4' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '5' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '6' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '7' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '8' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '9' subwf RCREG, W btfsc STATUS, Z goto process_digits_command goto int_end cr_command: ; If zero digits were entered, do nothing movlw COMMAND_OFF subwf digit_cnt, W btfsc STATUS, Z ; if zero digits do nothing goto int_end movlw COMMAND_READ_ADC subwf command, W btfsc STATUS, Z goto end_read_adc_command movlw COMMAND_TEMP_SETPOINT subwf command, W btfsc STATUS, Z goto end_temp_setpoint_command movlw COMMAND_SET_PWM subwf command, W btfsc STATUS, Z goto end_pwm_command movlw COMMAND_PRINT_MILLIVOLTS subwf command, W btfsc STATUS, Z goto end_print_millivolts_command movlw COMMAND_PRINT_TEMPERATURE subwf command, W btfsc STATUS, Z goto end_print_temperature_command movlw COMMAND_SET_MINUTES subwf command, W btfsc STATUS, Z goto end_set_minutes_command movlw COMMAND_SET_HOURS subwf command, W btfsc STATUS, Z goto end_set_hours_command movlw COMMAND_CALIBRATE_CLOCK subwf command, W btfsc STATUS, Z goto end_calibrate_clock_command movlw COMMAND_OUTSIDE_TEMP_CALIBRATION subwf command, W btfsc STATUS, Z goto end_outside_temp_calibration_command movlw COMMAND_INSIDE_TEMP_CALIBRATION subwf command, W btfsc STATUS, Z goto end_inside_temp_calibration_command goto int_end ; these commands have no numeric argument print_help: call help_pri goto int_end_clr print_setpoint_command: movlw COMMAND_PRINT_SETPOINT movwf command movlw MODE_PRINT_SETPOINT movwf mode goto int_end_clr hcs_command: movlw COMMAND_HCS movwf command movlw MODE_HCS movwf mode goto int_end_clr slow_mode_command: bcf flags, FAST_MODE_FLAG goto int_end_clr fast_mode_command: bsf flags, FAST_MODE_FLAG goto int_end_clr print_clock_calibration_command: movlw COMMAND_PRINT_CLOCK_CALIBRATION movwf command movlw MODE_PRINT_CLOCK_CALIBRATION movwf mode goto int_end_clr print_inside_temp_calibration: movlw COMMAND_PRINT_INSIDE_TEMP_CALIBRATION movwf command movlw MODE_PRINT_INSIDE_TEMP_CALIBRATION movwf mode goto int_end_clr print_outside_temp_calibration: movlw COMMAND_PRINT_OUTSIDE_TEMP_CALIBRATION movwf command movlw MODE_PRINT_OUTSIDE_TEMP_CALIBRATION movwf mode goto int_end_clr ; these commands have a numeric argument set_pwm_command: movlw COMMAND_SET_PWM movwf command goto command_end read_adc_command: movlw COMMAND_READ_ADC movwf command goto command_end temp_setpoint_command: movlw COMMAND_TEMP_SETPOINT movwf command goto command_end print_millivolts_command: movlw COMMAND_PRINT_MILLIVOLTS movwf command goto command_end print_temperature_command: movlw COMMAND_PRINT_TEMPERATURE movwf command goto command_end set_minutes_command: movlw COMMAND_SET_MINUTES movwf command goto command_end set_hours_command: movlw COMMAND_SET_HOURS movwf command goto command_end calibrate_clock_command: movlw COMMAND_CALIBRATE_CLOCK movwf command goto command_end inside_temp_calibration_command: movlw COMMAND_INSIDE_TEMP_CALIBRATION movwf command goto command_end outside_temp_calibration_command: movlw COMMAND_OUTSIDE_TEMP_CALIBRATION movwf command goto command_end ; final processing of commands with numeric arguments, we have the number in 'value' now. end_pwm_command: movfw value movwf pwm_pulse_width goto command_end_status end_read_adc_command: movfw value movwf analog_select movlw MODE_READ_ADC movwf mode goto command_end_status end_temp_setpoint_command: movfw value movwf temp_setpoint call save_settings goto command_end_status end_print_millivolts_command: movfw value movwf analog_select movlw MODE_PRINT_MILLIVOLTS movwf mode goto command_end_status end_print_temperature_command: movfw value movwf analog_select movlw MODE_PRINT_TEMPERATURE movwf mode goto command_end_status end_set_minutes_command: movfw value movwf minutes goto command_end_status end_set_hours_command: movfw value movwf hours goto command_end_status end_calibrate_clock_command: movfw value movwf timer1_reload_l call save_settings goto command_end_status end_inside_temp_calibration_command: movfw value movwf inside_temp_calibration call save_settings goto command_end_status end_outside_temp_calibration_command: movfw value movwf outside_temp_calibration call save_settings goto command_end_status process_digits_command: movlw D'48' subwf RCREG, W ; digit now in W movwf digit_in ; value = value * 10 ; value += RCREG movfw value ; W = original value ; look, I know about rlf, but we have 4096 words available. addwf value ; value * 2 addwf value ; value * 3 addwf value ; value * 4 addwf value ; value * 5 addwf value ; value * 6 addwf value ; value * 7 addwf value ; value * 8 addwf value ; value * 9 addwf value ; value * 10 movfw digit_in ; input digit in W addwf value ; add W to value incf digit_cnt goto int_end ; end of interrupt routines command_end_status: banksel 0 clrf status_display_timer command_end: ; reset the value, so we can continue for x, y, f, or z with 200ENTER banksel 0 clrf value clrf digit_cnt goto int_end int_end_clr: ; for non numeric commands, no digits expected. banksel 0 clrf status_display_timer clrf command int_end: banksel 0 ; OLD IF COMPARATOR movf CMCON, W ; read CMCON to end mismatch because of comparator output change ; INTCON: GIE PEIE T0IE INTE RABIE T0IF INTF RABIF bcf INTCON, GIE ; bcf INTCON, PEIE bcf INTCON, T0IE bcf INTCON, INTE bcf INTCON, RABIE bcf INTCON, T0IF ; timer 0 overflow interrupt flag bcf INTCON, INTF ; GP2/INT bcf INTCON, RABIF ; port change interrupt flag bit ; PIR1 -- ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF bcf PIR1, ADIF bcf PIR1, RCIF bcf PIR1, TXIF bcf PIR1, SSPIF bcf PIR1, CCP1IF bcf PIR1, TMR2IF bcf PIR1, TMR1IF ; PIR2 OSFIF C2IF C1IF EEIF -- -- -- -- bcf PIR2, OSFIF bcf PIR2, C2IF bcf PIR2, C1IF bcf PIR2, EEIF restore_w_stat ; get back W and status retfie reset_entry: ; set internal OSC speed, the output on pin 3 (OSC2) is this clock / 4. ; SELECT BANK banksel OSCCON ; crlf OSCCON ; all zero ; bcf OSCCON, 7 ; bit 7 not implemented bsf OSCCON, 6 ; bit 6 osc speed 111=8 MHz, 110=4MHz (default), 101=2MHz, 100=1MHz, 011=500kHz, 010=250kHz, 001=125kHz, 000=32kHz (LFINTOSC) bsf OSCCON, 5 ; bit 5 osc speed bsf OSCCON, 4 ; bit 4 osc speed bcf OSCCON, 3 ; bit 3, HTS 0 = internal OSC, 1 = external ; bcf OSCCON, 2 ; bit 2, HTS HFINTOSC status, 1 = stable ; bcf OSCCON, 1 ; bit 1, LTS LFINTOSC status, 1 = stable bcf OSCCON, 0 ; bit 0, SCS 0 = clock source defined by FOSC<2:0> of the CONFIG register, 1 = internal osc used for system clock ; initialize IO output latches banksel 0 ; GPIO bank 0, address 0x05 clrf PORTA ; init all output latches to zero clrf PORTB clrf PORTC ; portb init (4 to 7) banksel 0 CLRF PORTB ; Init PORTB banksel TRISB MOVLW 00h ; Set RB<7:4> as outputs MOVWF TRISB ; banksel 0 ; init async serial out ;12.1.1.6 Asynchronous Transmission Set-up: ;1. Initialize the SPBRGH, SPBRG register pair and ; the BRGH and BRG16 bits to achieve the desired ; baud rate (see Section 12.3 "EUSART Baud ; Rate Generator (BRG)"). ;2. Enable the asynchronous serial port by clearing ; the SYNC bit and setting the SPEN bit. ;3. If 9-bit transmission is desired, set the TX9 con- ; trol bit. A set ninth data bit will indicate that the 8 ; Least Significant data bits are an address when ; the receiver is set for address detection. ;4. Enable the transmission by setting the TXEN ; control bit. This will cause the TXIF interrupt bit ; to be set. ;5. If interrupts are desired, set the TXIE interrupt ; enable bit. An interrupt will occur immediately ; provided that the GIE and PEIE bits of the ; INTCON register are also set. ;6. If 9-bit transmission is selected, the ninth bit ; should be loaded into the TX9D data bit. ;7. Load 8-bit data into the TXREG register. This ; will start the transmission. ; 8 bit async ; SYNC = 0, BRG16 = 0, BRGH=1, at 8MHz: 19231 Baud, 0.16% for SPBRG=25 ; banksel TRISB ; bcf TRISB, TRISB7 ; pin 10 (RB7,TX,CK) output banksel SPBRG movlw D'25' movwf SPBRG ; default 8 bit mode, SPBRGH not used ; banksel SPBRGH banksel BAUDCTL bcf BAUDCTL, BRG16 banksel TXSTA bsf TXSTA, BRGH bcf TXSTA, SYNC banksel RCSTA bsf RCSTA, SPEN banksel TXSTA bsf TXSTA, TXEN ;12.1.2.8 Asynchronous Reception Set-up: ;1. Initialize the SPBRGH, SPBRG register pair and ; the BRGH and BRG16 bits to achieve the ; desired baud rate (see Section 12.3 "EUSART ; Baud Rate Generator (BRG)"). ;2. Enable the serial port by setting the SPEN bit. ; The SYNC bit must be clear for asynchronous ; operation. ;3. If interrupts are desired, set the RCIE interrupt ; enable bit and set the GIE and PEIE bits of the ; INTCON register. ;4. If 9-bit reception is desired, set the RX9 bit. ;5. Enable reception by setting the CREN bit. ;6. The RCIF interrupt flag bit will be set when a ; character is transferred from the receive shift ; register to the receive buffer. An interrupt will be ; generated if the RCIE interrupt enable bit was ; also set. ;7. Read the RCSTA register to get the error flags ; and, if 9-bit data reception is enabled, the ninth ; data bit. ;8. Get the received 8 Least Significant data bits ; from the receive buffer by reading the RCREG ; register. ;9. If an overrun occurred, clear the OERR flag by ; clearing the CREN receiver enable bit. banksel RCSTA bsf RCSTA, CREN ; SYNC and SPEN already done. ; Load 5 Hz ticck movlw D'5' movwf seconds_5 ; clock to 00:00 clrf seconds clrf minutes clrf hours ;This code block configures the ADC ;for polling, Vdd reference, Frc clock ;and AN0 input. ; ;Conversion start & polling for completion ; are included. ; BANKSEL ADCON1 ; MOVLW B'00100000' ; not used, :32, ; bit 7 not used, zero ; bit 6 clock speed ; bit 5 clock speed ; bit 4 clock speed ; bit 3 not used ; bit 2 not used ; bit 1 not used ; bit 0 not used MOVWF ADCON1 ; BANKSEL TRISC ; BSF TRISC, 1 ; Set RC1 as input banksel TRISC ; 1 = input, 0 = output movlw B'11111010' ; io port direction ; bit 7 AN7 outside temperature ; bit 6 not used ; bit 5 ; bit 4 ; bit 3 ; bit 2 RC2 FROST out ; bit 1 AN5 in inside temperature ; bit 0 RC0 HEATER out movwf TRISC ; select which port c pins are analog inputs BANKSEL ANSEL ; default is 1111 1111 bcf ANSEL, 0 ; ASN 0 bcf ANSEL, 1 ; ASN 1 bcf ANSEL, 2 ; ASN 2 bcf ANSEL, 3 ; ASN 3 bcf ANSEL, 4 ; ASN 4 bsf ANSEL, 5 ; ASN 5 pin 15 analog in AN5 outside temperature bcf ANSEL, 6 ; ASN 6 bsf ANSEL, 7 ; ASN 7 pin 7 analog in AN7 outside temperature BANKSEL ANSELH ; default is 1111 bcf ANSELH, 0 ; ASN 8 bcf ANSELH, 1 ; ASN 9 bcf ANSELH, 2; ; ASN 10 bcf ANSELH, 3 ; ASN 11 UART RX ; only lower 4 bits used ; right justify analog result BANKSEL ADCON0 clrf ADCON0 bsf ADCON0, ADFM ; ADFM right justified bcf ADCON0, VCFG ; Vdd reference ; select an analog channel for the ADC bcf ADCON0, 5 ; 0 0101= AN5 bsf ADCON0, 4 ; 1 bcf ADCON0, 3 ; 0 bsf ADCON0, 2 ; 1 ; bit 1 is A/D conversion status bit ; start the ADC bsf ADCON0, ADON ; AD on ; OPTION_REG bank 1, address 0x81 ; !GPPU INTEDG T0CS T0SE PSA PS2 PS1 PS0 ; serial com init banksel OPTION_REG movlw B'00000001' ; prescaler 1 movwf OPTION_REG ; bsf OPTION_REG, NOT_GPPU ; no pullups ; serial in interrupt enable banksel PIE1 clrf PIE1 bsf PIE1, RCIE ; USART receive interrupt enable ; bsf PIE1, ADIE ; AD converter interrupt enable (12F675 only) bsf PIE1, TMR1IE ; timer 1 overflow interrupt enable ; bsf PIE1, CMIE ; comparator interrupt enable ; PIE2: OSFIE C2IE C1IE EEIE -- -- -- -- banksel PIE2 bcf PIE2, EEIE ; EEPROM write complete interrupt enable banksel PORTC ; set ouputs high (not active) bsf PORTC, HEATER_OUT bsf PORTC, FROST_ALARM ; flags to zero clrf flags clrf flags1 ; OPTION_REG bank 1, address 0x81 ; !GPPU INTEDG T0CS T0SE PSA PS2 PS1 PS0 banksel OPTION_REG movlw B'00000001' ; prescaler 1 movwf OPTION_REG ; bsf OPTION_REG, NOT_GPPU ; no pullups ; timer 1 :4 clock = 2MHz, :8 prescaler=250000 Hz, :50000 reload=5Hz interrupt ; T1CON: T1GINV TMR1GE T1CKPS1 T1CKPS0 T1OSCEN T1SYNC TMR1CS TMR1ON banksel T1CON bcf T1CON, T1GINV ; non invert bcf T1CON, TMR1GE ; no gate enable bsf T1CON, T1CKPS1 ; 11 = 1:8 prescaler bsf T1CON, T1CKPS0 ; bcf T1CON, T1OSCEN ; no external clock bcf T1CON, NOT_T1SYNC ; no sync bcf T1CON, TMR1CS ; select :4 internal osc = 2MHz bsf T1CON, TMR1ON ; timer 1 on ; set timer 1 reload 0, and clear TMR1IF as on page 88 pdf movlw D'0' banksel TMR1H ; 50000 reload movwf TMR1H banksel TMR1L movwf TMR1L ; reset timer 1 interrupt flag banksel PIR1 bcf PIR1, TMR1IF banksel 0 ; enable gobal and peripheral interrupt banksel INTCON clrf INTCON bsf INTCON, PEIE ; peripheral interrupts enable ; bsf INTCON, T0IE ; timer 0 overflow interrupt enable ; bsf INTCON, INTE ; GP2/INT interrupt enable ; bsf INTCON, GPIE ; port change interrupt enable bsf INTCON, GIE ; global interrupt enable banksel 0 ; print ID call print_id ; load settings from eeprom 0 banksel 0 call load_settings ; this sets flags1 ; clrf mode movlw MODE_HCS ; HCS by default movwf mode main_loop: banksel 0 movlw MODE_HCS subwf mode, w btfss STATUS, Z goto test_read_adc ; start hcs btfsc flags, FAST_MODE_FLAG goto clear_one_minute_flag btfss flags1, ONE_MINUTE_FLAG goto main_loop ; one_minute clear_one_minute_flag: bcf flags1, ONE_MINUTE_FLAG ; assume we do all im main within one minute ; print time call print_time banksel 0 movlw ' ' call tx_w ; outside temperature processing, if below zero set FROST_ALARM ; say outside movlw 'o' call tx_w movlw 'u' call tx_w movlw 't' call tx_w movlw 's' call tx_w movlw 'i' call tx_w movlw 'd' call tx_w movlw 'e' call tx_w movlw ' ' call tx_w ; read adc 5 movlw D'5' movwf analog_select call read_adc ; value in ad_h ad_l banksel 0 ; print adc 5 call print_adc banksel 0 ; print space movlw ' ' call tx_w ; convert adc5 to millivolts call ad_to_millivolts ; ad_h ad_l to millivilt_h millivolt_l banksel 0 ; convert millivolts to Celcius and Kelvin ; movlw D'17' ; DEFAULT 17 degrees Kelvin low byte from 273, +2 movfw outside_temp_calibration movwf temp_correction call millivolts_to_temp ; millivolt_h millivolt_l to kelvin_h kelvin_l, celcisu_h celcius_l and NEGATIVE_TEMPERATURE_FLAG banksel 0 ; print adc 5 millivolts call print_millivolts banksel 0 ; print space movlw ' ' call tx_w ; print adc5 Celcius call print_celcius ; print space movlw ' ' call tx_w ; call tx_crlf ; compare temp ; if temperature negative then frost alarm btfss flags1, NEGATIVE_TEMPERATURE_FLAG goto no_frost ; frost bcf PORTC, FROST_ALARM goto process_inside_temperature ; WAS compare_temp_to_setpoint no_frost: bsf PORTC, FROST_ALARM process_inside_temperature: ; inside temperature procesing, if below setpoint set HEATER_OUT ; say 'inside ' movlw 'i' call tx_w movlw 'n' call tx_w movlw 's' call tx_w movlw 'i' call tx_w movlw 'd' call tx_w movlw 'e' call tx_w movlw ' ' call tx_w ; read adc 7 movlw D'7' movwf analog_select call read_adc ; value in ad_h ad_l banksel 0 ; print adc 7 call print_adc banksel 0 ; print space movlw ' ' call tx_w ; convert adc7 to millivolts call ad_to_millivolts ; ad_h ad_l to millivilt_h millivolt_l banksel 0 ; convert millivolts to Celcius and Kelvin ; movlw D'17' ; degrees Kelvin low byte from 273 movfw inside_temp_calibration movwf temp_correction call millivolts_to_temp ; millivolt_h millivolt_l to kelvin_h kelvin_l, celcisu_h celcius_l and NEGATIVE_TEMPERATURE_FLAG banksel 0 ; print adc 7 millivolts call print_millivolts banksel 0 ; print space movlw ' ' call tx_w ; print adc7 Celcius call print_celcius ; print space ; movlw ' ' ; call tx_w call tx_crlf compare_temp_to_setpoint: ; add 273 to centigrade setpoint to get kelvin movlw D'0' movwf ACCbHI movfw temp_setpoint movwf ACCbLO movlw D'1' movwf ACCaHI movlw D'17' movwf ACCaLO ; add 273 call D_add ; Double Precision Addition ( ACCb + ACCa -> ACCb ) ; setpoint in kelvin now in ACCb, always positive (minimum 273) ; save in temp movfw ACCbHI movwf temp movfw ACCbLO movwf temp1 ; temperature to ACCa movfw kelvin_h movwf ACCbHI ; get temperature in kelvin movfw kelvin_l movwf ACCbLO ; setpoint to ACCa movfw temp movwf ACCaHI movfw temp1 movwf ACCaLO call D_sub ; Double Precision Subtraction ( ACCb - ACCa -> ACCb ) ; ACCb now temperature - setpoint ; if msb Hi is set then temperature was higher then setpoint, heater on banksel 0 btfss ACCbHI, 7 goto heater_off ; heater on bcf PORTC, HEATER_OUT ; heater on set switch output to zero (hcs heater on) goto main_loop heater_off: bsf PORTC, HEATER_OUT ; heater off ; do not reset mode, we stay in hcs loop by default on power on, or when requested, and only exit if other command requested goto main_loop test_read_adc: movlw MODE_READ_ADC subwf mode, w btfss STATUS, Z goto test_print_temperature call read_adc banksel 0 call print_adc banksel 0 call tx_crlf goto main_end test_print_temperature: banksel 0 movlw MODE_PRINT_TEMPERATURE subwf mode, w btfss STATUS, Z goto test_print_millivolts call read_adc call ad_to_millivolts call millivolts_to_temp call print_celcius call tx_crlf goto main_end test_print_millivolts: banksel 0 movlw MODE_PRINT_MILLIVOLTS subwf mode, w btfss STATUS, Z goto test_setpoint_pri call read_adc call ad_to_millivolts call millivolts_to_temp call print_millivolts call tx_crlf banksel 0 goto main_end test_setpoint_pri: banksel 0 movlw MODE_PRINT_SETPOINT subwf mode, w btfss STATUS, Z goto test_print_clock_calibration call setpoint_pri banksel 0 call tx_crlf goto main_end test_print_clock_calibration: banksel 0 movlw MODE_PRINT_CLOCK_CALIBRATION subwf mode, w btfss STATUS, Z goto test_inside_temp_calibration call clock_calibration_pri banksel 0 call tx_crlf goto main_end test_inside_temp_calibration: banksel 0 movlw MODE_PRINT_INSIDE_TEMP_CALIBRATION subwf mode, w btfss STATUS, Z goto test_outside_temp_calibration call inside_temp_calibration_pri banksel 0 call tx_crlf goto main_end test_outside_temp_calibration: banksel 0 movlw MODE_PRINT_OUTSIDE_TEMP_CALIBRATION subwf mode, w btfss STATUS, Z goto main_loop ; IF NOT, ONLY CLEAR mode if something found. call outside_temp_calibration_pri banksel 0 call tx_crlf goto main_end main_end: banksel 0 clrf mode goto main_loop ; ***************** subroutines ******************** read_adc: ; value for AD channel analog_select to ad_h and ad_l banksel 0 movfw analog_select ; TRISC or other ports must be set for input if used for ADC ; ANSEL and ANSELH must have bits set for analog input ; select an analog channel for the ADC ; set bit<5-2> in ADCONO for the selected input number ; mask out lowest 4 bits andlw D'15' movwf temp3 ; to position ; RLF: carry to LSB, MSB to carry ; clear carry bcf STATUS, C ; x 2 rlf temp3, 1 ; clear carry bcf STATUS, C rlf temp3, 1 ; x 4 ; to ADCON0 movfw temp3 BANKSEL ADCON0 clrf ADCON0 movwf ADCON0 ; select an analog channel for the ADC ; bcf ADCON0, 5 ; 0 0101= AN5 ; bsf ADCON0, 4 ; 1 ; bcf ADCON0, 3 ; 0 ; bsf ADCON0, 2 ; 1 bsf ADCON0, ADFM ; ADFM right justified bit 7 bcf ADCON0, VCFG ; Vdd reference bit 6 ; bit 1 is A/D conversion status bit ; start the ADC bsf ADCON0, ADON ; AD on bit 0 ; sample time delay movlw D'255' movwf delay_count1 adc_sample_delay: decfsz delay_count1, F goto adc_sample_delay ; set ADCON0 GO / !DONE bit to start the conversion BSF ADCON0, GO ; Start conversion bit 1 BTFSC ADCON0, GO ; Is conversion done? GOTO $-1 ; No, test again BANKSEL ADRESH MOVF ADRESH, W ; Read upper 2 bits banksel 0 MOVWF ad_h ; store in GPR space BANKSEL ADRESL MOVF ADRESL, W ; Read lower 8 bits banksel 0 MOVWF ad_l ; Store in GPR space return delay10us: ; delays W * 10 us movlw D'6' ; 10 us movwf delay_count1 delay_loop1: nop nop nop nop nop nop nop nop nop nop decf delay_count1,f btfss STATUS, Z goto delay_loop1 return delay_1ms: ; movlw D'100' ; 100 x 10 us = 1 ms movwf delay_count2 delay_loop2: call delay10us decf delay_count2,f btfss STATUS, Z goto delay_loop2 return ; send byte in W tx_digit_in_w: banksel TXREG addlw '0' ; zero tx_w: movwf TXREG ; call wait_tx_empty ; inline banksel 0 ; make sure we have a timeout movlw D'255' movwf tx_timeout test_tx_empty: decfsz tx_timeout, 1 goto test_txsta goto tx_has_timed_out test_txsta: btfss TXSTA, TRMT goto test_tx_empty tx_has_timed_out: banksel 0 return print_w_ascii_dec: ; prints register W in ASCII decimal bsf flags1, FIRST_ZERO_SUPPRESSED_FLAG movwf temp1 clrf count ; number of hundreds found loop_hundreds: movlw D'100' subwf temp1 btfss STATUS, C ; if no carry flag, no more hundreds, go count tenth goto count10 ; substraction failed incf count goto loop_hundreds count10: movlw D'0' subwf count, W btfsc STATUS, Z goto suppress_first_zero bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG movfw count call tx_digit_in_w ; print hundreds suppress_first_zero: movlw D'100' ; restore temp1 from one substract to many addwf temp1 clrf count ; number of tenth found loop_tenth: movlw D'10' subwf temp1 btfss STATUS, C ; if no carry flag no more tenth, only units left goto count1 incf count goto loop_tenth count1: movlw D'0' subwf count, W btfss STATUS, Z goto print_tenth ; tenth not zero ; tenth zero ; test if zero supression was active in hundreds (first digit) btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG goto print_units ; if first digit was not zero, print this zero print_tenth: movfw count call tx_digit_in_w ; print tenth print_units: movlw D'10' ; restore temp1 from 1 substract to many addwf temp1 ; units movfw temp1 call tx_digit_in_w ; print units return print_16_ascii_dec: ; prints 16 bit value in registers Hi_byte and LO_byte in ASCII decimal call b2bcd ; suppress leading zeros bsf flags1, FIRST_ZERO_SUPPRESSED_FLAG movfw bcd call print_bcd_in_w_decimal_2_digits movfw bcd+1 call print_bcd_in_w_decimal_2_digits movfw bcd+2 call print_bcd_in_w_decimal_2_digits ; if the zero suppress flag is still set we had zero, so print zero btfss flags1, FIRST_ZERO_SUPPRESSED_FLAG return movlw D'0' call tx_digit_in_w return ; Convert 32-bit binary number at into a bcd number ; at . Uses Mike Keitz's procedure for handling bcd ; adjust; Modified Microchip AN526 for 32-bits. b2bcd: bcf STATUS, C movlw D'16' ; 32 for 32-bits movwf ii ; make cycle counter clrf bcd ; clear result area clrf bcd+1 clrf bcd+2 ; clrf bcd+3 ; clrf bcd+4 b2bcd2: movlw bcd ; make pointer movwf FSR movlw 3 ; was 5 movwf count ; Mike's routine: b2bcd3: movlw 0x33 addwf INDF, f ; add to both nybbles btfsc INDF, 3 ; test if low result > 7 andlw 0xf0 ; low result >7 so take the 3 out btfsc INDF, 7 ; test if high result > 7 andlw 0x0f ; high result > 7 so ok subwf INDF, f ; any results <= 7, subtract back incf FSR, f ; point to next decfsz count goto b2bcd3 ; rlf bin+3,f ; get another bit ; rlf bin+2,f rlf bin+1,f rlf bin+0,f ; rlf bcd+4,f ; put it into bcd ; rlf bcd+3,f rlf bcd+2,f rlf bcd+1,f rlf bcd+0,f decfsz ii,f ; all done? goto b2bcd2 ; no, loop movlw 0 movwf bcd return print_bcd_in_w_decimal_2_digits: ; high digit first movwf temp ; save w swapf temp, w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_high_nibble test_lead_zero: ; test if leading zero suppress flag set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG goto low_nibble print_high_nibble: bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG call tx_digit_in_w low_nibble: ; low digit second movfw temp ; get w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_low_nibble ; test if leading zero suppress flag is set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG ; return if n ozero print return print_low_nibble bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG call tx_digit_in_w return tx_crlf: movlw D'13' call tx_w ; CR movlw D'10' ; LF call tx_w return ;id_text: ; DT "panteltje hcs_pic-0.1" ; generates RETLW instructions print_id: movlw 'P' call tx_w movlw 'a' call tx_w movlw 'n' call tx_w movlw 't' call tx_w movlw 'e' call tx_w movlw 'l' call tx_w movlw 't' call tx_w movlw 'j' call tx_w movlw 'e' call tx_w movlw ' ' call tx_w movlw 'h' call tx_w movlw 'c' call tx_w movlw 's' call tx_w movlw '-' call tx_w movlw '0' call tx_w movlw '.' call tx_w movlw '2' call tx_w call tx_crlf return help_pri: movlw 'F' call tx_w movlw 'o' call tx_w movlw 'r' call tx_w movlw ' ' call tx_w movlw 'h' call tx_w movlw 'e' call tx_w movlw 'l' call tx_w movlw 'p' call tx_w movlw ' ' call tx_w movlw 's' call tx_w movlw 'e' call tx_w movlw 'e' call tx_w movlw ' ' call tx_w movlw 'R' call tx_w movlw 'E' call tx_w movlw 'A' call tx_w movlw 'D' call tx_w movlw 'M' call tx_w movlw 'E' call tx_w call tx_crlf return eeprom_write: banksel INTCON bcf INTCON, GIE ; disable interrupts BANKSEL EEADR ; MOVF eeprom_address, W ; MOVWF EEADR ; Data Memory Address to write MOVF eeprom_data, W ; MOVWF EEDAT ; Data Memory Value to write BANKSEL EECON1 ; BCF EECON1, EEPGD ; Point to DATA memory BSF EECON1, WREN ; Enable writes BCF INTCON, GIE ; Disable INTs. BTFSC INTCON, GIE ; SEE AN576 GOTO $-2 MOVLW 0x55 ; MOVWF EECON2 ; Write 55h MOVLW 0xAA ; MOVWF EECON2 ; Write AAh BSF EECON1, WR ; Set WR bit to begin write BSF INTCON, GIE ; Enable INTs. eeprom_poll_write_complete_loop: banksel EECON1 btfsc EECON1, WR goto eeprom_poll_write_complete_loop BCF EECON1, WREN ; Disable writes BANKSEL 0 ; Bank 0 call eeprom_verify return eeprom_verify: ; data at EEDADR against eeprom_data call eeprom_read ; EEDATA in w banksel 0 bcf flags1, EEPROM_WRITE_ERROR_FLAG movfw eeprom_data banksel EEDATA subwf EEDATA, W ; compare to requested btfss STATUS, Z ; skip if no error goto eeprom_write_error banksel 0 return eeprom_write_error: call eeprom_report banksel 0 bsf flags1, EEPROM_WRITE_ERROR_FLAG return eeprom_report: ; eeprom write result banksel 0 movlw 'e' call tx_w movlw 'e' call tx_w movlw 'p' call tx_w movlw 'r' call tx_w movlw 'o' call tx_w movlw 'm' call tx_w movlw ' ' call tx_w movlw 'w' call tx_w movlw 'r' call tx_w movlw 'i' call tx_w movlw 't' call tx_w movlw 'e' call tx_w movlw ' ' call tx_w movlw 'r' call tx_w movlw 'e' call tx_w movlw 's' call tx_w movlw 'u' call tx_w movlw 'l' call tx_w movlw 't' call tx_w movlw ' ' ; address=nnn call tx_w movlw 'a' call tx_w movlw 'd' call tx_w movlw 'd' call tx_w movlw 'r' call tx_w movlw 'e' call tx_w movlw 's' call tx_w movlw 's' call tx_w movlw '=' call tx_w movfw eeprom_address call print_w_ascii_dec movlw ' ' call tx_w ; original=nnn movlw 'o' call tx_w movlw 'r' call tx_w movlw 'i' call tx_w movlw 'g' call tx_w movlw 'i' call tx_w movlw 'n' call tx_w movlw 'a' call tx_w movlw 'l' call tx_w movlw '=' call tx_w movfw eeprom_data call print_w_ascii_dec movlw ' ' ; read=nnn call tx_w movlw 'r' call tx_w movlw 'e' call tx_w movlw 'a' call tx_w movlw 'd' call tx_w movlw '=' call tx_w ; read eeprom call eeprom_read banksel 0 call print_w_ascii_dec call tx_crlf return eeprom_read: ; address in eeprom_address, returns data in W banksel 0 movfw eeprom_address banksel EEADR movwf EEADR banksel EECON1 bcf EECON1, EEPGD ; access data memory bsf EECON1, RD banksel EEDATA movfw EEDATA ; return data in w return save_settings: banksel 0 ; temp_setpoint movlw D'0' movwf eeprom_address ; 0 movfw temp_setpoint movwf eeprom_data call eeprom_write ; clock_calibration banksel 0 incf eeprom_address ; 1 movfw timer1_reload_l movwf eeprom_data call eeprom_write ; inside temp calibration banksel 0 incf eeprom_address ; 2 movfw inside_temp_calibration movwf eeprom_data call eeprom_write ; outside temp calibration banksel 0 incf eeprom_address ; 3 movfw outside_temp_calibration movwf eeprom_data call eeprom_write return load_settings: banksel 0 ; temp setpoint movlw D'0' movwf eeprom_address ; 0 call eeprom_read ; returns data in w banksel 0 movwf temp_setpoint ; clock calibration incf eeprom_address ; 1 call eeprom_read ; returns data in w banksel 0 movwf timer1_reload_l ; inside temp calibration incf eeprom_address ; 2 call eeprom_read ; returns data in w banksel 0 movwf inside_temp_calibration ; outside temp calibration incf eeprom_address ; 3 call eeprom_read ; returns data in w banksel 0 movwf outside_temp_calibration return setpoint_pri: ; print temperature setpoint banksel 0 movfw temp_setpoint call print_w_ascii_dec call tx_crlf return clock_calibration_pri: ; print timer1_reload_l banksel 0 movfw timer1_reload_l call print_w_ascii_dec call tx_crlf return inside_temp_calibration_pri: ; print inside_temp_calibration banksel 0 movfw inside_temp_calibration call print_w_ascii_dec call tx_crlf return outside_temp_calibration_pri: ; print outside_temp_calibration banksel 0 movfw outside_temp_calibration call print_w_ascii_dec call tx_crlf return neg_A comf ACCaLO, F ; negate ACCa ( -ACCa -> ACCa ) incf ACCaLO, F btfsc STATUS, Z decf ACCaHI, F comf ACCaHI, F retlw 0 ; Double Precision Subtraction ( ACCb - ACCa -> ACCb ) D_sub ; call neg_A ; At first negate ACCa; Then add ; inline comf ACCaLO, F ; negate ACCa ( -ACCa -> ACCa ) incf ACCaLO, F btfsc STATUS, Z decf ACCaHI, F comf ACCaHI, F ; Double Precision Addition ( ACCb + ACCa -> ACCb ) D_add movf ACCaLO, W addwf ACCbLO, F ; add lsb btfsc STATUS, C ; add in carry incf ACCbHI, F movf ACCaHI, W addwf ACCbHI, F ; add msb retlw 0 ; division macro ; divMac MACRO LOCAL NOCHK LOCAL NOGO ; bcf STATUS, C rlf ACCdLO, F rlf ACCdHI, F rlf ACCcLO, F rlf ACCcHI, F movf ACCaHI, W subwf ACCcHI, W ;check if a>c btfss STATUS, Z goto NOCHK movf ACCaLO, W subwf ACCcLO, W ;if msb equal then check lsb NOCHK btfss STATUS,C ;carry set if c>a goto NOGO movf ACCaLO, W ;c-a into c subwf ACCcLO, F btfss STATUS, C decf ACCcHI, F movf ACCaHI, W subwf ACCcHI, F bsf STATUS, C ;shift a 1 into b (result) NOGO rlf ACCbLO, F rlf ACCbHI, F ; ENDM ; Double Precision Divide ( 16/16 -> 16 ) ; ; ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output ; with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO). ; ; NOTE : Before calling this routine, the user should make sure that ; the Numerator(ACCb) is greater than Denominator(ACCa). If ; the case is not true, the user should scale either Numerator ; or Denominator or both such that Numerator is greater than ; the Denominator. ; ; setup movlw .16 ; for 16 shifts movwf temp movf ACCbHI, W ; move ACCb to ACCd movwf ACCdHI movf ACCbLO, W movwf ACCdLO clrf ACCbHI clrf ACCbLO retlw 0 ; D_divF ; IF SIGNED CALL S_SIGN ENDIF ; call setup clrf ACCcHI clrf ACCcLO ; ; use the divMac macro 16 times ; divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac ; IF SIGNED btfss sign,MSB ; check sign if negative retlw 0 goto neg_B ; negate ACCa ( -ACCa -> ACCa ) ELSE retlw 0 ENDIF ; Assemble this section only if Signed Arithmetic Needed ; IF SIGNED ; S_SIGN movf ACCaHI,W xorwf ACCbHI,W movwf sign btfss ACCbHI,MSB ; if MSB set go & negate ACCb goto chek_A ; comf ACCbLO ; negate ACCb incf ACCbLO btfsc STATUS,Z decf ACCbHI comf ACCbHI ; chek_A btfss ACCaHI,MSB ; if MSB set go & negate ACCa retlw 0 goto neg_A ; ENDIF ; ; Double Precision Multiply ( 16x16 -> 32 ) ; ( ACCb*ACCa -> ACCb,ACCc ) : 32 bit output with high word ; in ACCb ( ACCbHI,ACCbLO ) and low word in ACCc ( ACCcHI,ACCcLO ). ; D_mpyS ;results in ACCb(16 msb's) and ACCc(16 lsb's) ; IF SIGNED CALL S_SIGN ENDIF ; call setup mloop rrf ACCdHI, F ;rotate d right rrf ACCdLO, F btfsc STATUS,C ;need to add? call D_add rrf ACCbHI, F rrf ACCbLO, F rrf ACCcHI, F rrf ACCcLO, F decfsz temp, F ;loop until all bits checked goto mloop ; IF SIGNED btfss sign,MSB retlw 0 comf ACCcLO, F ; negate ACCa ( -ACCa -> ACCa ) incf ACCcLO, F btfsc STATUS,Z decf ACCcHI, F comf ACCcHI, F btfsc STATUS,Z neg_B comf ACCbLO, F ; negate ACCb incf ACCbLO, F btfsc STATUS,Z decf ACCbHI, F comf ACCbHI, F retlw 0 ELSE retlw 0 ENDIF #ifdef USE_BINARY_FLOATS ; multiplication macro ; mulMac MACRO LOCAL NO_ADD ; rrf ACCdHI, F ; rotate d right rrf ACCdLO, F btfss STATUS, C ; need to add? goto NO_ADD ; no addition necessary movf ACCaLO, W ; Addition ( ACCb + ACCa -> ACCb ) addwf ACCbLO, F ; add lsb btfsc STATUS, C ; add in carry incf ACCbHI, F movf ACCaHI, W addwf ACCbHI, F ; add msb NO_ADD rrf ACCbHI, F rrf ACCbLO, F rrf ACCcHI, F rrf ACCcLO, F ; ENDM ; Double Precision Multiply ( 16x16 -> 32 ) ; ( ACCb*ACCa -> ACCb,ACCc ) : 32 bit output with high word ; in ACCb ( ACCbHI,ACCbLO ) and low word in ACCc ( ACCcHI,ACCcLO ). ; D_mpyF ;results in ACCb(16 msb's) and ACCc(16 lsb's) ; IF SIGNED CALL S_SIGN ENDIF ; call setup ; ; use the mulMac macro 16 times ; mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac mulMac ; IF SIGNED btfss sign,MSB retlw 0 comf ACCcLO ; negate ACCa ( -ACCa -> ACCa ) incf ACCcLO btfsc STATUS,Z decf ACCcHI comf ACCcHI btfsc STATUS,Z neg_B comf ACCbLO ; negate ACCb incf ACCbLO btfsc STATUS,Z decf ACCbHI comf ACCbHI retlw 0 ELSE retlw 0 ENDIF IF SIGNED ; S_SIGN movf ACCaHI,W xorwf ACCbHI,W movwf sign btfss ACCbHI,MSB ; if MSB set go & negate ACCb goto chek_A ; comf ACCbLO ; negate ACCb incf ACCbLO btfsc STATUS,Z decf ACCbHI comf ACCbHI ; chek_A btfss ACCaHI,MSB ; if MSB set go & negate ACCa retlw 0 goto neg_A ; ENDIF ; ; Floating Point Subtraction ( ACCb - ACCa -> ACCb ) ; F_sub call neg_A ; At first negate ACCa; Then add ; F_add movf EXPa,W ; scale mantissas subwf EXPb,W ; find the greater exponent btfsc STATUS,Z goto padd ; exponents are equal btfsc STATUS,C ; call F_swap ; if A > B then swap ( A<->B ) movf EXPa,W subwf EXPb, F scloop call shftSR incfsz EXPb, F goto scloop movf EXPa,W movwf EXPb padd movf ACCaHI,W iorwf ACCbHI,W movwf sign call D_add ; compute double precision integer add btfss sign,MSB btfss ACCbHI,MSB retlw 0 bcf STATUS,C incf EXPb, F goto shftR shftSR bcf STATUS,C btfsc ACCbHI,MSB bsf STATUS,C ; set carry if < 0 shftR rrf ACCbHI, F rrf ACCbLO, F retlw 0 ; shftSL bcf STATUS,C ; if Mode16 rlf ACCcLO, F rlf ACCcHI, F endif ; rlf ACCbLO, F rlf ACCbHI, F bcf ACCbHI,MSB btfsc STATUS,C bsf ACCbHI,MSB retlw 0 ; Binary Floating Point Multiplication : ; ACCb(16 bits)EXP(b) * ACCa(16 bits)EXPa -> ACCb(16 bits)EXPb ; F_mpy call S_SIGN call setup mloop bcf STATUS,C ; clear carry bit ?????????? rrf ACCdHI, F ;rotate d right rrf ACCdLO, F btfsc STATUS,C ;need to add? call D_add rrf ACCbHI, F rrf ACCbLO, F rrf ACCcHI, F rrf ACCcLO, F decfsz temp, F ;loop until all bits checked goto mloop ; movf EXPa,W addwf EXPb, F ; IF Mode16 movf ACCbHI, F btfss STATUS,Z goto finup ; if ACCbHI != 0 movf ACCbLO, F btfss STATUS,Z goto Shft08 ; if ACCbLO != 0 && ACCbHI == 0 ; movf ACCcHI,W movwf ACCbHI ; if ACCb == 0, then move ACCc to ACCb movf ACCcLO,W movwf ACCbLO movlw .16 addwf EXPb, F goto finup ; Shft08 movf ACCbLO,W movwf ACCbHI movf ACCcHI,W movwf ACCbLO movlw .8 addwf EXPb, F ; ENDIF ; matching endif for IF Mode16 ; finup btfss sign,MSB goto F_norm ; decf ACCcLO, F ; negate ACCc comf ACCcLO, F btfsc STATUS,Z decf ACCcHI, F comf ACCcHI, F btfsc STATUS,Z ; neg_B decf ACCbLO, F ; negate ACCb comf ACCbLO, F btfsc STATUS,Z decf ACCbHI, F comf ACCbHI, F ; goto F_norm ; Floating Point Division : ; ACCb(16 bits)EXP(b) / ACCa(16 bits)EXPa -> ACCb(16 bits)EXPb , with ; remainder in ACCc ( 16 bits ). ; F_div call S_SIGN call setup clrf ACCcHI clrf ACCcLO dloop bcf STATUS,C ;???????? rlf ACCdLO, F rlf ACCdHI, F rlf ACCcLO, F rlf ACCcHI, F movf ACCaHI,W ; check if ACCa > ACCb subwf ACCcHI,W btfss STATUS,Z goto nochk movf ACCaLO,W subwf ACCcLO,W ;if msb equal then check lsb nochk btfss STATUS,C ;carry set if c>a goto nogo movf ACCaLO,W ;c-a into c subwf ACCcLO, F btfss STATUS,C decf ACCcHI, F movf ACCaHI,W subwf ACCcHI, F bsf STATUS,C ;shift a 1 into b (result) nogo rlf ACCbLO, F rlf ACCbHI, F decfsz temp, F ;loop untill all bits checked goto dloop movf EXPa,W subwf EXPb, F btfss sign,MSB goto F_norm decf ACCbLO, F ; negate ACCb comf ACCbLO, F btfsc STATUS,Z decf ACCbHI, F comf ACCbHI, F goto F_norm S_SIGN movf ACCaHI,W xorwf ACCbHI,W movwf sign btfss ACCbHI,MSB ; if MSB set go & negate ACCb goto chek_A ; comf ACCbLO, F ; negate ACCb incf ACCbLO, F btfsc STATUS,Z decf ACCbHI, F comf ACCbHI, F ; chek_A btfss ACCaHI,MSB ; if MSB set go & negate ACCa retlw 0 goto neg_A ; Normalize Routine ; Normalizes ACCb for use in floating point calculations. ; Call this routine as often as possible to minimize the loss ; of precission. This routine normalizes ACCb so that the ; mantissa is maximized and the exponent minimized. ; ; ; F_norm movf ACCbHI, F btfss STATUS,Z goto C_norm movf ACCbLO, F btfsc STATUS,Z retlw 0 C_norm btfsc ACCbHI,6 retlw 0 call shftSL decf EXPb, F goto C_norm ; Swap ACCa & ACCb [ (ACCa,EXPa) <--> (ACCb,EXPb) ] ; F_swap movf ACCaHI,W movwf temp movf ACCbHI,W ;ACCaHI <--> ACCbHI movwf ACCaHI movf temp,W movwf ACCbHI ; movf ACCaLO,W movwf temp movf ACCbLO,W ;ACCaLO <--> ACCbLO movwf ACCaLO movf temp,W movwf ACCbLO ; movf EXPa,W movwf temp movf EXPb,W ;EXPa <--> EXPb movwf EXPa movf temp,W movwf EXPb ; retlw 0 ; #endif ; USE_BINARY_FLOATS millivolts_to_temp: ; converts millivolts in millivolt_h and millivolt_l to temperature in degrees Centigrade in celcius_h and celcius_l, and Kelvin in kelvin_h and kelvin_l, ; sets NEGATIVE_TEMPERATURE_FLAG, assuming LM135 kelvin sensors. ; math: ; temp Celcius = 25 + 100 x (voltage - 2.982) (National handbook LM135) (range -273.2 to 226.8) signed 16 bit ; temp Kelvin = celcius + 273 ; Celcius = 25 + ( (millivolts - 2982) / 10 ) ; LM135: temperature coefficient of 10mV/K. The nominal output voltage is therefore 2.73V at 0°C, and 3.73V at 100°C. ; so 0 K is 0 mV, 0 C = 273 K = 273 x 10 = 2730 mV ; so Kelvin is mV / 10 movfw millivolt_h movwf ACCbHI movfw millivolt_l ; millivolts in ACCb movwf ACCbLO movlw D'0' movwf ACCaHI movlw D'10' ; 10 in ACCa movwf ACCaLO call D_divF ; ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output ; Kelvin = mV / 10 in ACCb ; millivolts - 2982 in ACCb banksel 0 movfw ACCbHI movwf kelvin_h movfw ACCbLO movwf kelvin_l ; Celcius = Kelvin - 273 movlw D'1' movwf ACCaHI ; movlw D'17' movfw temp_correction movwf ACCaLO ; 273 in ACCa call D_sub ; Double Precision Subtraction ( ACCb - ACCa -> ACCb ) banksel 0 movfw ACCbHI movwf celcius_h movfw ACCbLO movwf celcius_l bcf flags1, NEGATIVE_TEMPERATURE_FLAG ; if msb Hi is set, then temperature is negative. banksel 0 btfss ACCbHI, 7 return ; temp negative, subtract the other way around: ; -Celcius = 273 - Kelvin bsf flags1, NEGATIVE_TEMPERATURE_FLAG movfw kelvin_h movwf ACCaHI movfw kelvin_l movwf ACCaLO movlw D'1' movwf ACCbHI ; movlw D'17' movfw temp_correction movwf ACCbLO ; 273 in ACCb call D_sub ; ACCb - ACCa -> ACCb banksel 0 movfw ACCbHI movwf celcius_h movfw ACCbLO movwf celcius_l return print_celcius: banksel 0 btfss flags1, NEGATIVE_TEMPERATURE_FLAG goto print_celcius_positive ; negative ; print '-' sign movlw '-' call tx_w print_celcius_positive: movfw celcius_h movwf bin movfw celcius_l movwf bin+1 call print_16_ascii_dec banksel 0 movlw '°' call tx_w movlw 'C' call tx_w return print_millivolts: banksel 0 movfw millivolt_h movwf bin movfw millivolt_l movwf bin+1 call print_16_ascii_dec banksel 0 movlw 'm' call tx_w movlw 'V' call tx_w return print_adc: banksel 0 movfw ad_h movwf bin movfw ad_l movwf bin+1 call print_16_ascii_dec banksel 0 return ad_to_millivolts: ; converts AD reading in ad_h and ad_l to millivolts in volts_h and volts_l. ; math: ; Vref = 5070 mV ; voltage = (AD / 1023) x Vref (range 0 to Vref) use signed 16 bit ; voltage = AD * (5.07 / 1023) = AD * 4.956 mV is aprox AD * 5 mV ; AD * 49 / 10 ; Vref in milli volts movlw VREF_H ; D'19' movwf ACCaHI movlw VREF_L ; D'206' ; Vref in mV, 5070 movwf ACCaLO movfw ad_h movwf ACCbHI movfw ad_l movwf ACCbLO call D_mpyS ; Double Precision Multiply ( 16 x 16 -> 32 ( ACCb * ACCa -> ACCb, ACCc ) : 32 bit output with high word, results in ACCb(16 msb's) and ACCc(16 lsb's) ; AD * 5000 in ACCb - ACCc range 0 to 5115000 for 5V Vref (32 bits) ; now divide by 1023 to get mV ; if we divide by 1024 we can use simple shifts and throw low bits away. ; sr 1 = :2, sr 2 = :4, sr 3 = :8, sr 4 = :16, sr 5 = :32, sr 6 = :64 , sr 7 = :128 , sr 8 = :256, sr 9 = :512, sh 10 = :1024 ; 1024 makes 1 byte plus 2 bits. ; result ACCbHI, ACCbLO, ACCcHI, ACCcLO ; after 1 byte shift: ; result ACCbHI, ACCbLO, ACCcHI ; we know never more then about 5000, this fits in 2 bytes, throw away ACCbHI. ; now 16 bits divide by 4. movfw ACCbLO movwf ACCbHI ; overwrites ACCbHI movfw ACCcHI movwf ACCbLO ; divid by 4 movlw D'0' movwf ACCaHI movlw D'4' movwf ACCaLO call D_divF ; Double Precision Divide ( 16/16 -> 16 ) ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO). ; milli volts now in ACCb banksel 0 movfw ACCbHI movwf bin movwf millivolt_h movfw ACCbLO movwf bin+1 movwf millivolt_l return print_time: movfw hours call print_w_ascii_dec movlw ':' call tx_w movfw minutes call print_w_ascii_dec return end