; 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 io-0.1 ; copyright Jan Panteltje 2007-always ; integer math copyright Microchip, some other routines copyright others, my part is GPL. ; ; ; This is analog and digital IO for control via RS232, for example from a PC. ; Uses 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 ; annENTER displays AD channel nn count ; vnnENTER displays AD channel nn converted to voltage ; GnnnENTER sets clock calibration, timer1 reload low byte, use 175 for nominal, is saved in EEPROM ; g displays clock calibration (timer1 reload low byte) ; at first power up (directly after programming) the EEPROM is set with the default value for G(175). ; h help message tells you to read README, there is no README yet :-) ; pnnnENTER sets PWM output value. ; snENTER sets output n ; cnENTER clears output n ; inENTER reads input n ; ; 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 ; Command list: ; analog in: ; a0ENTER AN0 pin 19 ; a1ENTER AN1 pin 18 ; a2ENTER AN2 pin 17 ; a3ENTER AN3 pin 3 ; a5ENTER AN5 pin 15 ; a7ENTER AN7 pin 7 ; a8ENTER AN8 pin 8 ; a9ENTER AN9 pin 9 ; ; digital in: ; i0ENTER RA3 pin 4 ; i1ENTER RA5 pin 2 ; ; ; digital out: ; S0ENTER RB4 pin 13 ; C0ENTER RB4 pin 13 ; ; s1ENTER B6 pin 11 ; c1ENTER B6 pin 11 ; ; s2ENTER C0 pin 16 ; c2ENTER C0 pin 16 ; ; s3ENTER C2 pin 14 ; c3ENTER C2 pin 14 ; ; s4ENTER C4 pin 6 ; c4ENTER C4 pin 6 ; ; ; PWM out: ; p100ENTER CCP1 pin 5 TRUE equ 1 FALSE equ 0 MSB equ 7 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. ; for commands with numeric arguments. #define COMMAND_OFF D'0' #define COMMAND_READ_ADC D'1' #define COMMAND_PRINT_IIME D'2' #define COMMAND_SET_PWM D'4' #define COMMAND_PRINT_MILLIVOLTS D'5' #define COMMAND_SET_BIT D'6' #define COMMAND_CLEAR_BIT D'7' #define COMMAND_SET_MINUTES D'8' #define COMMAND_SET_HOURS D'9' #define COMMAND_CALIBRATE_CLOCK D'10' #define COMMAND_PRINT_CLOCK_CALIBRATION D'11' #define COMMAND_GET_BIT D'12' #define MODE_OFF D'0' #define MODE_READ_ADC D'1' #define MODE_ADJUST_PWM D'2' #define MODE_PRINT_MILLIVOLTS D'3' #define MODE_SET_BIT D'4' #define MODE_CLEAR_BIT D'5' #define MODE_CALIBRATE_CLOCK D'6' #define MODE_PRINT_CLOCK_CALIBRATION D'7' #define MODE_PRINT_TIME D'8' #define MODE_GET_BIT 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 ; Internal OSC, pin 3 (RA4,AN3,T1G,OSC2,CLKOUT) available for IO. __CONFIG _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTOSCIO ; 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 ; How about 8 analog in, 5 binary out, 2 binary in, 1 PWM out, TX, RX, ground, +Vdd, makes 20 pins? ; IO pin bit assignment PORTA, PORTB, PORTC ; RA and RB have change interrupt ; PORTA 6 bit wide ;RA0 pin 19 AN0 ICSPDAT I? analog input ;RA1 pin 18 AN1 ICSPCLK I? analog input ;RA2 pin 17 AN2 Vpp analog input ;RA3 pin 4 MCLR I input change interrupt ;RA4 pin 3 AN3 CLOCK OUT IN USE analog input ;RA5 pin 2 I? input change interrupt ;RA6 ;RA7 ; PORTB 4 bit wide ;RB4 pin 13 AN10 output ;RB5 pin 12 AN11 RX ;RB6 pin 11 output ;RB7 pin 10 TX ; PORTC 8 bit wide ; TRISA inputs: RA0, RA1, RA2, RA3, RA4, RA5 ; TRISA output: none ; TRISB inputs: RB5 ; TRISB outputs: RB4, RB6, RB7 ; TRISC inputs: RC1, RC3, RC5, RC6, RC7 ; TRISC outputs: RC0, RC2, RC4, RC5 ; Analog inputs: AN0, AN1, AN2, AN3, AN5, AN7, AN8, AN9 ; PWM outputs: RC5 ; Digital outputs: RB4, RB6, RC0, RC2, RC4 ; Digital inputs: RA3, RA5 ; 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' bit_number 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' 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 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' offset equ D'125' temp_w equ D'126' temp_s equ D'127' ; define flags1 ONE_MINUTE_FLAG equ D'0' FIRST_ZERO_SUPPRESSED_FLAG equ D'2' HAVE_INTERRUPT_FLAG equ D'4' EEPROM_WRITE_ERROR_FLAG equ D'5' 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: 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 clear_bit_command ; movlw 'E' ; subwf RCREG, W ; btfsc STATUS, Z ; goto mark_eeprom_erased_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 print_help movlw 'i' subwf RCREG, W btfsc STATUS, Z goto get_bit_command movlw 'm' subwf RCREG, W btfsc STATUS, Z goto set_minutes_command movlw 'p' subwf RCREG, W btfsc STATUS, Z goto set_pwm_command movlw 's' subwf RCREG, W btfsc STATUS, Z goto set_bit_command movlw 't' subwf RCREG, W btfsc STATUS, Z goto print_time_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_SET_PWM subwf command, W btfsc STATUS, Z goto end_set_pwm_command movlw COMMAND_PRINT_MILLIVOLTS subwf command, W btfsc STATUS, Z goto end_print_millivolts_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_GET_BIT subwf command, W btfsc STATUS, Z goto end_get_bit_command movlw COMMAND_SET_BIT subwf command, W btfsc STATUS, Z goto end_set_bit_command movlw COMMAND_CLEAR_BIT subwf command, W btfsc STATUS, Z goto end_clear_bit_command movlw COMMAND_CALIBRATE_CLOCK subwf command, W btfsc STATUS, Z goto end_calibrate_clock_command goto int_end ; these commands have no numeric argument print_help: call help_pri 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_time_command: movlw COMMAND_PRINT_IIME movwf command movlw MODE_PRINT_TIME movwf mode goto int_end_clr ;mark_eeprom_erased_command: ; banksel 0 ; movlw D'255' ; movwf eeprom_address ; movlw D'255' ; movwf eeprom_data ; call eeprom_write ; 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 print_millivolts_command: movlw COMMAND_PRINT_MILLIVOLTS 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 set_bit_command: movlw COMMAND_SET_BIT movwf command goto command_end clear_bit_command: movlw COMMAND_CLEAR_BIT movwf command goto command_end get_bit_command: movlw COMMAND_GET_BIT movwf command goto command_end ; final processing of commands with numeric arguments, we have the number in 'value' now. end_set_pwm_command: movfw value movwf pwm_pulse_width movlw MODE_ADJUST_PWM movwf mode goto command_end_status end_read_adc_command: movfw value movwf analog_select movlw MODE_READ_ADC movwf mode goto command_end_status end_print_millivolts_command: movfw value movwf analog_select movlw MODE_PRINT_MILLIVOLTS 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 end_set_bit_command: movfw value movwf bit_number movlw MODE_SET_BIT movwf mode goto command_end_status end_clear_bit_command: movfw value movwf bit_number movlw MODE_CLEAR_BIT movwf mode goto command_end_status end_get_bit_command: movfw value movwf bit_number movlw MODE_GET_BIT movwf mode 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 ; org 600 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 ; load settings from eeprom 0 banksel 0 ; fake erased eeprom ; movlw D'255' ; movwf eeprom_address ; movlw D'255' ; movwf eeprom_data ; call eeprom_write ; call load_settings ; this sets flags1 ; 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. banksel 0 ; Load 5 Hz ticck movlw D'5' movwf seconds_5 ; clock to 00:00 clrf seconds clrf minutes clrf hours ; init PWM call pwm_init ;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 ; TRISA inputs: RA0, RA1, RA2, RA3, RA4, RA5 ; TRISA output: none ; TRISB inputs: RB5 ; TRISB outputs: RB4, RB6, RB7 ; TRISC inputs: RC1, RC3, RC5, RC6, RC7 ; TRISC outputs: RC0, RC2, RC4, RC5 ; Analog inputs: AN0, AN1, AN2, AN3, AN5, AN7, AN8, AN9 ; PWM outputs: RC5 ; Digital outputs: RB4, RB6, RC0, RC2, RC4 ; Digital inputs: RA3, RA5 banksel TRISA movlw B'111111' movwf TRISA banksel TRISB movlw B'1000' movwf TRISB banksel TRISC ; 1 = input, 0 = output movlw B'11001010' ; io port direction ; bit 7 AN7 analog in ; bit 6 RC6 analog in ; bit 5 RC5 CCP1 PWM out ; bit 4 RC4 digital out ; bit 3 RC3 analog in ; bit 2 RC2 digtal out ; bit 1 AN5 analog in ; bit 0 RC0 digital out movwf TRISC ; select which port c pins are analog inputs ; Analog inputs: AN0, AN1, AN2, AN3, AN5, AN7, AN8, AN9 BANKSEL ANSEL ; default is 1111 1111 bsf ANSEL, 0 ; ASN 0 bsf ANSEL, 1 ; ASN 1 bsf ANSEL, 2 ; ASN 2 bsf ANSEL, 3 ; ASN 3 bcf ANSEL, 4 ; ASN 4 bsf ANSEL, 5 ; ASN 5 pin 15 analog in AN5 bcf ANSEL, 6 ; ASN 6 bsf ANSEL, 7 ; ASN 7 pin 7 analog in AN7 BANKSEL ANSELH ; default is 1111 bsf ANSELH, 0 ; ASN 8 bsf 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 ; OPTION_REG: RABPU INTEDG T0CS T0SE PSA PS2 PS1 PS0 banksel OPTION_REG ; PSA: 1= prescaler to WDT, 0 = prescaler to TIMER0 movlw B'00000001' ; prescaler 1 ; movlw B'00001111' ; watchdog prescaler 128 movwf OPTION_REG ; bsf OPTION_REG, NOT_GPPU ; no pullups ; watchdog prescaler 128 111 bit<2-0> ; bsf OPTION_REG, PS2 ; bsf OPTION_REG, PS1 ; bsf OPTION_REG, PS0 ; Watchdog prescaler 128 ; from pdf page 84 ; BANKSEL TMR0 ; ; CLRWDT ;Clear WDT ; CLRF TMR0 ;Clear TMR0 and prescaler ; BANKSEL OPTION_REG ; ; BSF OPTION_REG, PSA ;Select WDT ; CLRWDT ; ; MOVLW b'11111000' ;Mask prescaler ; ANDWF OPTION_REG, W ; bits ; IORLW b'00000111' ;Set WDT prescaler 1:128 ;; IORLW b'00000101' ;Set WDT prescaler ; MOVWF OPTION_REG ; to 1:32 ; 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 ; set binary ouputs high (not active) banksel PORTB bsf PORTB, 4 bsf PORTB, 6 bsf PORTB, 7 banksel PORTC bsf PORTC, 0 bsf PORTC, 2 bsf PORTC, 4 ; bsf PORTC, 5 ; PWM ; 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 WDTCON ; configure watchdog timer ; WDTCON: -- -- -- WDTPS3 WDTPS2 WDTPS1 WDTPS0 SWDTEN ; movlw B'00011111' ; software enable watchdog timer, : 65535 prescaler 31kHz osc, second prescaler : 128, 268 seconds ; movwf WDTCON banksel 0 ; print ID call print_id ; load settings from eeprom 0 banksel 0 call load_settings ; this sets flags1 banksel 0 ; clrf mode main_loop: banksel 0 ; test_print_time movlw MODE_PRINT_TIME subwf mode, w btfss STATUS, Z goto test_adjust_pwm ; print time call print_time banksel 0 call tx_crlf goto main_end ; configure watchdog timer ; banksel WDTCON ; WDTCON: -- -- -- WDTPS3 WDTPS2 WDTPS1 WDTPS0 SWDTEN ; movlw B'00011111' ; software enable watchdog timer, : 65535 prescaler 31kHz osc, second prescaler : 128, 268 seconds ; movwf WDTCON ; from pdf page 84 ; BANKSEL TMR0 ; CLRWDT ; Clear WDT ; CLRF TMR0 ; Clear TMR0 and prescaler ; BANKSEL OPTION_REG ; BSF OPTION_REG, PSA ; Select WDT ; CLRWDT ; MOVLW b'11111000' ; Mask prescaler ; ANDWF OPTION_REG, W ; bits ; IORLW b'00000111' ; Set WDT prescaler 1:128 ;; IORLW b'00000101' ; Set WDT prescaler ; MOVWF OPTION_REG ; to 1:32 test_adjust_pwm: banksel 0 movlw MODE_ADJUST_PWM subwf mode, w btfss STATUS, Z goto test_read_adc ; adjust pwm, wait for correct reload moment here? movfw pwm_pulse_width banksel CCPR1L movwf CCPR1L goto main_end test_read_adc: banksel 0 movlw MODE_READ_ADC subwf mode, w btfss STATUS, Z goto test_print_millivolts call read_adc banksel 0 call print_adc banksel 0 call tx_crlf goto main_end test_print_millivolts: banksel 0 movlw MODE_PRINT_MILLIVOLTS subwf mode, w btfss STATUS, Z goto test_set_bit call read_adc banksel 0 call ad_to_millivolts banksel 0 call print_millivolts banksel 0 call tx_crlf banksel 0 goto main_end test_set_bit: ; set bit bitnumber to 1 banksel 0 movlw MODE_SET_BIT subwf mode, w btfss STATUS, Z goto test_clear_bit ; Digital outputs: RB4, RB6, RC0, RC2, RC4 ; 0, 1, 2, 3, 4 test_set_output_0: movfw bit_number xorlw D'0' btfss STATUS, Z goto test_set_output_1 ; set output RB4 bsf PORTB, D'4' goto main_end test_set_output_1: movfw bit_number xorlw D'1' btfss STATUS, Z goto test_set_output_2 ; set output RB6 bsf PORTB, D'6' goto main_end test_set_output_2: movfw bit_number xorlw D'2' btfss STATUS, Z goto test_set_output_3 ; set output RC0 bsf PORTC, D'0' goto main_end test_set_output_3: movfw bit_number xorlw D'3' btfss STATUS, Z goto test_set_output_4 ; set output RC2 bsf PORTC, D'2' goto main_end test_set_output_4: movfw bit_number xorlw D'4' btfss STATUS, Z goto main_end ; wrong request ; set output RC4 bsf PORTC, D'4' goto main_end test_clear_bit: ; set bit bit_number to zero banksel 0 movlw MODE_CLEAR_BIT subwf mode, w btfss STATUS, Z goto test_get_bit ; Digital outputs: RB4, RB6, RC0, RC2, RC4 ; 0, 1, 2, 3, 4 test_clear_output_0: movfw bit_number xorlw D'0' btfss STATUS, Z goto test_clear_output_1 ; clear output RB4 bcf PORTB, D'4' goto main_end test_clear_output_1: movfw bit_number xorlw D'1' btfss STATUS, Z goto test_clear_output_2 ; set output RB6 bcf PORTB, D'6' goto main_end test_clear_output_2: movfw bit_number xorlw D'2' btfss STATUS, Z goto test_clear_output_3 ; set output RC0 bcf PORTC, D'0' goto main_end test_clear_output_3: movfw bit_number xorlw D'3' btfss STATUS, Z goto test_clear_output_4 ; set output RC2 bcf PORTC, D'2' goto main_end test_clear_output_4: movfw bit_number xorlw D'4' btfss STATUS, Z goto main_end ; wrong request ; set output RC4 bcf PORTC, D'4' goto main_end test_get_bit: ; print bit bit_number banksel 0 movlw MODE_GET_BIT subwf mode, w btfss STATUS, Z goto test_print_clock_calibration ; Digital inputs: RA3, RA5 ; 0, 1 test_get_bit_0: movfw bit_number xorlw D'0' btfss STATUS, Z goto test_get_bit_1 movfw PORTA andlw B'00001000' btfss STATUS, Z goto print_a_one goto print_a_zero test_get_bit_1: xorlw D'1' btfss STATUS, Z goto main_end ; wrong request movfw PORTA andlw B'00100000' btfss STATUS, Z goto print_a_one ; goto print_a_zero print_a_zero movlw '0' goto print_one_or_zero print_a_one: movlw '1' print_one_or_zero: call tx_w call tx_crlf goto main_end test_print_clock_calibration: banksel 0 movlw MODE_PRINT_CLOCK_CALIBRATION subwf mode, w btfss STATUS, Z goto main_loop ; IF NOT FOUND, ONLY CLEAR mode if something found. call clock_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 #ifdef OLD_CODE 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 #endif ; OLD_CODE ; 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 'I' call tx_w movlw 'O' call tx_w movlw '-' call tx_w movlw '0' call tx_w movlw '.' call tx_w movlw '1' 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 movlw D'0' ; clock_calibration movwf eeprom_address ; 0 movfw timer1_reload_l movwf eeprom_data call eeprom_write banksel 0 incf eeprom_address ; 1 movfw pwm_pulse_width movwf eeprom_data call eeprom_write banksel 0 return load_settings: ; this will load settings from EEPROM. ; To see if any value was actually programmed in EEPROM, first EEPROM address 255 is read. ; if EEPROM address 255 reads 255, then nothing was programmed, defaults are then written to EEPROM, ; and the value 123 is written to EEPROM address 255 as a marker that it is programmed. banksel 0 movlw D'255' movwf eeprom_address ; 255 call eeprom_read ; returns data in w banksel 0 xorlw D'123' ; test for programmed btfsc STATUS, Z goto eeprom_is_programmed ; program default values in eeprom banksel 0 movlw D'175' movwf timer1_reload_l movlw D'128' movwf pwm_pulse_width call save_settings ; mark writtem banksel 0 movlw D'123' movwf eeprom_data movlw D'255' movwf eeprom_address call eeprom_write banksel 0 return eeprom_is_programmed: banksel 0 movlw D'0' ; clock calibration movwf eeprom_address ; 0 call eeprom_read ; returns data in w banksel 0 movwf timer1_reload_l incf eeprom_address ; 1 call eeprom_read banksel 0 movwf pwm_pulse_width banksel 0 return clock_calibration_pri: ; print timer1_reload_l banksel 0 movfw timer1_reload_l 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 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 ; org D'2048' print_time: movfw hours call print_w_ascii_dec movlw ':' call tx_w movfw minutes call print_w_ascii_dec return pwm_init: banksel TMR2 clrf TMR2 banksel PR2 ; movlw d'249' ; PWM Period movlw 0x65 ; 8MHz 1x prescaler 8 bits resolution movwf PR2 ; init 50 % duty cycle banksel 0 ; movlw D'128' ; Load Duty cycle into CCPR1L movlw D'50' ; seems 0 (0V) to - 102 (+5V). movwf pwm_pulse_width banksel CCPR1L movwf CCPR1L ; CCP1CON: P1M1 P1M0 DC1B1 DC1B0 CCP1M3 CCP1M2 CCP1M1 CCP1M0 movlw b'00001100' banksel CCP1CON movwf CCP1CON ; Setup PWM mode ; set prescaler 1x banksel T2CON clrf T2CON bcf T2CON, T2CKPS0 ; 00 = /1, 01 = /4, 1x = /16 bcf T2CON, T2CKPS1 ; timer 2 on bsf T2CON, TMR2ON banksel 0 return end