;********************************************************************** ; This file is a basic code template for assembly code generation * ; on the PICmicro PIC17C44. This file contains the basic code * ; building blocks to build upon. * ; * ; If interrupts are not used all code presented for that interrupt * ; can be removed or commented out with a semicolon. Also the * ; interrupt code is structured for the microcontroller mode of * ; operation (up to 8K words of program memory) If desiring to use * ; this template for the extended microcontroller or microprocessor * ; modes (up to 64K words of program memory) the call or goto * ; instructions may need to be changed to the lcall or computed * ; long goto instructions. * ; * ; Refer to the MPASM User's Guide for additional information on * ; features of the assembler (Document DS33014). * ; * ; Refer to the respective PICmicro data sheet for additional * ; information on the instruction set. * ; * ; Template file assembled with MPASM V2.11.01 * ; * ;********************************************************************** ; * ; Filename: lockin.asm * ; Date: 7/2000 * ; File Version: 6 * ; * ; Author: Bill O'Donnell * ; Company: UNLV Physics Dept. * ; * ; * ;********************************************************************** ; * ; Files required: p17c44.inc (in mplab directory) * ; * ; * ; * ;********************************************************************** ; * ; Notes: Digital lockin amplifier (Provides TTL sync signal out) * ; Set Config bits for microcontroller mode * ; (internal memory only) * ; Set oscillator type to EC (External control) * ; Set postscaler to /64 for 12mS * 64 = 768mS * ; and enable watchdog time (~0.75 Second time out) * ; Use: AD796 100KS/S 16 bit A/D +-10V * ; AD660 10uS settle to 1/2LSB 16 bit D/A +-10V * ; * ; This version integrates each half cycle before digitizing * ; There is no pre-integration hold due to memory effects * ; Also the uP lights an LED if the A/D is overloaded * ; * ;********************************************************************** list p=17c44 ; list directive to define processor #include ; processor specific variable definitions __CONFIG _XT_OSC & _WDT_OFF & _MC_MODE ; '__CONFIG' directive is used to embed configuration data within .asm file. ; The lables following the directive are located in the respective .inc file. ; See respective data sheet for additional information on configuration word. ;******* RAM EQUATES ; EQU <= 0x1F aren't banked (one inst movfp and pf if p<=1F) TXDES EQU 0x1A ; original number of bytes that need sending CYLDES EQU 0x1B ; desired # of low to high cycles to sum TSUML EQU 0x1C ; temp sum low byte TSUMH EQU 0x1D ; temp sum high byte TSUMO1 EQU 0x1E ; temp sum first overflow byte TSUMO2 EQU 0x1F ; temp sum second overflow byte ; EQU > 0x1F are banked ; note: CRET must me in the next mem loc after TSUM02 (indirect mem access) CRET EQU 0x20 ; store a carriage return here (ascii 13) TXCNT EQU 0x21 ; counter for # of bytes left to send CYLCNT EQU 0x22 ; counter for low to high transitions of REF SHDES EQU 0x23 ; desired # of bits to shift TSUM for DAC SHCNT EQU 0x24 ; counter for shifting TSUM data GAIN EQU 0x25 ; gain setting for analog input (0-7) OLDREF EQU 0x26 ; 0xFF if last REF was high else 0x00 SUMLOW EQU 0x27 ; low byte of sum SUMHIGH EQU 0x28 ; high byte of sum SUMOVF1 EQU 0x29 ; first overflow byte of sum SUMOVF2 EQU 0x2A ; second overflow byte of sum VOUTL EQU 0x2B ; low byte for Vout VOUTH EQU 0x2C ; high byte for Vout VOUTO1 EQU 0x2D ; first overflow byte for Vout VOUTO2 EQU 0x2E ; second overflow byte for Vout CMD EQU 0x2F ; command byte read from serial port DATA1 EQU 0x30 ; first data byte read from serial port TEMP EQU 0x31 ; temporary variable CNT EQU 0x32 ; counter for integration time CNTDES EQU 0x33 ; desired loop count for integration time SKP EQU 0x34 ; counter for dead time SKPDES EQU 0x35 ; desired loop count for dead time RAWLOW EQU 0x36 ; low byte read from A/D RAWHIGH EQU 0x37 ; high byte read from A/D TEMP_WREG EQU 0x38 ; used for PUSH and POP macros TEMP_ALUSTA EQU 0x39 TEMP_BSR EQU 0x3A TEMP_PCLATH EQU 0x3B ;******* CONSTANTS AND MASKS ; PORT A - RA4 & 5 (RX & TX) are used for the RS232 communication ; RA2 & 3 are used for the reset and input to the integrator OVLED EQU d'1' ; A/D overflow LED on when low (open drain) ; PORT B - Note:DAHBE is inverted and fed to LBE (the DAC low byte enable) DALDAC EQU d'0' ; D/A load dac (updates output) DAHBE EQU d'1' ; D/A high byte enable (byte mux control) DACS EQU d'2' ; D/A chip select (used to load bytes) PWMPH EQU d'3' ; PWM output for phase adjust REFCOMP EQU d'4' ; ref input from comparator (for phase calc) GAIN0 EQU d'5' ; LSB of gain setting for input amp (A0) GAIN1 EQU d'6' ; middle gain setting for input amp (A1) GAIN2 EQU d'7' ; MSB of gain setting for input amp (A2) ; A0,A1,A2 - 000->111, GAIN = 1,3,10,30,100,300,1000,3000 ; PORT C - all inputs from A/D converter ; PORT D - all outputs to D/A converter ; PORT E ADBYTE EQU d'0' ; A/D byte select PORTE (byte on A/D) ADSTRB EQU d'1' ; A/D strobe output PORTE (R/C on A/D) REF EQU d'2' ; ref input PORTE (sync in - TTL) ;******* MACROS ; Note: canged PUSH macro - replaced all movfp with movpf and moved ; associated registers to a banked location above 0x1F PUSH MACRO ;macro for saving registers movpf WREG,TEMP_WREG movpf ALUSTA,TEMP_ALUSTA movpf BSR,TEMP_BSR movpf PCLATH,TEMP_PCLATH ENDM POP MACRO ;macro for restoring registers movfp TEMP_PCLATH,PCLATH movfp TEMP_BSR,BSR movfp TEMP_ALUSTA,ALUSTA movfp TEMP_WREG,WREG ENDM ;********************************************************************** ORG 0x000 ;you may want to clear PCLATH and ALUSTA registers here goto start ;************ INT PIN INTERRUPT VECTOR ORG 0x008 PUSH ;save specific registers goto intpin_isr_handler ;************ TIMER0 INTERRUPT VECTOR ORG 0x010 PUSH ;save specific registers goto timer0_isr_handler ;************ T0CKI PIN INTERRUPT VECTOR ORG 0x018 PUSH ;save specific registers goto t0cki_isr_handler ;************ PERIPHERAL INTERRUPT VECTOR ORG 0x020 PUSH ;save specific registers ; isr code can go here or be located as a call subroutine elsewhere POP ;restore specific registers retfie ;return from interrupt ;************************************************************************ ;***** MAIN PROGRAM ***** start clrwdt ; clear the watchdog timer bsf CPUSTA,GLINTD ; disable all interupts call usart ; set up serial port movlb 1 ; select bank 1 movlw d'6' ; # of cycles to sum before sending data movpf WREG,CYLDES ; set desired cycle count movlw d'5' ; # of characters to send out serial port movpf WREG,TXDES ; set transmit count to 5 (4 bytes + CR) movlw d'13' ; carriage return (ASCII 13) will be sent movpf WREG,CRET ; out after the data bytes movlw d'2' ; # of bits to shift right before VOUT->DAC movpf WREG,SHDES ; set desired shift count (avoid overflow) movlw d'0' ; A2,A1,A0 are LSB's of GAIN movpf WREG,GAIN ; set gain value to 1 (000) call gainst ; set the I/O pins to the new gain value movlw d'22' ; loop counter for delay2 - integration movpf WREG,CNTDES ; time (t = ? + 5 + 38 * CNTDES) movlw d'33' ; loop counter for delay3 - edge jitter movpf WREG,SKPDES ; time (t = ? + 5 + 4 * SKPDES) ;************************** movlb 0 ; select bank 0 ; port B: bit 0 = DALDAC, 1 = DAHBE, 2 = DACS, 3 = PWM_PH ; port B: bit 4 = REFCOMP, 5 = GAIN0 , 6 =GAIN1 , 7 = GAIN2 movlw b'00010000' ; port B has one input and three outputs movpf WREG,DDRB movlb 1 ; select bank 1 setf DDRC,1 ; port C all inputs (A/D low/high bytes) clrf DDRD,1 ; port D all outputs (D/A low/high bytes) movlw b'11111000' ; port E three outputs movpf WREG,DDRE ; ADBYTE = 0 ,ADSTRB = 1, REF = 2 ; set pins to startup (default) state ; set PORT E pins movlb 1 ; select bank 1 bcf PORTE,ADBYTE ; select low byte of A/D nop bsf PORTE,ADSTRB ; set A/D strobe line nop bcf PORTE,REF ; set ref signal low ; set PORT B pins movlb 0 ; select bank 0 bcf PORTB,DALDAC ; set DAC load line nop bsf PORTB,DAHBE ; enable low byte mux for DAC nop bsf PORTB,DACS ; set DAC chip select nop bcf PORTB,PWMPH ; clear output pin (PWM not enabled) ; set input gain to 1 bcf PORTB,GAIN0 ; clear LSB of input gain A0 nop bcf PORTB,GAIN1 ; clear middle bit of input gain A1 nop bcf PORTB,GAIN2 ; clear MSB of input gain A2 bcf RCSTA,OERR ; clear any overflow that may have occured ; just for testing set registers to output to allow non-zero reading movlb 1 ; select bank 1 ; clrf DDRC,1 ; clrf DDRD,1 ; clrf DDRE,1 ; clear the sum and reset the cycle counter clrf SUMLOW,1 clrf SUMHIGH,1 clrf SUMOVF1,1 clrf SUMOVF2,1 clrf TXCNT,1 ; initially no data to send out serial clrf SHCNT,1 ; initially no shift needed for VOUT ; set default values (or read from EEPROM ?) movpf CYLDES,CYLCNT ; cyldes addr <= 0X1F movfp CNTDES,WREG movpf WREG,CNT ; write 0x8000 to D/A to zero output (D/A clr tied high on PCB) clrf PORTD,1 ; set low byte to zero movlb 0 ; select bank 0 bcf PORTB,DACS ; have DAC read the low byte nop bsf PORTB,DACS nop bcf PORTB,DAHBE ; select high byte for DAC mux movlb 1 ; select bank 1 ; clrf PORTD,1 ; set high byte to zero movlw 0x80 ; MSB is one (zero for bi-polar) movpf WREG,PORTD ; set high byte movlb 0 ; select bank 0 bcf PORTB,DACS ; have DAC read the low byte nop bsf PORTB,DACS nop bsf PORTB,DALDAC ; transfer 16 bit latch to outputs of DAC nop bcf PORTB,DALDAC nop bsf PORTB,DAHBE ; select low byte for DAC mux movlb 1 ; select bank 1 ;********************************************************** ; start of never ending loop ; high part of reference cycle top ;refl bcf PORTE,REF ; set ref low ; reset the integrator ; S1 = LOW, S2 = HIGH INTEGRATE ; S1 = HIGH, S2 = HIGH HOLD ; S1 = HIGH, S2 = LOW RESET ; new reset - keep S1 low movlw b'00000000' movlb 0 ; select bank 0 rst1 movpf WREG,PORTA ; RA2 = S1, RA3 = S2 movlb 1 ; read the A/D and subtract it from the sum movfp PORTC,WREG ; read low byte from A/D bsf PORTE,ADBYTE ; select high byte subwf SUMLOW,1 ; SUMLOW = SUMLOW - WREG movpf WREG,RAWLOW ; store the low byte movfp PORTC,WREG ; read high byte from A/D subwfb SUMHIGH,1 ; SUMHIGH = SUMHIGH - WREG - borrow movpf WREG,RAWHIGH ; store the high byte clrf WREG,0 ; clear WREG but Carry set from above btfsc PORTC,7 ; check if number negative (MSB high) setf WREG,0 ; if negative extend leading ones subwfb SUMOVF1,1 ; SUMOVF1 = SUMOVF1 - 0 - borrow clrf WREG,0 ; clear WREG but Carry set from above btfsc PORTC,7 ; check if number negative (MSB high) setf WREG,0 ; if negative extend leading ones bcf PORTE,ADBYTE ; select low byte for next time subwfb SUMOVF2,1 ; SUMOVF2 = SUMOVF2 - 0 - borrow ; delay to give op-amp time to come to zero call delay ; reset (slew rate of op-amp 3V/uS) call delay3 ; dead time for jitter removal ; start the integrator ; S1 = LOW, S2 = HIGH INTEGRATE ; S1 = HIGH, S2 = HIGH HOLD ; S1 = HIGH, S2 = LOW RESET ; integration time (60 + 5 + (38 * CNTDES))*121ns @ 33.333MHz movlw b'00001000' movlb 0 ; select bank 0 si1 movpf WREG,PORTA ; RA2 = S1, RA3 = S2 movlb 1 clrwdt ; clear the watchdog timer call shift ; check if need to shift word for DAC ; check if a byte has come in the serial port btfsc PIR,RCIF ; if a byte is waiting to be read call byterd ; read the byte ; check if a character needs to be sent out the serial port call sndout dcfsnz CYLCNT,1 ; if cycle counter is zero then goto mvsum ; move the sum into a temporary space nop ; if cycle counter isn't zero then nop ; do some nop's instead that will take nop ; the same amount of time nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop mvret ; return to this point after doing mvsum call delay2 ; delay for rest of integration time ; have the integrator hold the current value ; S1 = LOW, S2 = HIGH INTEGRATE ; S1 = HIGH, S2 = HIGH HOLD ; S1 = HIGH, S2 = LOW RESET movlw b'00001100' ; both S1 and S2 open movlb 0 ; select bank 0 hld1 movpf WREG,PORTA ; hold the current value movlb 1 call delay ; delay to let voltage stabilize call delay4 ; delay to let voltage stabilize digs1 bcf PORTE,ADSTRB ; strobe the A/D (trigger sample and hold nop bsf PORTE,ADSTRB ; and begin conversion) call delay3 ; dead time for jitter removal ; delay ~8us to give A/D time to convert the held voltage call delay5 nop ; to make the high & low times match nop mid ;refh bsf PORTE,REF ; set ref high ; reset the integrator ; S1 = LOW, S2 = HIGH INTEGRATE ; S1 = HIGH, S2 = HIGH HOLD ; S1 = HIGH, S2 = LOW RESET ; new reset - keep S1 low movlw b'00000000' movlb 0 ; select bank 0 rst2 movpf WREG,PORTA ; RA2 = S1, RA3 = S2 movlb 1 ; read the A/D and add it to the sum movfp PORTC,WREG ; read low byte from A/D bsf PORTE,ADBYTE ; select high byte addwf SUMLOW,1 ; SUMLOW = SUMLOW + WREG movpf WREG,RAWLOW ; store the low byte movfp PORTC,WREG ; read high byte from A/D addwfc SUMHIGH,1 ; SUMHIGH = SUMHIGH + WREG + Carry movpf WREG,RAWHIGH ; store the high byte clrf WREG,0 ; clear WREG but Carry set from above btfsc PORTC,7 ; check if number negative (MSB high) setf WREG,0 ; if negative extend leading ones addwfc SUMOVF1,1 ; SUMOVF1 = SUMOVF1 + 0 + Carry clrf WREG,0 ; clear WREG but Carry set from above btfsc PORTC,7 ; check if number negative (MSB high) setf WREG,0 ; if negative extend leading ones bcf PORTE,ADBYTE ; select low byte addwfc SUMOVF2,1 ; SUMOVF2 = SUMOVF2 + 0 + Carry ; delay ~8us to give op-amp time to come to zero call delay ; reset (slew rate of op-amp 3V/uS) call delay3 ; dead time for jitter removal ; start the integrator ; S1 = LOW, S2 = HIGH INTEGRATE ; S1 = HIGH, S2 = HIGH HOLD ; S1 = HIGH, S2 = LOW RESET movlw b'00001000' movlb 0 ; select bank 0 si2 movpf WREG,PORTA ; RA2 = S1, RA3 = S2 movlb 1 clrwdt ; clear the watchdog timer call shift ; check if need to shift output word for DAC ; check if a byte has come in the serial port btfsc PIR,RCIF ; if a byte is waiting to be read call byterd ; read the byte ; check if a character needs to be sent out the serial port call sndout ; update the DAC if needed (any shifting should be done by now) movlw d'1' ; check if CYLCNT = 1 (last osc of ref) cpfseq CYLCNT ; before start new summing cycle) goto aaa ; skip to some nop's if CYLCNT <> 0 ; set sign bit for VOUTH before sending to DAC bcf VOUTH,7 ; sign change (A/D is 2's comp, D/A isn't) btfss VOUTO2,7 ; check if pos or neg bsf VOUTH,7 ; set or clear MSB as necessary ; load and update the DAC movfp VOUTL,PORTD ; put the low byte for DAC on port D movlb 0 ; select bank 0 bcf PORTB,DACS ; have DAC read the low byte bsf PORTB,DACS bcf PORTB,DAHBE ; select high byte for DAC mux movlb 1 ; select bank 1 movfp VOUTH,PORTD ; put the high byte for DAC on port D movlb 0 ; select bank 0 bcf PORTB,DACS ; have DAC read the low byte bsf PORTB,DACS bsf PORTB,DALDAC ; transfer 16 bit latch to outputs of DAC bcf PORTB,DALDAC bsf PORTB,DAHBE ; select low byte for DAC mux movlb 1 ; select bank 1 retdac ; return here if the above portion was skipped nop ; to balance high & low integration time call delay2 ; delay for rest of integration time ; have the integrator hold the current value ; S1 = LOW, S2 = HIGH INTEGRATE ; S1 = HIGH, S2 = HIGH HOLD ; S1 = HIGH, S2 = LOW RESET movlw b'00001100' ; both S1 and S2 open movlb 0 ; select bank 0 hld2 movpf WREG,PORTA ; hold the current value movlb 1 call delay ; delay to let voltage stabilize call delay4 ; delay to let voltage stabilize digs2 bcf PORTE,ADSTRB ; strobe the A/D (trigger sample and hold nop bsf PORTE,ADSTRB ; and begin conversion) call delay3 ; dead time for jitter removal ; delay ~8us to give A/D time to convert the held voltage call delay6 goto top ;***************************************************************** ; nop's to sync when not updating the DAC aaa nop nop nop nop nop nop nop nop nop nop nop nop nop nop goto retdac ;****** move the sum into a temporary holding area until it can be sent ; to the serial port and shifted and sent to the DAC mvsum ; should be in bank 1 when accessing TSUM and SUM variables ; move sum into temp storage for serial port sending movfp SUMLOW,TSUML ; store the low byte in TSUML movfp SUMHIGH,TSUMH ; store the high byte in TSUMH movfp SUMOVF1,TSUMO1 ; store first overflow byte in TSUMO1 movfp SUMOVF2,TSUMO2 ; store second overflow byte in TSUMO2 ; move sum into regs to shift before sending to DAC movpf TSUML,VOUTL ; store the low byte in VOUTL movpf TSUMH,VOUTH ; store the high byte in VOUTH movpf TSUMO1,VOUTO1 ; store first overflow byte in VOUTO1 movpf TSUMO2,VOUTO2 ; store second overflow byte in VOUTO2 movfp SHDES,WREG ; reset the shift counter movpf WREG,SHCNT clrf SUMLOW,1 ; clear the sum registers clrf SUMHIGH,1 clrf SUMOVF1,1 clrf SUMOVF2,1 movpf CYLDES,CYLCNT ; reset the cycle counter movpf TXDES,TXCNT ; reset the transmit word counter goto mvret ;****** send a byte out the serial port if needed and buffer empty sndout clrf WREG,0 ; W = 0 cpfsgt TXCNT ; if TXCNT > 0 (still need to send) goto del14 btfss PIR,TXIF ; check if the transmit reg is empty goto del13 ; if not empty don't send byte - goto del1 decf TXCNT,1 ; dec number of bytes needing to be sent movfp TXCNT,WREG ; W = counter offset sublw CRET ; W = addr of CRET - counter offset movpf WREG,FSR0 ; put W in the pointer register movfp INDF0,WREG ; W = value in reg pointed to by FSR movlb 0 ; select bank 0 movpf WREG,TXREG ; load the transmit register movlb 1 ; select bank 1 return del14 nop ; delay so time through sndout is the nop ; same weather or not a byte needs to del13 nop ; be sent or weather or not the buffer nop ; is full nop nop nop nop nop return ;**************************************************************** ; skip does some things and takes about 8us to complete delay clrwdt ; clear the watchdog timer call shift ; check if need to shift output word for DAC ; check if a byte has come in the serial port btfsc PIR,RCIF ; if a byte is waiting to be read call byterd ; read the byte ; check if a character needs to be sent out the serial port call sndout delay4 call rawchk ; check for A/D out of range nop nop ;delay4 movlw d'6' ; delay loop of nop's ; movpf WREG,TEMP ;skplp1 decfsz TEMP,1 ; goto skplp1 return ;**************************************************************** ; skip does some things and takes about 8us to complete delay5 clrwdt ; clear the watchdog timer ; check if a character needs to be sent out the serial port call sndout ; check if a byte has come in the serial port btfsc PIR,RCIF ; if a byte is waiting to be read call byterd ; read the byte nop nop nop nop nop nop refh bsf PORTE,REF ; set ref high call shift ; check if need to shift output word for DAC nop nop movlw d'4' ; delay loop of nop's movpf WREG,TEMP skplp2 decfsz TEMP,1 goto skplp2 return ;**************************************************************** ; skip does some things and takes about 8us to complete delay6 clrwdt ; clear the watchdog timer ; check if a character needs to be sent out the serial port call sndout ; check if a byte has come in the serial port btfsc PIR,RCIF ; if a byte is waiting to be read call byterd ; read the byte nop nop nop nop nop nop refl bcf PORTE,REF ; set ref low call shift ; check if need to shift output word for DAC nop nop movlw d'4' ; delay loop of nop's movpf WREG,TEMP skplp3 decfsz TEMP,1 goto skplp3 return ;**************************************************************** ; delay while doing integration ; delay from call to return = (5 + 38 * CNTDES) instructions delay2 movfp CNTDES,WREG ; get the desired count movpf WREG,CNT ; and put it in CNT topdel clrwdt ; clear the watchdog timer call shift ; check if need to shift output word for DAC call sndout ; send char out port if needed ; check if a byte has come in the serial port btfsc PIR,RCIF ; if a byte is waiting to be read call byterd ; read the byte decfsz CNT,1 ; check if still need to stay in loop goto topdel ; if yes stay in loop else return ; return ;**************************************************************** ; delay before and after reference edge transition ; delay from call to return (5 + 4 * CNTDES) * 121ns @ 33.333MHz delay3 movfp SKPDES,WREG ; get the desired count movpf WREG,SKP ; and put it in the counter td3 clrwdt ; clear the watchdog timer decfsz SKP,1 ; check if still need to stay in loop goto td3 ; if yes stay in loop else return ; return ;**************************************************************** ; check if the A/D is maxed out at +/- 10V (0111.../1000...) rawchk movfp RAWHIGH,WREG ; w = rawhigh btfsc WREG,7 ; check if MSB is high or low goto skpraw ; if high skip down bsf WREG,7 ; if MSB was low - set it high andwf RAWLOW,0 ; rawlow = 11111111 comf WREG,0 ; set zero bit if all ones (OVFL) btfss ALUSTA,Z ; check if zero bit is set goto ledoff ; if not set line high (LED off) ; if zero bit set then overflow ledon movlb 0 ; select bank 0 movpf PORTA,TEMP ; read port a bcf TEMP,OVLED ; turn on overflow LED movfp TEMP,PORTA ; write result to port movlb 1 ; select bank 1 return skpraw bcf WREG,7 ; if MSB was high - set it low iorwf RAWLOW,0 ; rawlow = 00000000 (set Z if zero) btfsc ALUSTA,Z ; check if zero bit is set goto ledon ; if yes then set line low (LED on) ledoff movlb 0 ; select bank 0 movpf PORTA,TEMP ; read port a bsf TEMP,OVLED ; turn out overflow LED movfp TEMP,PORTA ; write result to port movlb 1 ; select bank 1 return ;**************************************************************** ;****** set up the usart for async opperation (2400 - 57.6Kbaud) ; spbrg = 216,107,53,26,8 -> baud rate = 2400,4800,9600,19.2K,57.6K ; RA5 = RX, RA4 = TX (Values are correct for 33.333MHz clock) usart movlw d'8' movpf WREG,SPBRG ; default spbrg = 8 -> 57.6K baud movlw b'10100000' movpf WREG,TXSTA ; master clk, 8 bit, async mode, enable port movlw b'10010000' movpf WREG,RCSTA ; 8 bit, async mode, continuous receive return ;****** reads a two byte command from the serial port byterd btfss PIR,RCIF ; check if byte waiting to be read goto byterd ; stay in loop until byte comes in movlb 0 ; select bank 0 bcf TXSTA,TXEN ; disable any previous serial transmission movfp RCREG,WREG ; read first byte movlb 1 ; select bank 1 movpf WREG,CMD ; store the command byte clrwdt ; clear the watchdog timer bylp btfss PIR,RCIF ; wait here until next byte is received goto bylp movlb 0 ; select bank 0 movfp RCREG,WREG ; read second byte movlb 1 ; select bank 1 movpf WREG,DATA1 ; store the first data byte clrwdt ; clear the watchdog timer ; decode the command byte and jump to proper subroutine btfsc CMD,7 ; check if sync cycle number command call srsync ; set or read the # of sync cycles btfsc CMD,6 ; check if baud rate command call srbaud ; set or read the baud rate btfsc CMD,5 ; check if bit shift command call srshft ; set or read # shift before loading DAC btfsc CMD,4 ; check if gain command call srgain ; set or read the gain value btfsc CMD,3 ; check if # of samp per 1/2 cycle command call srsamp ; set or read the # of samples btfsc CMD,2 ; check if # of samp to skip on each side of ref call srskp ; set or read the # of samples ; tstfsz CMD ; check if restart data collection command ; goto byterd ; if not stay in loop (wait for next command) movlb 0 ; select bank 0 bsf TXSTA,TXEN ; re-enable transmit side of serial port movlb 1 ; select bank 1 return ;****** sets or reads the number of sync cycles to sum before sending data srsync btfss CMD,0 ; check if read or write command goto syrd ; if read command skip down ; do this part if write command clrf WREG,0 cpfsgt DATA1 ; if new cycle count is zero return ; then return without changing value movfp DATA1,CYLDES ; CYLDES = new cycle count value return syrd ; do this part if read command movfp CYLDES,WREG ; put desired cycle count in WREG movlb 0 ; select bank 0 movpf WREG,TXREG ; put in transmit reg bsf TXSTA,TXEN ; re-enable the serial port (send byte) movlb 1 ; select bank 1 return ;****** sets or reads the baud rate srbaud btfss CMD,0 ; check if read or write command goto bdrd ; if read command skip down ; do this part if write command ; check if legal value for baud rate movlw d'5' ; 5 legal values in the lookup table movpf WREG,TEMP ; temp will be the counter variable up2 movfp TEMP,WREG ; put count in WREG call bdlkup ; get next legal value for SPBRG cpfseq DATA1 ; skip if DATA1 has legal value goto dwn2 goto dwn3 dwn2 decfsz TEMP,1 ; check if done all values in table yet goto up2 ; if not stay in loop return ; if no value matched exit without change dwn3 ; if legal value then change the baud rate movfp DATA1,WREG ; put new cycle count value in WREG movlb 0 ; select bank 0 movpf WREG,SPBRG ; put WREG in baud rate register movlb 1 ; select bank 1 return bdrd ; do this part if read command movlb 0 ; select bank 0 movpf SPBRG,TXREG ; TXREG = desired baud rate bsf TXSTA,TXEN ; re-enable the serial port (send byte) movlb 1 ; select bank 1 return ;****** lookup table for leagal baud rate settings (33.333MHz clock) bdlkup ; returns with value for SPBRG (called with offset in WREG) addwf PCL,1 ; jump to location in lookup table retlw d'0' ; dummy line (PCL >= 1 & PCL <= 5) retlw d'216' ; 2400 retlw d'107' ; 4800 retlw d'53' ; 9600 retlw d'26' ; 19.2K retlw d'8' ; 57.6K ;****** sets or reads the number of bits to shift the sum before sending srshft ; it to the DAC (legal values 0-15 bits) btfss CMD,0 ; check if read or write command goto sysf ; if read command skip down ; do this part if write command movlw d'16' ; check that the value is less than 16 cpfslt DATA1 return ; if the number is > 16 don't change movfp DATA1,WREG ; w = new shift value movpf WREG,SHDES ; SHDES has new shift value return sysf ; do this part if read command movfp SHDES,WREG ; put desired shift value in WREG movlb 0 ; select bank 0 movpf WREG,TXREG ; put in transmit reg bsf TXSTA,TXEN ; re-enable the serial port (send byte) movlb 1 ; select bank 1 return ;****** sets or reads the gain setting for the analog input srgain ;legal values are 0-7 (1,3,10,30,100,300,1000,3000) btfss CMD,0 ; check if read or write command goto sygn ; if read command skip down ; do this part if write command movlw d'8' ; check that the value is less than 8 cpfslt DATA1 return ; if the number is > 8 don't change movfp DATA1,WREG ; w = new gain value movpf WREG,GAIN ; GAIN has new value call gainst ; set the I/O pins for the new gain return sygn ; do this part if read command movfp GAIN,WREG ; put current gain value in WREG movlb 0 ; select bank 0 movpf WREG,TXREG ; put in transmit reg bsf TXSTA,TXEN ; re-enable the serial port (send byte) movlb 1 ; select bank 1 return ;****** sets the I/O pins for the current gain setting gainst movfp GAIN,WREG ; put gain setting in temp variable movpf WREG,TEMP swapf TEMP,1 ; change high and low order nibbles rlcf TEMP,1 ; shift gain bits to high order bits ; needed because port B is in bank 0 so copy temp variable ; into bank zero movfp TEMP, WREG ; move temp in bank 1 into Wreg movlb 0 ; select bank 0 movpf WREG, TEMP ; put Wreg into temp in bank 0 ; set or clear LSB btfsc TEMP,GAIN0 ; check if new bit of GAIN is low bsf PORTB,GAIN0 ; if not set pin high btfss TEMP,GAIN0 ; check if new bit of GAIN is high bcf PORTB,GAIN0 ; if not clear pin ; set or clear middle bit btfsc TEMP,GAIN1 ; check if new bit of GAIN is low bsf PORTB,GAIN1 ; if not set pin high btfss TEMP,GAIN1 ; check if new bit of GAIN is high bcf PORTB,GAIN1 ; if not clear pin ; set or clear MSB btfsc TEMP,GAIN2 ; check if new bit of GAIN is low bsf PORTB,GAIN2 ; if not set pin high btfss TEMP,GAIN2 ; check if new bit of GAIN is high bcf PORTB,GAIN2 ; if not clear pin movlb 1 ; select bank 1 return ;****** sets or reads the integration loop counter (for 1/2 cycle) srsamp btfss CMD,0 ; check if read or write command goto sysp ; if read command skip down ; do this part if write command clrf WREG,0 cpfsgt DATA1 ; if new sample count is zero return ; then return without changing value movfp DATA1,WREG ; CNTDES = new sample count value movpf WREG,CNTDES return sysp ; do this part if read command movfp CNTDES,WREG ; put desired sample count in WREG movlb 0 ; select bank 0 movpf WREG,TXREG ; put in transmit reg bsf TXSTA,TXEN ; re-enable the serial port (send byte) movlb 1 ; select bank 1 return ;****** sets or reads the delay loop counter (per half cycle) srskp btfss CMD,0 ; check if read or write command goto sksp ; if read command skip down ; do this part if write command clrf WREG,0 cpfsgt DATA1 ; if new sample count is zero return ; then return without changing value movfp DATA1,WREG ; SKPDES = new sample count value movpf WREG,SKPDES return sksp ; do this part if read command movfp SKPDES,WREG ; put desired skip count in WREG movlb 0 ; select bank 0 movpf WREG,TXREG ; put in transmit reg bsf TXSTA,TXEN ; re-enable the serial port (send byte) movlb 1 ; select bank 1 return ;****** shifts VOUT one bit right (keeps MSB the same for sign) shift clrf WREG,0 cpfsgt SHCNT ; check if bits still need to be shifted goto skp22 ; if not skip down bcf ALUSTA,C ; clear carry bit before rotate btfsc VOUTO2,7 ; check if MSB is set or clear bsf ALUSTA,C ; if set then set carry before rotate rrcf VOUTO2,1 ; rotate all four bytes rrcf VOUTO1,1 rrcf VOUTH,1 rrcf VOUTL,1 decf SHCNT,1 ; decrement the shift counter return skp22 nop nop nop nop nop nop nop return ;****** INT PIN INTERRUPT SERVICE HANDLER intpin_isr_handler ;isr code goes here POP ;restore registers retfie ;return from interrupt ;****** TIMER0 INTERRUPT SERVICE HANDLER timer0_isr_handler ;isr code goes here POP ;restore registers retfie ;return from interrupt ;****** T0CKI INTERRUPT SERVICE HANDLER t0cki_isr_handler ;isr code goes here POP ;restore registers retfie ;return from interrupt END ;required directive