Project 022 - ZX Spectrum Multi I/O Interface
DISCLAIMER: This design is experimental, so if you decide to build one yourself then you are on your own, I can't be held responsible for any problems/issues/damage/injury that may occur if you decide to follow this build and make one yourself.
INTRO
The project - To design and built a Multi function I/O board for the ZX Spectrum.........
Back in the early 80's when I was a teenager I bought myself a 16k Sinclair ZX Spectrum computer, but rather than play games I spent all my time playing about with BASIC writing some wierd and wonderful programs. I also got into the actual hardware behind the Spectrum which of course is the Z80A Cpu by Zilog. At work we also used the Z80A so it was fun and easy for me to play around with the hardware building the odd interface and gleaning info from those around me more in the know of the Z80A.
Roll the clock on some 30 years and I find myself with the ZX Spectrum once again on the bench in-front of me and looking rather curiously at the edge connector at the back............hmmmm, I wonder!
A lot of my workshop projects recently have been with the Arduino and enjoying interfacing lots of analogue and digital signals, so I thought wouldn't it be nice if the Spectrum had similar analogue and digital I/O capability. So, I set to work and came up with a specification and a number of IC's I'd used previously with the Arduino and thought they might be easily ported to the ZX Spectrum. If anything a nice challenge!
- ADC - 4 analogue inputs - 12 to 18bit configurable
- DAC - 4 analogue outputs - 16bit
- 24 digital I/O
- I2C bus interface
The I2C interface is key to the design as I wanted to use the same I2C based ADC & DAC IC's that i'd used before (with the Arduino). Therefore, I'd need to find a converter IC that would allow this, i.e Spectrum data bus to I2C conversion. The following is the basic hardware behind the entire interface.
- MCP3424 ADC IC - http://ww1.microchip.com/downloads/en/DeviceDoc/22088b.pdf
- DAC8574IPW DAC - http://www.ti.com/lit/ds/slas377b/slas377b.pdf
- PCA9564 Parallel to I2C bus Controller - http://www.nxp.com/documents/data_sheet/PCA9564.pdf
- 82C55 Programmable peripheral interface - http://www.embeddedsys.com/subpages/resources/images/documents/82C55_datasheet.pdf
With the above and a few additional components the basics of the hardware design is there, and so I set to prototype a pcb, and then design a full board using Eagle PCB.
Note: This design has only been tested with a 48K ZX Spectrum rubber keys model. I have not tested it at all with any 128k Spectrums so am unsure of any compatibility there.
DCS Electronics now have the rights over the Multi I/O board - https://www.dcselectronics.co.uk/zx-spectrum-multiface.html
![]()
CIRCUIT DESIGN
The following is a brief rundown of the circuit.
POWER SUPPLY:
I chose to run the interface from the unregulated DC (9 to 12vdc) rather than the Spectrum's own +5vdc regulated supply. A couple of regulators have been used supplying 3.3vdc & 5.3vdc to the circuit. U6 the PCA9464D parallel to I2C IC requires 3.3vdc (but can handle 5vdc logic I/O). Everything else runs from 5.3vdc, choosing an LM317 to generate this voltage. I chose 5.3vdc so that I could guarantee 0-5vdc on the analogue side of the DAC & ADC. The Vref for the DAC is 5.000vdc (+/- 0.1%). All IC's on the board are spec'd to run at up to 5.5vdc so no problems there.
I/O DECODING:
The first thing to do is connect to the ZX Spectrum's interface, or to be more exact the 8-bit data bus of the Z80A Cpu. This is pretty easy normally and can be done in a number of ways using just one IC for the IO address decoding, and I chose the 74LS138 1 of 8 Decoder - http://www.ti.com/lit/ds/symlink/sn74ls138.pdf
One caveat with the 74LS138 though is that it will not carry full I/O decoding of the entire Z80's address range (A0 to A15) but actually only 3, and in particular I used A1, A2 & A3 thus accessing even adresses only as I believe the ULA utilizes a load of the odd addreses). By doing this not only limits the IO addresses that can be used, but also opens up the fact that many IO addresses share the same bottom addresses and thus possible conflict. Many ZX Spectrum interface designers over the years have done exactly that in order to keep costs down given the relatively low likelyhood that Joe Bloggs will buy 2 interfaces that conflict. For the design I also chose to do it this way. All said and done, the 74LS138 gives me 8 selectable lines I could use as chip selects (CS), and in the end only 2 were required.
In saying that, it wasn't long before I started experiencing an issue during prototyping i.e. glitching on 5 of the 8 chip selects, a little unusual and I had my suspicions on the Spectrum's internal design (the ULA etc!) which isn't your normal Z80A processor board and so I added an extra gate to tie in the RD/WR lines from the Cpu rather than lying on the Z80's IORQ line. In the end, much more reliable and glitch free.
I2C:
A PCA9564 IC is a parallel to I2C bus converter IC and gives the Spectrum an inductry standard I2C interface. The primary function is to interface to the I2C enabled ADC & DAC IC's, but I have also connected the SCL & SDA bus lines to a couple of screw terminals at the back of the board. This will allow the user to extend the bus to other devices or boards.
There is one caveat with this IC though and it's not quite as straight forward in the software as it is with say the Arduino, look up the code for more info.
ADC:
A simple to implement circuit using a MCP3424 quad channel ADC. No Vref is required as it's internal to the IC, and so all I have done is add a voltage divider / low pass filter on each of the four inputs. This gives each input a range of 0 - 10vdc. Resolution is configurable in the software from 12bit, 14bit, 16bit & 18bit. The software default i've set to 16bit to match the DAC.
In real life terms at 16bit I am certainly getting 1-bit stability from an analogue input which isn't too bad considering the sheer noise that can be found on the ZX Spectrums ground plane.
DAC:
I used a DAC8574 quad channel DAC. A 5.00vdc Vref sets the FSD for the outputs, and I used a rail-to-rail (input & output) chopper op-amp on each DAC output to guarantee a stable and accurate output. Optional resistors/caps allow for scaling the output accordingly. By default they are hooked up as voltage followers. Resolution is set at 16bit.
Note: If there's area in the design/layout I would have liked to improve then that would be the DAC, since despite setting VrefLO and VrefHI accordingly I am getting some slight offsets (in order of a few mV's)! Saying that though, stability is certainly in the order of tens of micro volts (uV).
DIGITAL INPUTS / DIGITAL OUTPUTS:
The 82C55 takes care of the digital I/O. This IC interfaces direct to the Z80A's data bus and is fairly easy to setup to control the three 8-bit ports. There's much more to this IC than meets the eye, but for simplicity I've set it up as follows:
Port A = 8 x digital inputs, pulled high to +5v, active low to GND.
Port B = 8 x digital outputs via ULN2803 driver, V+ supplied externally (any voltage).
Port C = Bit 7 is reserved (CS to PCA9564), the remaining 7 bits are configured to drive 3 on-board LEDs and read 4 on-board jumper headers. Upper nibble = 4off outputs, lower nibble = 4off inputs.
VIDEO:
I did record a video blog as I had to troubleshoot an I2C problem during development. See here.
I/O TESTING / SPECIFICATIONS:
The calculated & tested spec. is as follows.
16bit Analogue Output = Range 0 - 65535, 0 - 5vdc, 0.000076vdc per bit (0.076mV or 76uV) calculated.
Voltage at DAC output: 0bits = 0.01463vdc, 32767bits = 2.51308vdc, 65535bits = 5.01020vdc
Voltage at Op-amp output: 0bits = 0.01429vdc, 32767bits = 2.51270vdc, 65535bits = 5.010028vdc
Note: Most DACs don't work down to 0.000vdc very well, this particular DAC specs a zero scale error of 5 to 20mV (I measured 14mV on my particular DAC), see datasheet for explanation.
Note: DAC tested using my uncalibrated bench 6.5digit DMM. I really need to send it away for recalibration and will revisit this once thats done.
16bit Analogue Input = Range 0 - 65535, 0 - 10vdc, 0.000152vdc per bit (0.152mV or 152uV) calculated.
Real life figures = 0.000vdc = 0bit, 2.500vdc = 7994bits, 5.000vdc = 15988bits, 7.500vdc = 23973bits, 10.000vdc = 31966bits
Note: Better absolute accuracy would be attained by using precision resistors on the input voltage divider.
Note: ADC tested using my cheap Ebay Hao Qi Xin voltage ref. (based on AD584LH, 1mV accuracy).
Digital outputs = ULN2803 spec. gives 50vdc max., 500mA per channel, on-board back-emf/clamp diodes. Datasheet here.
Digital inputs = 10k pull-up to +5.3vdc, external connection via contact to 0vdc (GND).
I2C = Bi-directional I2C communication IC standard & fast mode capable. Max. master mode freq. = 360kHz, Max. slave mode freq. = 400kHz. Can operate as master, slave, transmitter or receiver.
GOING FORWARD:
I'd like to experiment further with the external I2C bus connections and possibly try interfacing to some other I2C devices such as a graphics/alphanumeric LCD.
EAGLE PCB FILES & ZX BASIC CODE
I have made available all the design files as I don't intend to sell the PCB's long term, except maybe a limited edition run of 3 or 4. Please contact me if you are interested (fully assembled).
PCB:
Current version = V1.0
Schematic in PDF format - here.
Eagle PCB files - here.
BOM file - here (supply part numbers where applicable).
CODE:
ZX Basic (Boriel) code here. The zip includes a compiled .TAP file as well as the .BAS basic file.
The BASIC code does not run natively on a ZX Spectrum. It requires to be compiled to a .TAP (machine code) file using Boriel's ZX Basic Compiler. See http://www.boriel.com/en/software/the-zx-basic-compiler/
The code by default runs some subroutines to demonstrate how to read and write to both digital & analogue I/O, the idea being that i've provided all the base information for anyone to take the system forward for their own bespoke work.
PHOTOS
Here's the final version of the board (top of photo) connected to the back of a Spectranet card which is plugged into the ZX Spectrum. The Spectranet card gives the Spectrum an IP address so I could upload the TAP file to the Spectrum easily and quickly via a Lan connection. Luckily it has a full thro' connector so my board could go at the back.
You can see a test relay and external LED connected to a couple of the digital outputs.
I have several ZX Spectrums, this is my dev. machine.......;-)
A close-up of the board. It's a 2-layer Pcb, all electronic components are surface mount, the only through hole devices are the connectors and headers.
An LED at the far left signifies unregulated DC input.
The pushbutton switch is a RESET switch for the Spectrum.
The large IC is the 82C55.
There are several labelled test pads on the board for quick access to the power supply voltages (+unreg, +5.3, +3.3) and the 5.000v Vref for the DAC.
Further view of the board in operation and connected to the back of my Spectranet card.
You can see along the top connectors my test cables etc for the various analogue inputs/outputs.
For those playing along at home, I used my reflow oven to solder up all the SMD parts, but strictly speaking it could all be done by hand with just the DAC8574 being a bit tricky due to it's TSSOP fine pitch (0.65mm).
The pcb, underside view. Most of it is the 0vdc groundplane.
You can also see various mounting holes which, if required, can be used to steady the board in a fixed installation.
Major components & I/O summary.
I designed the board to be the same width of the Spectranet card, just to keep things looking nice and even.
Note shown, the I2C connector also doubles to output the Spectrum Unreg V+ and the Spectrum's +5v regulated supply.
Arty-farty shot. In the foreground the power supply section and the ULN2803 digital output buffer. Over to the right is the address decoding IC's (74HCT86 & 74HCT138).
Eagle PCB artwork screenshot. Amongat everything else, you can see the data bus (D0 - D7) directly between the 82C55 and the PCA9564, and then down to the Spectrum's edge connector.

When I ordered my PCBs from the fab house I got a few extra, interested?....please contact me.
The prototype board.
I had used DIL (non-SMD) parts where I could, but the DAC & ADC in particular required the use of SMD adaptor boards. The 82C55 required a socket.
Full development in progress, including my small 8" LCD (composite input) I used for the Spectrum display.
Here's my test code for the Pcb, it's designed to be compiled to TAP using ZX Basic compiler (www.boriel.com).
1' ZX Spectrum Multi IO board2' By Ian Johnston 28/09/20143' 82C55 Port A = 8off Inputs4' 82C55 Port B = 8off Outputs5' 82C55 Port C = 4off Inputs (lower)6' 4off Outputs (upper)7'8' DAC8574IPW 4ch DAC IC, 16bit9' MCP3424 4ch ADC IC, 12-18bit running in 12bit mode10'11' This code is designed to be compiled to TAP using ZX Basic compiler (www.boriel.com)12' Use this .bat file to compile and sent to the Spectrum via a ZX Spectranet interface13' zxb interface2.bas --tap --BASIC --autorun --optimize=314' copy interface2.tap D:\ZX\TNFSD\_files15'16' Spectranet start.bat:17' d:\ZX\Spectranet\tnfsd.exe "d:\ZX\Spectranet\_files"18 19include "input.bas"20 21' Vars22 DIM DacSend(3) AS INTEGER23 DIM DacCh(4) AS UInteger24 DIM digBkeep AS UInteger: LET digBkeep=025 DIM digCkeep AS UInteger: LET digCkeep=026 DIM digSET AS UInteger: LET digSET=027 DIM A AS UByte: LET A=028 DIM B AS UByte: LET B=029 DIM digout AS UInteger: LET digout=030 DIM AdcReceive(2) AS UByte ' 16bit max with 2 vars31 DIM AdcCh AS Float ' 16bit max 0-65535 (this was UInteger but needs to be float for VoltCh)32 DIM Adcval(4) AS Float33 DIM Voltch(4) AS Float34 DIM Adcchconfig(4) AS UInteger: LET Adcchconfig(1)=184: LET Adcchconfig(2)=216: LET Adcchconfig(3)=248: LET Adcchconfig(4)=15235 DIM adc AS UByte=036 DIM dac AS UByte=037 DIM i AS UByte38 DIM onoff AS UByte39 DIM testonoff AS UByte: LET testonoff=040 LET header$ = " Multi I/O Board - Ian Johnston "41 LET blankline$ = " "42 LET footer$ = " A=analogue D=digital "43 44'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''45' Setup (runs once at startup only)46 47 ' 82C55 ports48 'OUT 59,131 ' Control word for prototype board49 OUT 59,145 ' Control word = 10010001 (A=in, B=out, C upper=out, C lower=in)50 51 ' temporary settings - all outputs off port B52 OUT 27,053 GOSUB turnoffbit54 55 ' print stuff once56 PRINT PAPER 6;BRIGHT 1;AT 0,0;header$57 PRINT AT 1,0;blankline$58 PRINT AT 22,0;footer$59 PRINT BRIGHT 1;AT 2,0;"DIG. INPUTS"60 PRINT BRIGHT 1; AT 6,0;"DIG. OUTPUTS"61 PRINT BRIGHT 1; AT 16,0;"ANA. OUTPUTS"62 PRINT BRIGHT 1; AT 10,0;"ANA. INPUTS"63 PRINT AT 7,0;"Port B = ";"00000000"64 PRINT AT 8,0;"Port C = " ';"0000 "65 PRINT AT 17,0;"Ch.1 RAW ="66 PRINT AT 18,0;"Ch.2 RAW ="67 PRINT AT 19,0;"Ch.3 RAW ="68 PRINT AT 20,0;"Ch.4 RAW ="69 PRINT AT 11,0;"Ch.1 Vdc ="70 PRINT AT 12,0;"Ch.2 Vdc ="71 PRINT AT 13,0;"Ch.3 Vdc ="72 PRINT AT 14,0;"Ch.4 Vdc ="73 PRINT AT 3,0;"Port A ="74 PRINT AT 4,0;"Port C ="75 PRINT BRIGHT 1;AT 2,13;"76543210"76 PRINT BRIGHT 1;AT 6,13;"76543210"77 78 GOSUB pc9564init ' Initialize I2C controller IC79 80 GOSUB dac8574setup ' Initialize DAC81 82 'FOR delay=1 to 4: NEXT delay83 'LET port$="a"84 'LET bit=085 'LET onoff=186 'GOSUB turnonbit87 'OUT 11,25588 89 ' output startup defaults - analogue90 port$="1"91 dac=VAL(port$)92 value$="0"93 DacCh(dac)=val(value$)94 GOSUB dac857495 port$="2"96 dac=VAL(port$)97 value$="0"98 DacCh(dac)=val(value$)99 GOSUB dac8574100 port$="3"101 dac=VAL(port$)102 value$="0"103 DacCh(dac)=val(value$)104 GOSUB dac8574105 port$="4"106 dac=VAL(port$)107 value$="0"108 DacCh(dac)=val(value$)109 GOSUB dac8574 110 111'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''112' Main Loop113 114mainloop:115 ' Keyboard116 IF INKEY$ = "d" THEN117 GOSUB inputfromuserdigout118 END IF119 120 IF INKEY$ = "a" THEN121 GOSUB inputfromuseranaout122 GOSUB dac8574setup123 GOSUB dac8574124 END IF125 126 ' Digital inputs127 GOSUB digitalinputportA ' full byte port A128 GOSUB digitalinputportC ' lower nibble port C129 130 ' ADC131 LET adc=adc+1132 IF adc=5 THEN LET adc=1: END IF133 GOSUB mcp3424chchange ' Config ADC channel134 GOSUB mcp3424 ' ADC IC135 136 ' Test137 GOSUB anatest138 GOSUB digtest139 140GOTO mainloop141 142'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''143' Test routines - These demo how to use the various I/O144 145anatest:146 IF INKEY$ = "" THEN RETURN: END IF147 IF INKEY$ = "1" THEN LET dac=1: DacCh(dac)=DacCh(dac)+1: GOTO checkdaclimits: END IF148 IF INKEY$ = "q" THEN LET dac=1: DacCh(dac)=DacCh(dac)-1: GOTO checkdaclimits: END IF149 IF INKEY$ = "2" THEN LET dac=2: DacCh(dac)=DacCh(dac)+1: GOTO checkdaclimits: END IF150 IF INKEY$ = "w" THEN LET dac=2: DacCh(dac)=DacCh(dac)-1: GOTO checkdaclimits: END IF151 IF INKEY$ = "3" THEN LET dac=3: DacCh(dac)=DacCh(dac)+1: GOTO checkdaclimits: END IF152 IF INKEY$ = "e" THEN LET dac=3: DacCh(dac)=DacCh(dac)-1: GOTO checkdaclimits: END IF153 IF INKEY$ = "4" THEN LET dac=4: DacCh(dac)=DacCh(dac)+1: GOTO checkdaclimits: END IF154 IF INKEY$ = "r" THEN LET dac=4: DacCh(dac)=DacCh(dac)-1: GOTO checkdaclimits: END IF155 IF INKEY$ = "5" THEN LET dac=1: DacCh(dac)=DacCh(dac)+100: GOTO checkdaclimits: END IF156 IF INKEY$ = "t" THEN LET dac=1: DacCh(dac)=DacCh(dac)-100: GOTO checkdaclimits: END IF157 IF INKEY$ = "6" THEN LET dac=2: DacCh(dac)=DacCh(dac)+100: GOTO checkdaclimits: END IF158 IF INKEY$ = "y" THEN LET dac=2: DacCh(dac)=DacCh(dac)-100: GOTO checkdaclimits: END IF159 IF INKEY$ = "7" THEN LET dac=3: DacCh(dac)=DacCh(dac)+100: GOTO checkdaclimits: END IF160 IF INKEY$ = "u" THEN LET dac=3: DacCh(dac)=DacCh(dac)-100: GOTO checkdaclimits: END IF161 IF INKEY$ = "8" THEN LET dac=4: DacCh(dac)=DacCh(dac)+100: GOTO checkdaclimits: END IF162 IF INKEY$ = "i" THEN LET dac=4: DacCh(dac)=DacCh(dac)-100: GOTO checkdaclimits: END IF163 checkdaclimits:164 IF DacCh(dac)<=0 THEN LET DacCh(dac)=0: END IF165 IF DacCh(dac)>=65535 THEN LET DacCh(dac)=65535: END IF166 GOSUB dac8574setup167 GOSUB dac8574168RETURN169 170digtest:171 ' Digital Output - Blinky, toggle of a couple of digital outputs (port b,bit0 & port c,bit 4) direct from code172 LET port$="c"173 LET bit=6174 IF testonoff = 255 THEN LET onoff=1: GOSUB turnonbit: END IF175 IF testonoff = 0 THEN LET onoff=0: GOSUB turnoffbit: END IF176 LET port$="b"177 LET bit=0178 IF testonoff = 255 THEN LET onoff=1: GOSUB turnonbit: END IF179 IF testonoff = 0 THEN LET onoff=0: GOSUB turnoffbit: END IF180 LET port$="b"181 LET bit=1182 IF testonoff = 255 THEN LET onoff=1: GOSUB turnonbit: END IF183 IF testonoff = 0 THEN LET onoff=0: GOSUB turnoffbit: END IF184 LET testonoff = NOT testonoff185RETURN186 187'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''188' Digital inputs (port A of 82C55)189 190digitalinputportA:191 LET digINa = IN 11192 LET A = digINa193 GOSUB binaryconvinputs194 'PRINT AT 3,0;"Port A = ";s$;" Decimal = ";digINa;" "195 PRINT AT 3,13;s$ ': PRINT AT 3,28;digINa;" "196RETURN197 198'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''199' Digital inputs (port C of 82C55 lower nibble)200 201digitalinputportC:202 LET digINc = IN 43203 LET A = digINc204 GOSUB binaryconvinputs205 PRINT AT 4,17;s$(4);s$(5);s$(6);s$(7) ' bit 0, 1, 2 & 3206RETURN207 208 209'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''210' Digital outputs211 212inputfromuserdigout:213 PRINT AT 22,0;"8255 Port (B or C upper nibble)"214 port$=INPUT (1)215 PRINT AT 22,0;"Bit (0 to 7, or 8 for all bits)"216 b$=INPUT (1): LET bit = VAL(b$)217 PRINT AT 22,0;"On/Off (1 or 0)"218 f$=INPUT (1): LET onoff = VAL(f$)219 PRINT AT 22,0;footer$220 IF onoff = 0 THEN GOSUB turnoffbit: END IF221 IF onoff = 1 THEN GOSUB turnonbit: END IF222RETURN223 224' Turn off bit225turnoffbit:226 IF bit=0 THEN LET digSET=254: GOTO bitwiseand: END IF227 IF bit=1 THEN LET digSET=253: GOTO bitwiseand: END IF228 IF bit=2 THEN LET digSET=251: GOTO bitwiseand: END IF229 IF bit=3 THEN LET digSET=247: GOTO bitwiseand: END IF230 IF bit=4 THEN LET digSET=239: GOTO bitwiseand: END IF231 IF bit=5 THEN LET digSET=223: GOTO bitwiseand: END IF232 IF bit=6 THEN LET digSET=191: GOTO bitwiseand: END IF233 IF bit=7 THEN LET digSET=127: GOTO bitwiseand: END IF234 235 ' Bitwise AND and retain copy of current outputs in digBkeep or digCkeep236 bitwiseand:237 IF port$="b" THEN238 LET digout = digBkeep bAND digSET239 LET digBkeep = digout240 OUT 27,digout241 END IF242 243 IF port$="c" THEN244 LET digout = digCkeep bAND digSET245 LET digCkeep = digout246 OUT 43,digout247 END IF248 249 GOSUB binaryconvoutputs250 IF port$="b" THEN PRINT AT 7,13;s$: END IF251 IF port$="c" THEN PRINT AT 8,13;s$(0);s$(1);s$(2);s$(3): END IF252RETURN253 254' Turn on bit255turnonbit:256 IF bit=0 THEN LET digSET=1: GOTO bitwiseor: END IF257 IF bit=1 THEN LET digSET=2: GOTO bitwiseor: END IF258 IF bit=2 THEN LET digSET=4: GOTO bitwiseor: END IF259 IF bit=3 THEN LET digSET=8: GOTO bitwiseor: END IF260 IF bit=4 THEN LET digSET=16: GOTO bitwiseor: END IF261 IF bit=5 THEN LET digSET=32: GOTO bitwiseor: END IF262 IF bit=6 THEN LET digSET=64: GOTO bitwiseor: END IF263 IF bit=7 THEN LET digSET=128: GOTO bitwiseor: END IF264 265 ' Bitwise OR and retain copy of current outputs in digBkeep or digCkeep266 bitwiseor:267 IF port$="a" THEN OUT 11,digout: END IF268 269 IF port$="b" THEN270 LET digout = digBkeep bOR digSET271 LET digBkeep = digout272 OUT 27,digout273 END IF274 275 IF port$="c" THEN276 LET digout = digCkeep bOR digSET277 LET digCkeep = digout278 OUT 43,digout279 END IF280 281 GOSUB binaryconvoutputs282 IF port$="b" THEN PRINT AT 7,13;s$: END IF283 IF port$="c" THEN PRINT AT 8,13;s$(0);s$(1);s$(2);s$(3): END IF284RETURN285 286'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''287' Binary conversion for digital outputs, s$ is the output288binaryconvoutputs:289 LET s$=""290 LET n=digout291 binloop:292 LET aa = n * 0.5293 IF aa = 0 THEN GOTO addzeros: END IF294 IF aa > INT aa THEN LET s$ = "1" + s$: LET n = ((n - 1)/2): GOTO binloop: END IF295 IF aa <= INT aa THEN LET s$ = "0" + s$: LET n = aa: GOTO binloop: END IF296 addzeros:297 IF LEN s$ = 7 THEN LET s$ = "0" + s$: GOTO endlen: END IF298 IF LEN s$ = 6 THEN LET s$ = "00" + s$: GOTO endlen: END IF299 IF LEN s$ = 5 THEN LET s$ = "000" + s$: GOTO endlen: END IF300 IF LEN s$ = 4 THEN LET s$ = "0000" + s$: GOTO endlen: END IF301 IF LEN s$ = 3 THEN LET s$ = "00000" + s$: GOTO endlen: END IF302 IF LEN s$ = 2 THEN LET s$ = "000000" + s$: GOTO endlen: END IF303 IF LEN s$ = 1 THEN LET s$ = "0000000" + s$: GOTO endlen: END IF304 IF LEN s$ = 0 THEN LET s$ = "00000000": GOTO endlen: END IF305 endlen:306RETURN307 308'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''309' Binary conversion for digital inputs, s$ is the output310 311binaryconvinputs:312 LET s$=""313 LET n=A314 binloopdigin:315 LET aa = n * 0.5316 IF aa = 0 THEN GOTO addzeros: END IF317 IF aa > INT aa THEN LET s$ = "1" + s$: LET n = ((n - 1)/2): GOTO binloopdigin: END IF318 IF aa <= INT aa THEN LET s$ = "0" + s$: LET n = aa: GOTO binloopdigin: END IF319RETURN 320 321 322'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''323' I2C PCS9564324' Initialize325 326pc9564init:327 port$="c"328 bit=7329 onoff=0330 GOSUB turnoffbit331 'OUT 43,0 ' Set PC7 low then high to reset PCA9564 IC332 PAUSE 5333 port$="c"334 bit=7335 onoff=1336 GOSUB turnonbit337 'OUT 43,128338 PAUSE 5339 OUT 13,127 ' Timeout function (MSB) enabled = 255, disabled = 127340 OUT 45,100 ' PCS9564 I2C address = 0x64 = 100341 OUT 61,68 ' Clock freq serial = 0x44 = 68 = 88kHz342RETURN343 344'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''345' DAC IC = DAC8574 setup346' Master Tx mode (to set up channel config only)347 348dac8574setup:349 OUT 61,228 ' Start command E4 = 228 350 351 ' Setup Slave device (Tx mode)352 OUT 29,152 ' Load Slave addr + R/W bit = 0 into I2CDAT (DAC8574IPW 0x4c = 76 = 1001100) + R/W bit= 10011000 = 152353 OUT 61,196 ' Reset SI and STA bits in I2CCON354 355 OUT 61,212 ' Generate stop command, write I2CCON with 0xD4 (212)356RETURN357 358'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''359' DAC IC = DAC8574360' Master Tx mode361 362dac8574:363 OUT 61,228 ' Start command E4 = 228 364 OUT 61,196 ' Reset SI and STA bits in I2CCON365 366 ' scan round all 4 outputs, value of each is in DacCh(dac)367 ' Build LSB (max int 65535)368 LET A=DacCh(dac): LET B=255369 LET A = A bAND B370 LET l=A371 ' Build MSB372 LET A=DacCh(dac)/256373 LET m=INT A374 375 ' Build data array ready for sending to the DAC: CHANNEL, MSB, LSB.376 IF dac=1 THEN LET DacSend(1)=32: LET DacSend(2)=m: LET DacSend(3)=l: PRINT AT 17,13;DacCh(1);" ": END IF ' Ch.1377 IF dac=2 THEN LET DacSend(1)=34: LET DacSend(2)=m: LET DacSend(3)=l: PRINT AT 18,13;DacCh(2);" ": END IF ' Ch.2378 IF dac=3 THEN LET DacSend(1)=36: LET DacSend(2)=m: LET DacSend(3)=l: PRINT AT 19,13;DacCh(3);" ": END IF ' Ch.3379 IF dac=4 THEN LET DacSend(1)=38: LET DacSend(2)=m: LET DacSend(3)=l: PRINT AT 20,13;DacCh(4);" ": END IF ' Ch.4380 381 ' Send data (channel, lsb, lsb)382 FOR i = 1 TO 3383 OUT 29, DacSend(i) ' Load data into I2CDAT, transfer data to DAC384 FOR delay=1 to 4: NEXT delay385 OUT 61,196 ' Reset SI and STA bits in I2CCON386 FOR delay=1 to 4: NEXT delay387 NEXT i388 389 OUT 61,212 ' Generate stop command, write I2CCON with 0xD4 (212)390RETURN391 392'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''393' User input394 395inputfromuseranaout:396 PRINT AT 22,0;"Analogue Channel (1, 2, 3 or 4)"397 port$=INPUT (1)398 dac=VAL(port$)399 PRINT AT 22,0;"Value (0 to 65535) "400 value$=INPUT (5)401 PRINT AT 22,0;footer$402 DacCh(dac)=val(value$)403 IF DacCh(dac)<=0 THEN LET DacCh(dac)=0: END IF404 IF DacCh(dac)>=65535 THEN LET DacCh(dac)=65535: END IF405RETURN406 407 408'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''409' ADC IC ADC = MCP3424410' Master Rx mode411 412mcp3424:413 OUT 61,228 ' Start command E4 = 228 414 415 ' Setup Slave device - receive data from device416 OUT 29,211 ' Load Slave addr + R/W bit = 1(read) into I2CDAT (MCP3424 0x69 = 105 = 1101001) + R/W bit= 11010011 = 211417 OUT 61,68 ' Reset SI and STA bits in I2CCON (0xC4 = 196), or 68 if AA is set to OFF418 FOR delay=1 to 4: NEXT delay419 420 ' Receive data421 FOR i = 1 TO 2422 OUT 61,196 ' Reset SI and STA bits in I2CCON423 FOR delay=1 to 4: NEXT delay424 LET AdcReceive(i)=IN 29 ' Read byte out425 NEXT i426 427 OUT 61,68 ' Reset SI and STA bits in I2CCON428 FOR delay=1 to 4: NEXT delay429 OUT 61,212 ' Generate stop command, write I2CCON with 0xD4 (212)430 431 Let MSB = AdcReceive(1) * 256 ' <<432 Let LSB = AdcReceive(2) bAND 255 ' &433 Let AdcCh = MSB bOR LSB ' |434 435 LET Adcval(adc) = AdcCh ' Load 4 channel values on at a time436 437 LET Voltch(adc) = Adcval(adc) / 3240 ' scale display (39k/10k input resistors) to give 0 - 2.048vdc438 LET Voltch(adc)=Voltch(adc)*100: LET Voltch(adc)=INT(Voltch(adc)): LET Voltch(adc)=Voltch(adc)/100 ' Round down to 2 DPs439 440 PRINT AT (10+adc),13;Voltch(adc);" " ' ;" ";Adcval(adc);" "441RETURN442 443'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''444' ADC IC ADC = MCP3424445' Master Tx mode (to set up channel config only)446 447mcp3424chchange:448 OUT 61,228 ' Start command E4 = 228 449 ' Setup Slave device (Tx mode)450 OUT 29,210 ' Load Slave addr + R/W bit = 0 into I2CDAT (MCP3424 0x69 = 105 = 1101001) + R/W bit= 11010010 = 210451 OUT 61,196 ' Reset SI and STA bits in I2CCON452 ' Send data453 PAUSE 1 ' Important pause otherwise the data does not go out on the I2C bus properly.454 OUT 29, Adcchconfig(adc) ' configuration byte = initiate,Ch.n,continuous,16-bit,x1Gain455 PAUSE 1456 OUT 61,196 ' Reset SI and STA bits in I2CCON457 PAUSE 1458 OUT 61,212 ' Generate stop command, write I2CCON with 0xD4 (212)459 ' FOR delay=1 to 4: NEXT delay460RETURNCodequote by Ian Johnston

