;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; QFPV, QwikFlash Performance Verification program ; Prepared by Dave Desrochers and Michael Cheng ; April 11, 2002 ; ;;;;;;; Description ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This program lets the user test all components of the QwikFlash board. These ; tested components are: U2 (MAX522 DAC), POT1, SW3, RPG1, TMP1 (Temperature ; sensor), LCD1, and LEDs D1, D2, D4, D5, and D6. ; ; To perform a complete test: ; 1) Turn the RPG left and right, you should see LEDs D4 and D5 light up to ; represent the binary values 0 to 3. ; 2) Push SW3 to toggle the D6 LED. In addition, it will complement the value ; being displayed on the lower right hand corner of the LCD. ; 3) Turn POT1 to change the lower and upper right hand values being displayed ; on the LCD. This uses the A/D convertor to test the POT. ; 4) The values on the right hand side should be close to the same or the ; complement (0xff-POTvalue) of each other. The lower number tests the POT ; value through the A/D converter while the upper value tests U2 (the DAC) ; on the board by connecting pin E2/AN7 to pin A for the POT value or pin B ; for the complemented POT value. The connection is done by shorting either ; pin A or pin B (complement) to E2/AN7. ; 5) The lower left hand corner should display the current temperature. This ; may not be calibrated since all analog temperatures give different results. ; In any case anything above 125 degrees is incorrect. ; 6) LED D2 should blink every 2.5 seconds for a 2.5 MHz internal clock ; (10 MHz oscillator). ; 7) LED D1 should be lit. This is the power indicator. ; 8) Reset button should, of course, reset the system. ; ;;;;;;; Program hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Mainline ; Initial ; InitLCD ; LoopTime ; DisplayC ; T40 ; BlinkAlive ; Pbutton ; PBToggle ; RPG ; LEDCounter ; ReadPot ; HexDisplay ; ConvertLowerNibble ; DisplayV ; T40 ; PotToDACToADC ; SPItransfer ; T40 ; HexDisplay ; ConvertLowerNibble ; DisplayV ; T40 ; ReadTemp ; T40 ; FXM1608U ; FXD2416U ; TempDisplay ; FXD0808U ; DisplayV ; T40 ; LoopTime ; ;;;;;;; Assembler directives ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC18F452, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON #include P18F452.inc __CONFIG _CONFIG1H, _HS_OSC_1H ;HS oscillator __CONFIG _CONFIG2L, _PWRT_ON_2L & _BOR_ON_2L & _BORV_42_2L ;Reset __CONFIG _CONFIG2H, _WDT_OFF_2H ;Watchdog timer disabled __CONFIG _CONFIG3H, _CCP2MX_ON_3H ;CCP2 to RC1 (rather than to RB3) __CONFIG _CONFIG4L, _LVP_OFF_4L ;RB5 enabled for I/O errorlevel -314, -315 ;Ignore lfsr messages ;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cblock 0x000 ; Beginning of Access RAM COUNT ; Counter available as local to subroutines ALIVECNT ; Counter for blinking "Alive" LED (D2) DELRPG ; Used DELRPG to let the programmer know whether ; or not the RPG moved and at which direction ; (-1 for left or +1 for right). OLDPORTD ; Holds the old value of PortD to compare with ; the new value. Used in RPG. PBSTATE ; Byte that holds all necessary bit values to ; make SW3 function. Set in PButton subroutine. PBCOUNT ; Used in PButton subroutine. If it's less than ; PBthresh, then ISC is set. Otherwise, ISA is set. ; This lets PButton know if it's a short or long push. BINCOUNT ; "Binary Count." Holds the value of the number ; currently being displayed in binary on LEDs D4 and D5. POTVALUE ; Holds the current value of the POT. POTVALUECOMP ; Holds the complemented (0xff-POTVALUE) value of POTVALUE. TEMPSTR:6 ; String that hold the current temperature to be displayed on the LCD. HEXSTR:4 ; String used to display the hex values located ; on the right side of the LCD (output of the POT and the DAC) BYTE ; Temporary variable used for anything. SPIRECEIVE ; Not used in this program, but if something ; was to be sent back to the SPI, it would ; be saved in this variable. RPGCNT ; Used internally for the RPG subroutine. endc #include c:\math18\mathvars.inc ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; These are the binary variables used for the PButton subroutine. ISC equ 0 ; Initiate screen change for long press. ISA equ 1 ; Initiate secondary action for short press. PDONE equ 2 ; Pushbutton action has been taken. OLDPB equ 3 ; Old state of pushbutton. NEWPB equ 4 ; New state of pushbutton. PBthres equ 30 ; Pushbutton threshold ofr a long press. ; Lets the programmer store a literal value in a RAM location directly. ; MOVLF macro literal,dest movlw literal movwf dest endm ; Used to point TBLPTRH to a constant string (stored in program memory) so that ; it can be displayed on the LCD. ; POINT macro stringname MOVLF high stringname, TBLPTRH MOVLF low stringname, TBLPTRL endm ;;;;;;; Vectors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0000 ; Reset vector. nop goto Mainline org 0x0008 ; High priority interrupt vector. goto $ ; If interrupt is generated, go into this infinite loop. org 0x0018 ; Low priority interrupt vector. goto $ ; If interrupt is generated, go into this infinite loop. ;;;;;;; Mainline program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Mainline rcall Initial ;Initialize everything. ;LOOP_ L1 btg PORTB,RB0 ; Toggles the B0/INT0 pin to measure the total time. Should be 10ms. rcall BlinkAlive ; Blinks the D2 LED every 2.5 seconds. rcall Pbutton ; Checks to see if SW3 is pushed or not. rcall PBToggle ; If SW3 is pushed, toggle D6. rcall RPG ; Tests whether or not the RPG turned. rcall LEDCounter ; Tests D4 and D5 LEDs. rcall ReadPot ; Reads the current value of the POT through the A/D converter. rcall PotToDACToADC ; Sends the value of the POT and its complement through the DAC, ; back to the A/D converter to be displayed in the upper right ; hand corner. rcall ReadTemp ; Reads the temperature using TMP1 and displays it. rcall LoopTime ; The remainder of the 10ms is spent in here by using Timer1. ;ENDLOOP_ bra L1 PL1 ;;;;;;; Initial subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine performs all initializations of variables and registers. Initial MOVLF B'01001110', ADCON1 ; Enable PORTA & PORTE digital I/O pins for the A/D converter. MOVLF B'01100001', ADCON0 ; Sets up the A/D converter for the POT. MOVLF B'11100001', TRISA ; Set I/O for PORTA. '1' is input while '0' is output. MOVLF B'11011100', TRISB ; Set I/O for PORTB. MOVLF B'11010010', TRISC ; Set I/0 for PORTC. MOVLF B'00001111', TRISD ; Set I/0 for PORTD. MOVLF B'00000100', TRISE ; Set I/0 for PORTE. MOVLF B'00010000', PORTA ; Turn off all four LEDs driven from PORTA. MOVLF B'00100000', SSPCON1 ; Sets up the SPI interface for the DAC MOVLF B'11000000', SSPSTAT ; to use with a 2.5 MHz clock. ; This sets up Timer1 and CCP1. MOVLF B'00001000', T3CON ; Sets up so that T1 is used with CCP1. MOVLF B'10000001', T1CON ; continue setting up T1 to CCP1. MOVLF B'00001011', CCP1CON ; Set up to trigger special event so that PIR1, CCP1IF will be set. MOVLF B'01100001', CCPR1H ; Set the comparator to 25,000. MOVLF B'10101000', CCPR1L ; Initialize variables for RPG and PButton subroutines. movff PORTD, OLDPORTD ; Initialize "old" value for RPG. clrf RPGCNT ; Initialize the RPG counter. MOVLF B'00001000', PBSTATE ; Initialize pushbutton states. clrf PBCOUNT clrf BINCOUNT ; Set up the characters that will not ever change MOVLF 0xC0, TEMPSTR ; Sets the position of TEMPSTR to the lower left hand corner. MOVLF 0xDF, TEMPSTR+3 ; Displays the degree symbol. MOVLF A'F', TEMPSTR+4 ; Displays 'F' for the temperature. MOVLF 0x00, TEMPSTR+5 ; Terminating byte for a string. MOVLF 0x00, HEXSTR+3 ; Terminating byte for HEXSTR will never change. rcall InitLCD ; Initialize LCD. POINT QFPV ; Display 'QFPV' on the LCD. rcall DisplayC return ;;;;;;; InitLCD subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Initialize the Optrex 8x2 character LCD. ; First wait for 0.1 second, to get past display's power-on reset time. InitLCD MOVLF 10,COUNT ; Wait 0.1 second. ;REPEAT_ L2 rcall LoopTime ; Call LoopTime 10 times. decf COUNT,F ;UNTIL_ .Z. bnz L2 RL2 bcf PORTE,0 ; RS=0 for command. POINT LCDstr ; Set up table pointer to initialization string. tblrd* ; Get first byte from string into TABLAT. ;REPEAT_ L3 bsf PORTE,1 ; Drive E high. movff TABLAT,PORTD ; Send upper nibble. bcf PORTE,1 ; Drive E low so LCD will process input. rcall LoopTime ; Wait ten milliseconds. bsf PORTE,1 ; Drive E high. swapf TABLAT,W ; Swap nibbles. movwf PORTD ; Send lower nibble. bcf PORTE,1 ; Drive E low so LCD will process input. rcall LoopTime ; Wait ten milliseconds. tblrd+* ; Increment pointer and get next byte. movf TABLAT,F ; Is it zero? ;UNTIL_ .Z. bnz L3 RL3 return ;;;;;;; T40 subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Pause for 40 microseconds or 40/0.4 = 100 clock cycles. ; Assumes 10/4 = 2.5 MHz internal clock rate. T40 movlw 100/3 ; Each REPEAT loop takes 3 cycles. movwf COUNT ;REPEAT_ L4 decf COUNT,F ;UNTIL_ .Z. bnz L4 RL4 return ;;;;;;;;DisplayC subroutine;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine is called with TBLPTR containing the address of a constant ; display string. It sends the bytes of the string to the LCD. The first ; byte sets the cursor position. The remaining bytes are displayed, beginning ; at that position. ; This subroutine expects a normal one-byte cursor-positioning code, 0xhh, or ; an occasionally used two-byte cursor-positioning code of the form 0x00hh. DisplayC bcf PORTE,0 ; Drive RS pin low for cursor-positioning code. tblrd* ; Get byte from string into TABLAT. movf TABLAT,F ; Check for leading zero byte. ;IF_ .Z. bnz L5 tblrd+* ; If zero, get next byte. ;ENDIF_ L5 ;REPEAT_ L6 bsf PORTE,1 ; Drive E pin high. movff TABLAT,PORTD ; Send upper nibble. bcf PORTE,1 ; Drive E pin low so LCD will accept nibble. bsf PORTE,1 ; Drive E pin high again. swapf TABLAT,W ; Swap nibbles. movwf PORTD ; Write lower nibble. bcf PORTE,1 ; Drive E pin low so LCD will process byte. rcall T40 ; Wait 40 usec. bsf PORTE,0 ; Drive RS pin high for displayable characters. tblrd+* ; Increment pointer, then get next byte. movf TABLAT,F ; Is it zero? ;UNTIL_ .Z. bnz L6 RL6 return ;;;;;;; DisplayV subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine is called with FSR0 containing the address of a variable ; display string. It sends the bytes of the string to the LCD. The first ; byte sets the cursor position. The remaining bytes are displayed, beginning ; at that position. DisplayV bcf PORTE,0 ; Drive RS pin low for cursor positioning code. ;REPEAT_ L7 bsf PORTE,1 ; Drive E pin high. movff INDF0,PORTD ; Send upper nibble. bcf PORTE,1 ; Drive E pin low so LCD will accept nibble. bsf PORTE,1 ; Drive E pin high again. swapf INDF0,W ; Swap nibbles. movwf PORTD ; Write lower nibble. bcf PORTE,1 ; Drive E pin low so LCD will process byte. rcall T40 ; Wait 40 usec. bsf PORTE,0 ; Drive RS pin high for displayable characters. movf PREINC0,W ; Increment pointer, then get next byte. ;UNTIL_ .Z. ; Is it zero? bnz L7 RL7 return ;;;;;;; RPG subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine decyphers RPG changes into values of DELRPG of 0, +1, or -1. ; DELRPG = +1 for CW change, 0 for no change, and -1 for CCW change. RPG clrf DELRPG ; Clear for "no change" return value. movf PORTD,W ; Copy PORTD into W. movwf TEMP ; and TEMP. xorwf OLDPORTD,W ; Any change? andlw B'00000011' ; If not, set the Z flag. ;IF_ .NZ. ; If the two bits have changed then... bz L8 rrcf OLDPORTD,W ; Form what a CCW change would produce. ;IF_ .C. ; Make new bit 1 = complement of old bit 0. bnc L9 bcf WREG,1 ;ELSE_ bra L10 L9 bsf WREG,1 ;ENDIF_ L10 xorwf TEMP,W ; Did the RPG actually change to this output? andlw B'00000011' ;IF_ .Z. ; If so, then change DELRPG to -1 for CCW. bnz L11 decf DELRPG,F ;ELSE_ ; Otherwise, change DELRPG to +1 for CW. bra L12 L11 incf DELRPG,F ;ENDIF_ L12 ;ENDIF_ L8 movff TEMP,OLDPORTD ; Save PORTD as OLDPORTD for ten ms from now. return ;;;;;;;;;;;;;;;;;; Pbutton subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine sorts out long and short pushbuttons presses into two outputs: ; ISC=1: Initiate screen change for slow press. ; ISA=1: Initialize secondary action for fast press. ; PDONE=1: One of the above actions has occurred for this press. Pbutton bcf PBSTATE,ISC ; Clear initiate screen change bit. bcf PBSTATE,ISA ; Clear initiate secondary action bit. ;IF_ PORTD,RD3 == 1 ; Copy pushbutton state to NEWPB. btfss PORTD,RD3 bra L13 bsf PBSTATE, NEWPB ;ELSE_ bra L14 L13 bcf PBSTATE, NEWPB ;ENDIF_ L14 ;IF_ PBSTATE,OLDPB == 1 ; Look for leading edge. btfss PBSTATE,OLDPB bra L15 ;IF_ PBSTATE,NEWPB == 0 ; (OLDPB = 1, NEWPB = 0). btfsc PBSTATE,NEWPB bra L16 MOVLF PBthres, PBCOUNT ; Start counter. ;ENDIF_ L16 ;ENDIF_ L15 ;IF_ PBSTATE,NEWPB == 0 ; Pushbutton is still pressed. btfsc PBSTATE,NEWPB bra L17 movf PBCOUNT, F ;IF_ .Z. ; and counter has passed threshold. bnz L18 ;IF_ PBSTATE,PDONE == 0 ; and no action has yet been taken. btfsc PBSTATE,PDONE bra L19 bsf PBSTATE,ISC ; Set ISC = 1 to indicate change in button. bsf PBSTATE,PDONE ; Done with pulse. ;ENDIF_ L19 ;ENDIF_ L18 ;ELSE_ ; Pushbutton has been release. bra L20 L17 bcf PBSTATE,PDONE ; so clear PDONE. ;ENDIF_ L20 ;IF_ PBSTATE,OLDPB == 0 ; Look for trailing edge. btfsc PBSTATE,OLDPB bra L21 ;IF_ PBSTATE,NEWPB == 1 ; (OLDPB = 0, NEWPB = 1). btfss PBSTATE,NEWPB bra L22 movf PBCOUNT, F ;IF_ .NZ. ; Fast pulse. bz L23 bsf PBSTATE,ISA ; Initiate secondary action. ;ENDIF_ L23 bcf PBSTATE,PDONE ; Done with pulse. clrf PBCOUNT ; Finish counting. ;ENDIF_ L22 ;ENDIF_ L21 movf PBCOUNT, F ; Has counter reached zero? ;IF_ .NZ. ; If not, then decrement it. bz L24 decf PBCOUNT, F ;ENDIF_ L24 ;IF_ PBSTATE,NEWPB == 1 ; Copy NEWPB to OLDPB. btfss PBSTATE,NEWPB bra L25 bsf PBSTATE,OLDPB ;ELSE_ bra L26 L25 bcf PBSTATE,OLDPB ;ENDIF_ L26 return ;;;;;;;;;;;;;;;;;; ReadPot ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine reads the value of the POT using the A/D and stores it in POTVALUE. ; It also complements POTVALUE and stores it in POTVALUECOMP. Next, depending on ; whether or not PORTA, RA1 (LED D6) is low or high, either POTVALUE or POTVALUECOMP ; is displayed respectively. ReadPot bsf ADCON0,GO_DONE ; Begins the A/D conversion. ;REPEAT_ L27 ;UNTIL_ ADCON0,GO_DONE == 0 ; Wait until A/D conversion is finished. btfsc ADCON0,GO_DONE bra L27 RL27 movff ADRESH,POTVALUE ; Move the upper byte from the A/D into POTVALUE. movf ADRESH,W ; Perform 0xFF-POTVALUE and store it in POTVALUECOMP. sublw 0xff movwf POTVALUECOMP MOVLF 0xC6,HEXSTR ; Set the position of HEXSTR to the lower right hand corner of the LCD. ;IF_ PORTA,RA1 == 1 ; Is D6 lit? btfss PORTA,RA1 bra L28 movff POTVALUECOMP, BYTE ; .. then set BYTE to display the complement. ;ELSE_ bra L29 L28 movff POTVALUE, BYTE ; .. otherwise display the value of the POT. ;ENDIF_ L29 rcall HexDisplay ; Converts BYTE into a displayable string and displays it. return ;;;;;; HexDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine takes in BYTE and converts into an ASCII string in hex and stores it into ; HEXSTR for display. HexDisplay rcall ConvertLowerNibble ; Converts lower nibble of BYTE to hex and stores it in W. movwf HEXSTR+2 ; Move the ASCII value into the most significant byte of HEXSTR. swapf BYTE,F ; Swap nibbles of BYTE to convert the upper nibble. rcall ConvertLowerNibble ; Convert the old upper nibble of BYTE to hex and stores it in W. movwf HEXSTR+1 ; Move the ASCII value into the 2nd byte of HEXSTR. lfsr 0, HEXSTR ; Loads address of HEXSTR to fsr0 to display HEXSTR. rcall DisplayV ; Call DisplayV to display HEXSTR on LCD. return ;;;;;; ConvertLowerNibble subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine takes lower nibble of BYTE and converts it into its ASCII hex value. ConvertLowerNibble movf BYTE, W ; Loads BYTE into W. andlw B'00001111' ; Masks out the upper nibble. sublw 0x09 ; Test if it's greater than 9. ;IF_ .N. ; If, after masking, it is greater than 9, then it is a letter. bnn L30 movf BYTE,W ; Load BYTE into W. andlw B'00001111' ; Mask out the upper nibble. addlw 0x37 ; Add offset to obtain the letter's ASCII value. ;ELSE_ ; If it's less than 9, then it's a number. bra L31 L30 movf BYTE,W andlw B'00001111' iorlw 0x30 ; Then add offset of 30 to obtain the numeric's ASCII value. ;ENDIF_ L31 return ;;;;;;; PBToggle subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine tests ISA and ISC to see if SW3 has been pressed and toggles the state ; of LED D6 accordingly. PBToggle ;IF_ PBSTATE,ISA == 1 ; If ISA = 1, the button's been pressed. btfss PBSTATE,ISA bra L32 btg PORTA,RA1 ; Then toggle RA1(LED D6). ;ENDIF_ L32 ;IF_ PBSTATE,ISC == 1 ; Or if ISC = 1, the button's been pressed. btfss PBSTATE,ISC bra L33 btg PORTA,RA1 ; Then toggle RA1(LED D6). ;ENDIF_ L33 return ;;;;;;; LEDCounter subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; In this subroutine, the value of DELRPG is tested to see if there is any change in ; RPG(or has it been turned). DELRPG holds the value 1 if turned clockwise, ; -1 if counter-clockwise, and 0 if there's no change. Utilizing DELRPG and the variable ; BINCOUNT, the function is able to implement a counter. The LEDs, D4 and D5, represents ; the RPG counting up or down (or turning clockwise or counter-clockwise). Also, in ; binary state, bit 0 of BINCOUNT represents the state of LED D5 and bit 1 represents D6. LEDCounter movf DELRPG,W ; Load DELRPG into W. sublw D'1' ; Subtract 1 from it to test for its value. ;IF_ .Z. ; If DELRPG was 1, bnz L34 incf BINCOUNT,F ; increment BINCOUNT. ;ELSE_ ; Otherwise, check to see if -1 was stored. bra L35 L34 movf DELRPG,W ; load DELRPG into W to test for other cases. addlw D'1' ; Add 1 to W, ;IF_ .Z. ; and if 0(DELRPG = -1), bnz L36 decf BINCOUNT,F ; decrement BINCOUNT. ;ENDIF_ L36 ;ENDIF_ L35 ;IF_ BINCOUNT,0 == 1 ; LED D5 is controlled by PORTA,RA2. btfss BINCOUNT,0 bra L37 bsf PORTA, RA2 ; Set RA2 if bit0 of BINCOUNT = 0. ;ELSE_ ; Otherwise, clear it. bra L38 L37 bcf PORTA, RA2 ;ENDIF_ L38 ;IF_ BINCOUNT,1 == 1 ; LED D6 is controlled by PORTA,RA3. btfss BINCOUNT,1 bra L39 bsf PORTA, RA3 ; Set RA3 according to bit1 of BINCOUNT. ;ELSE_ ; Thus bits 0 and 1 of BINCOUNT represent the binary bra L40 L39 bcf PORTA, RA3 ; state of RPG by toggling LEDs D4 and D5. ;ENDIF_ L40 return ;;;;;;; PotToDACtoADC subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine deals with DAC(D/A Converter) and ADC(A/D Converter). It first ; takes the value of POT and sends it to the DAC-A input and the complement of the value ; of POT sent to the input of DAC-B. The output of DAC is connected to AN7, which is ; an input to ADC. The output value of ADC is then displayed on the upper right-hand corner ; of the LCD to be verified against the POT value read straight from the ADC. The values should either ; match or complement each other depending on the state that is toggled by pressing ; SW3. PotToDACToADC ; Instructions dealing with DAC. bcf PORTC,RC0 ; Clear PORTC,RC0 to change DAC output. MOVLF 0x21,BYTE ; Load control byte 0x21 to BYTE for the output to go to DAC-A. rcall SPItransfer ; Transfer what is in BYTE. movff POTVALUE,BYTE ; Load the byte to be converted. rcall SPItransfer bsf PORTC,RC0 ; Set RC0 to finish transfer. bcf PORTC,RC0 MOVLF 0x22,BYTE ; Load control byte 0x22 to BYTE for the output to go to DAC-B. rcall SPItransfer movff POTVALUECOMP,BYTE rcall SPItransfer bsf PORTC,RC0 ; Instructions for ADC. MOVLF B'01111001',ADCON0 ; Select AN7 for ADC input. rcall T40 ; A/D needs time to set up before it is first used. bsf ADCON0,GO_DONE ; Set GO_DONE to initiate conversion. ;REPEAT_ ; Wait until conversion is done. L41 ;UNTIL_ ADCON0,GO_DONE == 0 btfsc ADCON0,GO_DONE bra L41 RL41 MOVLF 0x86,HEXSTR ; Load cursor position byte (upper right) to HEXSTR. movff ADRESH,BYTE ; Load the converted result to BYTE for display. rcall HexDisplay ; Call HexDisplay to display the result in hex. MOVLF B'01100001',ADCON0 ; Set ADC input back to AN4. return ;;;;;;; SPItransfer subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine controls the SPI. SPItransfer bcf PIR1,SSPIF ; Clear PIR1,SSPIF to ready for transfer. movff BYTE,SSPBUF ; Initiates write when anything is placed in SSPBUF. ;REPEAT_ ; Wait until transfer is finished. L42 ;UNTIL_ PIR1,SSPIF == 1 btfss PIR1,SSPIF bra L42 RL42 movff SSPBUF,SPIRECEIVE ; If result is desired, it is stored in SPIRECEIVE. return ;;;;;;; ReadTemp subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine reads the temperature sensor. The value is calculated with: ; Displaying Temperature = (Value * 1000) / 2048. Reason being for temperatures below ; 125F, the ADC output can be right-justified. The output must be scaled appropriately. ReadTemp MOVLF B'01000001',ADCON0 ; Select AN0 for temperature sensor. bsf ADCON1,ADFM ; Use ADRESL for temperature sensor. rcall T40 ; Wait for change to take place. bsf ADCON0,GO_DONE ; Set GO_DONE to start conversion. ;REPEAT_ L43 ;UNTIL_ ADCON0,GO_DONE == 0 ; Wait until conversion finishes. btfsc ADCON0,GO_DONE bra L43 RL43 MOVLF 0x03,AARGB0 ; Put 1000 into the variable needed to multiply. MOVLF 0xE8,AARGB1 movff ADRESL,BARGB0 ; Put result into the multiplicant. call FXM1608U ; Perform multiplication. MOVLF 0x08,BARGB0 ; Put 2048 into the divisor. MOVLF 0x00,BARGB1 call FXD2416U ; Perform division. movff AARGB2,BYTE ; Knowing the final result is small enough to ; fit in 1 byte, we only need the lowest byte of the result. rcall TempDisplay ; Calls TempDisplay to display scaled result ; for proper temperature displaying. MOVLF B'01100001',ADCON0 ; Set back to AN4 for POT. bcf ADCON1,ADFM ; Set back to use ADRESH to store most of the 10-bit A/D value. return ;;;;;; DecDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine takes BYTE, converts it to an ASCII decimal value of BYTE and displays ; it. This function does not set the positioning code. For the temperature, the code ; is constant, so the position is initialized in the Initial subroutine. TempDisplay lfsr 0, TEMPSTR+2 ; Loads FSR0 register to BYTESTR + 2. ;REPEAT_ L44 clrf WREG ; Clear work register. movff BYTE, AARGB0 ; Move BYTE to AARGB0 to be divided. MOVLF D'10', BARGB0 ; Divide BYTE by 10. call FXD0808U ; Perform division. movf REMB0, W ; Move remainder to work register. iorlw 0x30 ; Add offset to convert to an ASCII decimal number. movwf POSTDEC0 ; Load the ASCII value to the string and move to next string byte. movff AARGB0, BYTE ; Move result to divisor to be divided again. movf FSR0L,W ; Done? sublw low TEMPSTR ;UNTIL_ .Z. bnz L44 RL44 lfsr 0, TEMPSTR ; Set pointer to display temperature string: TEMPSTR. rcall DisplayV ; Call DisplayV to display temperature, a variable string. return ;;;;;;; BlinkAlive subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine briefly blinks the LED next to the PIC every two-and-a-half ; seconds. BlinkAlive bsf PORTA,RA4 ; Turn off LED. decf ALIVECNT,F ; Decrement loop counter and return if not zero. ;IF_ .Z. bnz L45 MOVLF 250,ALIVECNT ; Reinitialize BLNKCNT. bcf PORTA,RA4 ; Turn on LED for ten milliseconds every 2.5 sec. ;ENDIF_ L45 return ;;;;;;; LoopTime subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Gives a 10ms looptime by using Timer1 and comparing it to 25,000. LoopTime ;REPEAT_ ; Repeat until TIMER1 has reached 25,000. L46 ;UNTIL_ PIR1, CCP1IF == 1 btfss PIR1,CCP1IF bra L46 RL46 bcf PIR1, CCP1IF ; When it has, reset it to start over. return ;;;;;;; Constant strings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; LCDstr db 0x33,0x32,0x28,0x01,0x0c,0x06,0x00 ; Initialization string for LCD. QFPV db 0x80,'Q','F','P','V',0x00 ; Declaration of QFPV string on LCD. #include c:\math18\FXD2416U.INC #include c:\math18\FXD0808U.INC #include c:\math18\FXM1608U.INC end