;20bit DAC code comparator ; ;*************************************************** ; * ; Filename: dac20.asm * ; Date 12/4/2000 * ; File Version: 1.1 * ; * ; Author: Florin Oprescu * ; Company: Linear Technology Corp. * ; * ; * ;*************************************************** ; ; Variables ;============ ; uses 17 bytes of RAM as follows: ; ; {UB2, UB1, UB0} user input word buffer ;--------------------------------------- ; 24 bits unsigned integer (3 bytes): ; ; The information is transferred from the external input register ; into {UB2, UB1, UB0} whenever a "user input update" event ; is detected by testing the timer0 content. Following the data ; transfer, the UIU ("user input update") flag is set and the DAC ; ready flags RDY and RDY2 are cleared. UB0 uses the same physical ; location as U0. The user input double buffering is necessary ; because the loop error corresponding to the current ADC reading ; must be calculated using the previous user input value. ; The old user input value can be replaced by the new user input ; value only after the loop error calculation. ; The worst case minimum response time to an UIU event must be ; calculated. The user shall not update the external input register ; at intervals shorter than this response time. For the moment the ; program can not block the user access to the external input ; register during a read operation. In such a situation the result ; of the read operation can be very wrong. ; ; UB0 - least significant byte. Same physical location as U0 ; ; UB1 - second byte. ; ; UB2 - most significant byte. ; ; ; {U2, U1, U0} user input word ;------------------------------ ; 24 bits unsigned integer (3 bytes): ; ; The information is transferred from {UB2, UB1, UB0[7:4], [0000]} ; into {U2, U1, U0} whenever the UIU flag is found set within the ; CComp ("code comparator") procedure. The UIU flag is reset ; following the data transfer. ; ; U0 - least significant byte of current DAC input ; The 4 least significant bits U0[3:0] are set ; to zero. ; ; U1 - second byte of current DAC input ; ; U2 - most significant byte of current DAC input ; ; ; {CON} control byte ;-------------------- ; (1 byte): ; ; The 3 least significant bits CON[2:0] represent the ADC linearity ; correction factor transferred from UB[2:0] when the UIU flag ; is found set within the CComp procedure - at the same time as the ; {U2, U1, U0} variable is updated. ; ; The effect of CON[2:0] is additive and its weight is as follows: ; ; CON[0] = 1 linearity correction effect is about 1ppm ; CON[1] = 1 linearity correction effect is about 2ppm ; CON[2] = 1 linearity correction effect is about 4ppm ; ; The LTC2400 has a typical 4ppm INL error therefore the default ; curvature correction value can be set at CON[2:0] = 100 ; ; CON[3] is the control loop integration factor transferred from ; UB[3] when the UIU flag is found set within the CComp procedure. ; If CON[3]=0, after the control loop error becomes less than 4ppm ; the error correction gain is reduced from 1 to 1/4 ; If CON[3]=1, after the control loop error becomes less than 4ppm ; the error correction gain is reduced from 1 to 1/16 ; ; CON[7] is used as the UIU ("user input update") flag. It is set ; when {UB2, UB1, UB0} is updated and it is reset when {U2, U1, U0} ; and CON[3:0] are updated. ; ; CON[6] is used as the RDY ("DAC ready") flag. It is set when ; the DAC loop error becomes less than 4ppm and it is reset when ; the UIU flag is set. ; ; CON[5] is used as the RDY2 ("DAC ready twice") flag. It is set ; whenever the DAC loop error becomes less than 4ppm and the RDY ; flag has been previously set. It is reset when the UIU flag is set. ; ; ; The bit CON[4] is not used and is always set to 0. ; ; ; {ADC3, ADC2, ADC1, ADC0} formatted ADC conversion result ;--------------------------------------------------------- ; 32 bits signed integer (4 bytes). ; ; The ADC reading is necessary only for the calculation of the control ; loop error and in order to save RAM space, it can share the same ; physical space as the loop error variable. ; ; The LTC2400 output format is offset binary. It must be converted ; to 2's complement before any arithmetic operation. A number of ; possible codes are not valid LTC2400 output codes. If such codes ; are detected it can be inferred that a serial transfer error has ; occurred, the data must be discarded and a new conversion must ; be started. For all LTC2400 devices B31=0 and B30=0 always. In ; addition, with the exception of some early samples of the device ; the sequence B[29:28]=00 should not occur in a valid transaction. ; ; ADC0 - least significant byte ; contains ADC output bits B11(MSBIT) to B4 (LSBIT) ; ; ADC1 - second byte ; contains ADC output bits B19(MSBIT) to B12 (LSBIT) ; ; ADC2 - third byte ; contains ADC output bits B27(MSBIT) to B20 (LSBIT) ; ; ADC3 - most significant byte ; contains ADC output bits ~B29(as 7 MSBITS for ; 2's complement sign extension) and B28 (LSBIT) ; ; ; {ADCC} ADC curvature correction ;-------------------------------- ; 8 bits unsigned integer (1 byte) ; ; The LTC2400 transfer characteristic has a typical INL of about ; 4ppm and a parabolic shape symmetric with respect to mid-scale. ; This error can be corrected to a first and second order and ; ADDC contains the magnitude of this correction. ; ; ; {ER3, ER2, ER1, ER0} control loop error value ;---------------------------------------------- ; signed integer (4 bytes) ; ; Contains the value of the current control loop error calculated ; as the difference between the previous user input and the last ; ADC reading. It is used to adjust the Low_DAC setting. Uses the ; same physical location as {ADC3, ADC2, ADC1, ADC0}: ; ; ER0 - least significant byte, same location as ADC0 ; ; ER1 - second byte, same location as ADC1 ; ; ER2 - third byte, same location as ADC2 ; ; ER3 - most significant byte, same location as ADC3 ; ; ; {DL3, DL2, DL1, DL0} Low DAC control value ;------------------------------------------- ; signed integer (4 bytes): ; ; Contains the Low_DAC setting in a 2's complement, 32 bit ; format. Must be initialized to 0! ; ; DL0 - least significant byte - used for Low_DAC ; control ; ; DL1 - second byte - used for Low_DAC control after ; conversion to offset binary format {DL1, DL0} ; ; DL2 - third byte - may be only 00 or FF ; ; DL3 - most significant byte - may be only 00 or FF ; ; ; {INDX} Index variable for various program functions ;---------------------------------------------------- ; 1 byte. ; ; ; {TMPV} Temporary variable for various program functions ;-------------------------------------------------------- ; 1 byte. ; ; ; ; Algorithm ;=========== ; ; After each ADC conversion cycle the processor calculates the control ; loop error value as the difference between the desired output and ; the latest conversion result. Than it updates the DACs command ; such as to reduce the error magnitude. A new ADC conversion cycle ; is started following the DACs update operation. ; ; In order to maintain adequate control loop stability it is necessary ; for the DACs and the associated amplifiers to settle to better than ; 20 bits accuracy before the ADC starts sampling the system output. For ; an LTC2400 based system this settling time is 66ms. ; ; Initialization: ; Initializes the PIC controller and the hardware interface ; and starts the Scan procedure. ; ; 1. Load ADC control port with default values ; SCKAD = 0 ; SDOAD = 1 ; 2. Set ADC control port I and O pins ; SCKAD = output ; SDOAD = input ; 3. Load register control port with default values ; NCSR[2:0] = 111 ; NCSD[1:0] = 11 ; ADDAC = 1 ; NLDAC = 1 ; DACRDY = 0 ; 4. Set register control port in output mode ; 5. Set data bus to default value DBUS[7:0]=0x00 ; 6. Set data bus in output mode ; 7. Initialize internal registers and variables: ; OPTION = 0x2F ; Timer0 used as counter is incremented by low-to-high edge ; Prescaler works with watch dog timer in div128 mode ; CON = 0x80 ; Simulate a UIU "user interface update" event to force ; the update of both Low_DAC and High_DAC ; {DL3, DL2, DL1, DL0} = 0 ; { U2, U1, U0} = 0 ; 8. Update hardware using the initialized variables ; 9. Start new ADC conversion by reading and discarding ; 32 serial bits. ; 10.Start the Scan procedure ; ; Scan: ; Continuously looks for "user input update" events. When ; a "user input update" event is detected updates the ; input buffer {UB2, UB1, UB0}, resets timer, sets UIU flag ; and resets RDY and RDY2 flags. ; ; The active low write signal for the external input register ; (which is the same as the user interface NWR input signal) ; is driven by the user and it is connected to the counter ; input of Timer0. The Timer0 is used in counter mode without a ; prescaler and it increments whenever a low-to-high transition ; is detected at its input. This is the same transition which ; latches in the input register a new user command. ; Because of the PIC controller timing constraints, this write ; signal must be maintained low for at least 2*Tosc + 20ns ; where Tosc is the maximum PIC clock period. When a 4 MHz ; clock is used for the PIC processor, the low time must be ; longer than about 520ns. ; ; 1. Test for "user input update" events by testing the Timer0 ; value. ; If Timer0>0 an UIU event has occurred. Reset the timer ; and answer Yes. ; If Timer0=0 answer No. ; 1.1 If Yes, read input latch into {UB2, UB1, UB0}, ; reset DACRDY output line, set UIU flag and ; and reset RDY and RDY2 flags (CON[7:5]=100) ; Than continue ; 1.2 If No continue ; ; Continuously looks for the ADC end of conversion event. When ; the "end of conversion" is detected it reads the 28 most ; significant bits from the ADC and it constructs the ADC ; result {ADC3, ADC2, ADC1, ADC0} in 2's complement format ; If ADC3[1] == 0 => ADC3[7:1]=1111 111 ; If ADC3[1] == 1 => ADC3[7:1]=0000 000 ; For very early LTC2400 samples only, it is possible ; to obtain as a valid 0 conversion result ADC3[1:0]=00 ; In this case: ; If ADC3[1:0] == 0 => ADC3=0 ; It also calculates the first (x1) and second (x2) order ADC ; curvature correction ADCC as follows: ; x1 = {0x00, 0x80} - ; -abs({ADC3, ADC2, ADC1, ADC0}/(2^16)-{0x00, 0x80}) ; x2 = {0x00, 0x40} - ; -abs({0x00,{0,ADC2[6:0]},ADC1,ADC0}/(2^16)-{0x00,0x40}) ; ADCC = floor((x1 + x2/2) * {00000 CON[2:0]} / 4 ) ; The actual implementation uses only the least significant ; byte of x without any substantial additional error. ; Thus the above relation can be modified as follows: ; ADCC = floor((abs(ADC2) + abs({ADC2[6],ADC2[6:0]})/2) * ; * {00000 CON[2:0]} / 4 ) ; The maximum correction range is about 7ppm INL at mid ; scale for CON[2:0] = 111. ; ; 2. Test for ADC "end of conversion" event by testing the ; value of the ADC_SDO signal. ; If ADC_SDO = LOW answer Yes. ; If ADC_SDO = HIGH answer No. ; 2.1 If Yes read 28 most significant bits from the ADC, ; update {ADC3, ADC2, ADC1, ADC0} and calculate the ; curvature correction byte ADCC. Than start the CComp ; procedure. ; It should be noticed that while reading the first 28 ; most significant bits from the ADC the controller ; generates 27 serial clock pulses. An additional 5 serial ; clock pulses (for a total of 32) are necessary to restart ; the conversion. ; 2.2 If No restart the Scan procedure. ; ; ; CComp: ; Calculates the current control loop error as: ; ; error = current_user_input - ADC_reading + ; + new_user_input_LSB - current_user_input_LSB ; ; The curvature correction is included in the ADC ; conversion result and is always positive therefore: ; ; ADC_reading = {ADC3, ADC2, ADC1, ADC0} + ; + { 0, 0, 0, ADCC} ; ; The term "new_user_input_LSB - current_user_input_LSB" ; represents the residue of the new user command which ; is added to the Low_DAC. ; ; {ER3, ER2, ER1, ER0} = ; = {0, U2, U1, U0} - {ADC3, ADC2, ADC1, ADC0} - ; - { 0, 0, 0, ADCC} + ; + {0, 0, 0, UB0} - { 0, 0, 0, U0} = ; ; = {0, U2, U1, UB0} - {ADC3, ADC2, ADC1, ADC0} - ; - { 0, 0, 0, ADCC}. ; ; The loop error {ER3, ER2, ER1, ER0} is a 32 bit signed number ; and the weight of the least significant bit is 1/16ppm of ; the ADC reference voltage. A 4ppm error value is represented ; as {0, 0, 0, 0x40}. ; ; The ADC output noise is dominated by thermal noise and has a ; white distribution. The control loop noise can be reduced by ; the square root of N by averaging N successive ADC readings. ; The obvious penalty is a slow settling time. Due to the ; limited amount of RAM available a direct implementation ; of this noise reduction strategy is difficult. In an equivalent ; implementation, when the absolute value of the loop error ; {ER3, ER2, ER1, ER0} decreases below a certain threshold, the ; gain of the error correction loop can be decreased. The default ; threshold is set at a very conservative 4ppm. This value must ; always be larger than the peak noise level of the ADC. A very ; quiet implementation can probably operate with a threshold of ; 2ppm. If CON[3]=0 the gain of the error correction loop is ; decreased from 1 to 1/4. If CON[3]=1 the gain of the error ; correction loop is decreased from 1 to 1/16. ; ; The High_DAC is always controlled by the 16 most significant ; bits of the most recent user command {UB2, UB1} ; ; The Low_DAC is controlled by the {DL3, DL2, DL1, DL0} ; variable which integrates the control loop error. Under ; correct operating condition abs({DL3, DL2, DL1, DL0})<2^15. ; In order to avoid roll-overs during large transients the ; {DL3, DL2, DL1, DL0} must be clamped within the +/- 2^15 range. ; The 16 bit Low_DAC can than be controlled by {DL1, DL0} ; after conversion to offset binary format. ; ; The DACRDY output line reflects the state of the ; internal RDY2 flag. ; ; After the updates are completed we must start a new ADC ; conversion by completing the serial transfer. ; ; 1. Test if UIU flag is set ; 1.1 If Yes, move UB[3:0] into CON[3:0] ; and {UB0[7:4], 0000} into U0. The last ADC result ; is curvature corrected using the previous CON[3:0] value!. ; 2. Calculate {ER3, ER2, ER1, ER0}. ; 3. Test if UIU flag is set ; 3.1 If Yes, move {UB2, UB1} into {U2, U1} and ; clear UIU, RDY and RDY2 flags (CON[7:5]=000 ) ; 3.2 If No, test if abs({ER3, ER2, ER1, ER0}) < 4ppm ; 3.2.1 If Yes, test if CON[6]=1 (RDY flag) ; 3.2.1.1 If Yes, set RDY2 flag (CON[5]=1 ) ; 3.2.1.2 If No, set RDY flag (CON[6]=1 ) ; and test if CON[3]=0 (filter flag) ; 3.2.1.3 If Yes, {ER3, ER2, ER1, ER0} = ; = {ER3, ER2, ER1, ER0}/4 ; 3.2.1.4 If No, {ER3, ER2, ER1, ER0} = ; = {ER3, ER2, ER1, ER0}/16 ; 3.2.2 If No, clear UIU, RDY and RDY2 ; flags (CON[7:5]=000 ) ; 4 {DL3, DL2, DL1, DL0} = {DL3, DL2, DL1, DL0} + ; +{ER3, ER2, ER1, ER0}. ; 5. Update High_DAC, Low_DAC and DACRDY output line ; 6. Read the 4 least significant bits from ADC and start ; a new conversion ; 7. Restart the Scan procedure ; ; ; Hardware resources ;==================== ; ; Uses 8 input/output pins, 9 output pins, 1 input pin and 1 ; counter input pin ; ; DBUS[7:0] data bus ;------------------- ; 8 bit bi-directional data bus is used to read the 20 bit input ; command IC[19:0], the one bit filter selection FS[0] and the 3 bit ; curvature correction selection CC[2:0]. It is also used to write ; the 16 bit Low_DAC command LDAC[15:0] and the 16 bit High_DAC ; command HDAC[15:0]. ; ; assigned to PIC port C[7:0] ; ; The data format for the read and write operations is as follows: ; ; DBUS[ 7:0] = IC[19:12] when NCSR[2] = 0 ; DBUS[ 7:0] = IC[11: 4] when NCSR[1] = 0 ; DBUS[ 7:0] = {IC[3:0], FS[0], CC[2:0]} when NCSR[0] = 0 ; LDAC[ 7:0] = DBUS[7:0] when NCSD[0] = 0 and ADDAC = 0 ; LDAC[15:8] = DBUS[7:0] when NCSD[0] = 0 and ADDAC = 1 ; HDAC[ 7:0] = DBUS[7:0] when NCSD[1] = 0 and ADDAC = 0 ; HDAC[15:7] = DBUS[7:0] when NCSD[1] = 0 and ADDAC = 1 ; ; ; NCSR[2:0] active low output enable controls for input registers ;---------------------------------------------------------------- ; 3 output lines used to selectively enable the three 8-bit input ; registers in order to read the user updated DAC command, the 3 ; curvature correction bits and the one filter control bit. ; ; NCSR[0] enables the low input byte (LSB) and is assigned to port B[0] ; ; NCSR[1] enables the second input byte and is assigned to port B[1] ; ; NCSR[2] enables the high input byte (MSB) and is assigned to port B[2] ; ; ; NCSD[1:0] active low input enable controls for the DACs ;-------------------------------------------------------- ; 2 output lines used to selectively enable the two DACs ; ; NCSD[0] enables the Low_DAC and is assigned to port B[3] ; ; NCSD[1] enables the High_DAC and is assigned to port B[4] ; ; ; ADDAC DAC address control ;-------------------------- ; output line. A low enables a write operation to the low byte of ; Low_DAC or High_DAC. A high enables a write operation to the high ; byte of Low_DAC or High_DAC. ; ; ADDAC is assigned to port B[5] ; ; ; NLDAC active low DAC load control ;---------------------------------- ; output line. A high to low transition on this line updates the ; Low_DAC and High_DAC output values ; ; NLDAC is assigned to port B[6] ; ; ; DACRDY active high ready output signal ;--------------------------------------- ; output line. Indicates that the control loop error has been ; within a +/- 4ppm range for at least 250 ms ; ; DACRDY is assigned to port B[7] ; ; ; SCKAD external serial clock line for the ADC ;--------------------------------------------- ; output line. ADC external serial clock. An external 10Kohm ; pull-down resistor is necessary on this line for correct ; power-up configuration. ; ; SCKAD is assigned to port A[0] ; ; ; SDOAD serial data line from ADC ;-------------------------------- ; input line. Used to read ADC serial data. ; ; SDOAD is assigned to port A[1] ; ; ; ; NWRUI active low user interface write control ;---------------------------------------------- ; input line. The user must bring this line low in order to update ; the DAC input value. A minimum low and high time is required ! ; ; NWRUI is assigned to TOCKI counter input pin ; ; ; ; The spare I/O pins A[3:2] are configured as outputs and maintained LOW. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list p=16c55A ; list directive to define processor #include ; processor specific variable definitions __CONFIG _CP_OFF & _WDT_ON & _XT_OSC ;VARIABLE DEFINITIONS ;==================== UB0 EQU H'0008' ; user input word buffer LSB UB1 EQU H'0009' ; user input word buffer second byte UB2 EQU H'000A' ; user input word buffer MSB U0 EQU H'0008' ; user input word LSB U1 EQU H'000B' ; user input word second byte U2 EQU H'000C' ; user input word MSB CON EQU H'000D' ; control byte ADC0 EQU H'000E' ; ADC conversion result LSB ADC1 EQU H'000F' ; ADC conversion result second byte ADC2 EQU H'0010' ; ADC conversion result third byte ADC3 EQU H'0011' ; ADC conversion result MSB ADCC EQU H'0012' ; ADC curvature correction byte ER0 EQU H'000E' ; control loop error LSB ER1 EQU H'000F' ; control loop error second byte ER2 EQU H'0010' ; control loop error third byte ER3 EQU H'0011' ; control loop error MSB DL0 EQU H'0013' ; Low_DAC LSB DL1 EQU H'0014' ; Low_DAC second byte DL2 EQU H'0015' ; Low_DAC third byte DL3 EQU H'0016' ; Low_DAC MSB INDX EQU H'0017' ; index variable TMPV EQU H'0018' ; temporary variable #define OPRDF 0x2F ; OPTION register default value #define CONDF 0x80 ; CON register default value ;HARDWARE ASSIGNMENT DEFINITIONS ;=============================== #define DBUS PORTC ; 8bit I/O data bus #define REGCN PORTB ; register control port #define REGDF 0x7F ; register control port default value #define NCSR0 PORTB,0 ; LSB input register active low output enable #define NCSR1 PORTB,1 ; second byte input register active low output enable #define NCSR2 PORTB,2 ; MSB input register active low output enable #define NCSD0 PORTB,3 ; Low_DAC active low write enable #define NCSD1 PORTB,4 ; High_DAC active low write enable #define ADDAC PORTB,5 ; address bit for Low_DAC and High_DAC #define NLDAC PORTB,6 ; active low load control for Low_DAC and High_DAC #define DACRDY PORTB,7 ; 20bit_DAC ready indicator #define ADCCN PORTA ; ADC control port #define ADCTR 0x02 ; ADC control port configuration ; SDOAD input, the rest outputs #define ADCDF 0x02 ; ADC control port default value #define SCKAD PORTA,0 ; ADC external serial clock #define SDOAD PORTA,1 ; ADC serial data output ;THE CODE ;=============================== RESET ORG 0x1FF ; processor reset vector goto start ORG 0x000 ;Initialization procedure ;------------------------ start movlw ADCDF ;write ADC control port default value movwf ADCCN ; movlw ADCTR ;set the I and O pin states for the tris ADCCN ;ADC control port ; movlw REGDF ;write register control port default value movwf REGCN ; clrw ;set register control port pins as tris REGCN ;output only ; movwf DBUS ;set DBUS default value of 0 tris DBUS ;set DBUS as output ; movlw OPRDF ;set OPTION register default value option ; ; clrf TMR0 ; btfss STATUS,NOT_TO ;if this is not a power-on reset movwf TMR0 ;load Timer0 with a nonzero value ;to force an initial read of the ;external input register ; clrf DL3 ;initialize {DL3, DL2, DL1, DL0}=0 clrf DL2 ; clrf DL1 ; clrf DL0 ; clrf U2 ;initialize {U2, U1, U0}=0 clrf U1 ; clrf U0 ; ; movlw CONDF ;set CON variable default value movwf CON ; ;prepare to trigger a new ADC conversion ;after completing a hardware update movlw 0x20 ;read and discard 32 serial bits from movwf INDX ;the ADC ; goto iupdt ;go to the hardware update function ;ADC output buffer flush function ;-------------------------------- fladc movlw 0x20 ;reads and discards 32 serial bits from movwf INDX ;the ADC ;ADC dummy serial read function ;------------------------------ ;reads and discards the number of serial ;bits indicated by the INDX variable rddmy bsf SCKAD ;low-to-high ADC serial clock edge bcf SCKAD ;high-to-low ADC serial clock edge decfsz INDX,1 ;test if we read enough bits goto rddmy ;if No, read one more bit btfss SDOAD ;if Yes test that a new conversion has started goto fladc ;if No, there is an interface problem. Flush the ;ADC output buffer and start a new conversion goto scan ;if Yes restart the scan procedure ;external input register read function ;------------------------------------- rduiu movlw 0xFF ;input register read routine tris DBUS ;set data bus in read mode (input) bcf NCSR0 ;output enable for input reg. LSB nop ;wait for data bus to settle movf DBUS,0 ;read input reg. LSB bsf NCSR0 ;output disable for input reg. LSB bcf NCSR1 ;output enable for input reg. second byte movwf UB0 ;store input reg. LSB into input buffer movf DBUS,0 ;read input reg. second byte bsf NCSR1 ;output disable for input reg. second byte bcf NCSR2 ;output enable for input reg. MSB movwf UB1 ;store input reg. second byte into input buffer movf DBUS,0 ;read input reg. MSB movwf UB2 ;store input reg. MSB into input buffer clrw ;terminate input reg. read operation bsf NCSR2 ;output disable for input reg. MSB tris DBUS ;return data bus to write mode clrf TMR0 ;clear Timer0 to continue wait for a UIU event bcf DACRDY ;signal user that input data has been read bsf CON,7 ;set UIU flag bcf CON,6 ;clear RDY flag bcf CON,5 ;clear RDY2 flag ;scan procedure ;-------------- ;monitors UIU and end-of-conversion events scan movf TMR0,1 ;test if Timer0 = 0 btfss STATUS,Z ;if Timer0=0 no UIU has occurred, skip next goto rduiu ;a user interface update has occurred ;go and read the new DAC input data btfsc SDOAD ;test ADC end of conversion signal goto scan ;conversion not ready, rescan ;ADC serial read function ;------------------------ rdadc movlw 0x1B ;ADC conversion is done, read first 28 bits movwf INDX ;the first bit must be "0" to get here ;so do not bother with it rdbit bsf SCKAD ;low-to-high ADC serial clock edge bcf SCKAD ;high-to-low ADC serial clock edge bcf STATUS,C ;move ADC output bit to carry. First clear carry btfsc SDOAD ;read ADC output bit bsf STATUS,C ;if ADC output is "1" set carry rlf ADC0,1 ;load carry as msb of ADC result rlf ADC1,1 ;and shift left all 4 bytes of the ADC result rlf ADC2,1 ; rlf ADC3,1 ; decfsz INDX,1 ;test if all 28 bits have been read goto rdbit ;if not, continue ; ;we have skipped the first ADC bit (ADC bit31=0) ;which has been tested as =0 when we detected the ;end of conversion. ;we have read 27 additional bits and have generated ;27 clock pulses. To restart the conversion we must ;produce the 5 remaining clock pulses ;verify validity of ADC serial data and format it ;------------------------------------------------ btfsc ADC3,2 ;test if the ADC bit30 is "0" goto fladc ;if not there is an interface problem. Flush the ;ADC output buffer and start a new conversion ;if yes, put the ADC result in 2's complement form movlw 0x03 ;first clear the 6 most significant bits in ADC3 andwf ADC3,1 ; btfsc STATUS,Z ;tests for the [ADC_B29,ADC_B28]=00 ADC output goto rdend ;if Yes the formatting is completed. ;in very early LTC2400 samples the ADC output code ;[ADC_B29,ADC_B28]=00 is valid ; goto fladc ;for current LTC2400 devices improved error ;detection capability is obtained if the ;previous line is replaced with this line. ;The replacement is not mandatory. ;For current LTC2400 parts the output code ;[ADC_B29,ADC_B28]=00 is not valid thus it may ;be assumed that an ADC interface error has ;occurred. Flush the ADC output buffer and start ;a new conversion movlw 0x02 ;if No, convert ADC3 in 2's complement form btfss ADC3,1 ; movlw 0xFE ; xorwf ADC3,1 ; ;curvature correction calculator ;------------------------------- ;first order curvature correction multiplier ;use ADC2[7:0] as a 2's complement number rdend movf ADC2,0 ;calculate abs(ADC2) btfsc ADC2,7 ;if ADC2[7]=0 w = ADC2 comf ADC2,0 ;else w = !ADC2 movwf ADCC ;ADCC=w=abs(ADC2) ;second order curvature correction multiplier ;use ADC2[6:0] as a 2's complement number movf ADC2,0 ;calculate abs(ADC2[6:0]) btfsc ADC2,6 ;if ADC2[6]=0 w = ADC2 comf ADC2,0 ;else w = !ADC2 movwf TMPV ;TMPV=w=abs(ADC2[6:0]) rrf TMPV,0 ;w=TMPV/2 in order to scale the second order ;curvature correction andlw 0x1f ;clear 3 MSB of w to complete calculation addwf ADCC,0 ;w=abs(ADC2)+abs(ADC2[6:0])/2 movwf TMPV ;TMPV contains the curvature correction multiplier ; clrf ADCC ; bcf STATUS,C ;clear carry for div-by-2 operation btfsc CON,2 ;if CON[2]=1 movwf ADCC ;ADCC=ADCC+abs(ADC2) rrf TMPV,1 ;TMPV=TMPV/2 movf TMPV,0 ; bcf STATUS,C ;clear carry for div-by-2 operation btfsc CON,1 ;if CON[1]=1 addwf ADCC,1 ;ADCC=ADCC+abs(ADC2)/2 rrf TMPV,1 ;TMPV=TMPV/2 movf TMPV,0 ; btfsc CON,0 ;if CON[0]=1 addwf ADCC,1 ;ADCC=ADCC+abs(ADC2)/4 ;code comparator procedure ;------------------------- ccomp btfss CON,7 ;if the UIU flag is clear goto ercalc ;skip CON[3:0] and U0 update movlw 0xF0 ;else update CON[3:0] andwf CON,1 ;clear CON[3:0] movlw 0x0F ;extract new CON[3:0] andwf UB0,0 ;from input buffer iorwf CON,1 ;and load it movlw 0xF0 ;move UB[7:4] to U0[7:4] andwf UB0,1 ;UB0 and U0 use the same ;physical location ;calculate control loop error ;---------------------------- ercalc comf ADCC,1 ;ADCC 1's complement comf ADC0,1 ;ADC0 1's complement movlw 0x02 ;add carry-in for ADCC and for ADC0 ;2's complement conversion clrf TMPV ;prepare carry-out accumulator addwf UB0,0 ;w=carry-in + UB0 btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it addwf ADCC,0 ;w=carry-in + UB0 - ADCC btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it addwf ADC0,1 ;ER0=UB0 - ADC0 - ADCC ;has same location as ADC0 btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it comf ADC1,1 ;ADC1 1's complement movlw 0xff ;w=0xff (1's complement of ADCC second byte) addwf TMPV,0 ;w=0xff + carry-in clrf TMPV ;prepare carry-out accumulator btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it addwf U1,0 ;w=0xff + carry-in + UB1 btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it addwf ADC1,1 ;ER1=U1 - ADC1 - 0 + carry-in ;has same location as ADC1 btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it comf ADC2,1 ;ADC2 1's complement movlw 0xff ;w=0xff (1's complement of ADCC third byte) addwf TMPV,0 ;w=0xff + carry-in clrf TMPV ;prepare carry-out accumulator btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it addwf U2,0 ;w=0xff + carry-in + UB2 btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it addwf ADC2,1 ;ER2=U2 - ADC2 - 0 + carry-in ;has same location as ADC2 btfsc STATUS,C ;if there is a carry-out incf TMPV,1 ;accumulate it comf ADC3,1 ;ADC3 1's complement decf TMPV,1 ;ADCC 2's complement term. The next ;carry-in is not useful - discard movf TMPV,0 ;w=carry-in addwf ADC3,1 ;ER3= 0 - ADC3 - 0 + carry-in ;has same location as ADC3 btfsc CON,7 ;test if the UIU flag is set goto lduiu ;go to U1, U2 update ;error comparator ;---------------- ;calculate absolute value of loop error and ;compare loop error magnitude with the 4ppm ;threshold movf ER3,0 ;W = ER3 btfsc ER3,7 ;test if {ER3, ER2, ER1, ER0} < 0 comf ER3,0 ;if yes W = -ER3 btfss STATUS,Z ;test if W=0 goto nrdy ;if not absolute error >= 4ppm movf ER2,0 ;W = ER2 btfsc ER3,7 ;test if {ER3, ER2, ER1, ER0} < 0 comf ER2,0 ;if yes W = -ER2 btfss STATUS,Z ;test if W=0 goto nrdy ;if not absolute error >= 4ppm movf ER1,0 ;W = ER1 btfsc ER3,7 ;test if {ER3, ER2, ER1, ER0} < 0 comf ER1,0 ;if yes W = -ER1 btfss STATUS,Z ;test if W=0 goto nrdy ;if not absolute error >= 4ppm movf ER0,0 ;W = ER0 btfsc ER3,7 ;test if {ER3, ER2, ER1, ER0} < 0 comf ER0,0 ;if yes W = -ER0 andlw 0xC0 ;keep only W[7:6] which are bits >= 4ppm btfss STATUS,Z ;test if W[7:6]=0 goto nrdy ;if not absolute error >= 4ppm ;if we are here the absolute loop error is ;less than 4 ppm. Set the flags and ;scale the loop error. btfsc CON,6 ;test if RDY flag is already set bsf CON,5 ;if Yes, set RDY2 flag bsf CON,6 ;set RDY flag in any case ;error scaling ;------------- ;reduce error correction value for loop ;damping and ADC noise reduction btfsc CON,3 ;test if CON[3]=0 goto div4 ;if Yes ER0=ER0/4 ;if No ER0=ER0/16 rrf ER0,1 ;*1/2 rrf ER0,0 ;*1/2 andlw 0x3F ;clear 2 most significant bits btfsc ER3,7 ;if {ER3, ER2, ER1, ER0} < 0 iorlw 0xC0 ;set 2 most significant bits movwf ER0 ;ER0=ER0/4 div4 rrf ER0,1 ;*1/2 rrf ER0,0 ;*1/2 andlw 0x3F ;clear 2 most significant bits btfsc ER3,7 ;if {ER3, ER2, ER1, ER0} < 0 iorlw 0xC0 ;set 2 most significant bits movwf ER0 ;ER0=ER0/4 goto eracc ;go to error accumulator ;load latest user input ;---------------------- lduiu movf UB1,0 ; movwf U1 ;U1=UB1 movf UB2,0 ; movwf U2 ;U2=UB2 nrdy movlw 0x1F ; andwf CON,1 ;clear UIU, RDY and RDY2 flags ;error accumulator ;----------------- ;adds the current loop error to the ;previous Low_DAC control value ;{DL3, DL2, DL1, DL0}={DL3, DL2, DL1, DL0}+ ; +{ER3, ER2, ER1, ER0} eracc movf ER0,0 ;the carry-in is 0 clrf TMPV ;clear carry-in accumulator addwf DL0,1 ;DL0=DL0+ER0 btfsc STATUS,C ;if there is a carryout incf TMPV,1 ;accumulate in carry-in movf TMPV,0 ;load carry-in clrf TMPV ;clear carry-in accumulator addwf ER1,0 ;W=ER1+carry-in btfsc STATUS,C ;if there is a carryout incf TMPV,1 ;accumulate in carry-in addwf DL1,1 ;DL1=DL1+ER1 btfsc STATUS,C ;if there is a carryout incf TMPV,1 ;accumulate in carry-in movf TMPV,0 ;load carry-in clrf TMPV ;clear carry-in accumulator addwf ER2,0 ;W=ER2+carry-in btfsc STATUS,C ;if there is a carryout incf TMPV,1 ;accumulate in carry-in addwf DL2,1 ;DL2=DL2+ER2 btfsc STATUS,C ;if there is a carryout incf TMPV,1 ;accumulate in carry-in movf TMPV,0 ;load carry-in addwf ER3,0 ;W=ER3+carry-in addwf DL3,1 ;DL3=DL3+ER3 ;Low_DAC control truncation ;-------------------------- ;limits the {DL3, DL2, DL1, DL0} range to ; abs({DL3, DL2, DL1, DL0}) < 2^15 by ;truncation btfss STATUS,Z ;test if DL3=0 goto negpot ;if No, DL may be negative ;if Yes, DL is positive ;test for overflow (>= 2^15) movf DL2,1 ; btfss STATUS,Z ;test if DL2=0 goto ovflow ;if No, DL >= 2^15, must truncate ;if Yes continue testing for overflow btfsc DL1,7 ;test if DL1[7]=1 goto ovflow ;if No, DL >= 2^15, must truncate goto updt ;if Yes we are done with DL range control ovflow clrf DL3 ;if we arrive here DL >= 2^15. Must clrf DL2 ;truncate to DL=2^15-1 => DL3=DL2=0 movlw 0xFF ;and DL1=0xEF, DL0=0xFF movwf DL0 ; movwf DL1 ; bcf DL1,7 ; goto updt ;done with overflow correction udflow clrf DL1 ;if we arrive here DL < -2^15. Must bsf DL1,7 ;truncate to DL=-2^15-1 => DL3=DL2=0xFF clrf DL0 ;and DL1=0x80, DL0=0 movlw 0xFF ; movwf DL3 ; movwf DL2 ; goto updt ;done with underflow correction negpot btfss DL3,7 ;DL may be negative. Test if DL3[7]=1 goto ovflow ;if No, DL > 2^15, must truncate incf DL3,0 ;if Yes, DL <0. btfss STATUS,Z ;test if DL3=FF goto udflow ;if No, DL < -2^15, must truncate incf DL2,0 ;if Yes continue testing for underflow btfss STATUS,Z ;test if DL2=FF goto udflow ;if No, DL < -2^15, must truncate ;if Yes continue testing for underflow btfss DL1,7 ;test if DL1[7]=0 goto udflow ;if No, DL < -2^15, must truncate ;if Yes we are done with DL range control ;Hardware update function ;------------------------ ;Low_DAC and High_DAC update ; ;This is the hardware update function ;entry point for normal operation. ; updt movlw 0x05 ;prepare to generate the last 5 ADC external movwf INDX ;serial clock pulses ;when going to restart the scan procedure ;at the end of the hardware update function ;This will trigger a new ADC conversion. ; ;This is the hardware update function ;entry point for initial operation. ;The INDX variable has been initialized ;before to 0x2F which will generate ;32 serial clock pulses to the ADC thus ;starting a new conversion ; iupdt clrw ;set the data bus in write mode tris DBUS ; bcf ADDAC ;set DAC address for LSB movf U1,0 ;load High_DAC LSB movwf DBUS ; bcf NCSD1 ;write to High_DAC bsf NCSD1 ; movf DL0,0 ;load Low_DAC LSB movwf DBUS ; bcf NCSD0 ;write to Low_DAC bsf NCSD0 ; bsf ADDAC ;set DAC address for MSB movf U2,0 ;load High_DAC MSB movwf DBUS ; bcf NCSD1 ;write to High_DAC bsf NCSD1 ; movlw 0x80 ;change DL1 to offset binary xorwf DL1,0 ;load Low_DAC MSB movwf DBUS ; bcf NCSD0 ;write to Low_DAC bsf NCSD0 ; bcf NLDAC ;load Low_DAC and High_DAC bsf NLDAC ; ;DACRDY output update btfsc CON,5 ;test if RDY2 flag is set bsf DACRDY ;if Yes, set DACRDY output btfss CON,5 ;if No bcf DACRDY ;and only if No, clear DACRDY output ; clrwdt ;clear watch dog timer ; goto rddmy ;generate the necessary number ;of ADC serial clock pulses in order ;to start a new conversion END ; directive 'end of program'