' ' WPS Model 71, Measuring Device. ' Tom Jennings ' mru 22 Nov 98 ' ' Contents of this file and resulting binary are Copyright Tom Jennings, 1998. ' ' Loaded 27 Nov 98 ' Fixed divisor at ADC: such that it really runs 0..20 (21 symbols). This was ' incorrect previously, also. ' Loaded 27 Nov 98 ' Actually found three unused bytes to remember DDU "last position" to skip ' turning on motors when on the correct DDU symbol; eliminated motor creep! ' Tripled the width of "zero" by adding two more symbols at ADC:. ' Put DDU motion timeout back to 10 seconds. ' ' Loaded 22 Nov 98 ' Adjusted analog hardware (gain too low, couldn't reach +9; probe seems to have ' drifted). ' Changed DONE buss slow-down cap from .01 to 1000pf; TC was far too slow, made ' response terrible and was probably the source of the creep (eg. motor stayed on ' until the DONE bit charged slow-down cap and reached logic-1). ' Added additional DONE check right after motor select in DDU:, to lessen motor-creep. ' Increased wait to 20 seconds before errorhalt; then commented-out errohalt branch, ' to avoid death during a demo (argh). ' ' Loaded 18 May 98 ' Fixed the following bugs: after obtaining result, and after foo(), the contents of ' calV was 'restored' to the DDUs, not the actual last result (Deleted unused var ' sampleV, named it 'result', stored last result there.) Added kludge to DDU() so ' that symbol 1 on LDDU (Synopsys of DAY THE EARTH STOOD STILL) is never displayed... ' it's there as a tease. ' Loaded 14 May 98 ' Initial version. ' ' written 15 May 98 ' ' The heart of the Model 71 functionality is determined by the conductivity of the sample ' in the vial. The sample is part of a voltage divider; a calibration voltage is applied, and ' the sample voltage is read; the calibration voltage required to reach a standard sample ' voltage is used to conjure up a "measurement" (result). ' ' To operate, the RUN/CAL switch is first set to the CAL position (error if not). ' The operator turns the CALIBRATE pot until the SEQUENCE display reads "0", which ' is approximately half-scale on the ADC. The calibrate pot sets a calibration voltage, ' which is applied to the sample/voltage divider; current through the sample (a few ' microamps, under 5V) induces a sample voltage, both of which are read by the PICSTIC's ' internal two-channel ADC. ' When "0" is displayed on the IEE, the RUN/CAL switch is changed to RUN (error if not 0), ' then the software conjures up some symbols and random-appearing activities. When the ' RUN/CAL switch is changed back to CAL the process is repeated. ' ' The ADCs have 12 bit resolution; the IEE has 18 discrete symbols. Therefore the ' 12-bit range 0..4095 is normalized to the IEE range of -9..+9, with the resulting ' loss of resolution -- which might be generally a disadvantage, but in the Model 71 it ' allows for a range of results (DDU symbol combinations) per sample measurement. ' ' Brief instructions are displayed on the DDUs; the DDUs also indicate errors and other activity ' in generally inscrutable ways. ' ' Just for increased inscrutability, after 655 seconds of inactivity, the Model 71 ' generates a small burst of random DDU activity. ' ' Just to confuse things, the RUN/CAL switch is hacked into the CAL pot, such that 0V indicates ' CAL; therefore the calibration voltage cannot be read in the CAL position! ' ' GLOSSARY: ' ' DDU: Digital Display Unit, CBS Laboratories' DDU-1, electromechanical display ' device made of 12 metal flaps mounted radially around an axle; ' driven by a synchronous motor, the flaps are arranged such that ' a pair of flaps are folded flat, with an image printed across the ' pair of flaps. A rotary switch telegraphs the current position of ' the flaps, allowing software to turn the motor on, causing images ' (pairs of flaps) to pass by one at a time until the desired image is reached, ' then the motor is turned off. ' The DDUs are modified such that all 110AC is within each DDU; the relays were replaced ' with Crydom solid state relays (SSRs). Symbol selection is changed ' such that the 12-position switch attached to the motor shaft (and ' indicating symbol currently displayed) is driven logically "backwards"; ' 1 of 12 outputs are applied to the switch, and software detects when ' the desired position has come around into view, then the motor shut off. ' (Rather than interrupting the relay that powered the motor, causing it ' to stop itself at the correct digit [and the relays caused nasty EMI ' that blew out a PICSTIC in the original design].) ' I/O: 12 output bits to drive current-symbol switch (shared by all DDUs); ' one output bit to select DDU, one input bit to detect symbol-in-position ' (pole of current-symbol switch) (input bit shared by all DDUs). ' ' IEE: Industrial Electronic Engineers, Inc, "one-plane display". Consists ' of twelve simple optical assemblies (lamp, lens, mask) that displays ' an image (digits) onto a frosted plastic screen at the front of the ' device. It is controlled simply by turning one of the lamps on. Plus and ' Minus signs are separate images (eg. displaying "-9" requires turning on ' the "9" and the "-" lamps simultaneously). ' I/O: 12 output bits to select symbols 0..9, -, +. ' ' SR: Shift Register. The WPS Model 01 CPU/IO card consumes four bits ' of the PICSTIC to provide 24 bits of output on three 8-bit shift registers. ' 12 SR bits are used by the IEE; the remaining 12 by the DDUs (shared ' symbol-select bits). ' ' The CALIBRATE pot and the RUN/CAL switch are connected to one ADC channel; ' the sample voltage is read on the other ADC channel. The RUN/CAL switch causes ' the calibration voltage to go to zero; the CALIBRATE pot is wired such that ' it cannot generate 0 volts. Therefore, 0 volts can be used to indicate RUN/CAL ' position, leaving some 4000+ other values as calibration voltage readings. ' ' Copyright Tom Jennings, 1998. ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' file: c:\tomj\projects\model 01, CPU-IO card\model01.bas '' General purpose definitions and functions for the Model 01 '' CPU I/O card. '' '' '' Tom Jennings '' mru 2 Aug 98 '' written 17 July 98 '' '' For unrevised Model 01 card (prototype) card. '' '' Copyright Tom Jennings, 1998. '' ' ' General purpose registers. ' symbol i= b0 symbol n= b1 symbol j= b2 symbol D= b3 ' symbol written to each DDU '' '' The shift registers need data shifted to them MS bit first; the MS bit '' of the value to be written is isolated by using the byte/word organization '' of the PIC; the value to be shifted is placed in the LS byte, and added '' to itself to shift left; the MS bit of the LS byte is thence shifted into '' the LS bit of the MS byte. Easy, no? '' symbol k= w2 ' {b4, b5} word reference to following bytes symbol k_lower= b4 ' not used as-is symbol k_cy= b5 ' carry from K after shift symbol l= b6 ' used by low-level routines, generally SR address ' ' SR B is shared by the DDU and IEE hardware; this byte contains the last-written ' data so that the other half is preserved. ' symbol lastb= b7 ' symbol lastn= b8 ' used to detect change of sample V 'symbol lastL= b9 ' last LDDU value symbol result= w5 ' {b10, b11} result of "measurement" symbol calV= w6 ' {b12, b13} calibration voltage symbol calVl= b12 ' lower 8 bits of calV symbol calVh= b13 ' upper 8 bits of calV symbol lastL= b9 ' last LDDU value symbol lastM= b14 ' last MDDU value symbol lastR= b15 ' last RDDU value ' ' "Random" number generated by incrementing while waiting for the operator ' to do something, and is used to generate noise in response times. "idle" is ' a counter that increments during the loop, and is cleared when human activity ' is detected (RUN/CAL switch change or CALIBRATE pot value change). ' symbol rand= w8 ' {b16, b17} symbol idle= w9 ' {b18, b19} ' ' The compiler's ADC routines leave data in W10, aka V (b20, b21). The ADCs are ' 12 bits; with a 5.00V reference this is .00122V/bit. ' symbol V= w10 ' {b20, b21} symbol CALSW=20 ' voltage less than this is "0" ie. CAL/RUN in CAL position symbol REDX= 0 ' big red X symbol DATA= 1 ' (1..9) first data/symbol symbol INSTRUCT= 10 ' instructions. symbol BLANK= 11 ' blank (no) symbol '' '' These definitions are hard-wired on the PCB. '' Data is written to the shift registers by asserting data on the SD pin, and clocking '' the appropriate SR clock pin. 8 bits per shift register, MS bit output first. '' symbol SDpin= pin0 ' pin number, Serial Data to shift reg symbol SRC= 3 ' SR C clock symbol SRB= 2 ' SR B clock symbol SRA= 1 ' SR A clock symbol DONE= pin7 ' DDU wire-OR DONE bit (true=0) symbol RBITS= %01000000 ' right DDU select bit symbol MBITS= %00100000 ' middle DDU select bit symbol LBITS= %00010000 ' left DDU select bit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Set the pin directions, and make all the displays shut up. Use the IEE ' display as POST status indicator. main: dirs= %01111111 ' all pins outputs except PB7 pins= 0 ' deassert device selects lastL= 255 : lastM= 255 : lastR= 255 ' set current DDU positions to impossible lastn= -1 ' detect inactivity for n= 0 to 9 ' test DDU lamps, mainly gosub IEE pause 100 ' also sez CPU ain't ded next n n= 9 : gosub IEE : pause 600 ' show plus sign n= 137 : gosub IEE : pause 600 ' show minus 9 n= 255 : gosub IEE ' blank the IEE display, D= BLANK : gosub allDDU ' blank all the DDUs; pause 4000 ' we seem important when we inscrutably do nothing. ' ' Start of the main functionality loop. Proper operation requires starting in ' CAL mode, adjusting the calibration voltage until the sample voltage reads ' center-scale ("0" on IEE), then changing to RUN mode and generating the "result" ' on the DDUs. ' ' Completion of startup, and operator-error entry point; if RUN/CAL is not in CAL mode, ' then display red X's, pause, then display the instruction cards. ' m1: gosub ADC ' if RUN/CAL switch is if calV < CALSW then m1b ' in RUN mode, merr: n= 128 : gosub IEE ' display "-0", D= REDX : gosub allDDU ' set all DDUs to red X's, D= REDX : gosub foo ' (go check for inactivity) goto m1 ' wait for CAL mode. m1b: n= 255 : gosub IEE ' blank IEE display, D= INSTRUCT : gosub allDDU ' display instructions, idle= 0 ' not idle, operator changed switch. ' ' Wait until the RUN/CAL switch is set to RUN. During this, the operator ' is supposed to be turning the calibration voltage pot such that the IEE display ' reads "0", indicating the sample voltage is approximately mid-scale (see ADC code). ' We don't check for that now; all we do is read voltages, display them on the IEE, ' wait for the switch. ' m2: D= INSTRUCT : gosub foo ' check for inactivity, do funny things; gosub ADC ' read voltages, gosub IEE ' (N= IEE symbol) display current symbol, if calV < CALSW then m2 ' CAL mode while calV zero, idle= 0 ' switch changed position. ' ' Switch was changed to RUN mode; assuming goal was adjusted properly, use the ' calibration voltage to work up some symbols to apply to the DDUs. The symbol-generation ' makes no inherent sense, don't look for any. ' m3: if n <> 0 then merr ' operator screwed up, start over. n= 255 : gosub IEE ' blank IEE display, D= BLANK : gosub allDDU ' start with blank symbols, D= 10 ' for..step -1 doesn't work! m3a: D= D - 1 n= D : gosub IEE ' display N, (9, 8, 7, ...0) gosub ADC ' update random number, V= rand // 2000 + 750 ' random pause, pause V if D <> 0 then m3a ' to 0 inclusive. gosub ADC ' update rand again, V= rand / 91 ' Generate random starting position, n= 255 : gosub IEE ' blank IEE, gosub DDUmod8 ' position DDUs, pins= %01110000 ' then turn on all DDU motors V= rand // 10000 + 5000 ' for a random period. pause V ' ' We need to scale the cal. voltage (runs 0..4095) to run 0..727. Since 4096/728 is 5.6, ' and we can only do integers, we multiply by a fraction; 91/16 is pretty close. (4095 * 16 ' fits in 16 bits.) ' result= calV * 16 / 91 ' then normalize calV (0..4095) to DDU's (0..727), V= result : gosub DDUmod8 ' display "result" on DDUs. ' ' Continue to display the current result until the RUN/CAL switch is changed to CAL. ' m4: D= DATA : gosub foo ' the devil finds uses for idle CPUs, gosub ADC ' read the pot and switch, if calV > CALSW then m4 ' wait until RUN/CAL in CAL position, idle= 0 ' someone was there to change the switch, goto m1 ' start over. ' ' Called while waiting for human activity, this looks for long periods of idle ' and does odd things to the display. D contains the symbol to restore the DDUs to when ' we're done playing EXCEPT that DATA means restore the results display. ' foo: pause 10 ' we count 10 mS increments, idle= idle + 1 ' increment the idle counter, if idle <> 0 then ret ' when/if it reaches 0 (655 secs; ~11 min) pins= %01110000 ' turn all motors on, V= rand / 91 : gosub DDUmod8 ' display random symbols (0..65535 --> 0..727), pause 1000 ' pause only briefly. if D <> DATA then allDDU ' go display symbol D unless DATA; then V= result : goto DDUmod8 ' re-display results data. ' . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ' ' Display value V on the DDUs. V should already be normalized to 0..727, to fit the ' nine symbols per DDU. The three DDUs, A, B, C (left to right) are the mantissa of ' the base-9 value of calV appropriately scaled. Eg. DDUs display A*9^^2 + B*9^^1 + C*9^^0. ' Maximum value for the DDU display is 8*(81) + 8*(9) + 8*(1) - 1 or 727. ' Modifies N, J, K, L, updates LASTB. ' DDUmod8: n= V / 81 + DATA : gosub MDDU ' 100's digit (0..8) n= V // 81 / 9 + DATA ' 10's digit (0..8) if n <> DATA then du1 : n= DATA + 1 ' KLUDGE prevent "DAY ..." symbol du1: gosub LDDU n= V // 9 + DATA : gosub RDDU ' 1's digit (0..8) ret: return ' ' Read both the calibration voltage and sample voltage and leave in memory. ' This also calculates the goal voltage expressed ' as as -9..0..9 for the ' IEE display; 0 == goal voltage reached, returned in N. This also detects ' human (in)activity and runs the random number. ' NOTE that this is intentionally non-linear; it provides for a "wide" 0 ' range. ' ADC: call ad1 ' read sample voltage, n= V / 204 ' scale 0..4095 to range 0..18 (21 symbols), ' linear (19 values) ' lookup n, (137, 136, 135, 134, 133, 132, 131, 130, 129, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), n ' non-linear (21 values) lookup n, (137, 136, 135, 134, 133, 132, 131, 130, 129, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), n if n= lastn then adc1 ' if goal voltage changed, idle= 0 ' clear inactivity indicator, lastn= n ' remember new symbol. adc1: ' ' Generate a random number using the sample voltage as increment. We can use calV as a ' temporary register since we are about to set it to the value in V. ' calV= V ' V= sample voltage, j= calVl : calVl= calVh : calVl= j ' swap upper and lower bits of sample V, rand= rand + calV ^ calV ' increment rand by sample V, XOR with sample V. call ad0 ' read calibration voltage, calV= V ' set return value. return ' ' Set all DDUs to the same symbol, in D. ' allDDU: n= D gosub RDDU gosub MDDU ' fall through ' ' Display symbol N on the specified DDU. Modifies J, K, L, updates LASTB and LASTx. ' LDDU: if n = lastL then ret ' if same symbol, no change lastL= n : i= LBITS : goto DDU ' K= DDU select bit MDDU: if n = lastM then ret lastM= n : i= MBITS : goto DDU RDDU: if n = lastR then ret lastR= n : i= RBITS : ' fall through ' ' Display symbol N on DDU #I. The symbol is asserted onto the DDU buss, then the ' motor driven until the selected symbol appears on the display. A timeout makes ' sure the DDU is actually moving. Terrible things happen if N is out of range. ' Modifies J, K, L. Updates LASTB. ' DDU: gosub DDUout ' select DDU symbol, ' ' Turn on the appropriate motor. Note that this sets all other pins to 0; this ' works OK since only pulsing a SR clock bit high would affect the hardware. ' Also note that motor-select is the logic-high source that feeds the DONE ' bit; so if we're already at the desired symbol, the motor pulses on, and sometimes ' pushes the symbol off DONE (seems to depend on DDU); so we quickly check ' for DONE before entering the loop. It also makes a little scritching sound ' which is not nice. ' ' ' Turn motor on, wait for the DONE bit to set (PB7 goes to 0). Wait up to ' 10 seconds for this (else hardware failure). ' pins= i ' assert select for k= 1 to 1000 ' 1000 * 10 mS = 10 sec if DONE = 0 then dd1 ' if DONE asserted, exit rand= rand + 1717 pause 10 ' delay 10 mS, next k ' ' The DONE bit is asserted, ie. the switch wiper has just touched the pole. ' dd1: pins= 0 ' motor OFF return ' ' Convert value in N into DDU symbol, and assert on the 12-bit DDU buss. The symbols are, ' in order: 1..9, 0, -, ., repeat. 0 is really "10", not ASCII zero. ' DDUout: lookup n, (1, 2, 4, 8, 16, 32, 64, 128, 0, 0, 0, 0), k l= SRC : gosub shiftout ' assert LS bits, lookup n, (0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 8), k lastb= lastb & $F0 ' clear old DDU bits ' ' Update lastb, output SRB, the shift register that is shared by the IEE and DDUs. ' Local subr to DDUout and IEEout. ' outb: lastb= lastb + k l= SRB : k= lastb : goto shiftout ' update DDU ' ' Display a value on the IEE. This is simply a bunch of lamps tied to ' SR outputs. + and - are also on the SR hence must be ORed in. The upper ' four bits of IEE symbols are on SR B, shared with the DDU. ' The sign is derived from N; 0 is no sign lit, + lit unless bit 7 ' is set. The hardware::symbol relation is determined by the wiring, ' eg. 8="-", 4="+"; I chose to make the wiring neat, and untangle in ' software here. ' ' Symbols are: 0..9 display "0" through "9"; "+" on if n=1..9; "-" on ' if bit7 set (does allow "-0"). Blank display (no symbol) if n=255. ' ' Modifies K, L. Updates LASTB. ' IEE: if n = 255 then ie3 ' special-case blank k= n & $0F ' strip "sign" bit, lookup k, (1, 2, 4, 8, 16, 32, 64, 128, 0, 0), k l= SRA : gosub shiftout ' assert LS byte, k= n & $0f lookup k, (0, 0, 0, 0, 0, 0, 0, 0, 1, 2), k ' ' This mess determines the sign bit display. Temp L holds sign bit. ' l= 0 : if n = 0 then ie1 ' if N=0, no sign bit l= 8 : i= n & 128 : if i <> 0 then ie1 ' L="-" if neg. N l= 4 ' else "+" ie1: k= k | l * 16 ' shift into upper nybble ie2: lastb= lastb & $0F ' strip old IEE bits goto outb ' output SR B ' ' Blank IEE display requires special handling. ' ie3: k= 0 : l= SRA : gosub shiftout ' clear these bits, k= 0 : goto ie2 ' clear shared bits '' '' Shift value in K out to the shift register whose address is in L. Modifies K, J. '' Though K is a word it should be assumed to be a byte (eg. only LS byte is used.) '' shiftout: for j= 1 to 8 k= k + k ' put MS bit into LS bit of K_cy SDpin= k_cy ' assert data pulsout l, 1 ' pulse correct SR clock pin next j return ' ' Report fatal hardware errors here -- error number in N. N is displayed ' on the IEE display (look ma, no moving parts) and the + and - signs are ' blinked until the power is cycled. ' 'errorhalt: ' pins= 0 ' stop motor(s), ' gosub IEE ' display number, ' pause 500 ' delay half a second, ' n= n + 128 ' toggle sign, ' goto errorhalt ' repeat. END