; 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 swr-0.5 ; copyright Jan Panteltje 2008-always ; integer math copyright Microchip, some other routines copyright others, my part is GPL. ; ; ; This is a SWR and power meter with output on a 2 line 16 char LCD, and RS232 interface. ; It also has a 24 hour clock. ; Uses the PIC internal 8MHz oscillator, and seems quite accurate, ; CHANGES ; ; 0.1 ; First release ; ; 0.2 ; Now if zero input signal, SWR .0 is displayed, no longer false SWR alarm. ; Display is updated only once a second. ; ; 0.3: ; Correct formatting in debug mode. ; ; 0.4: ; SWR alarm beeper no on RA4, pin 3, separate from SWR alarm LED. ; some code in page1. ; Extra 'A' and 'a' command to enable / disable beeper, status saved in EEPROM. ; ; 0.5: ; Added -vENTER print version number. ; ; 0.6 ; ; Added data display mode from PC. Max 9 characters in line 2, ctrl A starts, ENTER ends and displays ; Added beep from PC, ctrl G beeps. ; ; 0.6.1: ; fixed crash if zero characters was entered in ctlr A in display. ; moved clock 1 pos to the right ; Now 10 characters can be displayed. ; The soft has a serial 19200 Baud, 8 bits no parity interface, and accepts the following commands: ; unnENTER sets hour. ; mnnENTER sets minute. ; BnnnENTER sets swr alert level, 17 is 1.7, stored in EEPROM. ; AENTER SWR alarm beeper on, status saved in EEPROM. ; aENTER SWR alarm beeper off, status saved in EEPROM. ; DENTER enters debug mode, continuously prints adc steps for forward, reflected, power, and flags2 ; dENTER exits debug mode. ; GnnnENTER sets clock calibration, timer1 reload low byte, use 175 for nominal, is saved in EEPROM. ; gENTER 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). ; PnnnENTER sets PWM output value, LCD backlight, range 0 to 100, stored in EEPROM. ; pENTER prints PWM setting. ; sENTER prints SWR. ; tENTER prints time. ; vENTER prints version number. ; wENTER prints power in Watts. ; ctrl A SOME TEXT ENTER sends 'SOME TEXT' to line 2 of the LCD, max 9 characters, text is displayed upon ENTER. ; ctrl G activates the beeper for part of a second, can be used to draw attention to a new text for example. ; The math is done in integer. ; CODE IS FOR GPASM, may work on MPLAB too. ; Calculations are for a Vref = Vsupply of 5.0V, use LM317 for example. #define VERSION_H '0' #define VERSION_L '6' #define VERSION_LL '1' ; ADC channels #define FORWARD D'8' #define REFLECTED D'9' #define POWER D'10' 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_PRINT_VERSION D'1' #define COMMAND_PRINT_IIME D'2' #define COMMAND_SET_PWM D'4' #define COMMAND_ENTER_DATA D'5' #define COMMAND_BEEP_USER D'6' #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_SET_SWR_ALERT D'12' #define COMMAND_PRINT_SWR_ALERT D'13' #define COMMAND_PRINT_POWER D'14' #define COMMAND_PRINT_SWR D'15' #define COMMAND_PRINT_PWM D'16' ; mode for main #define MODE_OFF D'0' #define MODE_READ_ADC D'1' #define MODE_ADJUST_PWM D'2' #define MODE_PRINT_VERSION D'3' #define MODE_DISPLAY_DATA D'5' #define MODE_BEEP_USER D'6' #define MODE_PRINT_CLOCK_CALIBRATION D'7' #define MODE_PRINT_TIME D'8' #define MODE_PRINT_SWR_ALERT D'10' #define MODE_PRINT_POWER D'11' #define MODE_PRINT_SWR D'12' #define MODE_PRINT_PWM D'13' ; ** NOTE: ** ; define config fuses ; IF YOU WANT TO RUN VERIFY, BETTER HAVE COPY PROTECTION OFF ;-) ; 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. PROCESSOR p16f690 include Dev_Freq EQU D'8000000' ; Device Frequency is 8 MHz LCD_INIT_DELAY EQU (HIGH ((( Dev_Freq / 4 ) * D'46' / D'10000' ) / 3 ) ) + 1 ; LCD Module commands ; DISP_ON EQU 0x00C ; Display on DISP_ON_C EQU 0x00E ; Display on, Cursor on DISP_ON_B EQU 0x00F ; Display on, Cursor on, Blink cursor DISP_OFF EQU 0x008 ; Display off CLR_DISP EQU 0x001 ; Clear the Display ENTRY_INC EQU 0x006 ; ENTRY_INC_S EQU 0x007 ; ENTRY_DEC EQU 0x004 ; ENTRY_DEC_S EQU 0x005 ; DD_RAM_ADDR EQU 0x080 ; Least Significant 7-bit are for address DD_RAM_UL EQU 0x080 ; Upper Left coner of the Display ; LCD stuff, set 4 bit LCD_DATA EQU PORTC LCD_DATA_TRIS EQU TRISC LCD_CNTL EQU PORTA ; LCD Display Commands and Control Signal names. E EQU 0 ; LCD Enable control line RW EQU 1 ; LCD Read/Write control line RS EQU 2 ; LCD Register Select control line SWR_ALARM_BEEPER EQU 4 ; RA4, pin 3 ; on PORTB SWR_ALARM_LED equ 6 ; RB6, pin 11 ; IO pin bit assignment PORTA, PORTB, PORTC ; RA and RB have change interrupt ; PORTA 6 bit wide ; PORTB 4 bit wide ; PORTC 8 bit wide ; IO used: ; LCD data ; DB3 RC3 pin 7 ; DB2 RC2 pin 14 ; DB1 RC1 pin 15 ; DB0 RC0 pin 16 ; LCD control ; RS RA2 pin 17 ; R/W RA1 pin 18 ; E RA0 pin 19 ; LCD backlight control ; PWM P1A pin 5 ; SWR alarm digital outputs ; SWR alarm LED RB6 pin 11 ; SWR alarm beeper RA4 pin 3 ; analog inputs ; power AN10 pin 13 ; refected AN9 pin 9 ; forward AN8 pin 8 ; 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 TEMP1 equ D'32' ; flags delay_count1 equ D'33' ; 1ms delay power_h equ D'34' power_l equ D'35' tx_timeout equ D'36' flags1 equ D'37' temp1 equ D'38' count equ D'39' temp_l equ D'40' temp_h equ D'41' temp equ D'42' ; temp for binary to BCD bin equ D'43' ; bin+1 for 16 bits equ D'44' bcd equ D'45' 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 old_seconds equ D'51' data_byte_cnt equ D'52' int_fsr_save equ D'53' int_beep_delay equ D'54' 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' ; saved in EEPROM swr_alert 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' MSD equ D'68' ; Temporary register, Holds Most Significant Digit of BIN to BCD conversion LSD equ D'69' ; Temporary register, Holds Least Significant Digit of BIN to BCD conversion TEMP equ D'70' ; Temporary register CHAR equ D'80' ; Temporary register, Holds value to send to LCD module. delay_count2 equ D'81' watts_10_h equ D'82' watts_10_l equ D'83' data0 equ D'84' data1 equ D'85' data2 equ D'86' data3 equ D'87' data4 equ D'88' data5 equ D'89' data6 equ D'90' data7 equ D'91' data8 equ D'92' data9 equ D'93' fsr_save equ D'95' fsr_save2 equ D'96' mode equ D'97' ad_h equ D'98' ad_l equ D'99' vfwd_h equ D'100' vfwd_l equ D'101' vref_h equ D'102' ; to be saved in EEPROM vref_l equ D'103' ; to be saved in EEPROM swr_h equ D'104' swr_l equ D'105' 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' s_fsr equ D'125' temp_w equ D'126' temp_s equ D'127' ; define flags1 ONE_MINUTE_FLAG equ D'0' PRINT_DOT_FLAG equ D'1' SAVE_SETTINGS_FLAG equ D'2' FIRST_ZERO_SUPPRESSED_FLAG equ D'3' DIVIDE_BY_TEN_FLAG equ D'4' EEPROM_WRITE_ERROR_FLAG equ D'5' DEBUG_FLAG equ D'6' ONE_SECOND_FLAG equ D'7' ; define flags2 USE_SWR_BEEPER_FLAG equ D'0' ; page macros CALL_0 macro subroutine_name CLRF PCLATH ; Next call will go to page 0 CALL subroutine_name endm call_1 macro subroutine_name ; MOVLW B'00001000' ; Next call or jump will go to page 1 ; MOVWF PCLATH bsf PCLATH, 3 CALL subroutine_name ; Jump to function that starts in this page endm ; 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 movfw FSR movwf s_fsr endm restore_w_stat macro movfw s_fsr movwf FSR 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 main_init 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 clrf PCLATH ; interrupts routines only call subroutines in page 0 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 ; force immediate response to serial input movfw seconds movwf old_seconds incf old_seconds movlw D'13' subwf RCREG, W btfsc STATUS, Z goto cr_command ; if in data entry command, store in data0 upwards movlw COMMAND_ENTER_DATA subwf command, w btfss STATUS, Z goto test_enter_data ; store in data0 upwards for 9 bytes. movfw int_fsr_save movwf FSR movfw RCREG movwf INDF incf int_fsr_save, f incf data_byte_cnt, f movlw D'10' subwf data_byte_cnt, w btfss STATUS, Z goto int_end ; loop for more bytes, stay in command goto end_enter_data_command test_enter_data: movlw D'1' ; ctrl A, start enter data command subwf RCREG, W btfsc STATUS, Z goto start_enter_data_command movlw D'7' ; Bell subwf RCREG, W btfsc STATUS, Z goto beep_user_command movlw 'A' subwf RCREG, W btfsc STATUS, Z goto set_swr_alarm_beeper_enable_command movlw 'a' subwf RCREG, W btfsc STATUS, Z goto set_swr_alarm_beeper_disable_command movlw 'B' subwf RCREG, W btfsc STATUS, Z goto set_swr_alert_command movlw 'b' subwf RCREG, W btfsc STATUS, Z goto print_swr_alert_command movlw 'D' subwf RCREG, W btfsc STATUS, Z goto debug_on_command movlw 'd' subwf RCREG, W btfsc STATUS, Z goto debug_off_command movlw 'G' subwf RCREG, W btfsc STATUS, Z goto calibrate_clock_command movlw 'g' subwf RCREG, W btfsc STATUS, Z goto print_clock_calibration_command ; movlw 'h' ; subwf RCREG, W ; btfsc STATUS, Z ; goto print_help 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 'p' subwf RCREG, W btfsc STATUS, Z goto print_pwm_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_version_command movlw 'w' subwf RCREG, W btfsc STATUS, Z goto print_power_command movlw 's' subwf RCREG, W btfsc STATUS, Z goto print_swr_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_ENTER_DATA subwf command, W btfsc STATUS, Z goto end_enter_data_command movlw COMMAND_OFF subwf digit_cnt, W btfsc STATUS, Z ; if zero digits do nothing goto int_end movlw COMMAND_SET_PWM subwf command, W btfsc STATUS, Z goto end_set_pwm_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_SET_SWR_ALERT subwf command, W btfsc STATUS, Z goto end_set_swr_alert_command goto int_end ; these commands have no numeric argument 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 print_pwm_command: movlw COMMAND_PRINT_PWM movwf command movlw MODE_PRINT_PWM movwf mode goto int_end_clr print_power_command: movlw COMMAND_PRINT_POWER movwf command movlw MODE_PRINT_POWER movwf mode goto int_end_clr print_swr_command: movlw COMMAND_PRINT_SWR movwf command movlw MODE_PRINT_SWR movwf mode goto int_end_clr print_swr_alert_command: movlw COMMAND_PRINT_SWR_ALERT movwf command movlw MODE_PRINT_SWR_ALERT movwf mode goto int_end_clr debug_on_command: bsf flags1, DEBUG_FLAG goto int_end_clr debug_off_command: bcf flags1, DEBUG_FLAG goto int_end_clr set_swr_alarm_beeper_enable_command: bsf flags2, USE_SWR_BEEPER_FLAG call save_settings ; goto int_end_clr goto int_end set_swr_alarm_beeper_disable_command: bcf flags2, USE_SWR_BEEPER_FLAG call save_settings ; goto int_end_clr goto int_end print_version_command: movlw COMMAND_PRINT_VERSION movwf command movlw MODE_PRINT_VERSION movwf mode goto int_end_clr start_enter_data_command: movlw COMMAND_ENTER_DATA movwf command clrf data_byte_cnt movlw data0 movwf FSR movwf int_fsr_save clrf mode ; mode 0 goto int_end ; !! not int_end_clr, stay in COMMAND_ENTER_DATA end_enter_data_command: ; test for zero data entered movlw D'0' subwf data_byte_cnt btfss STATUS, Z goto have_display_data clrf mode ; cancel mode goto int_end_clr have_display_data: movlw MODE_DISPLAY_DATA movwf mode goto int_end_clr beep_user_command: movlw MODE_BEEP_USER movwf mode goto int_end_clr ; these commands have a numeric argument set_pwm_command: movlw COMMAND_SET_PWM movwf command goto command_end set_swr_alert_command: movlw COMMAND_SET_SWR_ALERT 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 ; 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 call save_settings ; bsf flags1, SAVE_SETTINGS_FLAG goto command_end_status end_set_swr_alert_command: movfw value movwf swr_alert call save_settings ; bsf flags1, SAVE_SETTINGS_FLAG goto command_end_status end_calibrate_clock_command: movfw value movwf timer1_reload_l call save_settings ; bsf flags1, SAVE_SETTINGS_FLAG 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 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 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 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 main_init: ; set internal OSC speed, the output on pin 3 (OSC2) is this clock / 4. ; SELECT BANK banksel OSCCON ; clrf 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 ; GPIO bank 0, address 0x05 clrf PORTA ; init all output latches to zero clrf PORTB clrf PORTC ; force SWR alarm LED and beeper off, as it may take a ful lsecond before the flags is checked. bsf PORTA, SWR_ALARM_BEEPER ; SWR alarm beeper off bsf PORTB, SWR_ALARM_LED ; SWR alarm LED off ; portb init (4 to 7) banksel 0 ; 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_1 pwm_init clrf PCLATH ;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 ; IO used: ; LCD data ; DB3 RC3 pin 7 ; DB2 RC2 pin 14 ; DB1 RC1 pin 15 ; DB0 RC0 pin 16 ; LCD control ; RS RA2 pin 17 ; R/W RA1 pin 18 ; E RA0 pin 19 ; LCD backlight control ; PWM P1A pin 5 ; SWR alarm digital outputs ; SWR alarm LED RB6 pin 11 ; SWR alarm beeper RA4 pin 3 ; analog inputs ; power AN10 pin 13 ; refected AN9 pin 9 ; forward AN8 pin 8 banksel TRISA movlw B'101000' ; RA5 nc, RA4 (SWR_ALARM_BEEPER), RA3 nc(Vpp), RA2 LCD RS, RA1 LCD R/W (prog clk), RA0 LCD E (prog dat) movwf TRISA banksel TRISB ; RB7 (TX), RB6 beeper out, RB5 (RX), RB4 AN10 power in movlw B'00110000' movwf TRISB banksel TRISC ; 1 = input, 0 = output movlw B'11010000' ; RC7 (AN9 reflected in), RC6 (AN8 forward in), RC5 is P1A is PWM output, RC4 nc, RC3 LCD D3, RC2 LCD D2, RC1 LCD D1, RC0 LCD D0 movwf TRISC ; select which port c pins are analog inputs ; 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 bcf ANSEL, 5 ; ASN 5 bcf ANSEL, 6 ; ASN 6 bcf ANSEL, 7 ; ASN 7 BANKSEL ANSELH ; default is 1111 bsf ANSELH, 0 ; ASN 8 ; forward in bsf ANSELH, 1 ; ASN 9 ; refected in bsf ANSELH, 2 ; ASN 10 ; power in 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 ; 1 1010= AN10 bsf ADCON0, 4 ; 0 bcf ADCON0, 3 ; 1 bsf ADCON0, 2 ; 0 ; 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 ; flags to zero clrf flags1 ; clrf flags2 ;set by load_settings ; 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 banksel TMR1H ; 50000 reload clrf 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 main: banksel 0 ; print ID ; call print_id ; init LCD ; Initilize the LCD Display Module bcf LCD_CNTL, E bcf LCD_CNTL, RW bcf LCD_CNTL, RS DISPLAY_INIT: ; wait > 15ms after power up movlw D'254' call_1 delay_1ms clrf PCLATH MOVLW 0x02 ; Command for 4-bit interface low nibble MOVWF LCD_DATA BSF LCD_CNTL, E BCF LCD_CNTL, E ; This routine takes the calculated times that the delay loop needs to ; be executed, based on the LCD_INIT_DELAY EQUate that includes the ; frequency of operation. These uses registers before they are needed to ; store the time. LCD_DELAY: MOVLW LCD_INIT_DELAY ; MOVWF MSD ; Use MSD and LSD Registers to Initilize LCD CLRF LSD ; LOOP2 DECFSZ LSD, F ; Delay time = MSD * ((3 * 256) + 3) * Tcy GOTO LOOP2 ; DECFSZ MSD, F ; END_LCD_DELAY GOTO LOOP2 ; ; Command sequence for 2 lines of 5x7 characters CMD_SEQ: MOVLW 0X02 ; 4-bit low nibble xfer MOVWF LCD_DATA ; This code for both 4-bit and 8-bit modes BSF LCD_CNTL, E ; BCF LCD_CNTL, E ; ; MOVLW 0x08 ; 4-bit low nibble xfer MOVWF LCD_DATA ; BSF LCD_CNTL, E ; BCF LCD_CNTL, E ; ; Busy Flag should be valid after this point ; MOVLW DISP_ON ; CALL SEND_CMD ; MOVLW CLR_DISP ; CALL SEND_CMD ; MOVLW ENTRY_INC ; CALL SEND_CMD ; MOVLW DD_RAM_ADDR ; CALL SEND_CMD ; ;Send a message the hard way lcd_pri_test: ; line 1 ; movlw B'10000000' ; Address DDRam first character, second line movlw D'168' ; 128 + 40 is line 2 start call SEND_CMD movlw 'P' call SEND_CHAR movlw 'a' call SEND_CHAR movlw 'n' call SEND_CHAR movlw 't' call SEND_CHAR movlw 'e' call SEND_CHAR movlw 'l' call SEND_CHAR movlw 't' call SEND_CHAR movlw 'j' call SEND_CHAR movlw 'e' call SEND_CHAR ; load settings from eeprom 0 banksel 0 call load_settings banksel 0 ; clrf mode main_loop: banksel 0 ; update the display once a second. wait_for_seconds_change: banksel 0 wait_one_second_flag: movfw seconds subwf old_seconds, w btfss STATUS, Z goto seconds_changed ; same second btfss flags1, DEBUG_FLAG goto wait_for_seconds_change ; while waiting for seconds to change if debug we print the ASC steps ; debug mode, print ADC steps for all analog inputs movlw D'8' movwf analog_select debug_loop: call_1 read_adc clrf PCLATH movfw ad_h movwf bin movfw ad_l movwf bin+1 call print_16_ascii_dec banksel 0 movlw ' ' call tx_w incf analog_select movlw D'11' subwf analog_select, w btfss STATUS, Z goto debug_loop movfw flags2 call print_w_ascii_dec call tx_crlf goto wait_for_seconds_change seconds_changed: movfw seconds movwf old_seconds test_print_version: movlw MODE_PRINT_VERSION subwf mode, w btfss STATUS, Z ; goto test_print_time ; goto test_display_data goto test_beep_user ; print version call print_id goto main_end test_beep_user: movlw MODE_BEEP_USER subwf mode, w btfss STATUS, Z goto test_display_data ; beeper on bcf PORTA, SWR_ALARM_BEEPER ; wait a while movlw D'254' call_1 delay_1ms clrf PCLATH ;beeper off bsf PORTA, SWR_ALARM_BEEPER clrf mode goto main_end test_display_data: movlw MODE_DISPLAY_DATA subwf mode, w btfss STATUS, Z goto test_print_time ; print data ; LCD position movlw D'168' ; 128 + 40 is line 2 start call SEND_CMD ; clear old text movlw D'10' movwf temp1 clr_text_loop: movlw ' ' call SEND_CHAR ; banksel 0 decfsz temp1 goto clr_text_loop ; LCD position movlw D'168' ; 128 + 40 is line 2 start call SEND_CMD ; banksel 0 movlw data0 movwf FSR print_data_loop: banksel 0 movfw INDF call SEND_CHAR ; banksel 0 incf FSR, f decfsz data_byte_cnt goto print_data_loop goto main_end test_print_time: movlw MODE_PRINT_TIME subwf mode, w btfss STATUS, Z goto test_adjust_pwm ; print time movfw hours call print_w_ascii_dec movlw ':' call tx_w movfw minutes call print_w_ascii_dec ; 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_print_clock_calibration ; adjust pwm, wait for correct reload moment here? movfw pwm_pulse_width banksel CCPR1L movwf CCPR1L goto main_end test_print_clock_calibration: banksel 0 movlw MODE_PRINT_CLOCK_CALIBRATION subwf mode, w btfss STATUS, Z goto test_print_swr_alert movfw timer1_reload_l call print_w_ascii_dec ; banksel 0 call tx_crlf goto main_end test_print_swr_alert: banksel 0 movlw MODE_PRINT_SWR_ALERT subwf mode, w btfss STATUS, Z goto test_print_swr movfw swr_alert call print_w_ascii_dec call tx_crlf goto main_end test_print_swr: banksel 0 movlw MODE_PRINT_SWR subwf mode, w btfss STATUS, Z goto test_print_pwm clrf bin movfw swr_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec banksel 0 ; movlw ' ' ; space ; call tx_w movlw 'S' ; SWR call tx_w movlw 'W' call tx_w movlw 'R' call tx_w call tx_crlf goto main_end test_print_pwm: banksel 0 movlw MODE_PRINT_PWM subwf mode, w btfss STATUS, Z goto test_print_power movfw pwm_pulse_width call print_w_ascii_dec call tx_crlf goto main_end test_print_power: banksel 0 movlw MODE_PRINT_POWER subwf mode, w btfss STATUS, Z goto read_power banksel 0 movfw watts_10_h movwf bin movfw watts_10_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec banksel 0 ; movlw ' ' ; space ; call tx_w movlw 'W' ; say W call tx_w call tx_crlf goto main_end read_power: ; POWER ; read ADC banksel 0 movlw POWER movwf analog_select call_1 read_adc clrf PCLATH calc_power: banksel 0 ; calculate power Watt = (millivolts * millivolts) / 50 ; first scale to volts because of input attenuators. ; max power is 150 W in 50 Ohm, U^2 / 50 = 150, U^2 = 7500, U = 86.6 V eff, = 122.47 V peak (after diode and rectifier) ; 5V max in, so attenuator is 24.494 = 25 x ; for 25 x attenuator and 121.77 Vp (- diode drop), we have 121.77 / 25 = 4870.8 mV on the ADC. ; add 700 / 25 = 28mV for diode drop. ; use / sqrt(2) = 1.4142 to get effective voltage, or (V1 x V1 ) / 2 ; 4870 x 4870 = 23716900, / 2 = 11858450, *625 = 7411531250, / 50 = 148230626, / 1000000=148.2 Watt ; 959 steps = 150 W for 1:26 attenuator ; 700 mV = 26.93 mV = about 5 steps. = 964 steps ; 964 x 964 = 929 296 needs 20 bits, 2 ^20 = 1 048 576 ; 929296 / 150 = 6195 ; !!!!!!!!!!!!!!!!!!!!!!!! 6195 / 256 = 24.2 ; !!!!!!!!!!!!!!!!!!!!!!!! divide first by moving right one byte, this leaves a 16 bit result, then by 24 ; ************THIS ONE ******************** ; divide by 16 first, this reduces 20 bits to 16 bits, 6195 / 16 = 387 ; then divide by 39: ; 929296 : 16 = 58081 ; 58081 / 39 = 1489 -> 148.9W ; ***************************************** ; so we divide by 6195 = 150.0 ; BUT if we divide by 61.95 then the result = 15000 ; Now all we need is to print a decimal point ; 1W makes U^2 / 50 = 1, U = sqrt(50) = 7.071 V ; x sqrt(2)= 10 Vp ; 10 / 26 = 385 mV ; 385 mV is 385 x 1024 / 5000 steps = 78.8 steps is about 79 steps ; add diode 5 steps makes 84 steps. ; 84 x 84 = 7056 ; 7056 / 6195 = 1.139 makes 1 W ; 7056 / 62 = 113, decimal point 2 from right makes 1.13W ; 7052 / 16 = 440 ; 440 / 39 = 11 -> 1.1W ; 4W makes U^2 / 50 = 4, U = sqrt(200) = 14.1 V ; x sqrt(2)= 20 Vp ; 20 / 26 = 769 mV ; 769 mV is (769 / 5000) * 1023 steps = 157.49 steps is about 157 steps ; add diode 5 steps makes 162 steps. ; 162 x 162 = 26244 ; 26244 / 6195 = 4.23 makes 4 W ; 26244 / 62 = 423, makee 4.23 W ; 26244 / 16 = 1640 ; 1640 / 39 = 42 -> 4.2 W ; needed to divide by 2 (for sqrt(2) ), by 50 (ohms) ; : 100 = 9292 ; need to scale to millivolts ; * 5000/1024 = * 4.8828125^2 = 23.84 ; need to multiply by 676 (26 x 26). ; 9292 * 676 * 6 281 393, 6 281 393 * 23.84 = 149 760 079 makes 28 bits (2^28 = 268 435 456) ; need to divide because millivolts 1000^2 ; 149760079 / 1000000 = 149.76 W ; now 9292 / 150 = 61.94 so why not simply divide by 62: ; 9292 / 62 = 149.8 W so 149 W ; Check: 23.84 x 676 / 100 000 000 = banksel 0 movfw ad_h movwf ACCbHI movwf ACCaHI movfw ad_l movwf ACCbLO movwf ACCaLO 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) ; max 1023 x 1023 = 1 046 529 fits in 20 bits (1048576) in ACCbHI(zero), ACCbLO(upper 4 bits zero), ACCcHI, ACCcLO banksel 0 ; divide by 16 first to reduce 20 bits to 16 bits. movlw D'4' ; shift 4 times to the right movwf temp1 shift_right_loop: bcf STATUS, C rrf ACCbHI, f ; bit 0 to carry rrf ACCbLO, f ; carry to bit 7 rrf ACCcHI, f rrf ACCcLO, f decfsz temp1, F goto shift_right_loop ; now ACCcHI, ACCcLO hold maximum (1023 x 1023) / 16 65408 makes 16 bits ; value to ACCb movfw ACCcHI movwf ACCbHI movfw ACCcLO movwf ACCbLO ; divide by 39 clrf ACCaHI movlw D'39' 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). ; result Watts x 10 now in ACCbHi and ACCbLO banksel 0 movfw ACCbHI movwf watts_10_h movfw ACCbLO movwf watts_10_l movfw ACCbHI movwf bin movfw ACCbLO movwf bin+1 ; LCD print position movlw B'10000000' ; Address DDRam first character, second line call SEND_CMD ; Clear, write 7 spaces '150.0 W' movlw D'7' movwf temp1 space_loop: movlw ' ' call SEND_CHAR decfsz temp1, F goto space_loop ; LCD print position movlw B'10000000' ; Address DDRam first character, second line call SEND_CMD bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec_LCD ; print space movlw ' ' call SEND_CHAR movlw 'W' ; Say Watt call SEND_CHAR ; SWR ; read ADC volts forard movlw FORWARD movwf analog_select call_1 read_adc ; ad_h and ad_l clrf PCLATH banksel 0 movfw ad_h movwf vfwd_h movfw ad_l movwf vfwd_l ; read ADC volts reflected movlw REFLECTED movwf analog_select call_1 read_adc ; ad_h and ad_l clrf PCLATH banksel 0 movfw ad_h movwf vref_h movfw ad_l movwf vref_l ; if Vfwd = 0, then the SWR has no meaning. ; test if high byte forward zero movlw D'0' subwf vfwd_h, w btfss STATUS, Z goto calculate_swr ; test if low byte fwd zero movlw D'0' subwf vfwd_l, w btfss STATUS, Z goto calculate_swr ; say invalid SWR clrf swr_l goto print_SWR_LCD calculate_swr: ; calculate SWR ; Gamma = V_rev / V_fwd Then calculate the VSWR VSWR = (1 + Gamma) / (1 - Gamma) ; SWR = (1 + (Vref / Vfwd) ) / (1 - (Vref / Vfwd) ) ; SWR = (Vfwd * (1 + (Vref / Vfwd) ) / (Vfwd * (1 - (Vref / Vfwd) ) ; SWR = (Vfwd + Vref) / (Vfwd - Vref) ; calculate Vfwd - Vref movfw vfwd_h movwf ACCbHI movfw vfwd_l movwf ACCbLO movfw vref_h movwf ACCaHI movfw vref_l movwf ACCaLO call D_sub ; Double Precision Subtraction ( ACCb - ACCa -> ACCb ) ; ACCb now Vfwd - Vref banksel 0 movfw ACCbHI movwf temp_h movfw ACCbLO movwf temp_l ; temp_ holds Vfwd - Vref ; calculate Vfwd + Vref movfw vfwd_h movwf ACCbHI movfw vfwd_l movwf ACCbLO movfw vref_h movwf ACCaHI movfw vref_l movwf ACCaLO call D_add ; Double Precision Addition ( ACCb + ACCa -> ACCb ) ; ACCb now Vfwd + Vref ; multiply (Vfwd + Vref) by 10, so something useful like 11 is 1.1 SWR ; Vfwd + Vref is max 1023 + 1023 = 2046, *10 makes 20460 fits in 16 bits, so we can ignore ACCb in the next result banksel 0 clrf ACCaHI movlw D'10' movwf ACCaLO ; ACCa now 10 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) ; result 10 * (Vfwd + Vref) in ACCc, ignore ACCb movfw ACCcHI movwf ACCbHI movfw ACCcLO movwf ACCbLO ; ACCb now 10 * (Vfwd + Vref) ; calculate (10 * (Vfwd + Vref) ) / (Vfwd - Vref) movfw temp_h movwf ACCaHI movfw temp_l movwf ACCaLO ; ACCa holds Vfwd - Vref 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). ; ACCb now 10 * (Vfwd + Vref) / (Vfwd - Vref) banksel 0 movfw ACCbHI movwf swr_h movfw ACCbLO movwf swr_l ; SWR expected range 1 to many print_SWR_LCD: ; print SWR ; movfw swr_l ; only using swr_l here (0-255), makes SWR 1 to 25.5 and that should be sufficient bad SWR. ; LCD print position ; movlw B'10000000' ; Address DDRam first character, second line movlw D'136' ; 128 + 8 = 136 call SEND_CMD ; Clear, write 8 spaces, '25.6 SWR' movlw D'8' movwf temp1 space_loop2: movlw ' ' call SEND_CHAR decfsz temp1, F goto space_loop2 ; LCD print position ; movlw B'10000000' ; Address DDRam first character, second line movlw D'136' ; 128 + 8 = 136 call SEND_CMD clrf bin movfw swr_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec_LCD ; print space movlw ' ' call SEND_CHAR movlw 'S' ; Say SWR call SEND_CHAR movlw 'W' call SEND_CHAR movlw 'R' call SEND_CHAR ; test if SWR > swr_alert, if so SWR LED on, and beep movfw swr_alert subwf swr_l, w btfss STATUS, C ; carry if >= swr_alert, beep goto swr_alarm_off swr_alarm_on: bcf PORTB, SWR_ALARM_LED ; SWR alarm LED on btfsc flags2, USE_SWR_BEEPER_FLAG bcf PORTA, SWR_ALARM_BEEPER ; SWR alarm beeper on goto print_time_lcd swr_alarm_off: bsf PORTB, SWR_ALARM_LED ; SWR alarm LED off ; set beeper always off bsf PORTA, SWR_ALARM_BEEPER ; SWR alarm beeper off print_time_lcd: banksel 0 ; print position ; movlw B'10000000' ; Address DDRam first character, second line ; movlw D'178' ; 128 + 40 + 10 = 178 movlw D'203' ; makes no sense? call SEND_CMD ; Clear, write 5 spaces uu:mm movlw D'5' movwf temp1 space_loop3: movlw ' ' call SEND_CHAR decfsz temp1, F goto space_loop3 ; position again movlw D'203' ; makes no sense? call SEND_CMD bcf flags1, DIVIDE_BY_TEN_FLAG clrf bin movfw hours movwf bin+1 call print_16_ascii_dec_LCD movlw ':' call SEND_CHAR clrf bin movfw minutes movwf bin+1 call print_16_ascii_dec_LCD last_part: goto main_loop ; IF NOT FOUND, ONLY CLEAR mode if something found. main_end: banksel 0 clrf mode goto main_loop ; ***************** subroutines ******************** ; send byte in W tx_digit_in_w: addlw '0' ; zero tx_w: banksel TXREG ; is in bank 0 movwf TXREG ; call wait_tx_empty ; inline ; banksel 0 ; make sure we have a timeout ; movlw D'255' ; movwf tx_timeout banksel TXSTA ; is in bank 1 test_tx_empty: ; banksel 0 ; 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 banksel 0 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 banksel 0 return print_16_ascii_dec: ; prints 16 bit value in registers bin (high byte) and bin+1 (low byte) in ASCII decimal banksel 0 call_1 b2bcd clrf PCLATH ; 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 btfss flags1, DIVIDE_BY_TEN_FLAG goto pri_16_no_dot ; force dot printing just before last digit bsf flags1, PRINT_DOT_FLAG bcf flags1, DIVIDE_BY_TEN_FLAG pri_16_no_dot: movfw bcd+2 call print_bcd_in_w_decimal_2_digits bcf flags1, PRINT_DOT_FLAG ; 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 print_bcd_in_w_decimal_2_digits: banksel 0 ; 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: ; test if we need to print a dot here banksel 0 btfss flags1, PRINT_DOT_FLAG goto no_dot movlw '.' call tx_w no_dot: banksel 0 ; 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 no zero 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 SWR-0.5" ; 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 'S' call tx_w movlw 'W' call tx_w movlw 'R' call tx_w movlw '-' call tx_w movlw VERSION_H call tx_w movlw '.' call tx_w movlw VERSION_L call tx_w movlw '.' call tx_w movlw VERSION_LL 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 eeprom_verify: ; data at EEDADR against eeprom_data banksel 0 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 ; address=nnn movlw 'a' 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 'g' call tx_w movlw '=' call tx_w movfw eeprom_data call print_w_ascii_dec movlw ' ' call tx_w ; read=nnn movlw 'r' 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'1' ; clock_calibration movwf eeprom_address ; 1 movfw timer1_reload_l movwf eeprom_data call eeprom_write banksel 0 incf eeprom_address ; 2 movfw pwm_pulse_width movwf eeprom_data call eeprom_write banksel 0 incf eeprom_address ; 3 movfw swr_alert movwf eeprom_data call eeprom_write incf eeprom_address ; 4 movfw flags2 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 movlw D'19' movwf swr_alert clrf flags2 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'1' ; clock calibration movwf eeprom_address ; 1 call eeprom_read ; returns data in w banksel 0 movwf timer1_reload_l incf eeprom_address ; 2 call eeprom_read banksel 0 movwf pwm_pulse_width incf eeprom_address ; 3 call eeprom_read banksel 0 movwf swr_alert incf eeprom_address ; 4 call eeprom_read banksel 0 movwf flags2 banksel 0 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 ;******************************************************************* ;* SEND_CHAR - Sends character to LCD * ;* This routine splits the character into the upper and lower * ;* nibbles and sends them to the LCD, upper nibble first. * ;* The data is transmitted on the PORT<3:0> pins * ;******************************************************************* SEND_CHAR MOVWF CHAR ; Character to be sent is in W ; CALL BUSY_CHECK ; Wait for LCD to be ready call_1 BUSY_CHECK clrf PCLATH SWAPF CHAR, W ANDLW 0x0F ; Get upper nibble MOVWF LCD_DATA ; Send data to LCD BCF LCD_CNTL, RW ; Set LCD to read BSF LCD_CNTL, RS; Set LCD to data mode BSF LCD_CNTL, E ; toggle E for LCD nop nop nop nop BCF LCD_CNTL, E MOVF CHAR, W ANDLW 0x0F ; Get lower nibble MOVWF LCD_DATA ; Send data to LCD BSF LCD_CNTL, E ; toggle E for LCD nop nop nop nop BCF LCD_CNTL, E RETURN ;******************************************************************* ;* SendCmd - Sends command to LCD * ;* This routine splits the command into the upper and lower * ;* nibbles and sends them to the LCD, upper nibble first. * ;* The data is transmitted on the PORT<3:0> pins * ;******************************************************************* ; ; 4-bit transfers on the low nibble of the PORT SEND_CMD MOVWF CHAR ; Character to be sent is in W ; CALL BUSY_CHECK ; Wait for LCD to be ready call_1 BUSY_CHECK clrf PCLATH SWAPF CHAR, W ANDLW 0x0F ; Get upper nibble MOVWF LCD_DATA ; Send data to LCD BCF LCD_CNTL, RW ; Set LCD to read BCF LCD_CNTL, RS; Set LCD to command mode BSF LCD_CNTL, E ; toggle E for LCD nop nop nop nop BCF LCD_CNTL, E MOVF CHAR, W ANDLW 0x0F ; Get lower nibble MOVWF LCD_DATA ; Send data to LCD BSF LCD_CNTL, E ; toggle E for LCD nop nop nop nop BCF LCD_CNTL, E RETURN print_16_ascii_dec_LCD: ; prints 16 bit value in registers bin (high byte) and bin+1 (low byte) in ASCII decimal call_1 b2bcd clrf PCLATH ; suppress leading zeros bsf flags1, FIRST_ZERO_SUPPRESSED_FLAG movfw bcd call print_bcd_in_w_decimal_2_digits_LCD movfw bcd+1 call print_bcd_in_w_decimal_2_digits_LCD btfss flags1, DIVIDE_BY_TEN_FLAG goto pri_16_no_dot_LCD ; force dot printing just before last digit bsf flags1, PRINT_DOT_FLAG bcf flags1, DIVIDE_BY_TEN_FLAG pri_16_no_dot_LCD: movfw bcd+2 call print_bcd_in_w_decimal_2_digits_LCD bcf flags1, PRINT_DOT_FLAG ; if the zero suppress flag is still set we had zero, so print zero btfss flags1, FIRST_ZERO_SUPPRESSED_FLAG return movlw '0' call SEND_CHAR return print_bcd_in_w_decimal_2_digits_LCD: ; 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_LCD test_lead_zero_LCD: ; test if leading zero suppress flag set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG goto low_nibble_LCD print_high_nibble_LCD: bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG addlw '0' call SEND_CHAR low_nibble_LCD: ; test if we need to print a dot here btfss flags1, PRINT_DOT_FLAG goto no_dot_LCD movlw '.' call SEND_CHAR no_dot_LCD: ; low digit second movfw temp ; get w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_low_nibble_LCD ; test if leading zero suppress flag is set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG ; return if no zero print return print_low_nibble_LCD: bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG addlw '0' call SEND_CHAR return ; PAGE 1 org D'2048' ; 4-bit transfers on the low nibble of the PORT ; ;******************************************************************* ;* This routine checks the busy flag, returns when not busy * ;* Affects: * ;* TEMP - Returned with busy/address * ;******************************************************************* ; BUSY_CHECK BSF STATUS, RP0 ; Bank 1 MOVLW B'11011111' ;0xFF ; Set Port C for input MOVWF LCD_DATA_TRIS BCF STATUS, RP0 ; Bank 0 BCF LCD_CNTL, RS; Set LCD for Command mode BSF LCD_CNTL, RW ; Setup to read busy flag BSF LCD_CNTL, E ; Set E high nop nop nop nop BCF LCD_CNTL, E ; Set E low SWAPF LCD_DATA, W ; Read upper nibble busy flag, DDRam address ANDLW 0xF0 ; Mask out lower nibble MOVWF TEMP ; BSF LCD_CNTL, E ; Toggle E to get lower nibble nop nop nop nop BCF LCD_CNTL, E MOVF LCD_DATA, W ; Read lower nibble busy flag, DDRam address ANDLW 0x0F ; Mask out upper nibble IORWF TEMP, F ; Combine nibbles BTFSC TEMP, 7 ; Check busy flag, high = busy GOTO BUSY_CHECK ; If busy, check again BCF LCD_CNTL, RW BSF STATUS, RP0 ; Bank 1 MOVLW B'11010000' ; 0xF0 MOVWF LCD_DATA_TRIS ; RB7 - 4 = inputs, RB3 - 0 = output BCF STATUS, RP0 ; Bank 0 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 delay10us: ; delays W * 10 us movlw D'60' ; 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 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 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 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 #ifdef PAGE_TEST org D'2048' p1add: call whoopy ; addlw '0' ; movlw 'A' goto exitp1 exitp1: clrf PCLATH return whoopy: addlw '0' return #endif ; PAGE_TEST #ifdef UPPER_PAGE_TEST test1: movlw LOW Table ; get low 8 bits of address addwf offset, F ; do an 8-bit add operation movlw HIGH Table ; get high 5 bits of address btfsc STATUS, C ; page crossed? addlw 1 ; yes then increment high address movwf PCLATH ; load high address in latch movf offset,w ; load computed offset in w reg call Table XORLW 0 BTFSS STATUS, Z call tx_w goto test1 return org D'2048' Table: addwf PCL, F ; value in pc will roll over to pag retlw 'A' retlw 'B' retlw 'C' retlw D'0' #endif ; UPPER_PAGE_TEST end