; ***************************************************************************************************** ; * 9850, CW, Working, Final * ; ***************************************************************************************************** ; * W2EB - 9850-based VFO for generic rigs with IF Offset * ; * Version: Final * ; * 8 Apr 2013 * ; * Author: Bill Lazure, W2EB * ; * **Based on PICELGen 6.0 by AA0ZZ, Craig Johnson, as modified by Bob Okas W3CD, orginally by * ; * Curtis W. Preuss - WB2V * ; ***************************************************************************************************** ; *Description: ; *This is the control program for a DDS VFO built with an AD9850 DDS chip, a rotary encoder, two * ; *push button switches and a Hitachi 44780-compatible Liquid crystal display. * ; * ; * ; * Features for This Version: ; * ; * 1. Push Button Decade Selection. The LCD now displays an underline ; * cursor which identifies the lowest frequency digit (as well as ; * the higher decades) which will be modified as the shaft encoder ; * is turned. PB1 moves the cursor. ; * ; * 2. Hardware-Coded Band Memories. Insertion of Band Modules that ; * contain a hardare configured 4-bit binary output, MAKE THE vfo ; * sense which band the module is designed for. ; * ; * 3. The same table that reads the band-specific startup frequency also ; * reads the IF offset, whether additive or subtractive. ;****************************************************************************************************** ; ; Target Controller - PIC18F2520 ; -------___------- ; TX-- -----------RE3 |1 28| RB7---------LCD_D7 (LCD Pin 14) ; DDS_UPDATE------RA0 |2 27| RB6---------LCD_D6 (LCD Pin 13) ; DDS_CLOCK-------RA1 |3 26| RB5---------LCD_D5 (LCD Pin 12) ; dds_data--------RA2 |4 25| RB4---------LCD_D4 (LCD Pin 11) ; PB1(Decade)-----RA3 |5 24| RB3--------- ; PB2-------------RA4 |6 23| RB2---------LCD_E (LCD Pin 6) ; TX(PB3)---------RA5 |7 22| RB1---------LCD_RW (LCD Pin 5) ; GND-------------VSS |8 21| RB0---------LCD_RS (LCD Pin 4) ; N/C------------OSC2 |9 20| VCC---------+5 V ; N/C------------OSC1 |10 19| VSS---------Gnd ; Band------------RC0 |11 18| RC7---------Encoder ; Band------------RC1 |12 17| RC6---------Encoder ; Band------------RC2 |13 16| RC5---------Spare ; Band------------RC3 |14 15| RC4---------Spare ; ------------------- ; ***************************************************************************************************** ; * Device type and options. * ; ***************************************************************************************************** ; processor 18F2520 #include radix dec errorlevel 0, -205, -220, -302 ; Skip nuisance messages ; * ;****************************************************************************************************** ; **************************************************************************************************** ; * DDS Frequency control equates. These may be changed to accommodate the * ; * reference clock frequency and the desired upper frequency limit frequency. * ; **************************************************************************************************** ; ; ref_osc represents the change in the frequency control word which results ; in a 1 Hz change in output frequency. It is interpreted as a fixed point ; integer in the format . ; ; The values for common oscillator frequencies are as follows: ; ; Frequency ref_osc_3 ref_osc_2 ref_osc_1 ref_osc_0 ; ; 120.00 MHz 0x23 0xCA 0x98 0xCE ; 100.00 MHz 0x2A 0xF3 0x1D 0xC4 ; 90.70 MHz 0x2F 0x5A 0x82 0x7A ; 66.66 MHz 0x40 0x6E 0x52 0xE7 ; 66.00 MHz 0x41 0x13 0x44 0x5F ; 50.00 MHz 0x55 0xE6 0x3B 0x88 ; ; To calculate other values: ; ref_osc_3 = (2^32 / oscillator_freq_in_Hertz). ; ref_osc_2, ref_osc_1, and ref_osc_0 are the fractional part of ; (2^32 / oscillator_freq_in_Hertz) times 2^24. ; Note: 2^32 = 4294967296 and 2^24 = 16777216 ; ; For example, for a 120 MHz clock: ; ref_osc_3 is (2^32 / 120 x 10^6) = 35.791394133 truncated to 35 (0x23) ; ref_osc_2 is the high byte of (.791394133 x 2^24) = 13277390.32 ; 13277390.32 = 0xCA98CE, so high byte is CA. ; ref_osc_1 is the next byte of 0xCA98CE, or 98 ; ref_osc_0 is the last byte of 0xCA98CE, or CE ; ;==== Currently set for 125 MHz Oscillator ======= ref_osc_3 equ 0x22 ; Most significant osc byte ref_osc_2 equ 0x5C ; Next byte ref_osc_1 equ 0x17 ; Next byte ref_osc_0 equ 0xD0 ; Least significant byte ; Info for power-up display MCODE_REV_0 equ 'C' ; Current code version is "FINAL" MCODE_REV_1 equ 'W' ; MCODE_REV_2 equ ' ' ; MCODE_REV_3 equ 'w' ; MCODE_REV_4 equ '/' ; MCODE_REV_5 equ 'I' ; MCODE_REV_6 equ 'F' ; MCODE_REV_7 equ ' ' ; ;****************************************************************************************************** ; * ; General definitions * ; * ;****************************************************************************************************** int_0 equ 0x00 ;The I.F. Frequency; 4.9152MHz int_1 equ 0x00 ; " int_2 equ 0x4B ; " int_3 equ 0x00 ; " EEPROM_BASE equ 0x2100 ; Where EEPROM begins in the 16F84A TRANSMIT equ 0x03 ;Bit that says we are in Transmit Mode UPDT equ 0x05 ;Bit that tells to Refresh DDS and LCD ; ***************************************************************************************************** ; * Assign names to IO pins. * ; ***************************************************************************************************** ; ; A Register Bits: ; dds_ud equ 0x00 dds_clk equ 0x01 dds_data equ 0x02 pb1 equ 0x03 ; Decade select switch pb2 equ 0x04 ; Currently unused pb3 equ 0x05 ; The "TX" line. Low = TX ; ; B register bits: ; LCD_E equ 0x02 ; LCD Enable Bit LCD_RW equ 0x01 ; LCD Read/Write Control LCD_busy equ 0x03 ; LCD busy bit LCD_RS equ 0x00 ; LCD Mode Setting ; ; C register bits: ; Band_0 equ 0x00 ; Band input LSD Band_1 equ 0x01 ; Band_2 equ 0x02 ; Band_3 equ 0x03 ; Band input MSD Enc1 equ 0x06 ; Encoder Input Enc2 equ 0x07 ; Encoder input ; ; E Register Bit: ; tx equ 0x00 ;Transmit/receive status bit ; ; ; **************************************************************************** ; * Allocate variables in general purpose register space * ; **************************************************************************** ; CBLOCK 0x000 ; Start Data Block freq_0 ; Display frequency (hex) freq_1 ; (4 bytes) freq_2 ; Used by bin2BCD only freq_3 freq_0A ; Freq info Placeholders freq_1A ; freq_2A ; freq_3A ; BCD_0 ; Display frequency (BCD) BCD_1 ; (5 bytes) BCD_2 ; BCD_3 ; BCD_4 ; AD9850_0 ; AD9850 control word AD9850_1 ; (5 bytes) AD9850_2 ; AD9850_3 ; AD9850_4 ; fstep_0 ; Frequency inc/dec fstep_1 ; (4 bytes) fstep_2 ; update_decade fills these in fstep_3 ; with values from decade_table BCD_count ; Used in bin2BCD routine BCD_temp ; " mult_count ; Used in calc_dds_word bit_count ; " byte2send ; osc_0 ; Current oscillator osc_1 ; (4 bytes) osc_2 osc_3 osc_temp_0 ; Oscillator frequency osc_temp_1 ; (4 bytes) osc_temp_2 ; Used by calibrate osc_temp_3 LCD_char ; Character being sent to the LCD LCD_read ; Character read from the LCD timer1 ; Used in delay routines timer2 ; " encdr_new ; New value of encoder pins A and B encdr_old ; Old value of encoder pins A and B encdr_read ; Encoder pins A and B and switches last_dir ; Indicates last direction of encoder next_dir ; Indicates expected direction count ; Multipurpose loop counter rs_value ; The LCD rs line flag value temp ; Miscellaneous variable tx_status VFOcontrol ;Used to control Tx vs Rx status decade ; Frequency decade subject to updating cur_pos ; Cursor position: 0-7 for 8 char LCD vfo_a_0 ; Used to store the frequency setting vfo_a_1 ; of VFO A when B is in use vfo_a_2 vfo_a_3 vfo_b_0 ; Used to store the frequency setting vfo_b_1 ; of VFO B. vfo_b_2 vfo_b_3 vfo_b_4 tbloffst ; Used in table reads holder ; temporary storage ENDC ; End of Data Block ; ; ***************************************************************************************************** ; * The 18F2520 resets to 0x00. * ; * The Interrupt vector is at 0x04. (Unused) * ; ***************************************************************************************************** ; ORG 0x0000 reset_entry goto start ; Jump around the band table to main program ; ****************************************************************************************************** ; * Band Table * ; ****************************************************************************************************** ; * * ; * This is the band table. Each entry is four instructions long, with each * ; * group of four literals representing the frequency as a 32 bit integer. * ; * New entries can be added to the end of the table or between existing * ; * entries. * ; * * ; * The routine that runs before the actual table is an offset for the * ; * 18F2520 processor. The actual table being called is band_table, the * ; * table name bandtbl is used for internal purposes and is not called from * ; * the main program. * ; * * ; ****************************************************************************************************** band_table movwf tbloffst ;This routine replaces the original Table counter jump computation. bcf STATUS,C ;It must be used because the program counter rlcf tbloffst,F ;on a 18F2520 counts by twos rather than ones. movlw HIGH(bandtbl) btfsc STATUS,C incf WREG,W movwf PCLATH movlw LOW(bandtbl) addwf tbloffst,W btfsc STATUS,C incf PCLATH,F movwf PCL bandtbl retlw 0x80 ; 100,000 Hz retlw 0x01 ; retlw 0x86 ; retlw 0xA0 ; retlw 0x80 ; 570,000Hz retlw 0x08 ; retlw 0xB2 ; retlw 0x90 ; retlw 0x80 ; 1.9MHz retlw 0x1C ; retlw 0xFD ; retlw 0xE0 ; retlw 0x80 ; 3.56MHz retlw 0x36 ; retlw 0x52 ; retlw 0x40 ; retlw 0x00 ; 7.040MHz retlw 0x6B ; retlw 0x6C ; retlw 0xC0 ; retlw 0x00 ; 10.0MHz retlw 0x9A ; retlw 0x1D ; retlw 0x20 ; retlw 0x00 ; 30 meters retlw 0x9A ; retlw 0x1D ; retlw 0x20 ; retlw 0x00 ; 30 meters retlw 0x9A ; retlw 0x1D ; retlw 0x20 ; retlw 0x00 ; WWV retlw 0x99 ; retlw 0x96 ; retlw 0x80 ; retlw 0x00 ; 14.060MHz retlw 0xD6 ; retlw 0x89 ; retlw 0xE0 ; retlw 0x01 ; 17 meters retlw 0x13 ; retlw 0xB2 ; retlw 0x20 ; retlw 0x01 ; 21.060MHz retlw 0x41 ; retlw 0x59 ; retlw 0xA0 ; retlw 0x01 ; 12 meters retlw 0x7B ; retlw 0xCA ; retlw 0x90 ; retlw 0x01 ; 28.060MHz retlw 0xAB ; retlw 0x3F ; retlw 0x00 ; retlw 0x01 ; 29 MHz retlw 0xBA ; retlw 0x81 ; retlw 0x40 ; retlw 0x01 ; 30 MHz retlw 0xC9 ; retlw 0xC3 ; retlw 0x80 ; ; ***************************************************************************************************** ; * Decade Table * ; ***************************************************************************************************** ; * * ; * Each entry is four instructions long, with each group of four numbers representing the multiplier * ; * of each successive decade. Each four byte entry represents the decade increment which can get be * ; * assigned to fstep_3, fstep_2, fstep_1 and fstep_0. Data is stored MSB first. * ; * * ; ***************************************************************************************************** ; Jump to the appropriate value to return decade_table movwf tbloffst ;This routine replaces the original Table counter jump computation. bcf STATUS,C ;It must be used because the program counter rlcf tbloffst,F ;on a 18F2520 counts by twos rather than ones. movlw HIGH(dectbl) btfsc STATUS,C incf WREG,W movwf PCLATH movlw LOW(dectbl) addwf tbloffst,W btfsc STATUS,C incf PCLATH,F movwf PCL dectbl retlw 0x00 ; 1Hz increment retlw 0x00 retlw 0x00 retlw 0x01 retlw 0x00 ; 10 Hz increment retlw 0x00 retlw 0x00 retlw 0x0a retlw 0x00 ; 100 Hz increment retlw 0x00 retlw 0x00 retlw 0x64 retlw 0x00 ; 1 KHz increment retlw 0x00 retlw 0x03 retlw 0xE8 retlw 0x00 ; 10 KHz increment retlw 0x00 retlw 0x27 retlw 0x10 retlw 0x00 ; 100 KHz increment retlw 0x01 retlw 0x86 retlw 0xa0 ;retlw 0x00 ; 1 MHz increment ;retlw 0x0F ;retlw 0x42 ;retlw 0x40 ;retlw 0x00 ; 10 MHz increment ;retlw 0x98 ;retlw 0x96 ;retlw 0x80 ; ***************************************************************************************************** ; * Cursor Positioning Table * ; ***************************************************************************************************** ; * * ; * This table tells where to position the cursor when each new decade is selected. * ; ***************************************************************************************************** cursor_table movwf tbloffst ;This routine replaces the original Table counter jump computation. bcf STATUS,C ;It must be used because the program counter rlcf tbloffst,F ;on a 18F2520 counts by twos rather than ones. movlw HIGH(curtbl) btfsc STATUS,C incf WREG,W movwf PCLATH movlw LOW(curtbl) addwf tbloffst,W btfsc STATUS,C incf PCLATH,F movwf PCL curtbl retlw 0x8A retlw 0x89 retlw 0x88 retlw 0x86 retlw 0x85 ;retlw 4 ;retlw 2 ;retlw 1 ; ; ***************************************************************************************************** ; * IF Status Table * ; ***************************************************************************************************** ; * This table tells whether the IF frequency should be added or subtracted form the VFO displayed * ; * frequency. It is laid out by band. Thus, when a band is selected via the band coding on the * ; * band module, the program also reads this table to tell whether the band is initally USB or LSB. * ; * * ; ***************************************************************************************************** int_freq_table movwf tbloffst ;This routine replaces the original Table counter jump computation. bcf STATUS,C ;It must be used because the program counter rlcf tbloffst,F ;on a 18F2520 counts by twos rather than ones. movlw HIGH(tbl_if) btfsc STATUS,C incf WREG,W movwf PCLATH movlw LOW(tbl_if) addwf tbloffst,W btfsc STATUS,C incf PCLATH,F movwf PCL tbl_if retlw 0xFF ;100khz retlw 0xFF ;570kHz retlw 0xFF ;1.9MHz retlw 0xFF ;3.5MHz retlw 0xFF ;7MHz retlw 0x00 ;10MHz retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 retlw 0x00 ; ; ***************************************************************************************************** ; * setup * ; ***************************************************************************************************** ; * * ; * Purpose: This is the setup of the program. It initializes the LCD and DDS, sets PIC operating * ; * paramters, presets registers, and enables/disables device peripherals. Further, it * ; * establishes the starting frequency and mode. * ; ***************************************************************************************************** start ;Config statements movlw 0xF8 ;Set Internal Clock, movwf _CONFIG1H ; pins A6 & A7 are I/O movlw 0xF1 ;Brownout Reset Disabled movwf _CONFIG2L ; Power-Up imer Disabled movlw 0xFE ;Watchdog Timer Disabled movwf _CONFIG2H movlw 0x81 ;MCLR Enabled, PORTB A/D Disabled movwf _CONFIG3H ; CCP2 Mux on RC1 movlw 0xC0 ;No Background Debugger movwf _CONFIG4L ; No ICSP, No Stack Resets ;Register and Peripheral Setup clrf PIE1 ;No Peripheral Interrupts clrf PIE2 ; " movlw 0xCE ;Set Internal Oscillator for movwf OSCCON ; 1MHz clrf INTCON ; No interrupts for now movlw 0x07 ; Set the 3 LSBs of CMCON movwf CMCON ; to enable normal I/O clrf RCSTA ; Disable USART Receiver movlw 0x0F ;set up for ADCON1 to turn off movwf ADCON1 ; all ADCs clrf TXSTA ; Disable USART Transmitter clrf CVRCON ; Disable voltage reference clrf T1CON movlw 0x80 ;Setup to enable PortB pullups movwf INTCON2 ; accomplished movlw 0x38 ; Set Port A bits. movwf TRISA ; clrf LATA clrf TRISB ; Set port B to all outputs movlw 0xFF movwf TRISC call init_LCD ; Initialize the LCD call display_version ; Display title and version number call wait_512ms movff PORTC, holder ; read band info from port C movlw 0x0F ; "set up a band table read mask" andwf holder ; apply mask to hide the upper bits rlncf holder ; Reading must be multiplied by 4 rlncf holder ; to read properly from table call get_band call send_dds_word ; Send the power-on frequency to the ; AD9850 in serial mode call send_dds_word ; Send it twice. Sometimes needed for ; a clean start-up movlw 0x86 movwf cur_pos movlw 0x03 movwf decade call show_freq ; COnvert to BCD and display it clrf fstep_3 ;pre-load fstep values clrf fstep_2 ; " movlw 0x03 movwf fstep_1 ; " movlw 0xE8 ; " movwf fstep_0 ; " call wait_256ms clrf last_dir ; Clear the knob direction indicator ; Write "RCV" to LCD movlw 0xCD ; Point LCD at digit 9 of 2nd line call cmnd2LCD ; movlw 'R' ; Say "RCV " call data2LCD ; movlw 'C' ; call data2LCD ; movlw 'V' ; call data2LCD ; bcf VFOcontrol,TRANSMIT ; Proceed to the Main Program Loop ; ; ***************************************************************************************************** ; * Main Program Loop * ; ***************************************************************************************************** ; * * ; * Purpose: This is the Main Program Loop. The program's main loop calls poll_encoder, which * ; * continuously polls the rotary shaft encoder. When the shaft encoder has changed, the * ; * direction it moved is determined and stored in last_dir. The subroutine then returns to* ; * main. * ; * * ; ***************************************************************************************************** main call poll_encoder ; Check for knob movement (wait there!) ; Return here when encoder change detected call step ; No, just step goto main alt_update call show_freq ; Display the new frequency on the LCD call send_dds_word ; Send the new control word to the DDS chip return ;****************************************************************************************************** ; * step * ; ***************************************************************************************************** ; * This routine Adjusts the frequency by examining the last_dir value and either adds or subtracts * ; * the value of fstep_3..0 from the current frequency setting. * ; * * ;****************************************************************************************************** step ; Based on the knob direction, either add or ; subtract the increment, then update the ; LCD and DDS. btfsc last_dir,6 ; Is the knob going up? goto up ; Yes, then add the increment down ; Else, shaft direction is CCW call sub_step ; Subtract fstep from freq goto update ; Update LCD and DDS up call add_step ; Add fstep to freq update call show_freq ; Display the new frequency on the LCD call send_dds_word ; Send the new control word to the DDS chip return ; Continue main loop ; ; ****************************************************************************************************** ; * get_band * ; ****************************************************************************************************** ; * * ; * This routine reads the frequency value of a band table entry pointed to by band and returns it in * ; * freq_3...freq_0. * ; * * ; ****************************************************************************************************** get_band movf holder,w ; Get the index of the high byte call band_table ; Get the value into W movwf freq_3 ; Save it in freq_3 bcf freq_3,7 ;clear the MSD. It's used for IF Offset only. incf holder,f ; Increment index to next byte movf holder,w ; Get the index of the next byte call band_table ; Get the value into W movwf freq_2 ; Save it in freq_2 incf holder,f ; Increment index to the next byte movf holder,w ; Get the index to the next byte call band_table ; Get the value into W movwf freq_1 ; Save it in freq_1 incf holder,f ; Increment index to the low byte movf holder,w ; Get the index to the low byte call band_table ; Get the value into W movwf freq_0 ; Save it in freq_0 movlw 0x03 ; Get a constant three subwf holder,f ; Restore original value of band return ; Return to the caller ; ***************************************************************************************************** ; * add_step * ; ***************************************************************************************************** ; * This routine adds the 32 bit value of fstep to the 32 bit value in freq. * ; * * ; ***************************************************************************************************** add_step bcf STATUS, C ;clear the carry bit movf fstep_0,w ;Place the "frequency Step" value into W addwf freq_0, ;Add the frequency step value to Freq_0 movf fstep_1,w ;Place Fstep_1 into W addwfc freq_1, ;Add Fstep_1 to Freq_1 including the carry from the previous add movf fstep_2,w ;Place FStep_2 into W addwfc freq_2,F ;Add Fstep_2 to Freq_2 including the carry from the previous add movf fstep_3,w ;Place FStep_3 into W addwfc freq_3,F ;Add Fstep_3 to Freq_3 including the carry from the previous add return ; ***************************************************************************************************** ; * sub_step * ; ***************************************************************************************************** ; * * ; * Subtracts fstep0...3 from freq0...3 and leaves the result in freq0...3 * ; ***************************************************************************************************** sub_step movf fstep_0,w ;Place FStep_0 into W subwf freq_0,f ;Subtract FStep_0 from Freq_0 movf fstep_1,w ;Place FStep_1 into W subwfb freq_1,f ;Subtract FStep_1 and borrow from the previous subtract,from Freq_1 movf fstep_2,w ;Place FStep_2 into W subwfb freq_2,f ;Subtract FStep_2 and borrow from the previous subtract,from Freq_2 movf fstep_3,w ;Place FStep_3 into W subwfb freq_3,f ;Subtract FStep_3 and borrow from the previous subtract,from Freq_3 return ; ***************************************************************************************************** ; * poll_encoder * ; ***************************************************************************************************** ; * * ; * Purpose: This routine does the following: * ; * 1. Reads the encoder bits until a change is detected, then determines the direction the * ; * knob was moved. * ; * 2. Performs minor rotary encoder debounce. * ;****************************************************************************************************** poll_encoder call transmitter_active_look ; See if transmitter is active btfsc VFOcontrol,UPDT ; Need a DDS and LCD update? call alt_update ;Update the DDS and LCD call PB1 ;We aren't transmitting, process normally movff PORTC,encdr_old ; Get the current encoder value place it into "old" movlw 0xC0 ; Setup a mask andwf encdr_old ;mask off unimportant bits call wait_1ms movff PORTC,encdr_new ; Get the current encoder value place it into "old" movlw 0xC0 ; Setup a mask andwf encdr_new ;mask off unimportant bits movf encdr_new,w cpfseq encdr_old ;compare W and Encdr_old, skip if they're equal goto next ; they aren't equal, so proceed goto poll_encoder ; they are equal, there's been no change, return to main and do it again ; It changed. Now determine which direction the encoder turned. ;=============================================================================P ; Encoder bits are on RA0 and RA1 - the two low order bits of ren_new P ; A and B are "gray code" - 90 degrees out of phase (quadrature) P ; ___ ___ P ; | | | | P ; A ____| |___| |___ P ; ___ ___ P ; | | | | P ; B ___| |___| |___ P ; ^ ^ ^ ^ ^ ^ ^ ^ P ; a b c d a b c d P ; P ; A B P ; At point a: 0 0 P ; At point b: 1 0 P ; At point c: 1 1 P ; At point d: 0 1 P ; P ; Going UP, the sequence is a,b,c,d,a,b,c,d, etc. so the sequence is: P ; 00, 10, 11, 01, 00, 10, 11, 01, etc. P ; P ; Going DOWN, the sequence is d,c,b,a,d,c,b,a, etc. so the sequence is: P ; 01, 11, 10, 00, 01, 11, 10, 00, etc. P ; P ; To determine if the sequence is UP or DOWN: P ; 1) Take the "Right-Bit" of any pair. P ; 2) XOR it with the "Left-Bit" of the next pair in the sequence. P ; 3) If the result is 1 it is UP P ; If the result is 0 it is DOWN P ;=============================================================================P next bcf STATUS,C ; Clear the carry bit to prepare for rotate rrncf encdr_new,f ; Rotate old bits left to align "Right-Bit" movf encdr_old,w ; Set up new bits in W xorwf encdr_new,f ; XOR old (left shifted) with new bits movf encdr_new,w ; Put XOR results into W also andlw 0x40 ; Mask to look at only " right Bit" of pair movwf next_dir ; Save result (in W) as direction (bit=UP) xorwf last_dir,w ; See if direction is same as before ; ; Prevent encoder slip from giving a false change in direction. ; btfsc STATUS,Z ; Zero flag set? (i.e, is direction same?) goto pe_continue ; Yes, same direction so no slip; keep going movf next_dir,w ; No Zero-flag, so direction changed movwf last_dir ; Update the direction indicator movf encdr_new,w ; Save the current encoder bits (now in W) movwf encdr_old ; for next time goto poll_encoder ; Try again pe_continue clrf last_dir ; Clear last_dir (default is DN) btfsc next_dir,6 ; Are we going UP? goto pe_up ; Yes, go process it. ; Else, we are goiong down goto pe_movement ; Indicate that the encoder has moved pe_up movlw 0x40 ; Get UP value movwf last_dir ; and set in last_dir pe_movement ; Arrive here when encoder is being turned movff PORTC,encdr_new ; Get the current encoder value place it into "old" movlw 0xC0 ; Setup a mask andwf encdr_new ;mask off unimportant bits movff encdr_new,encdr_old ; Get the current encoder bits goto pe_exit ; Finish up pe_exit return ; Return to the caller ;****************************************************************************************************** ; * PB1 * ; ***************************************************************************************************** ; * ; Purpose: This function examines the state of pb1 by lloking for its corresponding input pin * ; to go low. It then waits briefly and checks if the pin is still low (being pushed) * ; or has returned to high (released). If it's still low, it waits again, and checks * ; again. If it's high, it then: * ; 1. Increments the cursor position to the right * ; * ; 2. Changes the step value by calling the next decade's values from the decade * ; table and placing those values into fstep. * ; * ;****************************************************************************************************** PB1 movff PORTA, temp ;is PB1 pressed? btfsc temp,pb1 goto pb_exit ; No. Exit this subroutine call wait_1ms movff PORTA,temp BTFSS temp, pb1 ; Yes. Has it been released yet? goto PB1 ; No. Wait for PB1 to be released incf decade ; Yes, incement the decade value movlw 0x05 ; Load the "highest_decade" comparison value cpfslt decade ; Does the current decade value exceed the highest value? clrf decade ; Yes. Reset the decade value movff decade,holder ; No. accept the value and place it into "holder" movf decade,w ;prepare to read the proper cursor position call cursor_table ;read the position movwf cur_pos ;store the cursor position info rlcf holder ; multiply holder by 4 for the table read rlcf holder ; " movf holder,w ;move holder into w for the table read call decade_table ; table read movwf fstep_3 ; move the obtained value into Step_3 incf holder ;increment holder movf holder,w ;move holder into w for the next table read call decade_table ; next table read movwf fstep_2 ;move obtained value into step_2 incf holder ;increment holder for next table read movf holder,w ;move holder into w for the next table read call decade_table ;call the table read movwf fstep_1 ;move the results into step_1 incf holder ;increment holder movf holder,w ;move to w for the next table read call decade_table ;perform the final read movwf fstep_0 ;move the results into step_0 movf cur_pos,w call cmnd2LCD ; Display the new frequency on the LCD pb_exit return ;****************************************************************************************************** ; * process_int * ; ***************************************************************************************************** ; * Function: For a CW transceiver, the receive signal is offset by the intermediate frequency (IF)* ; * Thus, if I display the actual DDS frequency, it would be off by the IF. This offset * ; * adjusts for that. * ; * * ; ***************************************************************************************************** process_int movff freq_0,vfo_a_0 movff freq_1,vfo_a_1 movff freq_2,vfo_a_2 movff freq_3,vfo_a_3 btfsc VFOcontrol,TRANSMIT return movff PORTC,holder ;read the "Band" info movlw 0x0F andwf holder ;mask all but actual band info movf holder,w ;put the number in holder into w call int_freq_table ;call the band table movwf holder ;transfer the table read into holder tstfsz holder goto add_if ;bit is set, add the IF goto sub_if ;is clear, subtract the IF add_if bcf STATUS, C ;clear the carry bit movlw int_0 addwf vfo_a_0,f movlw int_1 addwfc vfo_a_1,f movlw int_2 addwfc vfo_a_2,f movlw int_3 addwfc vfo_a_3,f return ;go to the place in "calc_dds_word" where the ; transmit status is tested, and IF is processed or not. sub_if movlw int_0 subwf vfo_a_0,f movlw int_1 subwfb vfo_a_1,f movlw int_2 subwfb vfo_a_2,f movlw int_3 subwfb vfo_a_3,f return ;go to the place in "calc_dds_word" where the ; transmit status is tested, and IF is processed or not. ;****************************************************************************************************** ; * calc_dds_word * ; * * ;****************************************************************************************************** ; * Purpose: Multiply the 32 bit number for oscillator frequency times the 32-bit number for the * ; * displayed frequency. The result is stored in AD9850_3...0. * ; * * ; ***************************************************************************************************** calc_dds_word clrf AD9850_0 ; Clear the AD9850/AD9851 control word bytes clrf AD9850_1 ; clrf AD9850_2 ; clrf AD9850_3 ; clrf AD9850_4 ; call process_int ;Call sub to compute the I.F. offset (if any) retn_to_word clrf osc_temp_3 clrf osc_temp_2 clrf osc_temp_1 clrf osc_temp_0 movlw 0x20 ; Set count to 32 (4 osc bytes of 8 bits) movwf mult_count ; Keep running count movlw ref_osc_3 movwf osc_temp_3 movlw ref_osc_2 movwf osc_temp_2 movlw ref_osc_1 movwf osc_temp_1 movlw ref_osc_0 movwf osc_temp_0 mult_loop bcf STATUS,C ; Start with Carry clear btfss osc_temp_0,0 ; Is bit 0 (Least Significant bit) set? goto noAdd ; No, don't need to add freq term to total movf vfo_a_0,w ; Get the "normal" freq_0 term addwf AD9850_1,f ; Add it in to total btfss STATUS,C ; Does this addition result in a carry? goto add7 ; No, continue with next freq term incfsz AD9850_2,f ; Yes, add one and check for another carry goto add7 ; No, continue with next freq term incfsz AD9850_3,f ; Yes, add one and check for another carry goto add7 ; No, continue with next freq term incf AD9850_4,f ; Yes, add one and continue add7 movf vfo_a_1,w ; Get the "normal" freq_0 term addwf AD9850_2,f ; Add freq term to total in correct position btfss STATUS,C ; Does this addition result in a carry? goto add8 ; No, continue with next freq term incfsz AD9850_3,f ; Yes, add one and check for another carry goto add8 ; No, continue with next freq term incf AD9850_4,f ; Yes, add one and continue add8 movf vfo_a_2,w ; Get the "normal" freq_2 term addwf AD9850_3,f ; Add freq term to total in correct position btfss STATUS,C ; Does this addition result in a carry? goto add9 ; No, continue with next freq term incf AD9850_4,f ; Yes, add one and continue add9 movf vfo_a_3,w ; Get the "normal" freq_3 term addwf AD9850_4,f ; Add freq term to total in correct position noAdd rrcf AD9850_4,f ; Shift next multiplier bit into position rrcf AD9850_3,f ; Rotate bits to right from byte to byte rrcf AD9850_2,f ; rrcf AD9850_1,f ; rrcf AD9850_0,f ; rrcf osc_temp_3,f,1 ; Shift next multiplicand bit into position rrcf osc_temp_2,f,1 ; Rotate bits to right from byte to byte rrcf osc_temp_1,f,1 ; rrcf osc_temp_0,f,1 ; decfsz mult_count,f ; One more bit has been done. Are we done? goto mult_loop ; No, go back to use this bit clrf AD9850_4 ; Last byte to be sent ; Mult answer is in bytes _3 .. _0 return ; Done. No movlw 0xC3 call cmnd2LCD movlw 'N' call data2LCD goto retn_to_word Yes movlw 0xC3 call cmnd2LCD movlw 'Y' call data2LCD goto retn_to_word ; ; ***************************************************************************************************** ; * send_dds_word * ;****************************************************************************************************** ; * Purpose: This routine sends the computed frequency word to the DDS using a serial data transfer. * ; * * ; ***************************************************************************************************** send_dds_word call calc_dds_word movff AD9850_0, vfo_b_0 movff AD9850_1, vfo_b_1 movff AD9850_2, vfo_b_2 movff AD9850_3, vfo_b_3 movff AD9850_4, vfo_b_4 bcf LATA, dds_data ;preset data output to "0" movlw 0x08 movwf bit_count ;preset bit_count with 8 serial1 btfsc vfo_b_0,0 ;is bit "0" low? bsf LATA, dds_data ; -->no, so set the data bit High bsf LATA, dds_clk ; whether data is set high or low, bcf LATA, dds_clk ; toggle the clock bcf LATA, dds_data ; set the data bit back low to prepare for the next bit rrcf vfo_b_0 ; rotate the original data 1 bit to the right decfsz bit_count ; decrement bit_count by 1. Have we examined & sent 8 bits yet? goto serial1 ; -->no, go back to the beginning and examine another bit ; -->yes, move on to the next word movlw 0x08 ; same routine, next digit movwf bit_count serial2 btfsc vfo_b_1,0 bsf LATA, dds_data bsf LATA, dds_clk bcf LATA, dds_clk bcf LATA, dds_data rrcf vfo_b_1 decfsz bit_count goto serial2 movlw 0x08 ; same routine, next digit movwf bit_count serial3 btfsc vfo_b_2,0 bsf LATA, dds_data bsf LATA, dds_clk bcf LATA, dds_clk bcf LATA, dds_data rrcf vfo_b_2 decfsz bit_count goto serial3 movlw 0x08 ; same routine, next digit movwf bit_count serial4 btfsc vfo_b_3,0 bsf LATA, dds_data bsf LATA, dds_clk bcf LATA, dds_clk bcf LATA, dds_data rrcf vfo_b_3 decfsz bit_count goto serial4 movlw 0x08 ; same routine, last digit movwf bit_count serial5 bcf LATA, dds_data bsf LATA, dds_clk bcf LATA, dds_clk bcf LATA, dds_data rrcf vfo_b_4 decfsz bit_count goto serial5 bcf LATA, dds_data ; Leave the data bit low (just in case) bsf LATA, dds_ud ; Send load signal to the AD9850/AD9851 bcf LATA, dds_ud ; return ; Go back to the calling routine ; ;***************************************************************************** ; * transmitter_active_look * ; **************************************************************************** ; * * ; * Purpose: This routine will determine if the transmitter is active. * ; * It will set or clear TRANSMIT flag based on the input signal * ; * from transmitter keying circuitry. It then updates the LCD * ; * and DDS. Finally, it places eeither Xmt or Rcv on the LCD * ; * as a visual indicator of the transmit status. * ;***************************************************************************** transmitter_active_look ; Detect whether XMIT_keyed pin has just changed on this pass btfsc PORTA,pb3 goto transmitter_not_being_keyed ; Transmitter is being keyed. See if it's a change from previous pass btfsc VFOcontrol,TRANSMIT ; Was it transmitting on last pass? goto transmit_setup_exit ; Yes, no change, so exit. bsf VFOcontrol,TRANSMIT ; No, set transmit active bsf VFOcontrol,UPDT ; We need an update of DDS and LCD ; Write to LCD movlw 0xCD ; Point LCD at digit 8 of 2nd line call cmnd2LCD ; movlw 'X' ; Say "XMT " call data2LCD ; movlw 'M' ; call data2LCD ; movlw 'T' ; call data2LCD ; goto transmit_setup_exit ; exit ; transmitter_not_being_keyed btfss VFOcontrol,TRANSMIT ; Was it transmitting on last pass? goto transmit_setup_exit ; No, no change, so exit. bcf VFOcontrol,TRANSMIT ; Yes, clear transmit active bsf VFOcontrol,UPDT ; We need an update of DDS and LCD ; Write to LCD movlw 0xCD ; Point LCD at digit 9 of 2nd line call cmnd2LCD ; movlw 'R' ; Say "RCV " call data2LCD ; movlw 'C' ; call data2LCD ; movlw 'V' ; call data2LCD ; transmit_setup_exit return ; Return to caller ; ****************************************************************************************************** ; * init_LCD * ; ****************************************************************************************************** ; * * ; * Purpose: Power on initialization of Liquid Crystal Display. ; * * ; ****************************************************************************************************** ; init_LCD call wait_64ms ; Wait for LCD to power up ; Put 4-bit command in RB3..RB0 ; PIC's RB3..RB0 lines connect to LCD's DB7..DB4 (pins 14-11) movlw 0x30 ; LCD init instruction (First) movwf PORTB ; Send to LCD via RB7..RB4 bsf PORTB,LCD_E ; Set the LCD E line high, call wait_64ms ; wait a "long" time, bcf PORTB,LCD_E ; and then Clear E movlw 0x30 ; LCD init instruction (Second) movwf PORTB ; Send to LCD bsf PORTB,LCD_E ; Set E high, call wait_64ms ; wait a while, bcf PORTB,LCD_E ; and then Clear E movlw 0x30 ; LCD init instruction (Third) movwf PORTB ; Send to LCD bsf PORTB,LCD_E ; Set E high, call wait_64ms ; wait a while, bcf PORTB,LCD_E ; and then Clear E movlw 0x20 ; 4-bit mode instruction movwf PORTB ; Send to LCD via RB3..RB0 bsf PORTB,LCD_E ; Set E high, call wait_64ms ; wait a while, bcf PORTB,LCD_E ; and then Clear E movlw 0x28 ; 2 line, 5x7 matrix call cmnd2LCD movlw 0x08 ;Turn Display Off call cmnd2LCD movlw 0x01 ; Clear Display call cmnd2LCD movlw 0x06 ; Write from Left to right, each call cmnd2LCD ; write advanced address by 1 movlw 0x0E ; Turn Display on with underline Cursor call cmnd2LCD return ; ; ; ; ; ***************************************************************************************************** ; * display_version * ; ***************************************************************************************************** ; * * ; * Purpose: Display version and other info on LCD for a moment when powered up. * ; * * ; ***************************************************************************************************** display_version movlw 0x80 ; Point LCD at digit 1 call cmnd2LCD ; movlw '9' ; digit 1 call data2LCD ; movlw '8' ; digit 2 call data2LCD ; movlw '5' ; digit 3 call data2LCD ; movlw '0' ; digit 4 call data2LCD ; movlw ' ' ; digit 5 call data2LCD ; movlw 'V' ; digit 6 call data2LCD ; movlw 'F' ; digit 7 call data2LCD ; movlw 'O' ; digit 8 call data2LCD ; movlw MCODE_REV_0 ; Get mcode rev byte call data2LCD ; and display it (digit 9) movlw MCODE_REV_1 ; Get mcode rev byte call data2LCD ; and display it (digit 10) movlw MCODE_REV_2 ; Get mcode rev byte call data2LCD ; and display it (digit 11) movlw MCODE_REV_3 ; Get mcode rev byte call data2LCD ; and display it (digit 12) movlw MCODE_REV_4 ; Get mcode rev byte call data2LCD ; and display it (digit 13) movlw MCODE_REV_5 ; Get mcode rev byte call data2LCD ; and display it (digit 14) movlw MCODE_REV_6 ; Get mcode rev byte call data2LCD ; and display it (digit 15) movlw MCODE_REV_7 ; Get mcode rev byte call data2LCD ; and display it (digit 16) ; return ; ; ***************************************************************************************************** ; * bin2bcd * ; ***************************************************************************************************** ; * Purpose: This subroutine converts a 32 bit binary number to a 10 digit ASCII number suitable for * ; * display on the LCD. * ; * * ; ***************************************************************************************************** ; ;This subroutine operates this way: ;The first "rlcf"s rotate the digits through the entire ;frequency word and make each digit available one at a ;time in the "carry" position. This carry is then used ;to increment the registers BCD_0 through BCD_4. It is ;not enough to simply increment these registers though, ;they must also be adjusted decimally---BCD digits ;represent up to 16 digits, while decimal (what we're ;actually displaying on the LCD) use 10. The decimal ;adjust must roll upward through each BCD register. Thus, ;after each "DAW" (Decimal Adjust "W"), the carry from ;that operation is introduced into the next higher ;register's DAW operation. ; This complete routine is then performed 32 times, ;once per digit, and counted down through the use of ;"BCD_Count" and the decfsz command. bin2BCD movlw 0x20 ;Load a count of 32 movwf BCD_count ; into bcd_count clrf WREG ;clear all local registers clrf BCD_0 clrf BCD_1 clrf BCD_2 clrf BCD_3 clrf BCD_4 clrf freq_0A clrf freq_1A clrf freq_2A clrf freq_3A movff freq_0, freq_0A ;place freq info into temp local registers movff freq_1, freq_1A ; " movff freq_2, freq_2A ; " movff freq_3, freq_3A ; " bcdloop rlcf freq_0A ;Rotate Freq_0 right with carry rlcf freq_1A ; same for freq_1 rlcf freq_2A ; same for freq_2 rlcf freq_3A ; same for freq_3 movf BCD_0,w addwfc BCD_0,w daw movwf BCD_0 movf BCD_1,w addwfc BCD_1,w daw movwf BCD_1 movf BCD_2,w addwfc BCD_2,w daw movwf BCD_2 movf BCD_3,w addwfc BCD_3,w daw movwf BCD_3 movf BCD_4,w addwfc BCD_4,w daw movwf BCD_4 decfsz BCD_count bra bcdloop return ; ; ****************************************************************************************************** ; * show_freq * ; ****************************************************************************************************** ; * * ; * Purpose: Display the frequency setting on the LCD in actual Hz; 14,025,000 Hz * ; * * ; ****************************************************************************************************** ; show_freq call bin2BCD movlw 0x80 ; Point the LCD to first LCD digit location call cmnd2LCD ; Send starting digit location to LCD movlw ' ' ; Send a space call data2LCD ; to position 1 of LCD ; ; Running 4-bit mode, so need to send Most Significant Nibble first. ; ; Extract and send "XXXX" from byte containing "XXXXYYYY" ; - Swap halves to get YYYYXXXX ; - Mask with 0x0F to get 0000XXXX ; - Add ASCII bias (0030XXXX) ; swapf BCD_3,w ; Swap 10MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; ; Extract and send "YYYY" from byte containing "XXXXYYYY" ; - Mask with 0x0F to get 0000YYYY ; - Add offset for ASCII character set in LCD (0030YYYY) ; movf BCD_3,w ; Put 1MHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD movlw ',' ; Get a comma call data2LCD ; Send byte in W to LCD swapf BCD_2,w ; Swap 100KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; movf BCD_2,w ; Put 10KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; swapf BCD_1,w ; Swap 1KHz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send byte in W to LCD ; movlw ',' ; Get a period call data2LCD ; Send byte in W to LCD ; movf BCD_1,w ; Put 100 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send data byte in W to LCD ; swapf BCD_0,w ; Swap 10 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000XXXX) addlw 0x30 ; Add offset for ASCII char set (0030XXXX) call data2LCD ; Send data byte in W to LCD ; movf BCD_0,w ; Put 1 Hz BCD digit into lower nibble of W andlw 0x0F ; Mask for lower nibble only (0000YYYY) addlw 0x30 ; Add offset for ASCII char set (0030YYYY) call data2LCD ; Send byte in W to LCD ; movlw ' ' ; Send a space call data2LCD ; to position 12 of LCD ; movlw 'H' ; Send a 'k' call data2LCD ; to position 13 of LCD ; movlw 'z' ; Send an "H" call data2LCD ; to position 14 of LCD ; movlw ' ' ; Send a 'z' call data2LCD ; to position 15 of LCD ; movlw ' ' ; Send a space call data2LCD ; to position 16 of LCD movf cur_pos,w call cmnd2LCD return ; ****************************************************************************************************** ; * busy_check * ; ****************************************************************************************************** ; * * ; * Purpose: Check if LCD is done with the last operation. This subroutine polls the LCD busy flag * ; * to determine if the LCD is still working on any previous operations. * ; ****************************************************************************************************** ; busy_check clrf PORTB ; Clear all outputs on PORTB movlw b'10000000' ; Set RB7 input, others outputs movwf TRISB ; via Tristate bcf PORTB,LCD_RS ; Set up LCD for Read Busy Flag (RS = 0) bsf PORTB,LCD_RW ; Set up LCD for Read (RW = 1) movlw 0xFF ; Set up constant 255 movwf timer1 ; for timer loop counter LCD_is_busy bsf PORTB,LCD_E ; Set E high movf PORTB,w ; Read PORTB into W movwf LCD_read ; Save W for later testing bcf PORTB,LCD_E ; Drop E again nop ; Wait a nop ; while bsf PORTB,LCD_E ; Pulse E high (dummy read of lower nibble), nop ; wait, bcf PORTB,LCD_E ; and drop E again decf timer1,f ; Decrement loop counter btfsc STATUS,Z ; Is loop counter down to zero? goto not_busy ; If yes, return regardless btfsc LCD_read,7 ; Is Busy Flag (RB7) in save byte clear? goto LCD_is_busy ; If not, it is busy so jump back not_busy return ; ; ****************************************************************************************************** ; * cmnd2LCD * ; ****************************************************************************************************** ; * Purpose: Send Command or Data byte to the LCD * ; ****************************************************************************************************** ; cmnd2LCD ; ****** Entry point ****** movwf LCD_char ; Save byte to write to LCD clrf rs_value ; Remember to clear RS (clear rs_value) bcf PORTB,LCD_RS ; Set RS for Command to LCD goto write2LCD ; Go to common code data2LCD ; ****** Entry point ******** movwf LCD_char ; Save byte to write to LCD bsf rs_value,0 ; Remember to set RS (set bit 0 of rs_value) bsf PORTB,LCD_RS ; Set RS for Data to LCD write2LCD call busy_check ; Check to see if LCD is ready for new data clrf PORTB ; Clear all of Port B (inputs and outputs) movlw 0x00 ; Set up to enable PORTB data pins movwf TRISB ; All pins (RB7..RB0) are back to outputs bcf PORTB,LCD_RW ; Set LCD back to Write mode (RW = 0) bcf PORTB,LCD_RS ; Guess RS should be clear btfsc rs_value,0 ; Should RS be clear? (is bit 0 == 0?) bsf PORTB,LCD_RS ; No, set RS ; ; Transfer Most Significant nibble (XXXX portion of XXXXYYYY) ; movlw 0x0F ; Set up mask andwf PORTB,f ; Keep RB3..RB0 but clear old RB7..RB4 movf LCD_char,w ; Put byte into W andlw 0xF0 ; Mask to give XXXX0000 in W iorwf PORTB,f ; To RB7..RB4 with RB3..RB0 unchanged bsf PORTB,LCD_E ; Pulse the E line high, nop ; wait, bcf PORTB,LCD_E ; and drop it again ; ; Transfer Least Significant nibble (YYYY portion of XXXXYYYY) ; movlw 0x0F ; Set up mask andwf PORTB,f ; Clear old RB7..RB4 swapf LCD_char,w ; Swap the nibbles of LCD_Char and place in W andlw 0xF0 ; Mask to give YYYY0000 in W iorwf PORTB,f ; To RB7..RB4 with RB3..RB0 unchanged bsf PORTB,LCD_E ; Pulse the E line high, nop ; wait, bcf PORTB,LCD_E ; and drop it again return ; ***************************************************************************** ; * Delays * ; ***************************************************************************** ; * * ; * Purpose: Wait for a specified number of milliseconds. * ; * * ; * Entry point wait_a_sec: Wait for 1 second * ; * Entry point wait_256ms: Wait for 256 msec * ; * Entry point wait_128ms: Wait for 128 msec * ; * Entry point wait_64ms : Wait for 64 msec * ; * Entry point wait_32ms : Wait for 32 msec * ; * Entry point wait_16ms : Wait for 16 msec * ; * Entry point wait_8ms : Wait for 8 msec * ; * Entry point wait_1ms * ; * * ; * Note: The time intervals named here depend on the master clock * ; * frequency of the PIC. A better neaming convention would * ; * have been "Longest", "Long", Mid", "Short", "shortest". * ; * Thus, don't expect these intervals to be precise or even * ; * approximate. * ; ***************************************************************************** ; wait_a_sec ; ****** Entry point ****** call wait_256ms ; call wait_256ms ; wait_512ms ; ****** Entry point ****** call wait_256ms ; call wait_256ms ; return wait_256ms ; ****** Entry point ****** call wait_128ms ; call wait_128ms ; return wait_128ms ; ****** Entry point ****** movlw 0xFF ; Set up outer loop movwf timer1 ; counter to 255 goto outer_loop ; Go to wait loops wait_64ms ; ****** Entry point ****** movlw 0x80 ; Set up outer loop movwf timer1 ; counter to 128 goto outer_loop ; Go to wait loops wait_32ms ; ****** Entry point ****** movlw 0x40 ; Set up outer loop movwf timer1 ; counter to 64 goto outer_loop ; Go to wait loops wait_16ms ; ****** Entry point ****** movlw 0x20 ; Set up outer loop movwf timer1 ; counter to 32 goto outer_loop ; Go to wait loops wait_8ms ; ****** Entry point ****** movlw 0x10 ; Set up outer loop movwf timer1 ; counter to 16 goto outer_loop ; Into the wait loops wait_1ms movlw 0x2 movwf timer1 ; ; Wait loops used by other wait routines ; - 1 microsecond per instruction (with a 4 MHz microprocessor crystal) ; - 510 instructions per inner loop ; - (Timer1 * 514) instructions (.514 msec) per outer loop ; - Round off to .5 ms per outer loop ; outer_loop movlw 0xFF ; Set up inner loop counter movwf timer2 ; to 255 inner_loop decfsz timer2,f ; Decrement inner loop counter goto inner_loop ; If inner loop counter not down to zero, ; then go back to inner loop again decfsz timer1,f ; Yes, Decrement outer loop counter goto outer_loop ; If outer loop counter not down to zero, ; then go back to outer loop again return ; Yes, return to caller ; ; ***************************************************************************** ; END