METHOD AND APPARATUS FOR TRANSLATING A MASS FLOW METER SIGNAL

Abstract: An apparatus for translating a mass flow meter signal is connected to a mass flow meter of a mass flow controller and to a host process control system and includes a microcontroller which receives flow controller command calibration signal data from a calibration signal to which it may be temporarily connected. The microcontroller also includes a routine for storing the flow controller command calibration signal data in a nonvolatile or EPROM memory. An analog to digital converter of the microcontroller receives a linear flow controller analog command signal from a processs host controller. In response thereto the microcontroller produces an analog mass flow rate command correction signal which is responsive to the stored flow controller command calibration signal data. The analog mass flow rate command correction signal is added to the command signal to produce a translated or corrected mass flow rate command signal. The amplifier supplies the corrected flow command signal to the mass flow controller.

Description: -1- METHOD AND APPARATUS FOR TRANSLATING A MASS FLOW METER SIGN.AL BACKGROUND OF THE INVENTION The invention relates to a method and apparatus for translating or modifying a linear command signal from a host process controller to operate a nonlinear response control device. More particularly, the application relates to a method and apparatus for modifying a linear mass flow command signal for a mass flow controller to cause the mass flow controller to respond to the command signal with a linear gas mass flow output even when the mass flow controller's response is itself nonlinear. One of the problems associated with conventional thermal mass flow controllers is that the electrical response of the thermal mass flow sensing element, which usually comprises a single or pair of sensing windings wound about a sensor tube through which a portion of a flow of gas travels, is nonlinear with respect to the actual mass flow rate of gas or vapor through the sensor tube. In prior U.S. Patent No. 4,658,855 to Doyle, a mass flow controller includes a segment generator comprising a plurality of precision limiters having operational amplifiers, diodes and potentiometers. The segment generator provides piecewise linearization to the bridge output signal of the mass flow controller. Unfortunately, this type of segment generator must be adjusted by hand when the mass flow controller is manufactured and retains its hand adjustment settings even when the mass flow controller is incorporated in a gas shelf of a diffusion furnace or gas supply system of a chemical vapor deposition apparatus, or a plasma system or the like. In addition, the segment generator only will adjust at four points along the flow curve, the 25%, 50%, 75% and 100% flow points leaving some nonlinear -2- distortion between those points which may not be zeroed out of the mass flow controller. Conventional thermal mass flow controllers also suffer from the disadvantage that their response may change as they age in a gas shelf, as parts are worn or as material accumulates in the sensor tube. As a result, the response of the mass flow controllers may shift while the manual settings of their linearizing segment generators do not change. In order to relinearize such a system, it is conventionally necessary to remove the mass flow controller from the plumbed-in fittings in the gas lines of the gas shelf thereby opening the gas lines to the air which will necessitate having possibly to bake out the reassembled gas system once the mass flow controller is relinearized. This is time consuming and very expensive for a wafer fabricator. In some instances it leads to reluctance to recalibrate the thermal mass flow controllers until wafer yields are adversely affected by the changes in the thermal mass flow controllers. What is needed then is a method and apparatus which allows effective in situ recalibration of a mass flow controller and which provides highly accurate response to a given flow command signal. SUMMARY OF THE INVENTION The present invention comprises a method and apparatus for translating a mass flow meter signal, more specifically a nonlinear sensed mass flow signal received from a stand-alone mass flow meter or a mass flow meter portion of a mass flow controller. A translated sensed mass flow signal is produced by the inventive apparatus and supplied to a host process controller, such as a direct digital controller or the like. The apparatus, when used with a thermal mass flow controller, receives a linear mass flow command signal from the host process controiler and produces a translated mass flow command signal which is received by a mass flow command node or set point signal node of the mass flow controller. Such a nonlinear mass flow command signal is in the form of an analog signal which conventionally may assume any value between zero and five volts with zero volts representing zero mass flow rate and five volts representing 100% or full flow through the mass flow controller. Given the fact that the mass flow controller being driven by the mass flow command signal may have a nonlinear response to the command signal, it is clear, for instance, that when a one volt signal, nominally representative of 20% flow rate, is sent to the mass flow controller, the mass flow controller may flow gas at a 15% or 25% mass flow rate because the mass flow controller is responding nonlinearly to the linear mass flow command signal. The apparatus has stored in a nonvolatile memory connected to the microcontroller a multiple entry table of values which represents mass flow offsets or mass flow corrections. When the apparatus receives the linear mass flow command signal from the host process controller, the linear mass flow command signal is digitized by an analog-to-digital converter in the microcontroller, a processing unit of the microcontroller, in response to the flow command digitized signal, accesses a lookup table within the nonvolatile memory of the microcontroller, and the processing unit of the microcontroller generates a digitized mass flow correction signal. The digitized mass flow correction signal is fed to a digital-to-analog converter in the microcontroller. The digital-to-analog converter produces an analog flow command correction signal. The analog flow command signal is added with the linear mass flow command correction signal received from the host process controller to produce a translated or corrected mass flow command analog signal which is then fed to the command pin of the thermal mass flow controller. The thermal mass flow controller also includes a sensor output pin or node which generates a sensed mass flow signal which may be perturbed by the nonlinear response of the mass flow controller. The sensed mass flow signal is received by the apparatus and is digitized. The processing unit accesses a flow correction value from the EPROM. The flow correction value is supplied to the digital-to-analog converter which produces an analog sensed flow correction signal. The analog sensed flow correction signal is subtracted, in a summing amplifier, from the analog sensed mass flow signal to yield a translated or linearized mass flow rate signal. The linearized mass flow rate signal is supplied to the host process controller. Thus, the host process controller sends a linear zero to five volt mass flow command signal and receives back a linear second mass flow rate signal in a fashion which is completely transparent to the host process controller. The signals are modified by the inventive apparatus so that a nonlinearly responding mass flow controller can operate in response to a linear signalling host process controller. It should be appreciated that the signal which is being modified is the command signal and not the internal bridge signal within the flow controller as is taught by Doyle. In order to generate the data which is stored in the lookup table of the EPROM a calibration system embodying the instant invention is provided. The calibration system includes means for sweeping through a set of calibration values using a sine wave signal. The sine wave signal has a period approximately ten times longer than the time constant of the mass flow controller. One of the problems which has been encountered in the past is that in order to calibrate mass flow controllers, settling time must be allowed for after each commanded flow. The settling time, for instance, for four or five points for a conventional flow controller might require a calibration time of one half hour or more. The use of the single frequency or sine wave curve allows a large number of points, such as 256 points, to be rapidly swept through in the space of only about thirty seconds. This represents an increase of several orders of magnitude in the rate at which the mass flow controller may be calibrated. The lag primarily due to the mass flow controller time constant causes a constant phase shift to be effected because only a single frequency is being input into the mass flow controller. The phase shift may be matched by the calibrator after a first sine wave sweep. After the second sine wave sweep, the phase adjusted flow signal is compared to the setpoint on a point by point basis, allowing a full 256 point calibration table to be generated in only one minute. The generated calibration table is then sent via a digital link to the inventive command signal modifying apparatus where it is stored in the nonvolatile memory of the microcontroller. The non-volatile memory has sufficient space to store up to ten tables each of which, for instance, may be representative of the mass flow rate calibration offsets for a different gas species, e.g., nitrogen, phosphine, oxygen, phosphorus oxychloride, diborane, sulfur hexafluoride, silane and the like. It is a principal aspect of the present invention to provide a thermal mass flow command signal linearizer which, in response to a linear command signal, generates a corrected command signal for controlling a nonlinear mass flow controller. It is another aspect of the present invention to provide an apparatus for linearizing a sensed mass flow rate signal output by a mass flow controller for supply to a host process controller. It is another aspect of the present invention to provide a calibration system for a mass flow controller which can rapidly calibrate a number of points by the use of a single frequency sine wave sweep. Other aspects and advantages of the present invention will become obvious to one of ordinary skill in the art upon a perusal of the following specification and claims in light of the accompanying drawings. BRIEF DESCRIPTION OF THE DRAWINGS FIG. 1 is a block diagram of a system embodying the present invention; FIG. 2 is a schematic diagram of a signal translator shown in FIG. 1; FIG. 3 is a block diagram of a microcontroller included in the signal translator shown in FIG. 2; FIG. 4 is a block diagram of an interface board adapted for connection to an IBM compatible type PC AT microcomputer AT bus and for connection to a calibrator as shown in FIG. 1; FIG. 5 is a schematic diagram of a parallel to serial interface; FIG. 6 is a block diagram of the inventive translator connected between the host processor and the calibrated mass flow controller; FIG. 7 is a flow diagram of the cable_start routine executed by the microcontroller upon power up; FIG. 8 is a flow diagram of the check_sys_mem routine executed by the microcontroller on power up; FIG. 9 is a flow chart of the init_all routine executed by the microcontroller on power up; FIG. 10 is a flow diagram of the check_switch routine executed by the microcontroller on power up; FIG. 11 is a flow diagram of the flow_mode routine executed by the microcontroller in the normal control operation; FIG. 12 is a flow diagram of a timer routine to allow the analog-to-digital converter to operate; FIG. 13 is a flow diagram of an analog-to- digital converter routine; FIG. 14 is a flow diagram to determine whether the microcontroller is in a normal control mode or is in a calibration mode; FIG. 15 is a flow diagram to control transfer of calibration information between the personal computer and the microcontroller; FIG. 16 is a flow diagram of a routine for reading and storing input analog signals from the calibration system and the mass flow controller during calibration; FIG. 17 is a flow diagram of a routine for verifying a command from the calibrator system to operate in flow mode using calibration data from a specified calibration table; FIGS. 18A and 18B are a flow diagram of a routine for transferring information from the calibrator system to a volatile memory of the inventive apparatus; FIG. 19 is a flow diagram of a routine that specifically transfers the downloaded calibration data from volatile to non-volatile memory; FIGS. 20A and 2OB are a flow diagram of a routine that transfers data from the non-volatile memory to the calibration system; FIG. 21 is a flow diagram of a software reset routine; FIG. 22 is a flow diagram of a switch reset routine for unhandled errors; FIG. 23 is a flow diagram of a main routine for execution in the personal computer of the calibration system; and FIG. 24 is a flow diagram of a calibration routine for generating a sinusoidal calibration function and downloading the resulting data correction points to the signal translator. DETAILED DESCRIPTION OF THE PREFERRED EMBODIMENT Referring now to the drawings and especially to FIG. 1, an apparatus or signal translator 10 for translating a nonlinear mass flow meter signal and embodying the present invention is generally shown therein. The signal translator 10 is coupled to a mass flow controller under test 12 and to a communications adapter card 14. The mass flow controller under test 12 is connected in a gas loop 16 to receive gas from a standard mass flow meter 18, which can be a transfer standard or secondary standard, and which in turn is connected to a Unit Instruments, Inc., XCAL-200, automatic calibrator 20. The XCAL-200 is part of the XCAL-200 system which includes a computer 22 comprising an IBM PC/AT personal computer having a Metrabyte Dash-16 signal processing board therein connected to a communications bus 24 which is also connected to the calibrator 20 and a parallel interface 8 of the type shown in FIG. 4 connected to a communications bus 26 which is also connected to the calibrator 20. Software listings appear at pages 29 through 140 herein. Software listed at pages 29 through 34, and 116 through 140 executes on a microcontroller within the signal translator 10. Software listed at pages 35 through 55 controls the communications adapter 14. Software listed at 56 through 63 executes on the interface 8. A source of gas, which may comprise a compressed gas tank 28, is connected by a gas feedline 30 to the calibrator 20. The calibrator 20 delivers gas via gas feedline 32 to the secondary standard mass flow meter 18. Mass flow meter 18 is connected by a communications line 34 to supply the mass flow reference signals to the calibrator system which is controlled by software executing in the PC/AT computer. The standard UICALSYS software, which is available with Unit Instruments XCAL-200 calibration system, is employed with the exception of certain software appearing at pages A-36 through A-87 which executes on the personal computer 22 for performing sine wave calibration. In operation, the computer 22 commands the calibrator 20 to select a commanded flow rate which is then measured by the standard mass flow meter 18 and fed back to the calibrator 20. The mass flow controller output signal is also fed to the calibrator 20 and compared with to the standard and an offset value is determined and stored in memory in the calibrator 20. More specifically, the calibrator 20, rather than being stepped through three or four points, is stepped through a plurality of points whose values are a sine wave function with respect to time. The sine wave function is a simple sine wave having a single frequency. The period of the sine wave is approximately ten times the time constant of the mass flow controller 12. Referring now to FIGS. 2 and 3, the signal translator 10 is shown therein in more detail. The signal translator 10 includes a Signetics 87C552 microcontroller generally identified by numeral 40 connected to a latch 42 which is a 74HC573D and also connected to a 28C65 EPROM memory 44. The EPROM memory 44 is used to store calibration data therein. The power for the signal translator is provided by a first power supply 46 and a second power supply 48 which generate regulated +8 volt, +5 volt and -5 volt potentials in a conventional fashion. In operation, the signal translator 10, as may best be seen in FIG. 6, is connected to the mass flow controller 12 via a pair of analog signal lines 50 and 52 with analog signal line 50 carrying a corrected or translated mass flow rate command signal to the mass flow controller 12 which is delivered to its set point pin. The line 52 carries the uncorrected or raw sensed mass flow rate signal from the mass flow controller 12 to the signal translator 10. A line 54 from the signal translator 10 to a host process controller 56 carries the corrected or translated linear sensed mass flow rate signal to the host process controller 56. A line 58, connected from the host process controller 56 to the signal translator 10, carries a linear mass flow rate command signal to the signal translator 10. The signals on lines 58 and 54 are substantially linear signals with respect to flow while the signals on lines 50 and 52 are non-linear signals with respect to flow. The linear mass flow command signal from line 58 is supplied to a resistor 60 connected to a non- inverting input pin 62 of an operational amplifier 64. Operational amplifier 64 is configured as a summing amplifier and has its output connected to the line 50 which is connected to the set point pin of the mass flow controller 12. In addition, the line 58 supplies the linear mass flow signal through a filter 70 to a line 72 connected to an ADC0 input pin 74 of the microcontroller 40. That signal, which is supplied to the microcontroller 40, is received by an onboard analog-to- digital converter 76 which produces an eight-bit digitized signal which is supplied to a central processing unit 78 of the microcontroller 40. The central processing unit 78 operates under the control of a control program stored in 8K EPROM memory 80 connected to a system bus 82 coupled to the CPU 78. Also coupled to the system bus 82 is an onboard digital-to-analog converter, more specifically comprising a pulse width modulator 84 providing outputs at lines PWMO and PWM1 which are respectively numbered 86 and 88. The pulse width modulated zero pin 86 is connected to a resistor 90. The pulse width modulated one pin 88 is connected to a resistor 92. Resistor 90 is connected to a five pole low pass Butterworth filter 94 which is an LTC 1062CS. The resistor 92 is connected to an identical low pass five pole Butterworth filter 96. The combination of the pulse width modulation feature and the low pass filters. generates analog output signals so that the pulse width modulators 86 and 88 and the low pass filters 94 and 96 act as a digital-to-analog converter. An output line 100 connected to the filter 94 supplies a signal through a resistor 102 to an amplifier 104 which feeds a signal through a line 106 to a resistor 108 connected with the resistor 60 to the summing junction of the amplifier 64. Similarly, the output signal on line 88 is supplied through the low pass filter 96 to a line 110 which drives a resistor 112 connected to the amplifier 104. The amplifier 104 is also connected to an amplifier 120 having the linearized flow signal output line 54 connected to the output thereof. The amplifier 120 also receives the raw or unconditioned, sensed mass flow controller signal from line 52 through a resistor 122, coupled to a line 124, which is coupled to the non- inverting pin 126 of the amplifier 120. The EPROM memory 44 has stored within it multiple calibration or set point tables, each of which consists of 256 points for which correction values are stored. The signal translator 10, acting through the microcontroller 40, receives the linear signal MFC IN through line 58 and supplies it to pin 74, where it is converted to a digital signal, processed by the microcontroller 40 and used to access a selected portion of the calibration table in the EPROM 44. That offset signal is then supplied to the microcontroller 40 through an external data bus 130 where it is used to control the pulse width modulator 84 to provide an output on lines 86 and 88. That output is low pass filtered and is fed through the amplifier 104 to the summing amplifier 64 where the analog corrected signal received from a low pass filter 94 is added to the linear signal from the line 58 to provide a corrected analog mass flow controller signal on line 50 fed to the mass flow controller. Likewise, the sensed signal being fed back through the line 52 is delivered to a line 140 coupled to an ADC4 pin 142 and fed to the analog-to-digital converter 76 where it is digitized, fed out over the system bus 82 to the CPU 78, and used by the CPU 78 to access the EPROM 44 through the bus 130. The offset from the calibration table stored in EPROM 44 is fed via the bus 82 to the pulse width modulator 84 where a pulse width modulated signal is produced at the pin 86 and supplied to the low pass filter 94. The filter 94 produces an analog sensed mass flow correction signal supplied through the amplifier 104 to the amplifier 120 at its inverting input. The non- inverting input receives the uncorrected sensed mass flow signal, from which the correction signal is subtracted to yield a corrected sensed mass flow analog signal which is output through line 54 to the host process controller 56. In certain situations, it may be desirable also to provide an offset for the ambient temperature. A temperature sensitive current source 150 is coupled to the microcontroller 40 to provide an analog input representative of the temperature, although not presently used in the present invention. More specifically, the microcontroller 40 operates by execution of software contained in the appendix contained at pages Al-6 and A88-112 and which is shown in FIGS. 7-22 comprising software flow charts. Referring now to FIG. 7, the cable or signal translator start routine is shown therein which is entered at a step 200 following which zero is stored in the pulse width modulator pre-scaling register in a step 202. The pulse width modulator is set at mid-range in a step 204. In step 206 register bank zero is selected. The random access memory is initialized in step 208. Interrupts are disabled in step 210. It may be appreciated that when the signal translator 10 is started on a warm start basis from a step 212, the interrupts are disabled first without any of the previous steps 200 through 208 being performed. Following step 210, the stack pointer is initialized in step 214. The check system memory routine may be called in a step 216. The initial init all routine is called in step 218 and the check switch routine is called in step 220. In step 222 the flow test routine is tested for previous execution. Control is transferred to the flow test routine in step 224. If the calibration test flag is off the calibration valid bit is tested in step 226. If calibration page is valid, the flow mode bit is set to one in a step 240. If it is not, Rl and the data pointer are both loaded with zeroes. The return value is returned by the main function in step 230. If the return value is zero in step 232, step 234, the C exit routine, is entered. If the return value is one, the check command routine is entered in a step 236. Referring now to FIG. 8, the check sys mem routine from step 216 is entered. A system memory valid flag is set to zero in a step 250. The countdown counter R2R3 is set to the length of the system memory, in order to indicate to the microcontroller 40 the size of the non-volatile memory 44. In step 254, DPTR is set to equal the base address for the non-volatile memory 44. R4 is set equal to zero in the step 256. In steps 258, 260 and 262, contents of the EPROM are summed in order to perform a check sum function. In step 264, the end location of the EPROM, which contains a check sum byte, is loaded into R5. The check sum R4 is compared to the storage check sum value. If they are equal, sys mem valid is set equal to one in step 268. Sample count is set equal to four in step 270, which is the number of times a particular flow data point must be sampled. The data pointer contents are loaded into the accumulator in step 272 and the signal translator or cable start routine is returned to in step 274. If the check sum equals the stored check sum, the general purpose flags are cleared in step 278. The accumulator is cleared in step 276 and the initialize all routine is then entered from the cable start routine at step 218. Referring now to FIG. 9 where the init all routine is set forth, in step 280 the system status low order bits are set to zeroes, the process status flag is cleared in step 282 and the timer status flag is cleared in step 284. GP flags are set to zero, with the exception of the lowest order bit, in step 286. The first time through variable is set equal to true in step 288. In step 290, the ports P0, PI, P2, P3 and P4 are set high with all their bits set equal to one. In step 292 register bank three is selected. In steps 294 and 296 register zero is loaded with the set point buffer address and register one is loaded with the miscellaneous analog address. R2 is loaded with four in step 298 and R4 is loaded with eight in step 300 so that there is a start bit loaded for the A to D conversion and the multiplexer channel is set to zero. In step 302, R5 is loaded with the bit pattern for the start bit for the analog-to-digital converter multiplexer channel one. In step 304, the control register for the A to D conversion is loaded with zero. In step 306, the previous register bank is selected. The sys mem valid bit is tested in step 308. If it is equal to one control transfers to step 310, where the data pointer is loaded with DACHINULL. If sys mem valid is equal to zero, the pulse width high and low bytes are set to the median range in step 312. In step 320, they are echoed to the storage bytes NUL_HI and NUL LO. In step 328 they are transferred to the pulse width modulator lines zero and one to generate the unfiltered signals supplied to lines 86 and 88. If the data pointer bits are all high, the sys mem valid flag is cleared and then tested again in step 308. If the data pointer bits are not all high, step 322 is executed where the NUL_HI and PWHI values are loaded with the value pointed to by the data pointer. The data pointer is incremented in step 324. In step 326, the NUL_LO and PWLO values are then loaded so that the NUL_HI, NUL_LO and PWHI and PWLO values are stored in consecutive locations in memory. After step 328 has been performed, the serial interface one is initialized in step 330. Register bank 2 is selected in step 332. The time counters are initialized in step 334. Subsequent to that, in step 336, the previous register bank is selected and the timers are initialized and started in steps 338 through 344. The routine then exits in step 346, following which the check switch routine set forth in FIG. 10 is usually entered. Referring now to FIG. 10, the routine for checking a DIP switch 143, connected to port 4 of the microcontroller 40 is shown therein. The electrical signals from port 4 are loaded into variable A in a step 350. The upper four bits or upper nibble of the byte are masked off in a step 352. A test is performed in a step 354 to determine whether the lower nibble is all ones. If it is not, the variable A is complemented in a step 356. In a step 358, the value of the variable A is tested to determine if it is less than eleven, indicting a valid calibration page. If it is, the variable A is loaded, in a step 360, into the calibrate table base variable which is the variable used to indicate the specific gas table selection. In a step 361, the calibrate page valid flag is loaded and in a step 362, the routine is returned from. In the event that the lower nibble of the A variable is all ones, a step 366 is entered. The calibrate test flag is tested. If it is zero, the routine returns. If it is one, the calibrate page valid flag is cleared or set to zero indicating that a valid calibration page was not sensed from the switches. Referring now to FIG. 11, the flow mode routine is entered in step 242. In step 368, the serial I/O is initialized and in step 370 the receive flag is set equal to one. In step 372 all interrupts are enabled and in step 374 the calibration flag is tested. If the calibration flag is equal to zero, a test is performed in step 376 to determine if the state of the DIP switch 143 has changed. If it is unchanged, the step 380 is entered. If it has changed, control is transferred in step 378 to the switch reset routine. In step 380, the data pointer is loaded. The low portion of the data pointer is set equal to zero in step 382 and the data pointer is loaded on the stack in step 384. In step 386, the NUL_HI is loaded from the DPTR. In steps 388 and 390, the high and low bytes are loaded into NUL_HI and NUL__LO, at which point the stack is exited in step 392. In step 394, the receive flag is tested. If it is equal to zero, the A to D interrupt is disabled in step 396. If it is equal to one, a test is entered in the step 400 to determine whether all the transmit and receive flags have been set to zero. If they have not, step 396 is entered, the interrupt is disabled, and the wait for command portion of the check command routine is entered in the step 398. If any of the transmit or receive flags are set equal to one, the step 402 is entered if MS001 is equal to zero. Testing is done in step 402 to ensure that four samples are being taken at two hundred fifty microsecond intervals. In step 404, pin J of the mass flow controller 12 is tested to determine if it has been set equal to zero indicating that the valve of the mass flow controller 12 is set off. If it has been set equal to zero, control is transferred to step 416 where the pulse width modulator 84 is loaded with the contents of the address to which the data pointer is pointing. The data pointer is incremented in step 418 and pulse width modulator one is loaded with the contents of the next address in step 420. The receive flag is tested in step 422. If it is equal to zero, the A to D interrupt is disabled. If it is not equal to zero, a test is made in step 426 to determine whether the flow mode.flag is equal to zero. If it is equal to zero, the calibration test flag is tested in step 428 and if the calibration test is set equal to zero, the switch reset is entered in step 378. In the event that pin J of the mass flow controller 12 is not equal to zero, the step 406 is entered and the sample storage is cleared. Four samples are fetched from the set point buffer. In step 408, they are averaged and in step 410 the average is stored in step 412. The data pointer is loaded with the previous contents of the data pointer plus two times the sample average in step 414. Referring now to FIG. 12, a timer routine 430 is set forth therein. The timer routine 430 functions by providing a signal to the microcontroller 40 indicating that 250 microseconds has passed. In order to do this, in step 432, the program status word, is pushed onto the stack. In step 434, register bank number three is selected. In step 436, the set point A to D conversion is initialized. In other words, the linear command signal received from the host-process controller 56 is converted to a digital value. In step 438, register bank two is selected and in step 440, the 250 microsecond flag is set equal to one. The R0 value, which was originally four, is decremented one. In step 442 and in step 444, R0 is tested to determine whether it is equal to zero. If R0 is greater than zero, the original register bank is selected in step 446, and return is exited in step 448. If R0 is equal to zero, it is set to four in step 450. The millisecond 001 variable is set to one in step 452. Rl is decremented in step 454 and is tested in step 456. If Rl is equal to zero, step 458 is entered where it is set equal to ten. The ten millisecond flag is set in step 460 and R2 is decremented in step 462. In the event that R2 is not equal to zero, step 446 is entered. If it is not equal to zero, the original register bank is selected in step 446. If it is equal to zero, R2 is set equal to 100 in step 466. SEC01 is set equal to one in step 468. In FIG. 13, the analog-to-digital converter routine 470 is entered. The program status word and the accumulator contents are pushed onto the stack in step 472. Register bank three is selected in step 474. A test is made in step 476 to determine whether the signal translator is in flow mode or not. If it is in flow mode, control branches to step 482. If it is not in flow mode, it should probably be in calibrate mode. Control is transferred to the calibrate test step 478. If calibrate test is set equal to zero, control is transferred to the calibrate A to D mode in step 480. If the calibrate test is not equal to zero, control is transferred to the fetch ADCON in step 482. ADCON is the control variable for the analog-to-digital converter. In step 484, if the AADRN bit is set equal to zero. Control is transferred to step 486 where the digital contents of the analog-to-digital converter are fetched. They are loaded into the location where R0 is pointing. R0 is incremented by one in step 488. R2 is decremented by one in step 490. R2 is tested in step 492. If it is not equal to zero, the analog-to-digital converter register is set equal to zero and the program status word and accumulator are restored from the stack in step 496. The routine returns in step 498. In the event miscellaneous analog-to-digital conversion is to be performed, the routine at step 500 is entered where the digital signal corresponding to the analog signal is loaded into the address pointed to by Rl. Rl is incremented in step 502 and two bits of the analog-to-digital control are also added at location Rl. Rl is incremented in step 506. A test is performed in order to determine whether the steps have been performed seven times. If they have, pointer register Rl is reset to point to the base of the miscellaneous analog buffer in step 512. A start bit is loaded in R5 in step 514. R5 is incremented in step 516. If the test of step 508 is negative, the next immediate multiplexer channel is selected by step 510 and control is transferred to step 494. In the event that the contents of R2 are equal to zero, the pointer RO is reset by loading it with the address of the set point buffer. The counter R2 is reset by loading it with four. In step 520, ADCON is set equal to zero in step 522 and then it is loaded with the contents of R5 in step 524 after which control is transferred to step 496. In FIG. 14, the check command routine, a command interrupt has occurred. The process mode is set equal to zero, in step 528. In step 530, preparation is made to receive the command packet. In step 532, the receive flag is set equal to one. In step 534, all interrupts may be enabled, depending upon input signals. In step 536, check_switch is called. After check_switch completes the cal__page_valid flag is tested in a step 538. If cal page_ alid is equal to one, control is transferred to warm start step 212. If cal_page_valid is equal to zero, the receive flag is tested in step 540. If the receive flag is one, wait for command is entered in step 398. If the receive flag is zero, a test is performed in step 542 to determine whether the serial I/O port one is idle. If the serial I/O port is active, control transfers to wait for command step 398. If it is idle, the receive_check_sum_error flag is set equal to zero and a test is made for receive verified. If the receive has not been verified to receive check sum error, is set equal to one in step 548. Control is transferred to step 550. If the receive is verified, control is transferred directly to step 550 where material received, stored in the receive buffer is copied into the transmit buffer for echoing. A test byte is fetched from the receive buffer. In step 552, it is tested to determine whether it is equal to two. In step 554, if it is not, the software reset is entered in step 378. If it is valid, the stx-flag is set equal to one, in step 556, and the command byte from the receive buffer is transferred into the accumulator. In step 558, the contents of the accumulator are multiplied by four. In step 560 and in step 562, the base address of the command table is added to the accumulator contents to enable control to be transferred to a command in the command table. In FIG. 15, the calibrate routine operating the signal translator 10 is shown and is entered in a step 564 the command flag is set equal to one in step 566. The calibrate mode flag is set equal to one in step 568. The command from the command buffer is stored in step 570. A test is made to determine whether this is the first time through the calibrate routine in step 572. If it is the first time through flag is set equal to false in step 574. In step 576,- a test is made to determine whether the pulse width modulator settings PWHI and PWLO are to be changed. If not, control is transferred to step 582 where the settings are loaded into the communication buffer. If the settings are to be changed, the second and third contents of the communication buffer are loaded in PWHI and PWLO in steps 578 and 580. Those values are then transferred to the pulse width modulator lines in step 584 and MS001 is cleared in step 586. A test is made in step 588 to determine whether a one millisecond boundary has been crossed. If it has, MS001 is cleared in step 590 and the A to D interrupt is enabled in step 592. The one millisecond test is again performed in step 594. In step 596, the A to D interrupt is disabled and the one millisecond flag is cleared in step 598. In steps 600 through 608, the transmit buffer contents are transmitted back to the calibrator 20. In steps 610, the calibrate mode flag is set equal to zero and control is transferred to check command in step 236. In FIG. 16, the calibrate mode for A to D, step 480 is shown therein. When that routine is entered, the ADCON and ADCH values are fetched from memory in step 612. The bits are rearranged in step 614 and stored in step 616. RO is incremented in step 618 and the multiplexer address R4 is fetched in step 620. The multiplexer address channel is tested to determine whether it is equal to four in step 622. If it is, R4 is set equal to zero in step 624. RO is located with the c_analog_buffer contents in step 626. In step 628, the start bit is added to R4. If the multiplexer channel is not set to R4, control is transferred immediately to step 628. In step 630, ADCON is set equal to zero and in step 632, the contents of the accumulator and program status word are restored from the stack, after which in step 634, the routine is exited. Referring now to FIG. 17, the flow test routine 224 is shown therein. In step 636, the command flag is set equal to one. In step 638, the calibrate test flag is set equal to one. In step 640 the contents of the command are saved in the command buffer. The calibrate page is fetched from the command buffer in step 642 and is tested in step 644 to determine whether it is less than eleven. If it is not less than eleven, a page boundary error flag is set in step 646 and the error code and system status byte are loaded back into the communication buffer in step 648 and the verified byte is loaded into the transmit buffer in step 650. If the calibrate page value is less than eleven, the calibrate table base is loaded from the communication buffer in step 654, and then transferred into the communication buffer for echoing in step 654, after which step 650 is performed. Following step 650, the transmit flag is set equal to one. If a background transmit function has completed, the transmit flag is set equal to zero. It is tested for in step 658, once it is equal to zero, the transmission is checked in step 660 to determine whether it is okay. If it is not, software reset is entered in step 378. If it is, flow mode is entered in step 242. Referring now to FIGS. 18A and 18B, the downloading routine 662 is set forth therein. In the download routine, step 664, the A to D interrupt is disabled. In step 666, command flag is set equal to one. The download flag is set to one in step 668 and the contents of the command buffer are loaded with the command in step 670. The command buffer download parameters are stored in step 672 and check address is called in step 674. A page boundary error is tested for in step 676. If the flag is set equal to zero, the packet count flag is tested for in step 678. If the packet count flag is one, the contents of the command buffer are loaded into the transmit buffer for echoing in step 680. The communications buffer is then equated to the receive buffer in order to receive data in step 682 and the verify byte is loaded in the transmit buffer in step 684. The transmit flag is set equal to one in order to be ready to echo in step 686. The transmit flag is tested for background transmissions in step 688, until the transmit flag is zero. Step 690 tests for transmission okay. If it is not, software reset is entered in step 378. If it is, the receive flag is set equal to one in step 692. The receive flag is tested in step 694. If it is zero, the STX byte is fetched from the communications buffer in step 696 and the STX byte is tested in step 698. If it is equal to two, the command byte is fetched from the communications buffer in step 700. If it is not equal to two, control is transferred to switch reset step 378. Once the command byte has been fetched from the communications buffer, step 702 tests for whether the command is for a download. If it is not, switch reset is entered. If it is, a test is made for whether the receive has been verified in step 704. In step 706, the error code and system status are loaded into the command buffer. In step 708, the command buffer is set to the transmit buffer. In step 710, the communications buffer is set to the receive buffer. In step 712, the verify byte is loaded in the transmit buffer. In step 714, the transmit flag is set equal to one, indicating ready to send. In step 716, the transmit flag is tested for until it is equal to zero, at which point control is transferred to software reset 378. In the event that the receive verified step 704 is true or yields a yes, control is transferred to step 718 in FIG. 18B where mem__fill is called. After mem_fill completes the cal_data_error flag is tested. If the flag is equal to one, control is transferred back to step 706. If it is equal to zero, the download parameters are retrieved from store and loaded into the command buffer in step 722. The verify byte is loaded into the transmit buffer in step 724. The ready to send transmit flag is set equal to one in step 726 and the transmit flag is tested for until it is equal to zero in step 728, indicating that background transmit tasks have been completed. The transmission is tested for in step 730 to determine if it is okay. If the transmit is not, the software reset routine is entered in step 378. If the transmission is okay, the packet count is tested for in step 732. If it is now equal to zero, control is transferred back to step 692. If it is equal to zero, control is transferred to warm start, step 212. Referring now to FIG. 19, the data from the communications buffer is transferred into the non¬ volatile memory 44 by the mem_fill routine 718. In the step 734, the non-volatile memory is tested to see if it is busy and continues to be tested until it is unoccupied after which control transfers to step 736 where the R0 is pointed to the calibrate data in the receive buffer. In step 738, R0 is placed on the stack. In step 740 the download destination address is loaded in the data pointer. In step 742, the download byte count minus 3 is loaded into R2. In step 744, the byte is loaded into EPROM. In step 746, the data pointer is incremented, RO is incremented and R2 is decremented. In step 748, R2 is tested to determine whether it is zero. If it is not, control is transferred back to step 744 for further data transfer into the EPROM. If it is equal to zero, the data packet has been transferred completely to the EPROM. In step 750 another test is made to determine if memory is busy. When memory is not busy, the RO value is recovered from the stack in step 752 and the download destination address is loaded in the data pointer, in step 754. R2 is loaded with the download byte count minus 3 in step 756. A test is made in step 758 to determine whether the contents of the EPROM are the same as the contents of the command buffer. In step 764, the pointers are incremented and decremented in order to step through the command buffer. A test is made in step 766 to determine whether the entire packet has been tested. In step 768, the data pointer is loaded into the download destination address, indicating the next free location in the non-volatile memory 44. The packet count is set equal to packet count minus 1 and the routine is exited in step 762. In the event that there is an error in loading in the EPROM, control is transferred to step 760, the calibrate data error flag is set and return is made in step 762 from the routine. In FIGS. 20A and 20B, the send data routine 772 is set forth. The analog-to-digital converter interrupts are disabled in step 774 and the command flag and upload flags are set equal to one in steps 776 and 778. The command buffer upload parameters are stored in step 780 and the check_address routine is called in step 782. Page boundary error is tested for in step 784 if the flag is set, control transfers to step 826. If the flag is not set, the packet count is tested to determine whether it is zero in step 786. If the packet count is equal to zero, control transfers to step 826. If the packet count is not equal to zero, the address acknowledge is disabled in step 788. The upload_from address is loaded in DPTR in step 790. Rl is loaded with the communications buffer +2 address in step 792. R3 is loaded with the upload byte count minus 3 address in step 794. The contents of the EPROM are then transferred to the communications buffer in steps 796-799. The verify byte is placed in the communications buffer in step 800. Enable address acknowledge occurs in step 802. The ready to send transmit flag is set equal to one in step 804. The transmit flag is tested for until it is equal to zero in step 806. Transmission okay is tested for in step 808. If the transmission has failed, step 826 is entered. If the transmission has succeeded, the packet count is decremented in step 810. It is tested to determine whether it is equal to zero in step 812. If it is equal to zero, the warm start routine 212 is entered. If it is not equal to zero, the data pointer is loaded with the upload from address in step 814. In step 816, R0 is loaded with the receive buffer address. In step 818, Rl is located with a transmit buffer address. In step 820, the STX and command flags are cleared. In step 822, the receive flag is set equal to one. In step 824, the receive flag is tested until it equals zero. When the receive flag equals zero, control is transferred to step 834 as shown in FIG. 20B. If the receive is verified, control transfers to step 836, the STX byte is fetched from the command buffer. STX is tested in step 838 to determine whether it equals two. If it does, the STX flag is set equal to one. In step 840 and in step 842, the command byte is fetched from the command buffer. In step 844, the command is tested to determine whether it is an upload command. If it is not, control transfers to step 852. It if is, control transfers to step 846 where the command flag is set equal to one. The old upload parameters are accessed from storage in step 848 and the new upload parameters are accessed from the command buffer in step 850. In step 852, a test is made to determine whether the old equals the new. If they do not, control is transferred to step 826. If the old does equal the new, the data is sent in step 772. In FIG. 21, the command reset routine 854 is shown. In step 856, the command flag is set equal to one, as is the command reset flag in step 858. The contents of the command buffer are loaded in COMMAND. The verify byte, in step 862, is loaded in the transmit buffer. The transmit flag is set equal to one in step 864. The transmit flag is tested in step 866. When it reaches zero, after background transmission has occurred, software reset occurs in step 378. In FIG. 22, the software reset routine 378 is set forth. In step 868, all interrupts are disabled. In step 870 the register bank zero is selected. In step 872, the accumulator is cleared. In step 874, the accumulator is transferred to the stack. The accumulator is also loaded onto the stack a second time in step 876 and the routine is returned from in step 878. The main calibration routine, which executes on the computer 22 to operate the calibrator 20 in order to calibrate the mass flow controller 12, is set forth in FIG. 23. In the main routine, step 881 initializes the parameters. In step 882, the display is initially for the personal computer 22. In step 884 it initializes the hardware mode to zero in step 886. Initializing hardware error is tested for in step 888. If there is not, a test is made in step 890 to determine whether the keyboard has been activated. If so, the keyboard input is retrieved in step 892. If not, a menu command routine is entered in step 894 and function keys F2 through F10 are tested for actuation in steps 896, 900, 904, 910, 912, 918, 922 and 926. If key F2 has been hit to change calibrate page, it is changed in step 898. If key F3 has been hit, the test cycle time is incremented in step 902. If key F4 has been hit, the calibrate routine is entered in step 906 and the display is reset in step 908. If F5, the maximum set point is changed in step 914. If F6 has been hit, the minimum set point is changed in step 916. If F7 has been hit, the toggle calibration/test mode is entered in step 920. If F10 is entered, the calibration process is aborted. If shift-F3 is hit in step 926, the test cycle time is decremented. After the key testing has been done, status is tested in step 930 to determine whether it is equal to zero. If not, the abort flag is tested for equal to true in step 932 and exit is verified in step 934. After which the status is again tested in step 936. If it is equal to zero, the abort flag is set to false. In step 940, the initialization flag is incremented in step 942. After which the abort flag is tested again in step 944. If it is false, step 890 is entered. If it is true, hardware mode one is initialized and step 946 and the display is reset in step 948. In FIG. 24, the calibrate routine is set forth. In step 954, the cable data or signal translator data command page is selected. In step 956, the parallel serial interface and the signal translator or cable are initialized. In step 958, sinusoidal data are generated. In step 960, the sinusoidal data is transferred to a digital-to-analog converter buffer. In step 962, the set points or data points which are being selected as the sinusoidal data in step 960 are transferred to the calibrator 20 causing the calibrator 20 to flow the selected amount of gas through line 32. The set point is tested to determine whether it is equal to zero. If it is not, the output of the standard mass flow meter 18 is read in step 968. In st^p 970, if any entries are already stored in the lookup table in the computer 22, an attempt is made to linearize or correct the flow at that point. After that step occurs, the flow output is displayed. In step 974 the ideal flow is calculated and any deviation as a result of the previous linearization in step 970 is also calculated. The calculated ideal and the deviation values are displayed in step 976. In step 978, those values are added to a history table. In step 980, the history table is evaluated. In step 982, a test is determined as to whether the values are changing or stable with time. In step 984, a test is made to determine whether the correction is within error margins. If it is, the more test points test step 988 is entered. If more test points are desired, control is transferred back to step 962. If not, the 256 correction points are calculated from the tested set points after which the 256 points are downloaded in step 992 to the signal translator 10 and the routine ends. In the event that the set point is set equal to zero in step 969, step 966 read, the mass flow controller output and transfers control to step 972. Thus, the routine in FIG. 24 generates multiple point sinusoidal data of up to 256 points wherein the period of the sinusoid is ten times the time constant of the mass flow controller. It may be appreciated that the sinusoid is stepped in small digital increments which do not substantially effect the settling time of the mass flow controller 12. In view of the above, it will be seen that the several objects of the invention are achieved and other advantageous results attained. As various changes could be made without departing from the scope of the invention, it is intended that all matter contained in the above description shall be interpreted as illustrative and not in a limiting sense. eeprom.inc Define the external memory map RSEG NO_INIT eeprom data segment, starts at 0x2000 $ base of system memory area 0020h 16? bytes reserved for s/n 0020h associated MFC OOOlh software programmable address OOOlh dac null hi byte OOOlh dac null lo byte OOOlh 1 = setpoint averaging enabled OOOlh number of setpoint samples to average OOOlh 1 = autozero enabled 0002h time in seconds, jpin lo to autozero 0002h hi lo val from last calibration 0002h correction factor for cable offset OOOah date last calibrated OOOah date calibration due alidate if bit=0, cal page init'd cal page 0 (2a00h - 2bffh) cal page (2c00h - 2dffh) cal page (2e00h - 2fffh) cal page (3000h - 31ffh) cal page (3200h - 33ffh) cal page (3400h - 35ffh) cal page (3600h - 37ffh) cal page (3800h - 39ffh) cal page (3a00h - 3bffh) cal page (3c00h - 3dffh) cal page (3e00h - 3fffh) / Gas formula table, stored as asciz string 'gnum, space, formula, 0' gas_table ds 0200h / gas formula table sys_mem_length ecju $ - sys_mem_base / this is the length sys_mem_cksum ds OOOlh / checksum byte for system area sl_drv-s03 inter-integrated circuit serial I/O interrupt handler / device driver On receipt of an siol interrupt, the processor vectors through location 2Bh to i2csvc. Here, the status register 'slsta' and the dptr register are used to compute the address of the routine that will service the interrupt. i2csvct / Target for the interrupt vector jump push psw / Save the registers used push ace / this is a little slower, but all push dph / possible states are handled push dpi / (even the interrupt on status f8) mov psw,#RblSel / Select RBI, switch context mov a,sista /get the status rr a / divide it by 2, multiples of 4 mov dptr,#Sl_Vctr_Tbl / point at the vector table jmp @a+dptr /Jump to the handler Bus_Error / State 0, Bus Error detected, Stop mov sicon,#0d4h /set 'stop', Siol & AdrAck enabled clr tx_flag / clear tx busy flag clr rx_flag / clear rx busy flag clr mrxs / clear the sio active flags clr mtxs clr srxs clr stxs orl system_status,#03Oh / bus error flag, tx+rx cksum errors jmp SI Exit Strt_Mstr_ Xmit / State 8, Start Master Transmit setb " mtxs /flag master tx active Rptd_Mstr_ Strt: / State 10, Repeated Master Start mov slda ,S1vAdr_d / Set up SlvAddr + direction mov slcon,#0c4h ;clr SI reg_init: mov rO,rxjbuffer+2 /pointer to rx buffer *3 byte ptrs mov r2,rx_count /rx byte count mov r1,tx_buffer+2 /pointer to tx buffer *c-51 v4.02a mov r3,tx__count /tx byte count jmp SI Exit /common exit point Cont_Mstr_Xmit: /State 28, Continue Master Xmit djnz r3,Xmit_Byte /Send another, if all sent... Xmit_Mstr_Sto : /State 20, Rcvd Nack on SlaW, Stop Xmit_Data_Stop: / State 30, Rcvd Nack on data, Stop clr tx_flag / if all sent, clear tx enable flag clr mtxs / and master tx start flag mov slcon,#0d4h / else stop jmp SI Exit SUBSTITUTE SHEET / state 18 Got ACK, send data / send it / bump it / boogie on / State 38, Lost as bus master ***Err? / Send stop / re-init the registers Strt_Mster_Rec : / State 40, Start Master Receive setb mrxs / flag master rx active smr_10: mov slcon,#0c4h jmp Sl_Exit Cont_Mstr_Recv: / State 50, Continue Master Receive mov @r0,sldat / fetch data inc rO / bump pointer djnz r2,smr 10 / check for last mov slcon, θcOh / was last, send nack jmp SI Exit Recv Data Sto : / State 58, Data Rcvd with Nack / State 48, Nack Rcvd on SIAR, Stop / become potential slave again / clear the ready to receive flag / and the receiver start flag Rcvd_Gnrl_Call: / State 70, Send Ack, Rev Data Strt_Slav_Recv: / State 60, Rcvd SLA+W, Ack'd (RcvDat) mov εlcon,#0c4h sjmp Init Slv Rx SlvRtilBusFree: / State 68, Lost Master Status, after Slv_ToGnrlCall: /State 78, Gen. Call, retry as Master mov slcon,#0e4h / Rcvd Slave Address, Init Slv Rx: / set start slave receive bit / point at the receive buffer / init the byte counter / State 80, if this is the last byte, / nack is sent. Store the rcvd byte / if not the last, bump pointer / else, AddrAck off. send nack SUBSTITUTE SHEET inc rO / bump the receiver pointer mov slcon,#0c4h /send ack jmp sl_Exit Rcvd_Slav_Las : / State 88, Last Data Rcvd RcvBogusGCData: / State 98, Don't save,=> NonAdrSlv Lost_Slav_Stat: / State A0, Stop or RptStrt Rcvd mov slcon,#0c4h / Nack Sent, *• *■> NonAdrSlv clr rx_flag / clear receiver enable clr srxs / and slave receiver active jmp SI Exit Rcvd_Gnrl_Data: / State 90, mov @ 0,sldat / Nack jmp NoMasAgui Strt Slav Xmit: / State A8, Begin Slave Data Xmit setb stxs / set start slave transmit flag mov rl,tx_buffer+2 / point at the tx buffer mov sldat,@rl / send the byte inc rl / bump the pointer mov slcon,#0c4h / ack address jmp SI Exit Slav_Xmit_Ends: / State CO, Last Data Xmtd Nack Rcvd, SlvXmitEndsTWo: / State C8, Last DAta Xmtd Ack Rcvd, mov slcon,#0c4h / Nack Sent, => NonAdrSlv clr tx_flag / clear the tx enable flag clr stxs /and the slave tx start flag jmp SI Exit SlvXtilBusFree: / State BO, Lost Master Status, mov rl,tx_buffer+2 / point to the tx buffer, slv tx now mov sldat,@rl / tx the data inc rl / bump the pointer mov slcon,#0e4h / wait for bus free, we wanna be mstr jmp SI Exit / run away Cont_Slav_Xmit: / State B8, Continue as Slv Xmitter mov sldat,@rl inc rl mov slcon,#0c4h /Common exit point for SIO routines / Restore the regs used /Back to Loafing Siol Vector Table Interrupt Handler vectors thru here RSEG i2c_vectors(4) Sl_Vctr_Tbl ljmp Bus_Error State 0, A Bus Error was detected nop State/2 = multiples of 4 ljmp Strt_Mstr_Xmit State 8, Start Master Transmit nop ljmp Rptd_Mstr_Strt State 10, Repeated Master Start nop ljmp Xmit_Byte State 18, Got ACK, send data nop ljmp Xmit_Mstr_Stop State 20, Rcvd Nack, send stop nop ljmp Cont_Mstr_Xmit State 28, Continue Master Xmit nop ljmp Xmit_Data_Stop State 30, Rcvd Nack on data, Stop nop ljmp Lost_Mstr_Stat State 38, Lost as bus master nop ljmp Strt_Mstr_Recv State 40, Start Master Receive nop ljmp Recv_Mstr_Stop State 48, Nack Rcvd on SLA+R nop ljmp Cont_Mstr_Recv State 50, Continue Master Receive nop ljmp Recv_Data_Stop State 58, Data Rcvd with Nack nop ljmp Strt_S1av_Recv State 60, Rcvd SLA+W, Ack'd (RcvDat) nop ljmp SlvRtilBusFree State 68,Lost Mstr, Rcvd SLA,Wt4BsFre nop ljmp Rcvd_Gnrl_Cal1 State 70, send Ack, Rev Data nop ljmp Slv_ToGnrlCall State 78,After Gen Call, etry as Mstr nop ljmp Cont_Slav_Recv State 80, If Last, will Nack nop ljmp Revd_S1av_Last State 88,D Rcvd, Nak Sent,=> NonAdrSlv nop ljmp Rcvd_Gnrl_Data State 90, Get 2nd GenCall Bytre, Nack nop ljmp RcvBogusGCData State 98, Don't save,=> NonAdrSlv nop ljmp Lost_Slav_Stat State A0, Red Stop/RptStrt=>NonAdrSlv nop ljmp Strt_S1av_Xmit State A8, Begin Slave Data Xmit nop ljmp SlvXtilBusFree State B0, Lost Mstr,Xmit SLA,Wt4BsFre nop ljmp Cont_slav_Xmit State B8, Continue as Slv Xmitter nop ljmp Slav Xmit Ends State CO, LstDXmtdNakRcd,=>NonAdrSlv SUBSTITUTE SHEET State C8, LstDXmtdAckRcd,=>NonAdrSlv State DO, Bogus state, jump to exit State D8, Bogus state, jump to exit State E0, Bogus state, jump to exit State E8, Bogus state, jump to exit State FO, Bogus state, jump to exit State F8, Bogus state, jump to exit SUBSTITUTE SHEET upsi.s03 Unit Parallel to Serial Interface 'C Entry module and assembly language functions Startup code and assembly language functions for the UII Serial Interface. Author Michael Upchurch Version 1.00 beta 9103.27 Revision History Date Remarks 9103.27 Release version 1.00 beta NAME usio_init /Always load this part $d:\zzz incs\c51 v04\defms.inc /Small memory model global entry points PUBLIC βw reset /software reset / status byte / communications status /timer status, tick flags SUBSTITUTE SHEET PUBLIC ts_sb7 PUBLIC ms_sbO PUBLIC ms_sbl PUBLIC ms_sb2 PUBLIC ms_sb3 PUBLIC ms_sb4 PUBLIC ms_sb5 PUBLIC ms_sb6 PUBLIC ms sb7 global byte locations PUBLIC Stx / should be 02h (ascii stx) PUBLIC command / command, what to do PUBLIC tx_buffer / Points to the start of the TX Buffer PUBLIC rx_buffer / Points to the start of the RX Buffer PUBLIC tx_count / Transmit byte count PUBLIC rx_count / Receive byte count PUBLIC packet_size / Tx / Rx data packet size PUBLIC packet_count / Tx / Rx data packet count PUBLIC SlvAdr_d / Slave address and direction PUBLIC cksum_count / Checksum byte count PUBLIC checksum / storage for Checksum PUBLIC cmd_buffer / command buffer (from PC) PUBLIC com buffer / communications buffer Tx & Rx / Where to go when program is done / Reg Bank Select, we let C use RBO / Entry to C routines $dϊ\zzz_incs\c51_v04\io_52.inc / 80C552 definitions Alloate the bit variable storage These constants allow byte mode access to the bit variable areas. If you add more bits beyond the spares, youmay need to modify the constants. / System Status Bits /Communications Status /System Timer Status Bits / spares, just in case / Valid stx rcvd from PC / Valid cmd rcvd from PC /Ready for transmit / Ready for receive / cksum error on tx data from pc / cksum error on rcvd data / page bndry error, for compat w/sc / calibration data error SUBSTITUTE SHEET / Retry Error, tries exhausted - ;I2C bus error / spare /spare / start master receive state passed /start master trasnmit state passed / start slave receive state passed / start slave transmit state passed Allocate the internal RAM /these go in the direct access area /stx should be 02h (ascii stx) / command, what to do /Points to the start of the Tx buffer /Points to the start of the Rx buffer /Tx Byte Count /Rx Byte Count / transmit data packet size / transmit data packet count /storage for slv addr and direction ≠ bytes for checksum routines ;storage for CkSum / these go in the indirect access area /Commands, PC outbound 16 bytes / Communications Sc<->Ub 80 bytes Allocate 64 bytes for the Stack RSEG CSTACK SUBSTITUTE SHEET Stack_Begin: ds 040h Reset Vector Code segments to start here RSEG CSTART / C's startup segment usio_init jmp init C Interrupt vectors for the Signetics 8XC552 external interrupt 0 timer 0 overflow external interrupt 1 timer 1 overflow 5100 (Tx or Rx) 5101 (Tx or Rx) T2 capture T2 capture T2 capture T2 capture ADC end of conversion T2 compare T2 compare T2 compare T2 overflow / Copyright notice and Version number Copyright @ 1990, 1991 Unit Instruments, Inc. All Rights Reserved Version: 1.00 Beta, 9103.27 SUBSTITUTE SHEET PUBLIC Init_Mstr_Rx Init_Mstr_Rx: /Init master receive setb esl / enable the Siol interrupt mov SlvAdr_d,#SmtCbl+R /Slave Address + Read setb Sio Strt /Start Master Receive ret SUBSTITUTE SHEET PUBLIC Init_Mstr_Tx Init_Mstr_T : / Init master transmit setb esl / enable the Siol interrupt mov SlvAdr-d,#SmtCbl+W / Smart Cable Address + Write setb Sio Strt /Start Master Transmit ret PUBLIC RxCkSum / rO & r2 (RBO) destroyed / save some registers /assume it's ok count / do the cksum on what we got /less the checksum / point at the buffer /rx_count-l /set up for checksum /add it (W/O CarryJ) / bump the pointer / put it here for now /don't borrow /compare the checksums /if equal, OK / else flag error /restore the registers PUBLIC TxCkSum TxCkSum: push psw / save some registers push ace clr Tx_CkSum_Error /assume it's ok mov cksum_count,tx_count /copy the tx byte count dec cksum_count /minus 1 mov rl,tx_buffer+2 /point at the buffer mov r3,cksum_count / get-the count clr a /set up for checksum TcslO: add a,@rl / add it (No Carr t) inc rl /bump the pointer djnz r3,TcslO mov checksum,a /save what we calculated clr c /toss the carry subb a,@rl /sub what they sent us jz Tcs20 /if it's zero, they're the same setb Tx_CkSum_Error / else, flag it bad call CommError /call the error handler Tcs20: pop ace / restore the registers SUBSTITUTE SHEET pop psw ret WtlOmS: /Guaranteed >= lOmS delay mov r7,#10 /the count is ten wlO 10: call WtlmS djnz r7,wl0 10 / millisends ret WtlmS: lmS delay +/- 1 or 2 uS mov a,tmod get current mode for timers 0 & 1 anl a,#00fh leave tmrO alone orl a,#010h set tmrl=mode 1 mov tmod,a send it mov thl,#0fch set up the count for mov tll,#02ah 1000uS-(call, setup and return o/h) mov a,tcon get the controls anl a,#033h leave tmrO alone orl a,#040h set the tmrl run bit mov tcon,a start it wl 10: jnb tfl,wl_10 /wait for it to roll over clr trl / stop the timer clr tfl / clear the flag ret / return PUBLIC CommError CommError: / Handle a communications error ret INTERRUPT SERVICE ROUTINES Insert handlers here. T.imer 0 Interrupt Service Routine When enabled, pass through here every 250 uS. Uses reg bank 1 to optimize context switching and access to time counters timer 0: SUBSTITUTE SHEET / get back the context / PSW too / Unused interrupt service routines and software reset (bad intr -> reset) sw_reset: / Software Reset, as near as possible, xiOsvc: / The labels in this block are for xilsvc: / interrupts that are not currently tmlsvc: / in use. These targets are included siosvc: / to give the jmp instructions at the t2cpt: / interrupt vectors a target, in case t2cmp: / any bad interrupts come along due tm2svc: / to the processor getting lost. adc eoc: / We'll handle this potential problem mov acc,#0 / by disabling the interrupts, mov ien0,acc / selecting Register Bank 0 mov ienl,acc / pushing a return to reset on the mov psw,ace / stack, and returning from the push ace / interrupt that brought us here. push ace reti include the IIC driver here Sd:\zzz_incs\c51_ 04\sl_drv.s03 •— — — — — — — — — — — — — — — - ENDMOD usio init MODULE Recv_Buffer RSEG CODE PUBLIC Recv_Buffer $DEFFN Recv_Buffer (0,0,0,0,0,0,0,0) $d:\zzz inels\c51 v04\io 552.inc / 80C552 definitions EXTERN CommError EXTERN Init_Mstr_Rx EXTERN RxCkSum EXTERN rx_count / Receive byte count EXTERN rx_flag EXTERN Retry_Error EXTERN SecOl Recv_Buffer: push psw / save the status / reg bank select mov psw,#RblSel / swap over to our register bank SUBSTITUTE SHEET mov r7,#0 / clear the retry counter rbrtl: / bump the retry counter / we have tries left / flag the type of error / handle it rbOlO: /get the rx byte count / ImS/byte for timeout (800uS actual) / divide byte counte by 10 to get / amount of time allowed for receive / get the remainder /if no remainder, skip next /round up all remainders to next 10ms rb020: /now we have the allowed time / clear the seconds flag / no carry / if more time in lOmS counter than we /need, start now rb030: / target / wait for next second boundary /clear the flag rb040: /set 'Receive in Progress' / make sure interrupts enabled / start reception rb050: /bp target /if 1 Sec flag, time out & retry / if Recv in progress, wait rb060s / get the i2c status / make sure it's done / check what we got / restore the status / return MODULE Xmit_Buffer RSEG CODE PUBLIC Xmit_Buffer $DEFFN Xmit_Buffer(0,0,0,0,0,0,0,0) $d:\zzz_incs\c51_v04\io_552.inc /80C552 definitions EXTERN CommError EXTERN TxCkSum SUBSTITUTE SHEET Function 7 module: exit(init code) /no exit, just start over END SUBSTITUTE SHEET /* */ /* upsi.c : Operating system for the Ucal-X200 serial interface */ /* Author : Mike Upchurch */ /* Version : 1.00 beta */ /* */ #include "d:\zzz_incs\c51_v04\io_552.h"/* header for I/O definitions */ #define byte unsigned char /* define byte /define tru Oxff /define fls 0x00 * */ /* External function prototypes */ * */ extern void exit( int x_code ) / extern void Xmit_Buffer( void )/ extern void Recv_Buffer( void )/ /* */ /* Local function prototypes */ /* */ byte cmd_exec( void )/ byte send_reset( void ) / byte ini_sys_mem( void ) / byte cal_mode( void )/ byte down_load( void ) / byte cal_test( void )/ byte up_load( void ) / byte gen_cmd( void ) / byte clock_test( void ) / byte bidio_tes ( void )/ void get_cmd_packet( void )/ byte tx__rx_cmd( void )/ void hw_init( void ) / byte rd_byte( void ) / void wr_byte( byte wb ) / void tm_dly( byte td ) / /* */ /* Global Variables */ /* */ /* Status Bits */ /•* */ extern byte system_status / /* system status byte */ extern byte comm_status / /* communications status */ extern byte timer_status / /* timer status */ extern byte misc_spr_bits/ /* spare bits */ rcvd valid start transmission */ Valid Cmd 1 */ Transmit in progress */ Receive in progress */ checksum error on tx data from pc*/ checksum error on rcvd data */ page bndry error, for compat w/sc*/ calibration data error */ Retry Error, tries exhausted */ I2C bus error */ 250 uS flag */ 1 mS flag */ 10 S flag */ 1 Sec flag /* UPIA handshaking definitions, Port 1 */ bit prog = 0x91/ /* 0 = Program - Calibrate mode */ bit jpin = 0x90/ /* 1 - Flow * UPIA handshakin definitions, Port 3 */ /* Lo = Go (output to PC) */ /* 1 = Input Buffer Empty */ /* 1 = Output Buffer Full */ /* 0 Strobes Input Latch (8255) */ /* 0 Enables Output Buffer (8255) */ extern byte stx / /* 02h (ASCII stx) */ extern byte command/ /* Command 1 says what to do */ SUBSTITUTE SHEET extern byte *tx_buffer / /* Points to the start of the Tx buffer */ extern byte *rx_buffer / /* Points to the start of the Rx buffer */ extern byte tx_count / /* Tx byte count used in RB1-R3 */ extern byte rx_count / /* Rx byte count used in RB1-R2 */ extern byte packet_size / /* Tx / Rx data packet size */ extern byte packet_count / /* Tx / Rx data packet count */ extern byte SlvAdr_d/ /* Slave address and direction */ extern byte cksum_count/ /* Checksum byte count */ extern byte checksum/ /* Checksum storage */ extern byte cmd_buffer[ 0x10 ]/ /* Command buffer (from PC) */ extern byte com_buffer[ 0x50 ]/ /* Communications buffer Tx & Rx */ /* */ /* Main Function Entry point for the C portion of this program */ void main( void) /* main upsi.c routine ■ C byte exit_flag = 0 / hw_init() / /* Init the ports, pwm etc.. do get_cmd_packe () / /* good cmd sets stx £ command flags */ exit_flag = cmd_exec() / /* Execute the command */ if( exit_flag ) /* exit or loop */ exit( (int)exit_flag ), /* exit if something to exit about, or */ }while( l(exit_flag) ) / /* just keep going around } * */ void get_cmd_packe ( void ) /* get a command (8 bytes) */ { byte *rx_ptr / /* pointer to the rev buffer stx_flag = 0 / /* assume no Stx, timeout later */ rx ptr ■* cmd_buffer/ /* fill the command buffer */ whTle( prog ) / /* wait for prog low */ while( prog) / /* check it twice for debounce */ GoFlag = 0/ /* tell the PC to go */ do /* stay in this loop */ { /* til we gen an stx */ *rx_ρtr = rd_byte() / /* fetch a byte */ if(l(*rx_j>tr == 0x02)) /* waiting for stx (02h) */ wr_byte( 0x15 ) / /* if not stx, echo a not ack */ }while(l(*rx_ptr == 0x02)) / /* loop if not a valid stx */ wr byte( *rx_ptr++ ) / /* otherwise, echo and bump */ stx flag = 1 / /* we have a valid stx */ do /* get the rest of the packet */ { *rx_ptr = rd_byte() / /* read a byte from the PC */ wr_byte( *rx_ptr++ ) / /* echo it & bump the pointer */ SUBSTITUTE SHEET } while( rx_ptr < (cmd_buffer + 8) ) / /* til done */ rx_ptr = cmd_buffer / /* point at the buffer again */ stx •= *rx_ptr/ /* save stx and command rx ptr +=1 / command = *rx_ptr/ } * byte cmd exec( void) byte exit_flag / if( stx_flag ) /* if stx valid, check command { switch( command) { case 0x01: /* send a reset to the cable */ cmd_flag = 1 / /* if we got a valid command */ exit_flag = send reset() / break / case 0x02: /* init the cable's sys memory */ cmd_flag = 1/ /* if we got a valid command */ exit_flag = ini_sys_mem() / break / case 0x08: /* calibration mode */ cmd_flag = 1 / /* if we got a valid command */ exit_flag = cal mode() / break/ case 0x09: /* download cal / system data */ cmd_flag = 1 / /* if we got a vlaid command */ exit_flag = down_load() / break / case 0x0a: /* calibration test */ cmd_ lag = 1 / /* if we got a valid command */ exit_flag = cal_test() / break / — case 0x0b: /* upload cal / system data */ cmd_flag = 1/ /* if we got a valid command */ exit_flag = up_load() / break / case OxOc: /* 'generic' command mode exit_flag = gen_cmd() / break / case 0x80: /* upsi bd clock test */ cmd_flag = 1 / /* if we got a valid command */ exit_flag - clock_test()/ break/ case 0x81: /* upsi bd - upia comm check */ cmd_flag = 1/ /* if we got a valid command */ exit_flag = bidio_test() / break / default: break/ SUBSTITUTE SHEET > return( exit_ lag ) / } /* */ byte send_reset( void ) /* release version, should get a reply! */ { tx_buffer = cmd_buffer/ /* transmit from the cmd buffer */ rx_buffer = com_buffer/ /* rev into the comm buffer */ tx_count = 0x08/ /* should reply if success rx_count — 0x08 / tx_rx_cmd() / /* send reset, get the reply */ return( fIs )/ /* errors handled elsewhere */ } /* */ byte ini_sys_mem( void ) /* init the cable's system memory area */ • C tx_buffer = cmd_buffer / /* transmit from the cmd buffer */ rx_buffer = com_buffer / /* rev into the comm buffer */ tx_count -= 0x08 / /* should reply if success rx_count = 0x08 / tx_rx_cmd() / /* send 'init', get the reply */ retur ( fls )/ /* errors handled elsewhere */ } /* byte cal_mode( void) i tx_buffer = cmd_buffer / /* set up the pointers to */ rx_buffer = com_buffer: /* the tx and rx buffers */ tx_count = 0x08 / /* we'll be sending 8 bytes */ rx_count = 0x10/ /* and receiving 16 */ tx_rx_cmd() / /* send cmd to cbl, echo to pc */ retur ( fls ) / /* don't exit, no problem */ } /* byte down_load( void) { byte lp_ctr, reply/ /* loop counter, reply byte */ byte *dptr/ /* data pointer */ tx_buffer = cmd_buffer/ /* set up pointers to the */ dptr = cmd_buffer / /* transmit and receive */ rx_buffer = com_buffer / /* buffers for the command */ tx_count = 0x08 / /* tx 8 cmd bytes */ rx_count = 0x08 / /* rx 8 cmd echo here */ packet_size = *(dptr +2)/ /* save the packet size */ packet_count = *(dptr +3)/ /* save the packet count */ tx_rx_cmd() / /* send cmd to cbl, echo to pc */ while ( packet_count 1=0) /* while packets left to send, i tx buffer = com buffer/ /* use the larger buffer now SUBSTITUTE SHEET dptr = com_buffer / rx_buffer = cmd_buffer / lp ctr = 0 / while ( lp ctr < packet size ) *dptr = rd_byte()z wr_byte( *dptr ) / dptr++/ lp_ctr++ / } tx count = packet_size / Xmϊt_Buffer()/ if( Retry_Error : : BusError ) return( tru ) / for(dptr = cmd_buffer / dptr < (cmd buffer + 8) / dptr++) *dptr - 0 / tm_dly(12)/ /* wait 12mS for mem write rx_count = 0x08 / /* get back 8 bytes */ Recv_Buffer() / /* otherwise, get the reply */ dptr-= cmd_buffer/ /* point at the reply packet */ packet_count -= 1/ /* count the packet we sent */ if( J (packet_count == *(dptr +3))) /* check packet count agreement */ i Cal_Data_Error = 1/ /* flag the error */ *(dptr + 6) = system status / /* put status in the message */ lp ctr = 0 / /* init a counter */ whTle ( lp_otr < rx_count ) /* send a reply packet to pc */ { wr_byte( *dptr ) / /* send the byte to the pc */ reply = rd_byte( ) / /* get the echo */ dptr++ / /* bump the pointer */ lp_ctr++ / /* and the counter */ } } return( fls ) / /* don't exit, no problem */ } */ byte cal_test( void ) { tx_buffer = cmd_buffer/ /* set up the pointers to */ rx_buffer = com_buffer/ /* the tx and rx buffers */ tx_count = 0x08/ /* we'll be sending 8 bytes rx_count = 0x08 / /* and receiving 8 */ tx_rx_cmd() / /* send cmd to cbl, echo to pc */ return( fls ) / /* don't exit, no problem SUBSTITUTE SHEET } * byte up_load( void ) { byte lp_ctr, reply / /* loop counter, reply byte */ byte *dptr / /* data pointer */ tx_buffer ■= cmd__buffer / /* tx is the small buffer now */ rx_buffer = com_buffer / /* rx is the large */ tx_count = 0x08 / /* we'll be sending 8 cmd bytes */ rx_count = * (tx_buf f er +2 ) / /* and receiving packet_size */ packet_size = *(tx_buffer +2)/ /* save the packet size */ packet_count = *(tx_buffer +3)/ /* save the packet count */ while ( paeket_count 1= 0 ) /* while the packets left to send, */ { tx_rx_cmd() / /* cmd to cbl, bfr back => PC */ if( Retry_Error :: BusError) /* ck 4 errors */ return( tru )/ /* failed somehow */ stx_flag = 0 / /* clear these so we'll know */ cmd_flag = 0 / /* if we got a valid command */ while( iprog ) / /* wait for prog hi */ while( iprog ) / /* wait for prog hi */ GoFlag = 1 / /* make it go to hi */ get_cmd_packe () / /* let PC tell us what to do */ if(command == 0x0b) /* if we're still uploading, */ cmd_flag = 1 / /* set the valid command flag */ else /* otherwise, */ cmd_flag = 0 / /* clear it to indicate exit */ if( lstx_flag S«Sc cmd_flag ) I* need bothl */ retur ( tru ) / I* or return & restart */ dptr = cmd_buffer / I* point at the command packet */ ρacket_count -= 1/ I* count the packet we sent if(I(packet count =-= *(dptr + 3)))/* check packet count agreement */ return( tru ) y /* fall out, someone messed up */ } retur ( fls ) / /* don't exit, no problem */ /* byte gen_cmd( void ) i /* generic command */ } /* not currently implemented */ /* */ byte clock_test( void ) { byte sec_counter = 0 / /* seconds counter */ byte reply = 0 / /* reply from the pc */ ienO = 0x82 / /* enable the interrupts */ do { while( iSecOl )/ /* Wait for a 1 Sec boundary */ SecOl = 0/ /* clear it til the next time */ wr_byte( sec_counter++ )/ /* send the number SUBSTITUTE reply = rd_byte() / /* get the echo */ } while( iprog ) / /* til prog goes hi */ ienoO = 0x02 / /* disable the interrupts */ GoFlag = 0x01/ /* ack and get off the bus */ hw_init()/ return( 0 ) / /* return 'passed' (0 problems) */ } /* */ byte bidio_test( void ) { byte test_byte/ do } test_byte = rd_byte()/ /* Read a byte from the PC */ wr byte( test_byte ) / /* echo it back */ } while( Iprog ) / /* til prog returns hi */ GoFlag = 0 / hw_init()/ return( 0 )/ /* return */ } /* */ byte tx_rx_cmd( void ) { byte reply/ /* from the pc */ byte *dρtr / /* pointer to reply buffer */ byte lp_cnt/ /* counts bytes sent to the pc */ Xmit_Buffer() / /* send the cmd packet */ if( Retry_Error BusΞrror ) /* ck 4 errors */ return( tru ); /* failed somehow */ switch(cmd_buffer[1] ) /* pick a delay time { case 0x01: tm_dly(5)z /* wait for the reset to happen */ break / case 0x08: /* calibrate mode */ tm_dly(5) / /* wait for data acquisition */ break / case 0x09 / /* down load to eeprom */ tm_dly(12) / /* wait 12 mS for mem write */ break / default: /* give it a little time */ tm dly(l) / /* in any case */ Recv_Buffer() / /* otherwise, get the reply */ dptr = com_buffer/ /* point at the data packet */ lp_ctr = 0/ /* init a counter */ do { SUBSTITUTE SHEET wr_byte( *dptr )/ /* send the byte */ reply * = rd_byte()/ /* get the echo */ dptr += 1/ /* bump the pointer */ lp_cntr += 1/ /* and the counter */ }while( lp_cnt < rx_count ); /* til done */ /* void hw init{ void ) /* hardware init for the SIO card */~ -C pwmp = 0x00/ /* init the PWM pre-scaler */ pwmp = Oxff / /* init the PWM registers */ pwml = Oxff / pi = Oxff; /* init the ports */ p3 = Oxffy /* init the ports */ p4 = Oxff/ /* init the ports */ /* */ byte rd_byte( void ) { byte rbl, rb2 / while( prog ) / /* make sure prog is lo */ while( obf ) / /* wait for it */ while( obf ) / /* wait for it */ while( obf )y /* wait for it */ ack = 0/ /* enable the bus do { rbl = p4; rb2 = p4; }while(rb2 != rbl)/ ack = 0x01/ p4 = Oxff / return(rbl)/ /* void wr_byte( byte wb ) { while( prog ) / while( ibe ) / while( ibe )/ while( ibe ) / p4 -*-*• wb / stb - 0 / stb -*** 1 / p4 = Oxff; /" */ void tm_dly( byte td ) /* time delay in milliseconds */ i byte tcnty for( tent -*■*■ tdy tent > Oy tent— ) /* decrement the counter SUBSTITUTE SHEET mSOOl = Oy /* once a millisecond */ while( imSOOl )/ /* til it's empty */ } /* SUBSTITUTE SHEET -56- SUBTTL Sw_Upia, Unit Peripheral Interface Driver COMMENT \ Original Code Copyright 1989,90 Unit Instruments, Inc. Michael Upchurch Developed for use with the 'Ucal-X200 Calibration' system, this driver is designed to handle low level digital I/O for programs that are written in Quick Basic. The functions in this driver provide the ability to set or reset individual output bits, write to or read from byte wide I/O ports, and to handle bi-directional communications with intelligent peripherals. \ .MODEL large,c .CODE .286 .**. ; Comment out the UcalX200 definition for the generic Upia driver. UcalX200 equ -1 .**. COMMENT \ The following Upia address definitions are Ucal-X200 specific, but any hardware setup that puts the Upia card at 0x310 can use this driver. To place the card at another location, you need only change the upia_base statement, to reflect the new base address, then reassemble the module. The other addresses will be adjusted automatically. Upia I/O Variables .**. COMMENT \ The following definitions allow direct access to the UDAT array from the driver, eliminating the need to transfer the array to local storage before performing the I/O. This makes the code smaller and faster than it would be if UDAT were copied locally. We only use the LS byte of the variables, but they are of integer size for ease of use in BASIC. /These array elements hold the data most yrecently read from / written to the port /or the control register. The control reg /is write only, as are ports D and E. The / ports can be written as bytes or as bits ; epending on the function selected. / current command /driver status ;0 = no error... see list below yselect a bit to set or clear ycurrent 8255 mode... see list below ythe following code is X200 specific /most recent data written to the latch yvalve status ;correction factor, hi byte ;correction factor, lo byte Cable DAC null value, hi byte yCable DAC null value, lo byte yCable's conversion of current setpoint SUBSTITUTE SHEET ;if not 0, corfac saved for this setpoint ;cable status, 0 = inactive yugli status, 0 = inactive Zcurrent autocal operating state calibration page ycurrently selected MFC slot ycurrently selected STD slot slot for active ugli βlot for active cable ENDIF yend of X200 specific definitions Control Codes for PiaCtrl the procedure (Universal) OOOOh ylnit the Upia to 'ctrl_mode' OOOlh / Mode 0, Write the Outs, Read the Ins 0002h /Tx a byte mode 2 0003h ;Rx a byte mode 2 0004h ySet a selected bit 0005h yClear a selected bit 0006h ysend a byte thru the x200 latch yX200 specific definitions 0007h set a bit thru the latch 0008h yclear a bit thru the latch 0009h yread a byte, Pd.6=A8, Pc=A7-0, Pa=data OOOah ywrite a byte, M OOObh yread port a implemented in C for now OOOch yread port c implemented in C for now ENDIF y The following are 8255 signals in mode 2 (combinational) stb equ OOlOh strobe input data (falling edge) ibf equ 0020h yhigh = input buffer full ack equ 0040h low = enable output buffer obe equ 0080h yhigh = output buffer empty The following constants are X200 control bit definitions for use by the bit set bit clear functions. ENDIF Misc. equates, constants etc. Upia Driver Error Codes 0 = OK 1 = Initialization error 2 = Command range error SUBSTITUTE SHEET 3 Bad 8255 control byte 4 Current mode inconsistent with command 5 Tx / Rx timeout 6 bit select, bad bit 7 bit select, bad port 8 No board or not Upia Local storage variables num_sex dw OOOOh y imeout seconds elapsed last sex dw OOOOh last seconds read udat cmd table label word dw init_upia ylnit the Upia to 'ctrl_mode" dw port refresh yRefresh ins and outs dw put_Eyte ;Tx a byte mode 2 dw get_byte / Rx a byte mode 2 dw set_bit / Set a selected bit dw clr bit / Clear a selected bit IFDEF UcalX200 dw latch_byte / output a byte to the latch ENDIF max cmd equ (($ - 2 - udat_cmd_table) / 2) _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ pio_mode_table label byte code EVEN db 082h db 083h db 08ah db 08bh db 092h db 093h db 09ah db 09bh db Odah db Odbh max mode equ (5 - 1 - pio mode table) PUBLIC Upia Upia PROC USES bp ss es ds si di, udat_ptr:PTR BYTE pushf / save the flags Ids bx,udat_ptr / get the udat pointer mov byte ptr upia_error[bx],0 yclear the error flag test byte ptr upia_cmd[bx],0ffh yif command <> init jnz deOO y check init status jmp short init_upia yelse init the driver deOO: cmp byte ptr upia_status[bx],1 / if 1, drvr •= init'd je delO / check the command mov byte ptr upia error[bx],l yelse, init error jmp short upia_exTt tell 'em about it delO: xor ax,ax yclear ax mov al,upia_cmd[bx] fetch the command cmp al,max_cmd is it legal? jle de20 yif so, continue mov byte ptr upia_error[bx],2 yelse, cmd range err jmp short upia__exit de20: sal ax,l ymake it 2 bytes mov di,ax jmp udat cmd table[di] do the test routine upia_exit: popf yrestore the flags SUBSTITUTE SHEET ret yexit Upia ENDP * _ _ _ _ . init_upia: yclear ax yget the mode code ycheck if negative yif mode >= 0, continue yelse error 3, bad mode yexit with error iuOO: ycheck upper range yif mode <= max, proceed yelse error 3, bad mode yexit with error iulO: add offset of table put it in an index clear ax get the control word save it in the array point at the control reg send it if not mode 2, skip the next else, short delay til bus settles address the port get control data if no garbage in skip else delay a tad for settling address the port toss the trash iu20; endif endif check for Upia 'IMA' get port e data set the IMA bit send it wait a tad ydx => port b yread the port yif IMA bit is set / skip /else, error 8, not Upia y exit with error iu30: now check for IMA clear yget port e data /clear the IMA bit / send it /wait a tad ydx => port b yread the port yif IMA is clear y skip yelse, error 8, not Upia y exit with error iu40: yset initialized flag yrefresh the ports SUBSTITUTE SHEET jnz pbOO yif mode 2, continue mov byte ptr upia_error[bx],4 yelse flag mode error jmp upia_exit yand exit pbOO: mov num_sex,0 yzero the timeout counter call read_sex yread the clock seconds mov last_sex,ax yand the last value pbcs: mov ex,2 / double check status pbcO: xor ax,ax clear the register mov dx,port_c address the control byte in al,dx get the control word test al,obe ck for tx buffer empty jz pblO ' if full, skip mov ax,010h else delay a tadtad call time_delay for I/O bus settling loop pbcO ck twice then fall thru mov al,pa_data[bx] get the tx byte mov dx,port_a address the port out dx,al write the byte jmp upia_exit exit the driver pblO: call read_sex yread the clock seconds push ax ysave the reading sub ax,last_sex ysubtract last reading pop ax yget back the value jz pbcs yno difference, retry mov last_sex,ax ysave it mov a ,num_sex yget the counter inc ax / bump it mov num_sex,ax / save it cmp ax,1 / timeout is 1 second jle pbcs / if not time, continue mov byte ptr upia_error[b ],5 / else timeout error jmp upia_exit / exit the driver get_byte: / Receive a byte mode 2 test byte ptr ctrljmode[bx],0008h /check the mode jnz gbOO / if mode 2, continue mov byte ptr upia_error[bx],4 / else flag mode error jmp upia_exit ~ / and exit gbOO: mov num_sex,0 yzero the timeout counter call read_sex yread the clock seconds mov last_βex,ax yand the last value gbes: mov ex,2 double check status gbcO: clear the register address the control byte get the control word ck for rx buffer full if empty, skip else delay a tad for I/O bus settling ck twice then fall thru address the port read the byte save the rx byte exit the driver gblO: yread the clock seconds / save the reading SUBSTITUTE SHEET clr bit: xor ax,ax yclear the reg mov al,bit_select[bx] yget the bit select code mov ex,ax ycopy it and cx,0007h / ex = bit position sar ax, yport/array offset (byte) mov si,ax ysave it add ax,upia_base /add the base address mov dx,ax ydx => port mov ax,l here's a bit sal ax,el yshift it where it goes xor ax,00ffh ycomplement to clear bit and al,pa_data[bx] [si] / mask it off mov pa_data[bx] [si],al / save the new value out dx,al / write it to the port jmp upia_exit latch byte: test byte ptr ctrl mode[bx],0ch / if mode 0, pa out jz lbOO / continue mov byte ptr upia_error[bx],4 yelse, cmd/mode error jmp upia_exit yreturn to caller lbOO: mov dx,port_a yaddress the port mov al,latch_data[bx] yfetch the data out dx,al ysend the byte mov ax,010h ydelay a tad call time_delay mov dx,port_d yaddress the latch enable mov al,pd_data[bx] yand nothing else or al,02h yle high out dx,al ysend it mov pd_data[bx],al ysave port d data mov ax,010h y elay a tad call time_delay mov dx,port_d yaddress the latch enable mov al,pd_data[bx] yand nothing else yle high ysend it ysave port d data .**. time_delay Used primarily to allow the I/O bus to settle. See page 9-11 in the 'AT Technical Reference'. Delay count received in AX Returns AX = 0 All other registers preserved / swap registers to save ex /check for zero yif counter is empty, bug out yelse circle til empty address of status register ywrite the address /delay a tad / hang out a tad / read it back / check for busy /if not, read the seconds /delay a tad yshort delay loop to top yseconds address ywrite the address ydelay a tad ywait a short tad yread the seconds yvalue is 1 to 60 END SUBSTITUTE SHEET /* _ _ _ - _ _ _ _**_ _ _ _ _ _ _ _ - _ _ - _ _ - - */ /* Sw_Cal.C */ /* Sine Wave Calibration for the Smart Cable /* */ /* NOTE: This version requires that the cable/MFC be connected to MFC2 */ /* on the Ucal-X200, and that the standard be connected to STD1. */ /* The dashlβ driver was specially modified for this routine. */ /* - _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Declare headers for the libraries /include *<math.h> /include <conio.h> /include <graph.h> /include <stdio.h> /* _ — _ _ _ _ _ _ _ — _ — — _ — — — _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * * /* Include the Sine Wave Calibrator Definitions /include "sw^def.h" /* sine wave specific definitions /include dl6x.h" /* dl6x driver definitions /include "upia.h" /* upia specific definitions /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Global Data Structures */ * _ _ _ _ _ _ _ _ _ _ _ _ _ _ — _ — _**_ - _ — - _ — _ — — — — _ — _ - */ /* dac_bfr holds the dac output values */ short dac_bfr[8192] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 >y /* std_bfr holds the flow data from the standard */ short std_bfr[8192] -= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 >y /* mfc_bfr holds the flow data from the controller */ short mfc_bfr[8192] - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }y /* dl6c is the dashlδ control data structure */ struct dc dl6cy /* sw is the sine wave parameters data structure */ struct sd sw; /* cbl_data contains the cable's calibration data, cal page, etc */ struct cod cbl_datay /* cbl_sys contains the cable's system data, serial number, etc */ struct csd cbl_βysy /* init dl6d array, */ short dl6d[16] -*■* { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }/ /* init udat array, default mode for 8255 is mode 0, all inputs */ SUBSTITUTE SHEET byte uda [32] - { Oxff, Oxff, Oxff, 0x92, Oxfd, Oxbb, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 } / /* cmd_bfr holds the outbound command packet */ byte cmd bfr[ 16] - { 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ~~ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } / /* cal_bfr holds the inbound reply packet */ byte cal_bfr[ 16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } / * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ - _ - - - - _ - _ _ _ - - */ /* External Function Prototypes / * - — _ - — - — _ _ — — _- - _ — - - ** - _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * i extern short menu( short )/ /* the menu returns a command */ extern void dpy_init( void )y extern void rst_dpy( void )/ /* reset display to default mode */ extern void wt4key( void ) / /* print 'press a key...' and wait*/ extern void show_error( short )y /* display an error message */ extern short init_hardware( short )y /* init the h/w for startup */ extern short init_cable( void )y /* setup comm, init minmidmax table*/ extern short start_cal( void )/ extern void init_graph( void )/ /* init the sw display graph */ extern void dpy_sample( void )y extern void stop_cal( void )y /* terminate intr activities */ extern short calc_correction(double )y extern short fill_table( void ) / extern short tx_table( void )y extern short init_upsi( void )y /* init the upsi board */ extern short init_upia( byte )y extern short reset_cable( void )y /* reset the cable, no replyi */ extern void wt_n_msec( word )y /* delay to the nearest 10 mS */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * /* Local Function Prototypes short sw_cal_exec( void ) y void chg_vmax(void)y void chg~vmin(void)y void chg_cycle_tm( short); void chg_cal_page( void )y void init_params( short )y void calc sine( void ); SUBSTITUTE SHEET short adj lag( void )y short verXf _exit( void )y /* ** */ /* main handles init, command and exit, sw_cal_exee handles calibration */ main() { short abort_flag = false, init_flag = 0, status *= 0, usr_cmd = 0/ init params(init_flag)y dpy_Init()y if(i(init_hardware() * =- 0) /* mode 0 zeroes the std goto βw_cal_exity do SUBSTITUTE SHEET break; case 386: /* shift F 3 Next time down chg_cycle_tm(-l)/ break/ default: breaky } if(status) { if(abort_flag -- true) { status = verify_exit()y if(status) abort_flag = true else abort_flag = falsey } init_flag++y }while(labort_flag)y sw_cal_exit: init_hardware(1)y /* mode 1, skip zero std rst_dpy()y return(0)/ } /* -•ur¬ */ /* sine Wave Calibration Exec */ /* Calls the various functions required to init communications, null */ /* the cable/MFC zero offset, init the dac array and other variables. */ /* Controls the calibration process, */ short sw_cal_exec(void) { int kb_cmd Oy short old index 0, status 0, cycle_num - ly short die 0, p_adj 0, t cor = Oy short db_target 0, db_ndx 0y short std tot 0, mfc tot 0y double cur_flow O if(sw.cal_flag) /* if calibrate mode, */ cbl~data.cmd_pg = 0y /* just null it for now */ else cbl_data.cmd_pg cbl_data.pgaddry /* else, test using cal page */ status - init_cable() /* init upsi board and cable */ if(status) return(status)y SUBSTITUTE SHEET calc_sine() / /* init the dac array start_cal() y /* start up mode 18, if(isw.cal_flag) goto test_entryy /* if it's test mode, skip do /* main calibration loop status = dpy_sample() / /* display current data */ if(dl6c.smpl_ndx < old_index) cycle_num++/ /* if new cycle, count */ old_index = dl6c.smpl_ndxy /* update old index */ cur_flow + 9std_bfr[dl6c.smpl_ndx]-sw.std_ofst) * flo_sfacy if(sw.cal_flag S_ (cycle_num > 1)) /* and calib mode, */ status = 1/ /* adjust cable }while(Istatus && ikbhit())y if(istatus && sw.cal_flag Sδ kbhit()) /* if keypress and cal mode */ { /* pass thru here */ while(kbhi ()) geteh()/ /* flush the kbd buffer */ stop_cal() / /* terminate interrupt */ return(-1)y /* because they aborted */ } do { status = dpy_sample()y /* else, continue reading */ if(dl6c.smpl_ndx > (dl6c.smpl_cnt / 2)) /* til the down slope */ t_cor = 1 / }while(It_cor &_ ikbhit())y sw.cal_pass = 0 / /* this is the first pass cal_retry: if(it_cor &_ kbhit()) /* if they pressed a key, { */ pass thru here while(kbhit()) geteh()/ /* flush the buffer return(-1) / /* they aborted */ > db_target + dl6c.smpl_cnt / 2y for(db_ndx = 2 / db_ndx < db_target/ db_ndx++) i std_tot = std_bfr[db_ndx - 2] std_tot += std_bfr[db_ndx - 1] std_tot += std_bfr[db_ndx]y std_tot += std_bfr[db_ndx + 1] std_tot += std_bfr[db_ndx + 2] std_bfr[db_ndx] = std_tot / 5/ mfc_tot = mfc_bfr[db_ndx - 2] mfc_tot += mfc_bfr[db_ndx - 1] mfc_tot += mfc_bfr[db_ndx]y mfc_tot += mfc_bfr[db_ndx + 1] mfc_tot += mfc_bfr[db_ndx + 2]y mfc_bfr[db_ndx] = mfc_tot / 5 / } status = fill table()y /* adjust the cable SUBSTITUTE SHEET if ( status == 1 ) /* if comm ok { do { status = init_upia(M2I ) / status = init_upsi ( ) y /* init the upsi board */ status = reset_cable ( ) /* reset the cable */ } while ( 1 ( status == 0) __ ( i kbhit) ) y if ( 1 status •=• • * • 0) ) { show_error(cmd_com_error) return(true) / } status = tx_t_ble()y /* send them */ } p_adj = 0 / /* now loop and let 'em see */ cycle_num = 0 / old_index = 0 / sw.Tlag = Oy cbl data.cmd pg = cbl_data.pg addry /* set up the test page */ status = init_cable() 7* initialize the upsi board and the cable */ if(status) return(status) y test_entry: init_graph() : do /* This is the cal test loop */ status = dpy_sample()y /* display current data */ if(dl6c.smpl_ndx >= old_index /* check if new cycle */ old_index = dl6c.smpl_ndxy /* let old index follow till */ else /* sample index rolls over */ { old_index = dl6c.smpl_ndxy /* reset the old index and */ eyele_num++ / /* now bump the cycle count */ } cur_flow = (std.bfr[dl6c.smpl_ndx] - sw,std_ofst) * flo_sfacy if( (cycle_num > 1) && (cur_flow > (sw.Vmin + .1))) status = 1 / }while( !status _& 1kbhit() )y if(istatus _& kbhit()) /* if they pressed a key, */ { /* pass thru here */ kb_cmd = getch() / /* get the key */ if(kb_cmd *•*-= 'x') { stop_cal() / /* terminate intr activities */ return(-1) / /* they aborted */ } } do { do SUBSTITUTE SHEET status = dpy_sample() y /* continue reading }while(ikbhit())y t_cor = Oy kb_cmd = getch() switch( kb_cmd ) { case 'c': /* clear screen init_graph() y continue breaky case 'm': sw/T / ag -= ly continuey break/ case 'p': sw.Tlag += 1/ continuey break / case 'r': /* re-calibrate */ do { status = dpy_sample() / /* continue reading */ if(dl6c.smpl_ndx > (dl6c.smpl_cnt / 2)) t_cor = ly >while((t_cor = 0) SS (ikbhit()))y break / defaul : if((kb_cmd == Oxlb) 11 (kb_cmd == 0) il (kb_cmd — OxeO)) kb_cmd = Oxlby break/ } } hile((status == 0) __ (t_cor == 0) && 1 (kb_cmd == Oxlb))/ if(t_cor == 1) { sw.cal_pass += 1/ /* this is the next pass */ goto cal_retry > stop_cal()/ /* terminate interrupt */ retur (status)/ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ . /* init sw with DEFAULT CALIBRATION VALUES */ void init_params( short i_flag ) */ { short lp_cnt = Oy if(ii_flag) { sw.Vmax = 6.000/ /* max setpoint in volts SUBSTITUTE SHEET sw.Vmin = 0.500/ /* min setpoint in volts */ sw.Tcyc = 10.000/ /* 10 seconds */ sw.gas_ρage = 1 / /* default to page 1 */ sw.cal_flag = Oxffffy /* default is calibrate */ cbl_data.pg_addr = 1/ sw.Tlag = 0 / /* no lag yet */ cbl_data.cmd_pg = 0 / for( lp_cnt = 0 / lp_cnt < 16/ lp_cnt++ ) /* init cable data */ cbl_data.sp_adr_tbl[0] [lp_cnt] 0/ /* setpoints * cbl_data.sp_adr_tbl[1] [lp_cnt] 0//-• and readings * /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* fills dac_bfr with sinewave data, puts sample count in dl6c.smpl_cnt */ void calc_sine( void ) { double d_theta, theta, Vinst, Vpeak, Vmidy Vpeak = ((sw.Vmax - sw.Vmin) / 2.); d_theta = asin(rad * dac_sfac / Vpeak); dl6c.smpl_cnt = (word) (360. / d_theta); Vmid = sw.Vmin + Vpeak; for( dl6c.smpl_ndx = 0; dl6c.smpl_ndx < dl6c.smpl_cnt; dl6c.smpl_ndx++ ) < theta = (double) ( d_theta * dl6c.smpl_ndx ); Vinst = (double) ( Vmid + (Vpeak * sin((theta - 90.) / rad) ) ) ; dac_bfr[dl6c.smpl_ndx] = (word) ( Vinst / dac_sfac ); } init_graph(); } /* short adj_lag( void ) short txt_ndx = 0, mfcx, stdx, old_color; short mfc_max = 0, mfc_min = 0, mfc_tst_val 0, mfc_ndx_cnt = 0; short std max = 0, std min = 0, std tst val 0, std ndx ent = 0; long mfc_ndx_sum = 0, std_ndx_sum = 0; double h scale; for( tst_ndx = 0; tst_ndx < (short)dl6o.smpl_cnt; tst-ndx++ ) if(std_bfr[tst_ndx] > std max) std_max = std_bfr[tst_ndx] / if((std_min == 0) && (tst_ndx == 0)) stdjmin = std_bfr[tst_ndx]/ else if(std_bfr[tst_ndx] < std_min) std_min = std_bfr[tst_ndx] / SUBSTITUTE SHEET if(mfc_bfr[tst_ndx] > mfc_max) mfc_max « mfc_brf[tst_ndx]y if((mfc min »=- 0) && (tβt_ndx •=* 0)) mfc_mTn • » mfc_bfr[tst_ndx]y else if(mfc_bfr[tst_ndx] < mfejnin) mfc_min « • mfc_bfr[tst ndx]/ > std_tst_val = std_max - 64 / mfc_tst_val *••* mfc_max - 64 / for( tst_ndx ■- 0; tst_ndx < (short)dl6c.smpl_cnty tst_ndx++) { if(std_bfr[tst_ndx] > std_tst_val) { std_ndx_sum +* ■ ** tst_ndxy std ndx_cnt +*= ly " ~ > if(mfc_bfr[tst_ndx] > mfc_tst_val) { mfc_ndx_sum += tst_ndxy mfc_ndx_cnt += 1/ } } sw.Tlag = (short) ((mfc_ndx_sum / mfe_ndx_cnt)-(std_ndx_sum / std_ndx_cnt))y init_graph() return(l)/ > /* _ _ _ _ _ _ _ — _ _ _ — _ _ _ _ — _**— _ _ _ _ _ _ - _ _ - _ _ _ - - */ short verify_exit(void) { int reply «- 0; _settextposition( 29, 52 ); printf( "Press 'Y' to exit program, " )y _settextposition( 29, 52 ); printf( "any other key to continue..." )y while(lkbhit())y reply - getch() _settextposition( 29, 52 ); printf( " " )y if((reply — 'y') || (reply -- Υ')) return(true) else return(false) } * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * void chg_vmax(void) { sw.Vmax -= 0.250; /* drop Vmax by 5% */ if(sw.Vmax < sw.Vmin) sw.Vmax - 6.000/ SUBSTITUTE SHEET _settextposition( 4, 3 )y printf("Ma imum Setpoint =%4.0f%s", (100. * sw.Vmax / 5.)," %")y } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void chg_vmin(void) { sw.Vmin += 0.250; /* bump Vmin by 5% */ if(sw.Vmin > sw.Vmax) sw.Vmin - 0.250; _settextposition( 5, 3 ); printf("Minimum Setpoint *-%4.0f%s", (100. * sw.Vmin / 5.)," %"); } /* change the test cycle time, incs or decs by 5 seconds (5 - 60 range)*/ void chg_cycle_tm( short dir_flag ) { double delta = 0.; if(dir_flag > 0) delta = 5.; if(dir_flag < 0) delta = -5.; if(dir_flag == 0) sw.Tcyc = 10; sw.Tcyc +- delta; if(sw.Tcyc <= 0) sw.Tcyc = 300; if(sw.Tcyc > 300) sw.Tcyc - 5; _settextposition( 6, 3 ); printf("Cal. Cycle Time =%4.Of%s",sw.Tcyc," S"); } * — — - — - _ _ — _ _ — — — - — _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* change the calibration page */ void chg_cal_page( void ) { cbl_data.pg_addr += 1; if(cbl_data.pg_addr > 10) = 1; /* if cal mode, pg 1 */ cbl_data.pg_addr = 0; /* page 0 in test mode only */ } sw.gas_page = cbl_data.pg_addr; _settextposition( 7, 3 )y printf("Calibration Page =%4d ",sw.gas_page); } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* toggle the calibration flag */ void chg_cflg(void) { sw.cal_flag * ** = Oxffff; SUBSTITUTE SHEET _settextposition( 8, 3 )y if(sw.cal_flag) { printf("Calibrate Mode "); if(cbl_dat .pg_addr «*= 0) {. cbl_data.pg addr = 1; _settextposιtion( 7, 3 ); printf("Calibration Page =%4d ,sw.gas_page); printf("\a"); } else printf("Flow Test Mode ') > /* .**. SUBSTITUTE SHEET /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Sw_Cbl.C*/ /* Cable communications routines for the SineWave Calibrator */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Declare the headers for the libraries */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /include <conio.h> /include <stdio.h> /include <graph.h> /* */ /* Include the Sine Wave Calibrator Definitions */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ — - - */ /include "sw_def.h" /* sine wave specific definitions /include "dlβx.h" /* dl6x driver definitions /include "upia.h" /* upia specific definitions /* - */ /* Declare Global Variables */ /* .**. */ extern short dac_bfr[]]y /* waveform generator data storage */ extern short std_bfr[]y /* flow meter data storage */ extern short mfc_bfr[]y /* flow controller data storage */ extern struct dc dl6c/ /* dashlβ control structure extern struct sd sw / /* waveform generator control data extern struct ccd cbl_datay extern struct csd cbl_sysy extern short dl6d[]y /* dashlδ data storage (16 ch in) extern byte uda [ ]y /* Upia control/data storage extern byte cmd_bfr[] / /* command buffer, pc to cable extern byte cal_bfr[]y /* calibration data cable to pc /* ~ */ /* External Function Prototypes */ /* - */ extern void dl6x( struct dc * )y /* dash 16 driver */ extern void Upia( byte * )y /* upia driver extern void clear_bit( int )y /* resets a bit, updates udat */ extern short init_dl6x( byte )y /* initβ the dl6 board */ extern void init_tinter( void )y /* init the timer for n samples */ extern short init_upsi( void )y /* init the upsi board */ extern short init_upia( byte )y /* inits the 8255 extern byte rcv_bfr( short )y /* receive a data packet */ extern byte rd_byte( void )y /* reads a byte mode 2 */ extern byte rd_port_a( void ) /* reads port a directly */ extern byte rd_port_c( void ) y /* reads port c directly SUBSTITUTE SHEET extern short reset_cable( void )y /* reset the cable, no replyi extern byte send_comman ( byte, byte, byte, byte, byte, byte )y extern void set_mux_limitβ(short, short)/ /* set mux limits: hi,lo - */ extern void set_bit( int )y /* sets a bit, updates udat */ extern byte tx_cmd( void )y /* send a cmd pkt to cable */ extern short wt4golo( void )y /* Wait for the go flag lo */ extern short wt4gohi( void ) /* Wait for the go flag hi */ extern short wt4reply( void )y /* wait for ibf or keypress */ extern void blank_all( void )y extern void cbl_rdg_dpy(short, short, short, double); extern void dac_nul dpy( short, short, double, double ); extern void ofst__nuT_dpy( short ); extern void show~error( int ) ; /* displays an error message */ extern void show_status( short ); /* display a status message */ extern void sp_tBl_dpy( short ); /* show the setpt/addr table */ extern byte wr_byte( word ); /* writes a byte mode 2 extern void wt_n_msec( word )/ /* delay to the nearest 10 mS*/ extern void wt4key( void )/ 'Press any key...' and waits */ /*- */ /* Local Function Prototypes */ */ short init_addr_table( void )y /* init setpoint/address table*/ short init_cable( void ) ; /* setup comm, init minmidmax table */ short init_hardware( void ); /* initialize the h/w for startup */ short null_offset( short ) ; /* zero the dace on the dl6 and sc*/ short start_cal( void ) /* start intr activities */ void stop_cal( void ) /* stop intr activities */ /* */ /* Function Definitions */ /* .**. short init_addr_table( void ) {. short chnl = 0, rdng = 0, ch_ndx • *• ■ 0, tbl_ndx = 1, old_setpt = 0y short cbl_status •*■ *■ 0, k_press - 0, dly_cnt = 0, vma_ctr *=0y double cbl_volts *= 0, v_out - * ■ * ■ O.y double v_step * = ((sw.Vmax - sw.Vmin)/14.)y double cbl_sfac = (double) ( 5. / 1023. )y double spt_sfac = (double) ( 6. / 1023. )y blank_all(); /* clear the screen / show status( addr init ); do ~ ~ { v_out - (sw.Vmin + ((double) (tbl_ndx - 1) * v_step )); dl6c.mode num = 15; /* set the dac */ dlδc.HacO - (short) ( 4095. * v_out / 10.); /* to the setpoint*/ dl6c.dac_chnl ** ■*■ Oy /* make it channel 0 */ dl6x( _dl6c )y SUBSTITUTE SHEET e se { show_error( erase_error_dpy ) ; /* else, erase it / cbl_data.sp_adr_tbl[0] [tbl_ndx] = dlδc.dacO; /* save the setpoint ch_ndx = 0;~ /* display the 4 channels */ do { chnl = ((cal_bfr[ 4 + (2 * ch_ndx) ] & 0x70) » 4); rdng = ((cal_bfr[ 4 + (2 * ch_ndx) ] & 0x03) « 8); rdng += cal_bfr[ 4 + (2 * ch ndx) + 1 ]; if( ch_ndx == 0 ) " /* save reading @ setpt */ /* l,tbl_ndx = address */ cbl_data.sp adr_tbl[1] [tbl_ndx] = (rdng » 2)y /* 0 - 255*/ if (cbl_data.sp_adr_tbl[l] [tbl_ndx] = old_setpt) vma_ctr += ly else { old_setpt = cbl_data.sp_adr_tbl[l] [tb]_ndx] y vma_ctr = 0; } cbl_volts = (double)rdng * sρt_sfac; sp_tbl_dpy( tbl_ndx ); } else cbl_volts = (double)rdng * cbl_sfac; cbl_rdg_dpy( ch_ndx + 3, 4, chnl, cbl_volts ); ch_ndx += 1; if(ch_ndx == 4 ) } ch ndx = 0; SUBSTITUTE SHEET wt_n msec(10); } >while( vma_ctr < 3 ) vma_ctr *- Oy > tbl_ndx +=- 1; /* bump the counter */ k_preβs » kbhi ()y >while((tbl_ndx < 16) && ik__press); show_status(0) if((tbl_ndx < 15) && 1(kjpress = * = 0)) return(l)y else return(O); > /* */ short init_cable( void ) /* setup comm, init minmidmax table */ { byte pg_num = 0 / short status = 0 / status = init_upsi(); /* init upsi for communications */ if(status) return(status)y status = reset_cable()y /* init cable for comm. */ if(status) return(status); if((cbl_data.cmd_pg == 0) __ 1(sw.cal_flag == 0)) ■ C status -- null_offset( 0 )y /* null the cable/MFC offset */ if(status) return(status) status = init_addr_ able()y /* init the setpoint/address table */ if(status) retur (status) status = reset_cable() / /* init cable for comm. */ if(status) return(status) y } if(sw.cal_flag * *■*■-• 0) pg_num - (byte)cbl_data.pg_addry else pg_num = (byte)cbl_data.cmd_pgy status = send_command( OxOa, pg_num, 0, 0, 0, 0 )/ /* cbl flow test */ if(status) /* check errors */ i show_error( >3md_com_error ) y /* if error, */ return( status ) y /* return to caller*/ > wt4reply() y /* wait 4 upsi 2 call back */ status = rcv_bfr( 0x08 )y /* fetch the reply */ SUBSTITUTE SHEET if(status) /* if checksum is bad, */ show_error( rxd_csm_error )y /* show it */ } /* */ short init_hardware( short skip_zero ) { short error_flag = 0/ word old_mux_ctrly short avg_cnt, stbl_cnty double std_tot, std_avg, old_avg, new_chgy error_flag = init_upia(M0OII)y /* setup mode 0, A out, C in */ if(error_flag) return(error_flag) / udat[upia_cmd] = Latch/ udat[latch_data]=Oxff/ /* latch all off */ Upia( udat ) / error_flag = (short)udat[upia_error] if(error_flag) return(error_flag) error_flag = init_dl6x( (byte) (0) ) /* init the dl6x board */ if(error_flag) return(error_flag) / i (lskip_zero) { _settextposition(27,9) / printf("Releasing Pressure") / udat[upia_cmd] = Latchy /* if checking std offset */ udat[latch_data]=0x9fy /* mfc2 & stdl on, gas off */ Upia( udat ) error_flag = (short)udat[upia_error]y if(error_flag) return(error_flag) y for(stbl_cnt = Oy stbl_cnt < 100 / stbl_cnt++) /* allow 10 sees */ { _settextposition(28,9) / printf("%5.If sees to go",10. - ((float)stbl_cnt / 10.)) / wt_n_msec( 10 ) / } _settextposition(28,9) printf(" ") y _settextposition(27,9)/ printf("Zeroing Standard ") / udat[upia_cmd] = Latch / udat[latch_data]=0xff / /* now latch all off * Upia( udat ) ; error_flag = (short)udat[upia_error]; if(error_flag) return(error_flag) ; old_mux_ctrl = dl6c.mux_ctrl; /* save old mux limits */ SUBSTITUTE SHEET set_mux_limit( 7, 7) y /* std only */ old_avg *- 0. y std_avg *- 0. / std~~tot ** ■■ 0. ; stbT_cnt - Oy dl6c.mode_num -* 3 do i. avg_cnt -* Oy std_tot - O.y do reading */ /* add it to the sum*/ e se { new_chg - old_avg - std_avgy old_avg *•** std_avgy > _settextposi io (28,9)y printf("offset • * * * %7.3f mV", (βtd_avg * flo_sfac * 1000. )); if(new_chg < .5) stbl_ent +• ** - ly else stbl_cnt - Oy wt_n_msec(10) }while((stbl_cnt < 30) δδ l(kbhit()))y dl6c.mux_ctrl = old_mux_ctrly /* reset old mux limits */ set_mux_limits(((old_mux_ctrl » 4) δ OxOf), (old_mux_ctrl δ OxOf))/ } _settextposition(27,9)y printf(" ")y error_flag = init_upia(M2l)y /* setup mode 2, A out, C in */ if(error_flag) return(error_flag)y return(O)/ } /* _ _ _ _ _ _ _ _ _ — _ _ — — _ _ — _**. short null_offset( short cal_byte ) SUBSTITUTE SHEET byte pwO « 0, pwl **■ > Oy short cbl_status » 0, cbl_out - Oy short cf_temp = 0, cfhi = 0, cflo * ■* • Oy short chnl = 0, rdng = 0, lp_ctr * 0, nul_flag = Oy short vo_max *- 0, vo_min ■» 0, avg_cnt -■ 0x4000y double cbl_volts - 0.~ short nul_count - 5y double max offset = 0.00025y double VcfHi = (double) (2.5 / 255.); double Vcflo = (double)(Vcfhi * 24900. / 6190000. )y double cbl_sfac = (double) ( 5. / 1023. )/ double spt_sfac - (double) ( 6. / 1023. )/ blank_all()y /* clear the screen */ show_status( offset_null ) dl6c.mode_num - 16 ~ " /* null the dacs on the dl6 */ dlβc.dacO = 0; dlδc.dacl = 0; /* set both dacs to 0 */ dl6x( δdl6c ); if( i(dl6c.e_flag==0) ) show_error(dl6_error); cbl_data.sp_adr tbl[0] [0]=0; /* init the βetpt address 0 do { do { if(lcal_byte) /* if first time, init { cbl_statuβ - send_command( 0x08, 0, 0, 0, 0, 0 ); if(cbl_status) /* get the nul value */ { βhow_error( cmd_com_error ); /* fess upl */ return( cbl_status ); } } else /* else, leave the old cbl_status = send_command( 0x08, pwO, pwl, 0, 0, 0 )y /* vals * 7/ if(cbl_status) /* check errors 7 { show_error( cmd_com_error)/ return( cbl_status Jy } wt4reply()y cbl_status - rcv_bfr( 0x10 )y /* fetch a cal packet */ if(cbl_βtatus) /* if checksum is bad, */ show_error( rxd_csm_error ) y else { if(lcal_byte) { cbl_sys.dac_nul_hi •= cal_bfr[2] SUBSTITUTE SHEET pwO = cal_bfr[2]y cbl_sys.dac_nul_lo = cal_bfr[3]y pwl = cal_bfr[3]y show_error( erase_error_dpy )y > wt_n_msec(20) y }while(cbl_status δδ ikbhit())/ lp_ctr = 0 / do { chnl = ((cal_bfr[ 4 (2 lp_ctr) ] δ 0x70) » 4)y rdng = ((cal_bfr[ 4 (2 lp_c.tr) ] δ 0x03) « 8)y rdng += cal_bfr[ 4 (2 lp_ctr) + i ]; if( lp ctr == 0 ) /* save rdng @ 0 setpt */ cbl_data.sp_adr_tbl[1] [0] = rdngy /* 0,0-βsp l,0=rdng(adr) */ cbl_volts = (double)rdng * cbl_sfacy cbl_rdg_dpy( lp_ctr + 2, 4, chnl, cbl_volts )/ lp_ctr += 1/ >while( lp_ctr < 4 )y cbl_volts = Oy if(i(dl6c.mux_ctrl =-= 0x88)) ■ C dl6c.mode_num = ly /* set the mux scan limits to MFCl out dl6c.mux_ctrl = OxBBy dl6x(δdl6c)y } for(lp_ctr = Oy lp_ctr < avg_cnty lp_ctr++) { dl6c.mode_num *** - 3; /* mode 3, cnvrt δ bump */ dl6x(δdl6c); /* do a conversion */ cbl_out = dl6c.last_rdg; /* get the reading */ if(cbl_out > vo_max) /* check the noise, hi */ vo_max = cbl_out; if(cbl_out < vo_min) /* check the noise, lo */ vo_min = cbl_out; cbl volts += (double)cbl out; } cbl_ olts *= flo_sfac; cbl_ olts /= (double)avg_cnt; dac_nul_dpy( vo_max, vo_min, cbl_ olts, flo_sfac); if((cbl_volts > max_offset) || (cbl_volts < -max_offset) '! (cal_byte == 0)) { nul_flag = 0; ofst_nul_dpy( 0 ); cfhi = (short) ( cbl_ olts / Vcfhi ); /* zero q 250uV cflo = (short) ((cbl_volts - (cfhi * Vcfhi)) / Vcflo); if(( cbl volts < .00005f) δδ (cflo == 0)) { if(cbl_volts > .OOOOlf) cflo = 1; SUBSTITUTE SHEET else if(cbl_voltβ < -.OOOOlf) cflo - -1; } if((short) (pwl + (short)cflo) > 255) { cfhi += 1; cflo -- 250; } else if((short) (pwl + (short)cflo) < 0) { cfhi -= 1; cflo += 250 / } cf_temρ = (shor )pw0 + (short)cfhiy if( (cf_temp > 255) | | (cf_temp < 0) ) { pwO = 0x80y pwl = 0x80y continuey } else { pwO = (byte)cf_tempy } cf_temp - (short)pwl + (short)cflo; if(cf_temp > 255) cf_temp -- 250 / if(cf_temp < 0) cf_temp += 250 / pwl = (byte)cf_tempy } else { nul_flag += ly if(nul_flag > nul_count) { cbl_sys.cbl_offset_hi = pwOy cbl_sys.cbl_offset_lo - pwly for( lp_ctr = Oy lp_ctr < 8y lp_ctr++ ) cbl_data.corfac[lρ_ctr] = (short) ( (pwO « 8) + pwl)y } ofst_nul_dpy(1)y } if( 1cal_byte) cal_byte++y }while( l(nul_flag > nul_count) δδ 1 (kbhit()))y if( I(nul_flag > nul_count)) { rdng - (short)getch()y /* flush the buffer */ cbl_status -■ -1 / } else SUBSTITUTE SHEET -84- cbl_status •** • 0; ofst_nul_dpy(1)/ show_status(0) / return( cbl status )/ * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - _**- _ short start_cal( void ) { short error_flag = 0, lp_cnt - 0, status = 0, avg ent - 0x2000 double flo sum = 0., flo avg = 0./ dl6c.mode_num = 15/ /* null the dacs on the dl6 */ dl6c.dac_chnl = 0/ dlδc.dacO = dac_bfr[0]/ /* set dac 0 to first setpt */ dl6x( δdlβc )y if( i(dl6c.e_flag==0) ) show_error(dl6_error)y error_flag = init_upia(M0OII) /* setup mode 0, A out, C in if(error_flag) return(error_flag) y udat[upia_cmd] = Latch; udat[latch_data] = 0x9c; /* MFC2, STD1, Gas In, Gas Out Upia( udat ); error_flag = (short)udat[upia_error]; if(error_flag) retur (error_flag); for( lp_cnt = 0; lp_cnt < 4; lp_cnt++) /* allow a couple of sees */ wt_n_msec( 500 ); /* for flow to stabilize */ set_mux_limits( 8, 7); /* STD1 = Ch 7, MFC2 = ch 8 */ init_timer(); /* init the timer for intr rate */ error_flag = dl6c.e_flag; if(error_flag) return(error_flag); dl6c.mode_num — 14; /* read the inputs dl6x( δdlβc ); error_flag = dl6c.e_flag; if(error_flag) return(error_flag); dlδc.dio {= 1; /* set the gate */ dl6c.mode_num = 13; /* write the outputs */ dl6x( δdlδc ); error_flag = dl6c.e_flag; if(error__flag) return(error_flag) ; dl6c.mode_num = 18 /* prepare for mode 18 */ dl6c.dac_chnl = Oy use DACO, sample ent init'd in calc_sine */ dl6c.recycle " * *• 0; /* run continuously till stopped dl6x( δdlδc )y error_flag = dl6c.e_flag; if(error_flag) SUBSTITUTE SHEET return(error_flag) ; return(O) ; /* if no error flag, return 0 } * . void stop_cal( void ) /* stop intr activities { short error_flag; dlδc.mode nϋm = 7; dl6x( δdl_c); error_flag = init_hardware(1); /* skip the std zero */ } /* */ SUBSTITUTE SHEET /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Sw_D16.C */ /* Second floor analog functions. First floor is the D16 driver; */ /* Declare headers for the libraries /include <conio.h> /* — _ _ — _ — — — — — — _ — _ — — _ _**_ _ _ — _ _ _ — _ _ _ — _ _ _ _ */ /* Include the Sine Wave Calibrator Definitions */ /include "sw_def.h" /* sine wave specific definitions /include "dlβx.h" /* dl6x driver definitions /include "upia.h" /* upia specific definitions /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ — _ — * / * Declare Global Variables */ * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ extern short dac_bfr[]; /* waveform generator data storage */ extern short std_bfr[]; /* flow meter data storage */ extern short mfc_bfr[]; /* flow controller data storage */ extern struct dc dl6c; /* dashlδ control structure */ extern struct sd sw; /* waveform generator control data */ extern struct ccd cbl_data; extern struct csd cbl_sys; extern short dl6d[]; /* dashlδ data storage (16 ch in) */ extern byte udat[]; /* Upia control/data storage */ extern byte cmd_bfr[]; /* command buffer, pc to cable */ extern byte cal_bfr[]; /* calibration data cable to pc */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* External Function Prototypes */ extern void dl6x( struct dc * ) ; extern void show_error( short ); /* display an error message */ * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**— _ _ _ _ _ _ _ — _ — _ _ _ _ _ */ /* Local Function Prototypes */ short init_dl6x( byte ); /* inits the dl6 board */ void init_timer( void ); /* init the timer for n samples */ void set_mux_limits(short, short); /* set mux limits: hi, lo */ void start_cal( void ); /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**— _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ short init_dlδx( byte md_sel ) /* inits the dl6 board */ • C dl6c.mode_num = 0x0000; dl6c.mux_ctrl = OxOOfO; dlβc.dio = 0x0008; /* mfc2 for this one */ dlδc.dacO = 0x0000; dl6c.dacl = 0x0000; dlδc.control = 0x0000; dlδc.tmre = 0x0000; dlδc.tmrO = 0x0000; dlδc.tmrl = 0x0000; dlδc.tmr2 = 0x0000; dlδc.tmrc = 0x0000; dl6e.e_flag Oxaaaa; dlδc.set_ptr dac_bfr; dl6c.std_ptr std_bfr; dlδc.mfc ptr mfc bfr; dlδc.bfr_ptrl dlδd; dlδc.bfr_ptr2 dlδd; dlδx( δdlδc ); return(0); /' */ void init_timer( void ) /* init the timer for n samples */ -£ double cycle_time = sw.Tcyc; double intr_time = cycle_time / dl6c.smpl_cnt; long intr_ticks = (long) (intr_time * 1000000.); long dvdr = 2; short xflag = 0; sw.Tint = intr time * 1000; /* make it milliseconds do if( (intr_ticks % dvdr) == 0) { if( (intr_ticks / dvdr) <= 65535) { xflag = 1; break; } else dvdr++; } else dvdr++; if((dvdr > 65535) 1 (kbhit() == 0)) { xflag = -1; break; if((xflag == 1) δδ (intr_ticks == dvdr)) xflag = 0; dvdr = 2; SUBSTITUTE SHEET intr_ticks += ly /* try bump it by 1 } >while(xflag *-* 0)y if(xflag > 0) { dlδc.tmrl = (word) (intr_ticks / dvdr)y dl6c.tmr2 * ■ * ■* (word)dvdry- if(dlδc.tmrl == 1) { dlδc.tmrl * (word)(dvdr / 2); dl6c.tmr2 = (word)2 > dlδc.mode_num = 17 /* init timer .lhz for now dlδx( dlδc)/ > else { dlδc.tmrl = 0; dlδc.tmr2 = 0; show_error(dlδ error); } /-* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ void set_mux_limits( short hilim, short lolim ) • £ dl6c.mode_num -- 1; /* prepare for mode 1, set mux limits dlδc.mux ctrl = ((hilim « 4) | lolim) δ Oxff; dl6x( δdlδc ); > SUBSTITUTE SHEET /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ - - - - */ /* Sw Pia.C */ ' " - , /* Interface to the Upia driver for the Sinewave Calibrator */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ - - - - _ */ /* Include the headers for the library functions */ /include <conio.h> /include <graph.h> /include <stdio.h> /include <time.h> /include <sys\types.h> /include <sys\t * Lmeb.h> /* _ _ _ _ _ _ _ _ _ _ _ — — — - - -**— _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Library function Prototypes */ /* _ _ _ _ _ _ _ _ _ _ _ _ - _ - — -**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ time_t time(time_t *timeptr); void~ftime(struct timeb *timeptr); /* _ _ _ _ _ _ _ _ _ _ _ _ - _ - — _**- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - */ /* Include the Sine Wave Calibrator Definitions */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /include "sw def.h" /* sine wave specific definitions /include "dl-x.h" /* dl6x driver definitions /include "upia.h" /* upia specific definitions /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Declare Global Variables */ extern short dac_bfr[]y /* waveform generator data storage */ extern short std_bfr[]y /* flow meter data storage */ extern short mfc~bfr[ ]y /* flow controller data storage */ extern struct dc dlδcy /* dashlδ control structure */ extern struct sd sw; /* waveform generator control data */ extern struct ccd cbl_data; extern struct csd cbl_sys; extern short dlδd[]; /* dashlδ data storage (16 ch in) */ extern byte udat[]; /* Upia control/data storage */ extern byte cmd_bfr[]; /* command buffer, pc to cable */ extern byte cal_bfr[]; /* calibration data cable to pc */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* External Function Prototypes */ /* — _ — _ _ _ _ _ _ _ _ — — _ _ _ _**- _ _ _ _ _ _ _ _ _ _ _ _ — _ _ _ */ extern void Upia( byte * ); extern void wt4key( void ); /* 'Press any key...' and waits */ SUBSTITUTE SHEET extern void wt_n_msec( word ); /* delay to the nearest 10 mS */ extern void show_error( int ); /* displays an error message */ extern void show_upia_error( void ); extern void show_status( short ); /* display a status message /* - */ /* Local Function Prototypes */ /* - ' short init_uρia( byte mode_select ); byte rd_byte( void ); /* reads a byte mode 2 byte wrjbyte( byte ); /* writes a byte mode 2 */ void set_bit( byte )y /* sets a bit, updates udat */ void clear_bit( byte )y /* resets a bit, updates udat */ byte rd_port_a( void )y /* reads port a directly */ byte rd_port_c( void ); /* reads port c directly */ byte send_command( byte, byte, byte, byte, byte, byte ); byte tx_cmd( void )y /* send a cmd pkt to cable */ byte rcv_bfr( short )y /* receive a data packet */ short reset_cable( void ) short init_upsi( void )y /* init the upsi board */ void reset_upsi( void ); short wt4reply( void )y /* wait for ibf or keypress */ short wt4golo( void ); short wt4gohi( void ); * short init_upia( byte mode_select ) ■ C udat[ upia_cmd ] = InitUpia; /* Command 0 = Init */ udat[ ctrl_mode ] •*= mode_select; /* pass the mode they selected */ Upia( udat ); /* send the address of udat */ return( udat[ upia__error ] ); /* return the error, if any */ /* - - - - /* rd_byte reads a byte mode 2 byte rd_byte( void ) • C udat[upia_cmd] = GetByte; /* get a byte */ Upia(udat); /* from the upsi board */ if(udat[upia_error> == 0) /* if no error, */ { udat[pa_data] δ= Oxff; /* return the data */ return(udat[pa_data]); } else i show_upia_error(); /* else return ff for failure */ return(Oxff); /* caller must check upia error*/ } SUBSTITUTE SHEET /* wr_byte writes a byte mode 2 byte wr_byte( byte wb ) { udat [pa data] - wb; /* send a byte */ udat[upTa_cmd] = PutByte; /* to the upsi board */ Upia (udat) ; if (ud_t[upia_error] == 0) /* if no error, return success { return(0); } else { show upia error(); /* else return error */ return(udat[upia_error]); /* no other check needed */ } -**- /* set_bit sets a bit, updates udat void set_bit( byte b_sel) { udat[upia_cmd] = BitSet; udat[bit_select] = b_sel; Upia( udat ); } /* clear_bit resets a bit, updates udat void clear bit( byte b sel) { udat[upia_cmd] = BitClr; udat[bit_select] - b_sely Upia( udat )y /* .**- /* rd_port_a reads port a directly byte rd_port_a( void ) return( (byte)inp(port_a))y /* .**. /* rd_port_c reads port c directly byte rd_port_c( void ) { return( (byte)inp(port_c) ) SUBSTITUTE SHEET /* — — — _ — _ — — — — — — — — _ — —**_ — _ — - — _ - — — — - — _ — - */ /* Cable communications functions */ /* _ — — — — _ _ _ _ — _ — — _ — — _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* fill cmd_bfr with the data passed and calculate the checksum */ byte send_command( byte cmd, byte vl, byte v2, byte v3, byte v4, byte v5 ) { byte status = Oy short lp_ctr = Oy /* byte tx_cmd( void ) { byte reply = 0, pia_status = 0; short lp_ctr = 0, cbl_status = 0; short retry = 0, rst__flag = 0; do /* flush the input buffer { pia_status = (rd_port_c() δ ibf); if(pia_status == ibf) reply = rd_port_a(); wt_n_msec(1); }while(pia_status δδ ikbhit()); if(pia_status) /* if keypress, show δ exit { _settextposition( 3, 51 ); printf( "tx_cmd - abort buffer flush " ); wt4key(); return(-1); } do { if(rst_flag == true) • C rst_flag = false; lp_ctr = 0; } pia status = wr byte( cmd bfr[lp ctr] ); /* cmd bfr is global */ SUBSTITUTE SHEE if( 1 (pia_status == 0)) { _settextposition( 3, 51 ); /* put it on the screen printf( "Tx Byte Error %04x ", pia_status ) / wt4key(); _settextposition( 3, 51 ); /* put it on the screen */ printf( " " ); return(-1) / /* flag it as a failure */ } else < _settextposition( 18 + lp_ctr, 4 ); /* put it on the screen printf( "%02x", cmd_bfr[lp_ctr] ); } do /* give upsi a chance */ { /* to come back, check for */ udat[pc_data] = rd_port_c(); /* input buffer ready */ cbl_status = udat[pc_data] _ ibf/ /* before trying rd byte */ }while( icbl_status) / reply = rd_byte() / if( 1 (udat[upia_error] == 0)) { _settextposition( 3, 51 )/ /* put it on the screen printf( "Rx Data Error %04x " , udat[upia_error] ) wt4key() / _settextposition( 3, 51 )y /* put it on the screen */ printf( " ", udat[upia_error] )/ return(-1) /* flag it as a failure */ } else { _settextposition( 18 + lp_ctr, 8 ) / /* put it on the screen printf( "%02x", reply ) / } if( reply == cmd_bfr[lp_ctr] ) lp_ctr += 1/ /* bump the index else { if(reply == 0x15) /* nak? { rst_flag = true/ continue/ } else { _settextposition( 3, 51 )/ /* put it on the screen */ printf( "tx_cmd sent %04x rcvd %04x ', cmd_bfr[lp_ctr], reply ) / wt4key( ) / _settextposition( 3, 51 ) / /* put it on the screen */ printf( " ", udat[upia error] / SUBSTITUTE SHEET return (-1) y }while(lp_ctr < 8)/ cbl_status = wt4reply()/ if(cbl_status == 0) return(0x00 )y /* return success */ else return(Oxff)y /* or failure */ } byte rcv_bfr( short byt_cnt ) /* receive a data packet i byte cksum = 0, tflg = Oy short row = 0, col = 0, old_color = 0, byt_ptr = 0/ show_status(w4_reply)/ do { cal_bfr[byt__ptr] = rd_byte() / /* get the reply row = 18 + ( byt_ptr %8 )y col = 12 + (( byt_ptr »3 ) *4 )/ _settextposition( row, col )/ printf( "%02x", cal_bfr[byt_ptr] )y if((byt_ptr == 0x00) δδ 1 (cal_bfr[byt_ptr] == 0x02)) /* bad echo { tflg = udat[pc_data] δ obey if(tflg == obe) /* if upsi's waiting for us */ wr_byte( 0x15 )y /* send a NAK */ return(rx_phase_error)y > else i wr__byte( cal_bfr[ byt_ρtr ] )y /* echo it byt_ptr ++y > }while(byt_ptr < byt_cnt); show_status(0); for( byt_ptr = 0, cksum = 0; byt_ptr < (byt_cnt-l); byt_ptr++) { cal_bfr[byt_ptr] δ= Oxff; /* verify the checksum cksum += cal_bfr[byt_ptr]; cksum δ= Oxff; } if( cal_bfr[ byt_ptr ] == cksum ) return( 0 ); else { show_error( rxd_csm_error ); /* rxd_csm_error */ return( rxd_csm_error ); /* error 3 —■ checksum error */ SUBSTITUTE SHEET } * short reset_cable( void ) { byte cbl_status = 0, pia_statuβ 0; short lp_ctr = 0, retry = 0; show_status( cable_reset ) ; do if(retry > 0) { _settextposition( 6, 52); printf("Retries left %04d (16 - retry)); } cbl_status = send_command(0x01, 0, 0 >,, 00,, 0l , 0); if( 1cbl_status == 0) /* check errors */ { show_error(cmd_com_error); /* cmd_com_error fess up! */ return(cbl status); } show_status(w4_reply) ; /* tell 'em we're waiting */ do /* wait for reply */ • C /* or keypress udat[pc_data] = rd_port_c(); pia_status = udat[pc_data] δ ibf; if(pia_status == ibf) break; if(kbhitO) return(-1)/ }while( 1 (pia_status == ibf)); cbl_status = rcv_bfr( 0x08 ) / /* get a packet */ if(1 (cbl_status == 0)) /* if checksum is bad, */ { show_error(cbl_status) / /* cksum error or? */ lp_ctr = Oy /* reset and try again */ pia_status = Oy cbl status = Oxff /* flag retry */ } if( 1cbl_status) { for( lp_ctr = Oy lp_ctr < 2y lp_ctr++) { if( 1 (cmd_bfr[lp_ctr] == cal_bfr[lp_ctr] ) ) • { show_error(cmd_com_error)y /* cmd_com_error fess up! cbl_status = Oxffy } SUBSTITUTE SHEET if(cbl_status == Oxff) retry += 1; >while(l(cbl_status == 0) δδ (retry < 16) )y if(i(cbl_status == 0)) { _settextposition( 6, 52); printf("Retries Exhausted, Aborting ")y return(-1)y } wt_n msec(100); /* wait a tad for reset show_status(0) ; return(cbl_status); > */ short init_upsi( void ) /* init the upsi board */ ■ C short status = 0; set_bit(mfc2_prg); /* make sure mfcl_prg is hi */ reset_upsi(); /* reset the upsi board */ status = wt4gohi(); if(i(status == 1)) /* if keypress while waiting for */ retur (-1); /* upsi, return failure code */ clear_bit(mfc2_prg); /* tell upsi wake up status = wt4golo(); if(l(status = 1)) /* if keypress while waiting for */ retur (-1); /* upsi, return failure code */ else return(O); void reset_upsi( void ) { struct timeb start_time, elapsed_time; show_status( upsi_reset ); set_bit( bus_reset ); ftime( δstart_time ); do { ftime( δelapsed_time ); }while((elapsed_time.time - start_time.time) < 1 ); clear_bi ( bus_reset ); f ime( δstart_time ); do { ftime( δelapsed_time ); }while((elapsed_time.millitm - start_time.millitm) < 500 ) show status( 0 ); SUBSTITUTE SHEET */ short wt4gohi( void ) /* wait for the go pin low */ { /* or for a keypress short status; do { status = (rd_port_c() δ go_flag); }while(l (status == go_flag) δδ (kbhit() == 0)); if(status == go_flag) /* if 'GO' is hi, */ return(l); /* return success */ else /* else, */ return(-1); /* return failure code */ } * . */ short wt4golo( void ) /* wait for the go pin low */ { /* or for a keypress short status; do { status = (rd_port_c() δ go_flag) ; }while( (status == go_flag) δδ (kbhit() == 0)); if(status == go_flag) /* if 'GO' is not lo, */ return(-1); /* return failure */ else /* else, */ return(l); /* return success */ /* short wt4reply( void ) { short status = 0; show_status( w4_reply ) ; do { /*- wait for either */ status = rd_port_c() δ ibf; /* input buffer full */ wt_n_msec(1) ; }while(!(status == ibf) δδ !(kbhit())); /* or a keypress show_status( 0 ) ; if(status == ibf) return( 0 ) / /* return success */ else return(-1); /* or failure */ } /* SUBSTITUTE SHEET /* Sw_ϋui.C */ /* Sine Wave Calibrator User Interface */ /* Declare the headers for the various libraries */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /include <conio.h> /include <graph.h> /include <malloc.h> /include <stddef.h> /include <stdio.h> /include <stdlib.h> /include <time.h> /include <syε\timeb.h> /include <sys\types.h> /* _ - _ — _ - — - _ _ — _ - — - - - _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * / * /* Include the Sine Wave Calibrator Definitions */ /include H sw_def.h" /* sine wave specific definitions */ /include "dlδx.h" /* dlδx driver definitions */ /include "upia.h" /* upia specific definitions */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Declare Global Variables */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ extern short dac_bfr[]; /* waveform generator data storage */ extern short std_bfr[]; /* flow meter data storage */ extern short mfc_bfr[]; /* flow controller data storage */ extern struct dc dlδc; /* dashlδ control structure */ extern struct sd sw; /* waveform generator control data */ extern struct ccd cbl_data; extern struct csd cbl_sys; extern short dl6d[]; /* dashlβ data storage (16 ch in) */ extern byte udat[]; /* Upia control/data storage */ extern byte cmd_bfr[]; /* command buffer, pc to cable */ extern byte cal_bfr[]; /* calibration data cable to pc */ /* Prototypes for the keyboard library functions */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * int kbhit( void ); int getch( void ); * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Prototypes for text/graphics library functions */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * SUBSTITUTE SHEET short setpixel( short x, short y ); short far _floodfill(short x, short y, short boundary) ; short far __lineto(short x, short y ); short far _rectangle(short control, short xl, short yl, short x2, short y2 ) ; short far _getcolor(void); short far _setcolor( short color ); short far _gettextcolor(void) ; short far _settextcolor( short index ); short far _setvideomode( short vmode ); struct rccoord far _gettextposition(void); struct rccoord far settextposition( short row, short column ); /* _ — _ _ _ _ _ _ _ _ _ - _ - - _ - -**_ _ _ _ _ _ _ _ _ */ /* Prototypes for process control library functions */ /* _ _ _ _ _ _ _ _ _ _ _ - - _ - _ _ -**- _ _ _ _ _ _ _ _ void exit(int status); void ftime(struct timeb *timeptr); time t time(time t *timeptr); /* - */ /* Local Function Prototypes */ * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* Top level menu / editing functions short menu( short ); /* the menu returns a command */ /* Display functions void blank_all( void ) ; void blank_cal_dpy( void ); void blank_com_dpy( void ); void blank nenu( void ); void blank status( void )/ void cbl_r_g_dpy(short, short, short, double)/ void dac_nul_dpy( short, short, double, double ) / void dpy_init( void ) y /* initializes the display δ menu */ void init_graph( void ) / /* displays the target waveform */ void ofst_nul_dpy( short )y void rst_dpy( void )y /* resets display to default mode */ void show_error( short ) y /* display an error message */ void show_upia_error{ void ) y void show_status( short ) y /* display a status message */ void sp_tbl_dpy( short )/ /* show the setpoint/address table */ /* Keyboard functions */ short get_fun_key( void ) / /* fetches a function key command */ void wt4key( void ) / /* Press any key... and wait */ /* Miscellaneous functions void wt n msec( word ) / /* delay to the nearest 10 mS /* - _ Γ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ */ */ SUBSTITUTE SHEET / • * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ -**- _ _ _ _ - _ _ - _ - _ - - */ /* These are the Top level menu / editing functions /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ */ short menu( short init_flag ) { short cmd_code = Oy /* init the command code to 0 if(unit flag) /* if not initialized dpy_inιt()y /* init the display do ■ £ cmd_code = get_fun_key()y /* get a command >while(icmd_code)y return( cmd code )y /* */ /* These are the display functions /* */ void dpy_init( void ) { char far *buffery char cbuf[80]y size_t image_sizey short txt_rows = 0, n = Oy short x = 0, y = 0, vdo mode = VRESlδCOLORy txt_rows = _setvideomode( vdo_mode ) if ( itxt rows ) _settextposition( 2, 61 ); printf( "Video Init Error }; _setcolor( 11 ); */ _rectangle( GBORDER, 0, _setcolor( 9 ); */ _rectangle( _GBORDER, 1, _rectangle( _GBORDER, 2, _setcolor( 12 ); _rectangle( _GBORDER, 3, _rectangle( jGBORDER, 3, 3, 399, 255 ), _rectangle( jGBORDER, 3, 255, 399, 476 ); _rectangle( _GBORDER, 399, 3, 636, 255 ); _rectangle( _GBORDER, 399, 255, 636, 476 ); _settextcolor( 14 ); _settextposition( 2, 51 ); sprintf( cbuf, " Sine Wave Calibration " ) . _outtext( cbuf ); _settextposition( 17, 51 ) sprintf( cbuf, " MENU " ) ; _outtext( cbuf ); _settextposition( 18, 51 );printf(" " ) ; SUBSTITUTE SHEET _settextposition( 19, 51 );printf(" _settextposition( 20, 51 ) / printf(" Change Cal. Page _settextposition( 21, 51 ) / printf(" Change Cycle Time _settextposition( 22, 51 ) / printf(" Start Calibration _settextposition( 23, 51 ) / printf(" Change Max Setpoint _settextposition( 24, 51 ) / printf(" Change Min Setpoint _settextposition( 25, 51 ) / printf(" Toggle Cal / Test Mode _settextposition( 26, 51 );printf(" F 8 _settextposition( 27, 51 );printf(" F 9 _settextposition( 28, 51 );printf(" F10 Exit to DOS _settextposition( 29, 51 )/printf(" _settextposition( 2 , 3 ) / printf(" _settextposition( 3, 3 );printf(" _settextposition( 4, 3 printf("Maximum Setpoint =%4.0f%s",(100. * sw.Vmax / 5.)," %"); _settextposition( 5, 3 printf("Minimum Setpoint =%4.0f%s",(100. * sw.Vmin / 5.)," %"); _settextposition( 6, 3 printf("Cal. Cycle Time =%4.0f%s",sw.Tcyc," S"); _settextposition( 7, 3 ) / printf("Calibration Page =%4d ",sw.gas_page) / _settextposition( 8, 3) if(sw.cal_flag) printf("Calibrate Mode else printf("Flow Test Mode "); /* */ void show_error( short err_dpy_select ) { char cbuf[80)y short wt_flag=0/ _settextcolor( 12 )/ /* it's red, it's bad _settextposition( 3, 52 ) / /* put it in the message area switch( err_dpy_select ) { case erase_error_dpy: sprintf( cbuf, " ); _outtext( cbuf ) / break/ case upia_init_error: sprintf( cbuf, "UPIA Init Error _outtext( cbuf ) / wt_flag = true/ break/ case cmd_com_error: sprintf( cbuf, "Communications Error _outtext( cbuf ) / breaky case rxd_csm_error: sprintf( cbuf, "RxD Checksum Error " ); _outtext( cbuf ); break; case com_tmo_error: sprintf( cbuf, "Communications Timeout Error" ); _outtext( cbuf ); break; case cbl_nul_error: sprintf( cbuf, "Offset Null Error " ); _outtext( cbuf ); wt_flag = true; break; case dlδ_error: sprintf( cbuf, "D16 Interface Error %4d ",dl6c.e_flag ); _outtext( cbuf ); wt_flag = true; break; case cf_calc_error: sprintf( cbuf, "Correction Factor Error " ); _outtext( cbuf ); wt_flag = true; break; case dl_comm_error: sprintf( cbuf, "Download Comm. Error %02x ",cal_bfr[6] ); _outtext( cbuf ); wt_flag — true; breaky case rx_phase_error: sprintf( cbuf, "RxD Phase Error " )y _outtext( cbuf ); break; default: sprintf( cbuf, "Calibration System Error " ); __outtext( cbuf ); wt_flag = true; break; > if( wt_flag ) { wt4key(); _settextposition( 3, 52 ); /* put it in the message area */ sprintf( cbuf, " " ); _outtext( cbuf ); } _settextcolor( 14 ); /* it's back to yellow */ } / ** _ _ _ _ _ _ _ — _ — _ _ _ _ _ — _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void show upia error( void ) char cbuf[80]; short wt_flag = 0, old_color = 0; old_color = _settextcolor( 12 ); /* if it's red, it's bad */ _settextposition( 3, 52 ); /* put it in the message area */ switch( udat[upia_error] ) { case 0: sprintf( cbuf, " " ); _outtext( cbuf ) y breaky case 1: sprintf( cbuf, "UPIA Initialization Error " ); _outtext( cbuf ); wt_flag = true; break; case 2: sprintf( cbuf, "Invalid Upia Command " ); _outtext( cbuf ) ; wt_flag = true; break; case 3: sprintf( cbuf, "Invalid PIA Mode Command " ); _outtext( cbuf ) ; wt_flag = true; break; case 4: sprintf( cbuf, "Upia Command / Mode Mismatch" ); _outtext( cbuf ) ; wt_flag = true; break; case 5: sprintf( cbuf, "RxD / TxD Timeout«Error " ); _outtex ( cbuf ) ; break; case 6: sprintf( cbuf, "Invalid Select Bit " ); _outtext( cbuf ) ; wt_flag = true; break; case 7: sprintf( cbuf, "Invalid Bit Port " ) / _outtext( cbuf ) / wt_flag = truey break / case 8: sprintf( cbuf, "No Board or Not Upia " ) / _outtext( cbuf ) / wt_flag = true/ break/ default: sprintf( cbuf, "Calibration System Error " )/ _outtext( cbuf )y wt_flag = truey break/ } if( wt_flag ) { wt4key()y _settextposition( 3, 52 )y /* put it in the message area */ sprintf( cbuf, " " )y _outtext( cbuf )y > _settextcolor( 14 )y /* it's back to yellow */ } * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void show_status( short sel_status_dpy ) /* display a status message */ { char cbuf[80]y _settextcolor( 10 ) y /* it's green, just info dudes */ _settextposition( 4, 52 )/ /* put it in the status area */ switch( sel_status_dpy ) i case 0: sprintf( cbuf, " " ); _outtext( cbuf ); break; case upsi_reset: sprintf( cbuf, "Resetting UPSI Board... " ); _outtext( cbuf ); break; case cable_reset: sprintf( cbuf, "Resetting Smart Cable... " ); _outtext( cbuf ); break; case w4_reply: sprintf( cbuf, "Waiting for reply... " ); _outtex ( cbuf ); break; case offset_null: sprintf( cbuf, "Nulling MFC/Cable Offset " ); _outtext( cbuf ); break; case addr_init: sprintf( cbuf, "Initializing Address Table " ); _outtext( cbuf ); break; default: sprintf( cbuf, " " ) _outtext( cbuf ) ; break; _settextcolor( 14 ); /* it's back to yellow } /' .**- void cbl_rdg_dpy( short row, short col, short chnl, double c_volts ) { _settextposition( row, col ); printf( "mux %02x", chnl ); _settextposition( row, col + 8 ); printf( "%5.3f", c_volts ); } /* - _ _ _ - _ _ _ - - - _ - _ - - - -**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ void dac_nul_ipy( short vmax, short vmin, double cv_avg, double sfac) { _settextposition( 8, 4 ); printf( "max reading : %+5.3f", (vmax * sfac) ); _settextposition( 9, 4 ); printf( "min reading : %+5.3f", (vmin * sfac) ); _settextposition( 10, 4 ); printf( "noise level : %+5.3f", ((vmax-vmin)* sfac)); _settextposition( 11, 4 ) / printf( "cable output : %+09.6f", cv_avg ) / } * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void sp_tbl_dpy( short tbl_ndx ) /* show the setpoint/address table */ { short old_colory double Vsetpt, Vcabley double set_sfac = (double) ( 10. / 4095. )y if(tbl_ndx == 1) { old_color = _setcolor( 12 )/ _rectangle( _GBORDER, 150, 25, 360, 310 ) / _setcolor( 0 )y _rectangle( _GFILLINTERIOR, 161, 26, 359, 309 )/ _setcolor( old_color ) / _settextposition( 3, 24 ) / printf( "Setpoint Address" )y Vsetpt = ( (double)cbl_data.sp_adr_tbl[0] [0] * set_sfac) / _settextposition( 19, 25 ) / printf( "%5.3f",Vsetpt ); _settextposition( 19, 37 )y printf( "%04x", cbl_data.sp_adr_tbl[l] [0] )y } Vsetpt = ((double)cbl_data.sp_adr_tbl[0][tbl_ndx] * set_sfac)y _settextposition( 19 - tbl_ndx, 25 )y printf( "%5.3f",Vsetpt )y _settextposition( 19 - tbl_ndx, 37 ) printf( "%04x", cbl_data.sp_adr_tbl[1] [tbl_ndx] )/ > /* — — _ — _ - _ — _ _ — - — _ - - - _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ * / * void ofst_nul_dpy( short d_switch ) { if(d_switch ) { _settextposition( 5, 52 )/ printf( "Offset is nulled " )y } else { _settextposition( 5, 52 )y printf( " " )y } > * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void blank_all( void ) -I blank_cal_dpy() / blank_com_dp () / blank_menu()/ blank_status() / y /* — — — _ — _ _ _ _ _ _ — — _ - _ _ —**— _ _ _ _ — _ _ _ _ — _ _ _ — — */ void blank_cal_dpy( void ) i short old_colory /* blank the calibration area */ old_color = _setcolor( 0 ) _rectangle( _GFILLINTERIOR, 4, 4, 398, 254 )y _setcolor( old_color )y } * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void blank_com_dpy( void ) { short old_colory /* blank the comm display area */ old_color = _setcolor( 0 )y _rectangle( GFILLINTERIOR, 4, 256, 398, 475 )y _setcolor( old_color ) y > /* _ — — — _ _ — - — _ — — _ — — — _ -**- — _ _ _ — — _ _ - — _ — _ _ _ */ void blank_menu( void ) { short old_color / /* blank the menu area */ old_color = _setcolor( 0 )/ _rectangle( _GFILLINTERIOR, 400, 256, 635, 475 )/ _setcolor( old_color )y } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ - - - */ void blank_status( void ) { short old_colory /* blank the status display */ old_color = _setcolor( 0 ) / _rectangle( _GFILLINTERIOR, 400, 4, 635, 254 )/ _setcolor( old_color ) / } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void rst_dpy( void ) { _setvideomode( DEFAULTMODE )/ } /* - - _ - - - - - _ _ _ _ _ _ _ _ _ _**- _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* This is the start of the calibration graphics display area * _ _ — _ — — _ _ _ - _ _ _ _ _ — _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ void init_graph( void ) { char cbuf[80]y short xpos, ypos, old_colory word dndx double h_scale, v_εcale; old_color = _setcolor( 0 )y _rectangle( _GFILLINTERIOR, 3, 3, 636, 476 )y _setcolor( 7 ) _rectangle( _GB0RDER, 64, 35, 576, 445 ) h_scale = (double) ( 1. / ( (double)dlδc.smpl ent / 512.) )y v_scale = (double) (410. / 4096. )y ~ for(dndx = 0 / dndx < 10 / dndx++) { ypos = (short) ( 35. + (v_seale * 409.6 * (double)(dndx))) / _moveto( 64, ypos) _lineto(576, ypos)y } _setcolor( old_eolor ) / /* restore color */ old_color = _settextcolor(7)/ _settextposition(2,62) / sprintf( cbuf, "Set ")y _outtext(cbuf) _settextcolor(14)y _settextposition(2,66)/ sprintf( cbuf, "STD ")/ _outtext(cbuf) / _settextcolor(10) / _settextposition(2,70) / sprintf( cbuf, "MFC")/ EET _outtext(cbuf); _settextcolor(old_color) ; /* restore color */ } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ - _ _ _ _ _ — _ _ — _ — — * / * /* This is the start of the keyboard functions area /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ /* get_fun_key fetch a function key command */ short get_fun_key( void ) { short inkey = 0; do { inkey = getch(); if( (inkey == 0) | j (inkey=0xe0) ) { inkey = getch(); inkey += 300; } else { printf("\07"); } } while (inkey < 300); return( inkey ); } /* _ - _ - _ _ _ _ _ - _ - - _ _ - _ -**- _ - _ _ - _ _ _ _ _ _ _ _ _ _ */ void wt4key( void ) { _settextposition( 29, 52 ); printf( "Press any key... " ); while( !(getch()) ); _settextposition( 29, 52 ); printf( " " ); } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ void wt_n_msec( word n ) { struct timeb start_time, elapsed_timey ftime( δstart time ); do ■ C ftime( δelapsed_time ); if(elapsed_time.millitm < start_time.millitm) start_time.millitm = elapsed_time.millitm; }while((elapsed_time.millitm - start_time.millitm) < n ); } / *. /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ */ /* Declare the headers for the various libraries */ /* _ _ — _ _ _ - _ _ — _ — - — - - - -**_ _ _ _ _ _ _ /include <conio.h> /include <graph.h> /include <malloc.h> /include <stddef.h> /include <stdio.h> /include <stdlib.h> /include <time.h> /include <sys\types.h> /include <sys\timeb.h> /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**- _ _ _ _ _ _ */ /* Prototypes for text/graphics library functions */ /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ */ short _setpixel( short x, short y )y short far _setcolor( short color ) y struct rccoord far _settextposition( short row, short column ) ; time_t time(time_t *timeptr); void ftime(struct timeb *timeptr) ; /* .**. */ /* Include the Sine Wave Calibrator Definitions */ /* */ /include "sw_def.h" /* sine wave specific definitions */ /include "dl6x.h" /* dlδx driver definitions */ /include "upia.h" /* upia specific definitions */ /* - - -**- */ /* Declare Global Variables */ /* - - */ extern short dac_bfr[]; /* waveform generator data storage */ extern short std_bfr[]; /* flow meter data storage */ extern short mfc_bfr[ ]; /* flow controller data storage */ extern struct dc dlδc; /* dashlδ control structure */ extern struct sd sw; /* waveform generator control data */ extern struct ccd cbl_data; extern struct csd cbl_sysy extern short dlδd[]/ /* dashlδ data storage (16 ch in) */ extern byte udat[] / /* Upia control/data storage */ extern byte cmd_bfr[]/ /* command buffer, pc to cable */ extern byte cal_bfr[] / /* calibration data cable to pc */ /* - */ /* External Functions */ * . */ extern void dlδx( struct dc * ) / extern short rev bfr( short ) / /* receive a data packet */ extern byte rd_port_a( void )y /* reads port a directly */ extern byte rd_port_c( void ); /* reads port c directly */ extern void blank_all( void )y extern void blank_cal_dpy( void )y extern void blank_com_dpy( void )y extern void blank_menu( void )y extern void blank_status( void )y extern void cbl_rdg_dpy(short, short, short, double)y extern void dac_nul_dpy( short, short, double, double )y extern void ofst_nul_dρy( short )y extern short send_command( byte, byte, byte, byte, byte, byte ) extern void show_error( short )y /* displays an error message */ extern void show_status( short ). /* display a status message */ extern void sp_tbl_dpy( short ) / /* show the setpt/addr table */ extern void stop_cal( void ) / /* stop intr activities */ extern void wt4key( void )y /* Press any key... and wait */ extern void wt_n_msec( word )y /* delay to the nearest 10 mS */ extern short wt4reply( void ) /* wait for ibf or keypress */ extern byte rd_byte( void ) / /* reads a byte mode 2 */ extern byte wr byte( byte ) / /* writes a byte mode 2 */ */ /* Local Function Prototypes */ short dpy_sample( void )/ short calc_correction(double, short )/ short fill_table( void ) / short tx_table( void ) short dpy_sample( void ) short status, avg_cnt, var_loc; short vloc, xpos, ypos, old_color; short set_val, std val, mfc val, err val; double h scale, v_scaley double std_tmp, mfc_tmp; status = 0; h_scale = (double) ( 1. / ((double)dl6c.smpl_cnt / 512.)); v_scale = (double) (410. / 2048.); dl6c.smpl_ndx = std_bfr[dl6c.smpl_cnt]; /* get last reading's index */ vloc = (short)dlδc.smpl_ndx - sw.Tlag; /* var location adj for lag */ if(vloc < 0) vloc += dlδc.smpl_cnt; /* handle underflow in xpos xpos = (short) ( 64 + (vloc * h_scale)); std_tmp = 0; mfc_tmp = 0; set_val = dac_bfr[dl6c.smpl_ndx] / 2; for(avg_cnt = 15; avg_cnt >= 0; avg_cnt—) • C if((vloc - avg ent) < 0) SUBSTITU var_loc = dlδc.smpl_cnt - avg_cnt; else ~ " var_loc = vloc - avg_cnt; std_tmp += std_bfr[var_loc]; mfc_tmp += mfc_bfr[var_loc]y } std_val = (short)((std_tmp / 16)- sw.std_ofst)y mfc_val = (short) ( mfc_tmp / 16) err_val = std_val - mfc__valy old_color = _setcolor(7j; /* display the setpoint */ ypos = (short) (445 - (set_val scale) ); _setpixel(xpos, ypos)y _setcolor(14); /* display the std data */ ypos = (short) (445 - (std_val * v scale) ); _setpixel(xpos, ypos); _setcolor(10); /* display the mfc data ypos = (short) (445 - (mfc_val v_scale) ); _setpixel(xpos, ypos) ; _setcolor(ll) ; ypos = (short) ( 76 - (4 * err_val * v_scale)); /* error data display _setpixel(xpos, ypos) ; _setσolor(old_color); return(status); } /* */ /* Fill the 512 byte array with coarse / fine adjust data using the */ /* cbl_data.sp_adr_tbl for alignment. From 0 to Vmin, linear slope. */ /* From Vmin to Vmax, slope based on acquired data. */ /* */ short fill_table( void ) { short tbl_ndx = 0, cf_step 0, σ_fac = 0, target 0; short spt_ndx = 0, sp_step 0, cur_sp = 0, nxt_sp 0; short cur_adr = 0, nxt_ad 0, tst sp = 0, vloc = 0; short row = 0, col 0; short sm_bnd = 0, lp_cnt 0; double cbl_sfac •- (double) ( 5. / 1023. ); double spt_sfac • ■ (double) ( 6. / 1023. ); double setpoint -= 0., std_flo = 0., mfc flo - 0.; double Verror ■ 0. , Ve_step = 0.; double flo_sum ■ 0. , avg flo = 0., avg_cnt = 0. double mfc sum = 0. , avg mfc = 0.; vloc = (short)0 - sw.Tlag; /* var location adj for lag */ if(vloc < 0) vloc += dl6c.smpl_cnt; /* handle underflow in xpos */ setpoint = (dac_bfr[0] * dac_sfac); /* get the setpoint, */ std_flo = ( (std_bfr[vloc] - sw.std_ofst) * flo_sfac); /* std flow */ mfc_flo = (mfc_bfr[0] * flo_sfac); /* δ mfc flow as volts */ Verror = std_flo - mfc_flo; /* flow error in volts */ target = cbl_data.sp_adr_tbl[l] [1]; /* 1st test item in tbl */ SUBSTITUTE SHEET ve_step = verror / (double) (target - 7); /* 1st 8 Iocs dac null */ if(sw.cal_pass —•—■ 0) { Verror -*- * Ve_step; /* init Verror for ramp */ for(tbl_ndx = 8; tbl_ndx <= target; tbl_ndx++) /* fill nul to Vmin*/ { * with ramping corfac, */ c_fac = calc_correction(Verror, 0); /* dacnul - corfacl */ cbl_data.corfac[tbl_ndx] = c_fac; /* put it in corfac tbl */ Verror += Ve_steρ; /* bump the error */ } } spt_ndx = 0; tbl_ndx = 1; sm_bnd = 8; do { cur_sp = cbl_data.sp_adr_tbl[0][tbl_ndx]; nxt_sp = cbl_data.sp_adr_tbl[0] [tbl_ndx + 1]; cur_adr = cbl_data.sp_adr_tbl[l] [tbl_ndx]; nxt_ad = cbl_data.sp_adr_tbl[l][tbl_ndx + 1]; sp step = ((nxt_sp - cur_sp) / (nxt_ad - cur_adr)); do )) cur_adr = cbl_data.sp_adr_tbl[l] [15] - 1; SUBSTITUTE SHEET c_fac = cbl_data.corfac[cur_adr] ; for(tbl_ndx = cur_adry tbl_ndx < 256 tbl_ndx ++) cbl_data.corfac[tbl_ndx] = cbl_data.corfac[cur_adr]y return(1)/ } * — — — — _ — — — _ — — — — — — — — —**— _ _ _ _ _ _ _ _ _ short calc_correction(double Verror, short tndx) { static short oldx, oldy/ short old_color = 0, vpos = 0, hpos = 0 / short c_factor = 0, cfh = 0, cfl = 0, eft = 0/ short fti = cbl_data.sρ_adr_tbl[1] [1]y short Iti = cbl~data.sp_adr_tbl[1] [15]/ double Veorr = 0. / double Vpwhi = (double) (2.5 / 255.) / double Vpwlo = (double) (Vpwhi * 24900. / 6190000.) / if(Verror < 0) { cfh - (short) (.5 + (( -1 Verror) / Vpwhi))/ cfl = (short) (.5 + (((-1 Verror) - (cfh * Vpwhi)) / Vpwlo)) / if(sw.cal_pass == 0) { cfl += 0x80 / cfh += 0x80 / } else { cfl = ( (cbl_data.corfac[tndx] δ Oxff) + cfl)y cfh = (j[(cbl_data.corfac[tndx] δ OxffOO) » 8) + cfh), } } else { cfh = (short) (.5 + (Verror / Vpwhi) )y cfl = (short) (.5 + ((Verror - (cfh * Vpwhi)) / Vpwlo)) if(sw.eal_pass == 0) { cfh = 0x80 - cfh/ cfl = 0x80 - cfl/ } else { cfl = ((cbl_data.corfac[tndx] δ Oxff) + cfl)/ cfh = (((cbl_data.corfac[tndx] δ OxffOO) » 8) + cfh) } } if(cfl > 255) { cfh += 1 / cfl -= (short) (Vpwhi / Vpwlo)/ } if(cfl < 0) i cfh -= ly cfl += (short) (Vpwhi / Vpwlo)y } if((cfh > 255) ϋ (cfh < 0)) { cfh = cbl_sys.dac_nul_hiy /* error in cor fac */ show_error(cf_calc_error) } Vcorr = (cfh - cbl_sys.dac_nul_hi) * Vpwhiy Vcorr += (cfl - cbl_sys.dac_nul_lo) * Vpwlo; vpos = (short) (76 + ( Vcorr / flo sfac)); hpos = (short) (64+(short) ((I i-fti)*(((double)tndx-(double)f i)/(lti-fti)))); if(tndx == fti) • C oldx = hpos; oldy = vpos; } if(!(oldx < 64) δδ !(hpos < 64)) { old_color = _setcolor(12); _setpixel(hpos, vpos); _setcolor(old_color); } oldx = hpos; oldy = vpos; c_factor = (cfh « 8) + cfl; return(c_factor); } /* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _**_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ */ short tx_table( void ) ■ C byte dbuf[ 0x50 ]; /* 0x50 = max packet size */ byte pkt_cnt = 0x08, pkt_siz = 0x43, addr_lo = 0x00; byte addr_hi = (byte) (0x2a + (2 * cbl_data.pg_addr) ); short lp_cnt = 0, status = 0, tbl_ndx = 0; short row = 0, col = 0; _setcolor( 0 ) ; _rectangle( _GFILLINTERIOR, 4, 4, 635, 475 ); status = send_command( 0x09, pkt_siz, pkt_cnt, addr_hi, addr_lo, 0 ); if(! (status == 0)) return(-1); while (pkt_cnt) i status = rcv_bfr( 8 ); if(! (status == 0)) return(-1) ; if( 1 (cal_bfr[3] == pkt_cnt)) { show_error(dl_comm_error); } lp_cnt = 0; dbuf[lp_cnt++] = (byte)0x02; /* send stx */ dbuf[lp_cnt++] = (byte)0x09; /* and the command */ dbuf[pkt_siz -1] = dbuf[0] + dbuf[l]; /* init the cksum */ do » 8); { wr_byte( dbuf[ lp_cnt ]; if((lp_cnt > 1) δδ (lp_cnt < (short) (pkt_siz - (byte)l))) { row = 4 + ( ( lρ_cnt-2 ) %8 ) ; col = 26 + ((( lp_cnt-2 ) »3 ) *4 ); _settextposition( row, col ); printf( "%02x", dbuf[ lp_cnt ] ); } status = rd_byte(); if((lp_cnt . 1) δδ (lp_cnt < (short) (pkt_siz - (byte)l))) { row = 4 + ( ( lp_cnt-2 ) %8 ); col = 26 + ((( lp_cnt-2 ) »3 ) *4 ); _settextposition( row, col ); printf( "%02x", dbuf[ lp_cnt ] ); } }while( (lp_cnt == 0) δδ (status != 0x02)); } pkt_cnt—; wt_n_msec(20); } status = rcv_bfr( 8 ); /* now receive the last reply */ if( ! (status == 0)) return(-1); if( 1 (cal_bfr[3] == pkt_cnt) ) { show_error(dl_comm_error) ; } return(0); } / • cbl a.s03 smart cable with C startup code and hooks for diagnostics Startup code and assembly language functions for the smart cable Author :Michael Upchurch Version : 1.00 beta 9102.22 Revision History Date Remarks 9102.14 Revised old cable software to add C startup code and test hooks 9102.14 Added i2c start flags to improve serial i/o 9102.22 Release Version 1.00 beta NAME cable_init ;always load this $d:\zzz_incs\c51_v04\defmm.inc ;medium mem model $d:\zz__incs\c51_v04\io_552.inc ;I/0 port and other definitions PUBLIC cable_init ;It starts there PUBLIC sw reset PUBLIC process_mode PUBLIC rx_count PUBLIC stx_flag PUBLIC cmd_flag PUBLIC rx_flag EXTERN ?C_EXIT ;Where to go when program is done EXTERN _R ;Reg Bank Select, we let C use RBO EXTERN main yEntry to C routines Allocate the bit variable storage These constants allow byte mode access to the bit variable areas. If you add more bits beyond the spares, you will need to modify the constants. system_status: equ 02Oh System Status Bits process_mode: equ 02lh yProcess Mode timer_status: equ 022h ;System Timer Status Bits gp_flags: e«qu 023h ygeneral purpose flags 1 = true yValid stx rcvd from PC yValid command rcvd from PC yready to transmit yready to receive yTransmit checksum error? Checksum error on rcvd data packet yCal page boundary error Cal Data Error ds bytes didn't compare during DownLoad Flow control mode Calibration mode Calibration test mode Download data from PC Upload data to PC Init the system area commanded to reset Extended cal/diagnostic mode 250 uS flag 1 mS 10 mS 1 Sec 1 Min 1 Hour " spare spare system area in EΞPROM is valid selected cal page is ok cal mode, first time thru spare start master receive state passed start master transmit state passed start slave receive state passed start slave transmit state passed these go in the direct access area Storage for current command Corr. Factor Base Addr, Hi byte -> the Tx buffer -> the Rx buffer Tx Byte Count Rx Byte Count data packet size data packets to rx slave address and direction checksum byte count storage for cksum Hi Addr Byte for Up/Down load Lo Addr Byte for Up/Down load storage for outbound pwm hi byte ditto lo byte local dac null value hi ditto lo number of samples to go hi byte of sample total lo byte of sample total SUBSTITUTE SHEET εample average: ds ;lo byte of sample average ;these go in the indirect access area ;put the misc. analogs here ;buffer for commands from PC / buffer for data to/from PC ;dif names, same space, dif functions setpt_buffer equ com_buffer+4 / flow mode setpoint sample buffer c ana buffer equ com buffer+4 ycalibrate mode analog buffer Allocate 64 bytes for the Stack RSEG CSTACK Stack_Begin: ds 040h yallocate 64 bytes at top of ram Reset Vector Code segments start here RSEG CSTART yC's startup segment ycable_init: jmp cable start interrupt vectors for the Signetics 8XC552 ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp ds ljmp Copyright notice and Version number copyright: db Copyright @ 1990, 1991 db Unit Instruments, Inc. db All Rights Reserved version number db Version: 1.00 Beta, 9102.14 cable_start, reset vector target Initialize memory and the I/O ports, then read the selector switch. If the switch setting is valid we'll run in 'Flow Mode'. Otherwise, we'll wait for either the switch to be changed to a valid setting, or for a command to come in over the IIC bus. ;Segment for relocatable code ;init the PWM prescaler now / use default value for approx ;midscale, til we're init'd /make sure it's register bank 0 / Init RAM, point at the top /clr the ace, we'll put zeroes y@r0 till rO = 0, at which / point we fall thru with all / of memory init'd. / disable all interrupts / init the stack, warm start yck the sys area of the EEPROM ylnit ports, status, etc yThis is the top level loop ycheck for valid switch setting yif cal mode, go thru flow test entry yinit regs for entry to C / no parameters ;main() yif return val from c is zero, y take c exit yelse, m 5. SUBSTITUTE SHEET • jmp check_command y check for a command mllO: setb flow_mode e'll do this unless we get a jmp FlowMode / command. ml20: jmp FlowTest ygo to the test entry FlowTest commanded to calibration page from outside FlowMode flow mode from switch setting ySio command entry to flow mode yelse flag valid command ycalibration test L ysave it yget the cal page to use yno carry / check if page > 10 / no, continue with the test / else, flag the error ythis is the kind of error / flow test error bad page status / send the errors to 'em + 2 / save the base address ase / set up tx_buffer / for echo /send back 8 bytes / checksum on 7 bytes ydo a checksum yready to send ywait for transmit done ycheck for errors yRx or Tx yskip the next if none yelse, reset Switch setting entry yinit sio in case ythis tells us if a sg came yenable the A2D interrupt yenable all interrupts yif cal test, skip the switches yelse, check the switch again ymask off hi nybble ycomplement makes it a page num / no carry / verify the switch setting is SUBSTITUTE SHEET 32 fmOO /still the same clr flow_mode yelse clear flow mode flag jmp sw_reset yand restart fmOO: mov a,cal_tbl_base /get the page back rl a /multiply it by 2 mov dρtr,/2A00h /pointer to Cal Page Base - ,5k add a,dph /each gas table occupies .5k mov dph,a /put the adjusted addr in dph push dph ysave the address push dpi yon the stack for a moment movx a,©dptr yget null hi byte for this page mov nul_hi,a ystore it inc dptr ybu p the pointer movx a,@dptr yget the low byte mov nul lo,a ysave it too pop dpi yget back the base address pop dph that we saved fmw4d: jnb rx_flag,fm_msg_exit yif message, ck it out mov a,gp_flags ycheck for i2c startup flags *9102.14 anl a,/OfOh ymask off lower 4 bits *9102.14 jnz fm_msg_exit yif i2c start, exit *9102.14 j b mS001,fmw4d yelse, wait for a lmS boundary jnb pinj,fmlO yif Jpin low, adj offset mov sample_tot_hi,/0 yelse, clear the storage area mov s_mple_tot_lo,/0 yfor the total mov rO,/setpt_buffer ypoint at the data mov r2,/4 ythis many samples fm_add_loop: clr c yno carry mov a,@rO yget a byte inc rO /point at the next addc a,sample_tot_lo /add it to the subtotal • jnc fmalOO /if no carry, skip inc sample_tot_hi /else, bump the hi byte fmalOO- mov sample_tot_lo,a /save the subtotal lo byte djnz r2,fm_add_loop /repeat til done nop mov rl,sample_tot_lo yget the lo byte clr c yput a 0 in the carry mov a,sample_tot_hi yget the hi byte rrc a /divide by 2 xch a,rl /swap with rl, lo byte in a rrc a /div by 2, shift in carry clr c xch a,rl /get the hi byte back in a ydiv by 2 again /get back the lo byte /final shift, a has the answer /store it /no carry /multiply by 2 ysave the low pointer skip if no carry /else, upper page /get the hi byte /send the correction /bump the pointer /get the lo byte /send it along yif rcvd message, check it out /else, do flow mode? ;yes, continue to flow ;else, continue to flow ;disable the adc /check the Cmd in the msg /flow mode / cal test exit • jmp sw reset yno mode, no message, restart check command: check for a cmd from the pc ymask off previous cmd flags /set command packet size yno stx rcvd yet or command either /set the rx flag, 'ready to rx' /enable any enabled interrupts /while we wait, if a valid cal page _cmd_exit /shows up in the switch, exit /the command loop, else /wait til the rev process is finished /check the i2c status ywait til it's all done y (bp target) ycheck 7 bytes for validity point at the rx buffer yand the tx buffer yget the count ymove the cmd packet from the yrx buffer to the tx buffer / for echoing / bump the pointers ywe set r2 up earlier ypoint at the rx buffer again yand the tx buffer, for luck ycheck for byte 0 = stx (02h) yif invalid stx, comm error yelse, flag valid stx /bump the pointer /now check command / keep pointing at the buffer yvalid commands are OOh - Ofh ;4 bytes per command ;Acc is offset into the table / dptr points the way ;byte 0 <> stx (02h), reset! ;switch changed? Here is where we calibrate the MFC, we get 8 bytes and send back 16 our command packet is in cmd_buffer, we transmit from com_buffer transmit buffer contains: Stx, cmd, pwhi, pwlo, ad0-ad3, 3d, cksum ;calibration mode yflag valid command yand what kind of cmd 1 ysave the cmd yif not first time, skip ;clear the first time bit yck the hi byte they sent yif zero, no change yelse, get the dac settings yfrom the calibrate packet ytell the pc what we have yin the dac ysend it to the dac yboth bytes ;fill the buffer ;don't check the checksum ;clear the lmS flag / wait for lmS boundary /we'll take the next 4 /enable the A2D interrupt cm30: jnb mS001,cm30 ywait til some data's ready clr ead ygo DI for adc til done clr mSOOl yclear the flag, ready mov a,tx_buffer+2 yget the pointer to the buffer add a,cksum_count ythis is the length dec a / point at the status area mov rl,a / put it in an index register mov @rl,system_status yput the status in the message call TxCkSum ydo the checksum cmldl: setb tx_flag ready to send cmw4tx: jb tx_flag,cmw4tx ywait for transmit done clr calibr_md ysignal we're done jmp check command yget a new command DownLoad: clr ead ydisable the A2D interrupt setb cmd_flag yflag valid command setb down_load ythis is the one mov command,cmd_buffer + 1 ysave the command mov packet_size,cmd_buffer + 2 yget the data packet size mov packet_count,cmd_buffer + 3 / / of data packets to expect mov addr_hi,cmd_buffer + 4 / Hi byte of eeprom address mov addr_lo,cmd_buffer + 5 ylo byte of eeprom address call check_address yCk address boundaries jnb Pg_Bndry_Error,dlOO yskip over if no error jmp dl com error yelse, tell 'em error δ reset dlOO: mov a,packet count yare they sending us any? jnz dl02 ;yes jmp dl com error yelse, tell 'em error & reset dl02: mov rx_buffer + 2,/com_buffer ;the long buffer is for rx mov tx buffer + 2,/cmd buffer ;the short one is for tx mov rO,rx_buffer + 2 ;set up the pointers mov rl,tx_buffer + 2 ;to the buffers mov tx_count,/8 yCO Cl BCt PCt AHi Α o 3d Cksum mov cksum_count,/7 checksum on 7 bytes call TxCkSum ydo a checksum setb tx_flag yready to send dllO. jb tx_flag,dllO ywait for transmit done mov a,system_status yck for errors anl a,/030h ;Rx or Tx jz dl20 yif no error, continue jmp sw reset yelse, restart dl20: mov rx_count,packet_size yset rx_count to packet size SUBSTITUTE SHEET setb rx_flag yflag ready for receive w4dp: jb rx_flag,w4dp yWait for packet rx complete clr c yno carry mov a,com buffer yget the stx byte subb a,/2 ycheck for stx jz dl30 ystx is ok check command jmp sw reset yelse, reset dl30: clr yno carry mov a,com_buffer + ;get byte 2 subb a,/09h ;check for download command jz dl40 ;command ok, do checksum jmp sw reset ;else, reset dl40: mov cksum_count,rx_count ;get the byte count dec cksum_count ;to get the CkSum count call RxCkSum ;check data packet for validity jnb Rx_CkSum_Error,dl50 ;if no error, continue jmp dl com error ;else, send error & reset dl50: call mem_fill ;ok, write it jnb Cal_Data_Error,dlδO ;if no mem error, continue jmp dl_com_error yelse tell'em about the error dlδO: mov rO,rx_buffer + 2 ynow, point at mov rl,tx_buffer + 2 ythe buffers again mov cmd_buffer,/02h yecho the stx mov cmd buffer+l,/09h y and the command mov cmd_buffer+2,packet_size the packet size mov cmd_buffer+3,packet_count and count mov cmd_buffer+4,addr_hi Addr hi byte mov cmd_buffer+5,addr_lo and lo byte mov cmd_buffer+6,system_status the sys status mov cksum_count,/7 set up for tx checksum call TxCkSum ydo it setb tx_flag yReady to send it dl70: jb tx_flag,dl70 ywait for transmit done mov a,system status yck for errors anl a,/030h ;Rx or Tx jz dl72 yif no error, continue jmp sw_reset yelse, restart dl72: mov a,packet_count yget the packet counter jz dl80 yif we're done, back to get cmd jmp dl20 yor keep going til we are done dl80: jmp WarmStart yre-init and start over SUBSTITUTE SHEET SendData: / tx a block of memory clr ead •disable the A2D interrupt setb cmd_flag flag valid command setb up_load ythis one mov command,cmd_buffer + 1 store it mov packet_size,cmd_buffer+2 get the data packet size, and mov packet_count,cmd_buffer+3 / of data packets they want mov addr_hi,cmd_buffer+4 Hi byte of eeprom address mov addr_lo,cmd_buffer+5 lo byte of eeprom address call check_address Ck address boundaries jnb Pg_Bndry_Error,ulOO skip over if no error jmp ul_com_error else tell 'em and reset ulOO: mov a,packet_count yck if they want any packets jnz ul02 yif not, tell 'em error jmp ul_com_error /else tell 'em and reset ul02: clr ena_aack / disable addr ack for now mov dph,addr_hi /set up the pointer mov dpl,addr_lo /for external access mov rl,/com_buffer + 2 ypoint at the data buffer mov a,packet_size yget the count clr c yno borrow subb a,/3 ypkt size - stx, cmd δ cksum mov r3,a y put it in the counter ul04: movx a,@dptr yfetch a byte inc dptr ybump the outside pointer mov @rl,a ystuff it in the buffer inc rl ybump the buffer pointer djnz r3,ul04 ycount it and continue mov a,packet_size ytx packet_size mov tx_count,a ysave it dec a ysubtract 1 mov cksum_count,a ycksum on packet_size -1 bytes call TxCkSum ydo the checksum setb ena_aack yre-enable addr ack setb tx_flag yready to send ullO: jb tx_flag,ullO / wait for transmit done mov a,slsta /check status cjne a,/0f8h,ull0 yloop if not done nop mov a,system_status yck for errors anl a,/030h ;Rx or Tx jz ul20 yif no error, continue jmp ul_com_error yelse tell 'em and reset ul20: mov a,packet_count count 1 packet dec a yjust 1 jnz ul30 ymore to go? SUBSTITUTE SHEET cr : jb tx flag,cr00 ywait here til tx is done SUBSTITUTE SHEET yget the status yif not f8, not done ydo what they wanted us to do yck that the address we rcvd ylS somewhere in data memory, ynot vacant memory yif carry, upper boundary ok yelse, flag upper bound error ylocally also ydon't check lower yget it back yget rid of the carry ycheck the lower boundary if no carry, lower bound ok yelse flag lower bound error local error flag yassume it's no good / get number of bytes to check yset up counters, hi and low, ysystem area can be>256 bytes ypoint at the bottom ystart with a zero sum / temp storage / fetch a byte / add the previous result / store the new sum /bump the pointer / if lobyte - 1 <> 0, loop yelse check the hi byte yif zero, we're almost done yelse, dec hi byte and loop yput it back ycontinue yget the checksum byte ysave it yclear the carry ysubtract the sum yequal, set the valid bits yelse, clr the gp flags ydepart SUBSTITUTE SHEET ythe checksum is valid ;4 samples yget the checksum again yback to startup ycheck, switch = valid cal page? yread the switch, (*** OFF = 1 ***) ymask off upper nybble yif not Ofh, check for valid page yif cal test mode, exit yelse clear the flag yfall out / complement makes it a page number /save the switches /no carry /check upper bound /if <=10, ck lower bound yelse, not valid cal page yget back, valid page (0-10) ysave the page yset the bit mem_fill Transfer the contents of a data packet to the eeprom address specified in the command packet. For version 2, implement 'data barred' polling to allow the use of the 28c64 as well as the 28c65. In addition, the block transfer should be limited to 32 bytes to allow use of Samsung as a secomd source. ywait til mem is thru before writing + 2 yget the buffer address yskip the stx/cmd yput it in a pointer yand save it on the stack yget the address pointer yfrom where it was saved yget the byte count yno borrow ydon't move the Cmds or CkSum ythis is where we'll use it fetch it from inside ybump the inside pointer csm20: movx @dptr,a ystuff it to the outside inc dptr ybump the outside pointer djnz r2,mfl0 yloop til done mf20: jnb RdyBsy,mf20 ywait til mem is thru before reading pop ace yget the buffer address again mov r0,a / point at the start of the data mov dph,addr_hi / get the address pointer mov dpl,addr_lo / from where it was saved mov a,rx_count / get the byte count clr c / no borrow subb a,/3 / didn't move the Cmds or CkSum mov r2,a mf30: movx a,@dptr / fetch it from the outside clr c /no carry subb a,@r0 / compare it to the inside jnz mfex / if not equal, handle the error inc dptr / else, bump the outside pointer inc rO y and the inside pointer djnz r2,mf30 yloop mov addr_hi,dph ysave the pointer to the next byte mov addr_lo,dpl dec packet count ycount 1 data packet gone mf40: ret yget some more? mfex: ymem dump error exit setb Cal_Data_Error yflag the error sjmp mf40 yexit Error handlers go here as implemented, minimal is jump to warm start dl_com_error: mov rx_buffer + 2,/com buffer ythe long buffer is for rx mov tx_buffer + 2,/cmd^buffer ythe short one is for tx mov rO,rx_buffer + 2 yset up the pointers mov rl,tx_buffer + 2 yto the buffers mov cmd_buffer+2,/0 ycmd_buffer is tx_buffer, mov cmd_buffer+3,/0 yset to error condition mov cmd_buffer+4,/0 yby clearing the buffer, mov cmd_buffer+5,/0 yso they know it was bad mov cmd_buffer+6,system ^ status yput the flags in the message mov tx_count,/8 yCO Cl BCt PCt AHi ALo 3d Cksum mov cksum_count,/7 checksum on 7 bytes call TxCkSum ydo a checksum setb tx_flag yready to send dlceOO: jb tx_flag,dlceOO ywait for tx finished jmp sw reset yrestart ul com error: mov com_buffer+2,/0 yset tx buffer to error cond mov com_buffer+3,/0 mov com_buffer+ ,/0 yby clearing the buffer, mov com_buffer+5,/0 yso they know it's bad mov com_buffer+6,system_status / put the flags in the message mov tx_count,/8 / CO Cl BCt PCt AHi ALo 3d Cksum mov cksum_count,/7 ychecksum on 7 bytes call TxCkSum ydo a checksum setb tx flag yready to send ulceOO: jb tx_flag,ulceOO ywait for tx finished jmp sw reset yrestart Checksum calculation routines, for Rx and Tx RxCkSum: yrO δ r2 (RBO) destroyed push psw ysave some registers push ace mov rO,rx_buffer + ypoint at the buffer mov r2,cksum_count rx_count-l clr Rx_CkSum_Error yassume no error clr a yset up for CkSum rcslO: add a,@r0 yadd it (W/O Carry!) inc rO / bump the pointer djnz r2,rcsl0 mov checksum,a /stuff it here for now clr c / don't borrow subb a,@r0 /compare the checksums jz rcs20 yif equal, OK setb Rx CkSum Error yelse flag error rcs20: ylet the caller handle errors pop ace pop psw yrestore the registers ret rx checksum error handler, when ucal-x200 s/w ready to handle it! TxCkSum: push psw ysave some registers push ace calculate the cksum mov rl,tx_buffer + 2 yto be transmitted mov r3,cksum_count clr a TcslO: add a,@rl yadd it (No Carry!) inc rl ybump the pointer djnz r3,TcslO mov @rl,a ystore it pop ace pop psw / restore the registers ret Initialization routines init_all: call init_Status / set up the status block, call init_Ports yset I/O ports to all inputs call init_A2D_Cnvrtr yinit the A to D converter call init_correction yinit the dac from memory call init_Siol yand the serial I/O, (i2c) call init Timer yinit and start the timer ret init_A2D_Cnvrtr: yinit assuming flow mode ops push psw ysave processor status mov psw,/Rb3Sel yselect register bank 3 mov rO,/setpt_buffer yrO -> the setpt buffer mov rl, misc analog yrl -> the rest of the analogs mov r2,/4 /sample_countyinit the sample counter mov r4,/08h ;r4 = setpoint mux ch/ δ start bit mov r5,/09h yr5 - temperature mux ch/ δ start bit mov adcon,/0 yinit the a2d control reg. pop psw yand the status ret init correction: jb sys_mem_valid,inicorOO yif memory valid, null the DACs mov aa,.//008800hh y:eellssee,, wwee''llll uussee tthhee ddeeffaauulltt vvaalluuees mov pwhi,a yfor approximately midrange output, mov pwlo,a yput it in current storage mov nul_hi,a yand local fast access mov nul_lo,a ythat covers that sjmp init_cor_exit yso we'll go away inicorOO: mov dptr,/DacHiNull ythe EEPROM cksum is valid, the null movx a,@dptr yvalue s/b init'd, get the hi byte, cjne a,/Offh,inicorlO yif not Offh, probably ok, Offh illegal clr sys_mem_valid ydeclare it bad sjmp init correction yexit the bad way inicorlO: mov nul_hi,a ysave the value for later mov pwhi,a /this is where we get it for output inc dptr /bump the pointer movx a,€dptr yfetch the lo byte mov nul_lo,a ysave it for later mov pwlo,a / same like the hi byte init_cor_exit: mov pwmO,pwhi yinit the dacs, regardless mov pwml,pwlo / of the source of the data ret init_Mstr_Tx: yinit master transmit mov S1vAdr_d,/Upsi+W yUpsi Address + Write setb Sio Strt yStart Master Transmit ret init_Mstr_Rx: yinit master receive mov SlvAdr_d,/Upsi+R yUpsi Address + Read setb Sio Strt yStart Master Receive ret init_Ports: mov p0,/0ffh ymake sure all of the outputs mov pi,/Offh yact like pull up resistors to the mov p2,/0ffh youtside world, at least until we mov p3,/0ffh / know what it is the outside world mov p4,/0ffh yexpects from us ret init_Siol: yinit the Sio port, slave mode mov pi,/Offh / set Port 1 all hi mov rx_buffer + 2,/cmd_buffer /Rx -> command buffer mov tx_buffer + 2,/com_buffer /Tx -> communications buffer mov rx_count,/08h /command is 8 bytes setb esl / enable the Siol interrupt mov sladr,/SmtCbl+GenCall Set our SlvAddr and GC recognition mov slcon,/0c4h yenable slave function ret init_Status: / The upper 4 bits of system_status are — anl system_status,/OfOh ycleared only on reset mov processjmode,/0 yclear the process status, mov timer_status,/0 ythe timer status anl gp_flags,/01h yclear the gp flags setb cal_ftt_flag / flag first time thru ret init_sys mem: /calculate and store the system area checksum setb cmd_flag / valid command setb ini_sys / ours mov command,cmd buffer+1 ysave it clr sys_mem_valid ywe're changing it, not valid mov dptr,/sys_mem_length ythis is the size mov r2,dph yset up counters, hi and low, mov r3,dpi ysystem area can be>256 bytes mov dptr,/sys_mem_base ypoint at the bottom clr a ystart with a zero sum mov r4,a ytemp storage ismOO: SUBSTITUTE SHEET movx a,Sdptr yfetch a byte add a,r4 yadd the previous result mov r4,a store the new sum inc dptr ybump the pointer djnz r3,ism00 yif lobyte - 1 <> 0, loop mov a,r2 yelse check the hi byte jz ismlO / if zero, we're almost done dec a yelse, dec hi byte and loop mov r2,a yput it back sjmp ismOO ycontinue ismlO: mov a,r4 yget the sum movx @dptr,a ystore it mov r7,/0 ism_dly: djnz r7,ism_dly ywait a tad ism20: movx a,Θdptr ycheck using data bar polling anl a,r4 yshould be all 0's cjne a,/00h,ism20 yloop til it is call check_sys_mem yso go check it mov com_buffer + 2,a ysend 'em what we got mov com_buffer + 3,r4 / what we added mov com_buffer + 4,r5 / what we read mov tx_count,/08h yTx byte count mov cksum_count,/07h ycksum count call TxCkSum / calculate it setb tx_flag / we're ready to send setb rx_flag ;we would also receive ism30: jnb rx_flag,ism_rx_exit ;got a msg, see what it is jb tx_flag,ιsm30 ;else loop til our msg is gone ism40: mov a,slsta ;make sure it's gone cjne a,/0f8h,ism40 jnb sys_mem_valid,ini_sys_fail ;if it didn't work, tell PC call init all / else re-init ism_rx_exit: jmp check_command ywait for annother command ini_sys_fail: jmp sw reset yfailures start over init_Timer: push psw save the status mov psw,/Rb2Sel select Rb2 mov r0,/4 250 uS counter mov rl,/10 1 mS counter mov r2,/100 1 S counter pop psw and reg bank 0 mov tmod,/22h set timers 0 δ 1 for auto-reload mov th0,/6 set count for 250uS ticks SUBSTITUTE SHEET yhi and lo, auto-reload y ell timer 0 to run yenable the timer interrupt INTERRUPT SERVICE ROUTINES Insert handlers here. sw reset: ysoftware reset, and/or error handler Hardware interrupt service routines y hese are for interrupts that are ynot currently used. They're included yhere to give the jmp instructions at ythe interrupt vectors a target in ycase a bad interrupt comes along. ywe'll get as close to rst as possible ydisable all interrupts yall of 'em yReg Bank 0 please! ypush a 0 return address on the stack yall 16 bits ynow that that is done Timer 0 Interrupt Service Routine When enabled, pass through here every 250 uS. Uses reg bank 2 to optimize context switching and access to time counters. Since we only use our regs, we don't have to save anything but the psw, which holds the bank select. timer_0: push psw save context, processor status mov psw,/Rb3Sel select reg bank 3 mov adcon,r4 start an A2D conversion mov psw,/Rb2Sel select register bank 2 setb uS250 flag the tick djnz rO,T_Vector update 250uS counter mov r0,/4 lmS expired setb mSOOl Set the lmS flag djnz rl,T_Vector update lmS counter mov rl,/10 lOmS expired setb mSOlO flag lOmS boundary djnz r2,T_Vector update lsec counter mov r2,/100 lsec expired setb SecOl flag lsec boundary T_Vector: pop psw ypop off the processor status reti Analog to Digital Converter Interrupt Service Routine ADC conversions, started by the timer 0 interrupt service, vector through here to handle the end of conversion interrupt. This routine uses register bank 3 to optimize context switching. In 'flow control' mode, the setpoint input is sampled 4 times then averaged. At the end of the sampling period, the pulse width modulator registers are loaded with the correction factor pointed to by the averaged digitization of the setpoint. Only the hi byte of the setpoint is stored and used for averaging. Flow Control Mode (misc. analog storage) rO => d9 d8 d7 d6 d5 d4 d3 d2 (the 8 msb's of the result) rO + 1 => dl dO dc dc dc m2 ml mO (2 lsb's δ the mux address) registers used: rO -> setpoint sample rl -> misc. analog sample r2 sample count r4 setpoint mux channel and start bit r5 misc. analog mux ch/ and start bit rδ temp storage for the hi byte r7 temp storage for the lo byte ysave the status yand the ace yswap register banks yflow control mode /cal test mode / Calibrate mode /get the ms byte yget the lsb and mux addr yck if it's for us ymask off lo bits ynot ch 0, do misc. yelse, get what we want ystore it ybump the pointer / update the sample counter /refresh the pointer and the ;sample_count/reset on 1ms tick yso reset the adc and ystart a misc. conversion yrestore and return ynot a 1ms tick, just reset the adc yrestore and return ywe'll store it y here we're pointing ybump the pointer ysave both bytes ywe are pointing inc rl ypoint at the next space cjne rl,/misc_analog + 0eh,misc00 yck for last mov rl,/misc_analog yrestore the pointer mov r5,/08h ythe mux/start bit (miscOO will bump) miscOO: inc r5 ybump the mux channel mov adcon,/0 yreset the adc, timer start adc ret: pop ace yget back the ace pop psw yand the status reti yget a2d control reg a = lOxxxmmm yget d9-d2 r2 = 98765432 ymux in hi nybble a = xmmmlOxx ymask later, rl = xmmmlOxx ymask off mux addr a = xxxxlOxx ydl, dO to lsb a = xxxxxlOx ydO = lβb a = xxxxxxlO ysave dldO, get hi byte r2 = xxxxxxlO ynow get d9 and dδ a = 87654329 ywhere we want them a = 76543298 ysave the hi byte r3 = 76543298 ymask off d9, d8 a = 765432xx ynow the lo byte is done a = 76543210 yswitch 'em r3 = 76543210 ymask off mux a = xxxxxx98 yget the mux addr back rl = xxxxxx98 ymask off lo bits a = xmmmxxxx yor in the mux bits a = xmmmxx98 ystore the hi byte ybump the pointer yget the lo byte yput it in the buffer ybump the pointer yget the mux address ymask off the start bit ybump the mux /top of the list? yyes, start over yat the bottom of the buffer yelse, or in the start bit ysave the mux/start bit mov adcon,/0 yreset the adc sjmp adc ret •common adc interrupt exit, we're fone This is the command jump table, commands from outside vector through here. / defaults to software reset command = OOh, software reset command = Olh, commanded to reset / command = 02h, init system memory / command = 03h, cmd software reset / command = 04h, software reset command = 05h, software reset ;command = 06h, software reset / command = 07h, software reset /command = 08h, Calibrate /command = 09h, Download Cal Data /command = Oah, Cal Page Flow Test ;command - Obh, Upload Mem to PC / command = Och, software reset /command = Odh, software reset ;command = Oeh, software reset command = Ofh, software reset Table include the i2c driver here $d:\zzz_incs\c51_v04\sl_drv.s03 include the External Data Map (EEPROM) here, goes in external data segment $eeprom.inc ENDMOD cable init function / module: exi (int code) yno exit, just start over END /* */ /* cbl_c.c : C functions for the smart cable */ /* Author : Mike Upchurch */ /* Version : 1.00 beta */ * */ /* Revision History */ /* Date Remarks */ /* 9102.22 Release Version 1.00 beta */ * */ /include "d:\zzz_incs\c51_v04\io_552.h" /* header for I/O definitions */ /define byte unsigned char /* define byte */ /define tru Oxff /define fls 0x00 * */ /* External function prototypes */ * * /* */ /* Local function prototypes */ /* */ * * /* Global Variables */ /* * * */ /* Main Function Entry point for the C portion of this program */ * */ byte main( void ) /* main cbl_c.c routine */ • C return((byte)1); /* for now, just return 'continue' */ > /* */

Claims: WHAT IS CLAIMED IS: 1. A system for measuring a characteristic of a fluid related to its rate of flow, comprising: means for generating a detected fluid flow rate characteristic signal in response to the fluid flow rate characteristic of the fluid; means for receiving fluid flow rate characteristic data; means for storing the fluid flow rate characteristic data; means for producing a corrected fluid flow rate characteristic signal in response to the detected fluid flow rate characteristic signal and to the stored fluid flow rate characteristic data; and means for outputting the corrected fluid flow rate characteristic signal. 2. A mass flow meter system, comprising: means for generating a detected mass flow signal in response to the mass rate of flow of gas through a mass flow meter; means for receiving flow calibration data; means for storing the flow calibration data; means for producing a corrected mass flow signal in response to the detected mass flow signal and to the stored flow calibration data; and means for outputting the corrected mass flow signal. 3. A mass flow meter signal translator for connection to a mass flow meter to receive a detected mass flow signal from the mass flow meter and for connection to a process measuring system to provide to the process measuring system a corrected mass flow signal, comprising: means for receiving from the mass flow meter a detected mass flow signal in response to the mass rate of flow of gas through the mass flow meter; means for receiving flow calibration data; means for storing the flow calibration data; means for producing a corrected mass flow signal in response the detected mass flow signal and to the stored flow calibration data; and means for outputting the corrected mass flow signal to the process measuring system. 4. A mass flow controller system, comprising: means for receiving flow controller command calibration signal data; means for storing the flow controller command calibration signal data; means for receiving a flow controller command signal; means for producing a corrected flow controller control signal in response to the analog flow controller command signal and a set of stored flow controller command calibration signal data; means for controlling a mass rate of flow of gas in response to the corrected command signal; means for generating a detected mass flow signal in response to the mass rate of flow of gas through the mass flow controller; means for receiving measured flow calibration data; means for storing the measured flow calibration data; means for producing a corrected mass flow signal in response to the detected mass flow signal and to the stored measured flow calibration data; and means for outputting the corrected mass flow signal. 5. A mass flow control signal translator for connection to a process controller to receive a linear flow command signal therefrom and for connection to a mass flow controller to provide a corrected flow command signal to the mass flow controller, comprising: means for receiving flow controller command calibration signal data; means for storing the flow controller command calibration signal data; means for receiving a flow controller command signal from the process controller; means for producing the corrected flow command signal in response to the flow controller command signal and a set of stored flow controller command calibration signal data; means for supplying the corrected flow command signal to the mass flow controller; means for receiving from the mass flow controller a detected mass flow signal in response to the mass rate of flow of gas through the mass flow controller; means for receiving measured flow calibration data; means for storing the measured flow calibration data; means for producing a corrected mass flow signal in response the detected mass flow signal and to a set of stored measured flow calibration data; and means for outputting to the process controller the corrected mass flow signal. 6. An apparatus for calibrating a mass flow controller, comprising: means for generating a mass flow controller command signal which is substantially sinusoidal with respect to time and has a period longer than twice a time constant of the mass flow controller and flow standard; means for sensing an output signal produced by the mass flow controller in response to the controller following the sinusoidal mass flow controller command signal; means for measuring the gas flow through the mass flow controller and producing a measured flow signal in response; and means for comparing the mass flow controller output signal with the measured signal and producing a mass flow controller calibration signal in response thereto.

Inventors: DONALDSON RICHARD LEE, DOYLE JAMES HOLOHAN, UPCHURCH MICHAEL DEAN, DONALDSON, RICHARD, LEE, DOYLE, JAMES, HOLOHAN, UPCHURCH, MICHAEL, DEAN
Applicants: UNIT INSTR INC, UNIT INSTRUMENTS, INC.
Application year: 1992
Main classification: G05D7
IPC codes: G01F1, G01F25, G05D7