I2C Bus

Reduce your I/O interfacing projects to a handful of wires! This is very easy to fit in hardware terms, only a pair of open-collector i/o pins are needed to become a bus master. Many useful peripherals can be added, such as:


I2C Hardware

Before designing my own interface, I had a look around to see what was already available. Xilinx had some free VHDL, and so did OpenCores. I downloaded the latter, and found that it is a byte-level I2C master interface. This more than I really need, but less effort than inventing a new one.

I copied the relevant files and they compiled after I removed a few "report" statements. The synthesis report said it resulted in 337 logic blocks. Rather a lot I thought. Is it really worth it? I suppose it might be if you have a very fast processor that cannot spare the time to bit-bang. But in practice, this is generally not the case. Most CPUs would probably sit in a loop monitoring byte transfers instead of bit transfers. I can see the point of having a hardware controller when the serial signals require a relatively long time to send and have critical timing (i.e. UARTs for RS232 channels), but the I2C bus doesn't have such constraints.

So finally I decided not to use a hardware controller, and to use a software-driven I2C master interface instead.

Interfacing had some practical issues. The DS75, PCF8563 and the serial ROM used have a VIH that is 70% of VCC. In the worst case, 70% of 5V5 is 3V85. The VIH of the FPGA is 3V3, which is not quite high enough. External pull-ups can help, since the I2C lines are open-collector, but I decided to run the I2C devices from the 3V3 rail. The ROM appears at more I2C addresses than I expected, but the data sheet confirms that is is correct behaviour for this particular chip.

The DS75 chip came in a SO8 package which gives faster response to temperature change at the expense of being fiddlier to fit. Fortunately the pin pitch is the same as ribbon cable, so I mounted it on the end of some. Another point is that these chips are so small they do not have the usual notch to tell you which end is which. I took my best guess that when you have the writing the right way up pin 1 is on the left.

There is an address conflict between the RTC and EEPROM. The 8563 has address 1010001R. The 24LC02B appears at all addresses 1010XXXR, where XXX is used to select 256-byte blocks within the EEPROM (and thus look like eight separate 256-byte EEPROMs). This is an irreconcilable problem! Either they will need separate bus lines, or different devices are needed. The only practical solution seems to be to select an EEPROM that has 256 bytes or less. Not all EEPROMs have address-setting inputs. For the ones that don't, their address is fixed at 1010000R, so the RTC cannot use that address. Fortunately the PCF8563 does not.

The RTC has been fitted and responds to the correct address. It returns convincing-looking data, in that the seconds and minutes increment. Thus one can assume the 2nd-hand crystal was not heat damaged, and is oscillating. Some other bytes return inconsistent data, but this may be limited to bits that are not implemented. If it is noise, this may be due to pull-up resistors (4k7) too weak or software not allowing enough rise time.


I2C Software for 6502

A little careful hardware design can make software easier to write. When shifting data bytes out one bit at a time, it makes sense to have the data I/O bit at d0 or d7 of an I/O port byte location. I2C has a clock signal SDA, and it makes sense to place this on an adjacent pin. This is exactly what happens in the 8051 family members that have I2C interfaces:

P1.7 = SDA
P1.6 = SCL
83C524, 83C528 = bit-level I2C. DIP40,
80C552, 83C552 = byte-level I2C. PLCC44
P0.1 = SCL
P0.0 = SDA
P-semi 24-pin 8051 controllers

The positioning eases software implementation if the user decides to use a pin-compatible part that does not have a hardware I2C interface.

The 6502's memory-bit-testing instruction (BIT) sets the zero flag if the memory AND accumulator bits result in zero. This requires the accumulator to be loaded first. However, BIT also copies d7 and d6 of memory locations into the Negative and Overflow flags respectively. So you don't have to load the accumulator to test these bits. Thus for the 6502 it is sensible to read SDA and SCL on those bits and flags. This reduces the code size and execution time compared with code that loads A before testing and branching on the Z flag.


Existing Code

There are many I2C code examples on the web but tend to be very host-specific and not of trustworthy quality (even from some manufacturers!). Ease of porting varies: e.g. some examples assume particular hardware, and the PIC has useful bit-oriented I/O instructions that the 6502 does not.

A quick net search led to Lee Davison's web site, which has some 6502 code to be an I2C bus master. In fact, there don't seem to be any other 6502/I2C examples!

Lee told me he is aware of the optimisation for the 6502, and has already implemented it on his code, and is just waiting for time and inclination to update that page. You could modify the code yourself if you can't wait.

There is a flaw in Lee's multi-byte read routine: it acknowledges the last byte received. It acknowledges the last byte received, which is contrary to the I2C spec. This will cause problems with some devices, because they use it to see if the bus master needs any more data.

For example, the PIC's I2C slave interface software would see the ACK, and put another byte in the I2C output buffer. The master won't clock out this surplus byte, and the PIC state machine gets locked up. When it does this you can't get out of it without switching the power off and on.
Another example is serial EEPROMs, which use the NACK to know that the next time they are used the master will be sending a new data pointer. Without the NACK, the master will receive data bytes from the wrong locations.

Lee's code assumes a 1.842 MHz clock rate, because he uses this on his SBC design. He says they allow him to use this single clock for the CPU and baud rates, and that it is very easy to find canned oscillators of this frequency, in old PC boards.


I2C Software in general

I2C devices vary greatly in the way they are used. A simple example might be where you wanted to read or write a string of bytes to a serial EEPROM. The number of bytes is known in advance. This is not so for more complex devices

A not-so simple example would be where there are multiple devices sharing a single interrupt line. The master has to poll them to find the device responsible. Well-designed devices put their status registers at low internal addresses, so that the master can use them to decide if any more bytes need to be read. Therefore you need separate subroutines for reading slave data bits, and for sending ACK or NACK. This allows the master to read a byte before deciding whether to ACK or NACK it.


Implementation

I decided to use software I had written in Visual C for PC-driven projects, because I knew it worked and in the way I wanted. It was also easier for me to read my own C code than someone else's assembler.

There are many minimum set-up and hold times, which the original C simply satisfied by software delay loops. I have kept these seperate from the signal-change routines. Although this means more calls are made, it does make it easier to optimise the code later on. In many places the 6502 has other things to do while I2C signals change, which usually provides more than enough delay.

A 6502 running at 1.77 MHz is not fast enough to bit-bash I2C at 100 kHz, let alone 400 kHz, so you will have to find the actual rate by experiment.