Thursday, 18 July 2013

I2C on a 6809 computer and a mini review of a logic analyser

I haven't had much time to work on my electronics projects lately, or write on this blog. The main reason being the arrival of a bouncing baby boy! So for once hobbies must take a back seat. If I'm lucky I will be able to write here about once a month.

I have had a little time to work on improving my 6809 computer, however, by adding a Philips PCF8584 (PDF) I2C parallel bus controller. This 20 pin IC was released around 1997 as a means to add I2C functionally to 8 and 16 bit micros, and can work with bus signalling from Z80, 6800 and 68000 CPUs. It also seems to work nicely enough with the 6809, but more on that later.

One of the challenges I had when working on the I2C interface on the AVR was knowing what the circuit was doing. When my circuit was struggling to read data from the EEPROM I wasn't really sure if the data had, in fact, been first properly written. The typical solution to these kinds of problems is a Logic Analyser. These are hardware devices that can monitor and record digital signals, in a similar way to how an oscilarscope is used for monitoring analog signals.

In days gone by this kit would cost thousands of pounds. Luckily with the use of a USB interface, the cost of a useable logic analyser can be reduced significantly since the analysis and a graphical display is offloaded onto a PC, with only the capturing needing to be done in specialist hardware. So began my hunt for an affordable logic analyser, eventually leading me to the Saleae Logic16. This is a 16 channel 100Mhz device costing about 200 pounds. Still not cheap but just about within budget.

One of the things that attracted me to the Logic16 was the company behind the product. Saleae run blogs documenting the company and the people that work there, giving an insight into how the company is run and the people who designed the products. It reminded me a little bit of how Smoothwall was when it was a smaller company. They also seem open to improve the products based on customer feedback, and I have to say I found their support to be totally awsome. There are a number of reviews of the Logic16 on the net, including an excellent video on youtube, so I won't bother going into too much detail here. Suffice to say that I couldn't have gotten my I2C interface working without it.

Here's a photo of the Logic16 hooked up to the breadboard, capturing various signals including the E clock, various Chip Enable lines, key CPU control lines, as well as the two I2C lines generated by the I2C bus driver. You can see how cute and small the Logic16 is.

Along the bottom piece of breadboard is the PCF8584 I2C bus driver, 24LC256 EEPROM, DS1307 RTC and button battery.

Not only can the Logic16 display waveforms, but in the case of I2C and other signalling formats, it will even decode them for you. See the screenshots below for an illustration.

The Logic16 can do loads more and I've only just scratched the surface in terms of its functionality. There's even an SDK so the software can be expanded. And the software is cross platform, being available for all the three main OSes. It should come in handy as my homebrew cimputer grows ever more complex. I have to say also that having an analyser makes learning and experimenting with digital electronics much more fun too.

So back to the I2C bus chip. The PCF8584 is somewhat similar to the trusty 6850 UART in that it hooks into the data bus, R/W line, and is clocked by E. It has control, status, and data registers, and like the UART is currently programmed in a polling mode instead of using interrupts.

Currently the 6809 circuit only supports a single I/O device, though it takes up a quater of the address space. To accomodate the PCF8584 the I/O space has been cut in quaters with another layer of address decoding on A8 and A9, meaning I can have four devices each with upto 256 registers. The 6850 and PCF8584 are in this new I/O space, which has been implemented with another 1 in 4 decoder. Therefore there are another two lines available for other devices. Like the 6850 only a single address line is required on the chip as it only exposes 2 registers.

The datasheet nicely describes how the chip need to be configured for different datarates, by dividing its clock in various ways. I had to cheat a little since the chip has no options for a 1Mhz clock, only going down to 3Mhz. Luckily I2C is very flexible when it comes to data transfer ratses.  My I2C runs at a third the speed it should, at about 5Khz, or about 700 useable bytes per second.  Slow, but fast enough for the trivial bits of information floating about in my computer. The datasheet contains flowcharts describing  read and write operations which I've turned into various 6809 subroutines. Oddly the I2C implementation in 6809 assembly seems simpler to follow then the equivalent AVR C code. Maybe that just means I've been looking ay assembly too long.

For now I have two devices on the I2C bus:
  1. A 24LC256 EEPRPOM at I2C slave address $a0
  2. A DS1307 Real Time Clock at I2C slave address $d0
The circuit now looks like the following. Because it's getting quite large, I have split the circuit diagram in two.  First the core computer, with CPU, memory, glue logic and core address decoding:

Followed by the IO components; the 6850, and the newly added I2C hardware:

The additional layer of address decoding. for selecting which IO device should be used, is on the bottom left.

Two new commands have been implemented in the monitor to exercise the I2C functionality.
  • s for store. Takes a I2C bus address, a I2C memory address, a 6809 memory address and a byte count. Bytes from the 6809 memory are written onto the I2C bus. The I2C memory address can either be a word or a byte, since the EEPROM has a 16 bit address requirement and the RTC a 8 bit requiement.
  • l for load. Like load except the transfer is the other way.
Writes are trivial, and consist of sending a START, follwed by an I2C slave address, followed by an I2C memory address, followed by the actual data, before finishing up with a STOP.  These were implented easily and using the Logic16 I was able to verify that the data was flowing across the bus.  Reads in I2C, on the other hand, are a bit more complicated and are implemented as a write to set the adress followed by a read with a repeat START, and this has given me some trouble for a couple of weeks. Essentially I worked around the problem by ignoring the datasheet and doing an extra read when "discarding" the slave address just after the repeat START. It seems to work, save for the ability to properly do single byte reads, but I would like to better understand what is going on.

The below screenshots show the monitor being used to copy a 4byte sequence from 6809 ROM into the serial EEPROM, the serial EEPROM then is read into a free area of RAM and then the RAM dumped out.

First the screenshot of the monitor session, showing ROM and RAM dumps prior to the transfer, the two transfers and finally the RAM dump.

Next the screenshot from Logic16 showing the store operation.

Note how the START and STOP sequences are shown, as well as the hex values.

And finally the screenshot of the load operation showing the I2C data being outputted from the EEPROM, after the START sequence (the green blob) is sent a second time.

EEPROMs are all very well but the Real Time Clock IC is far cooler. The DS1307 is fitted with a 3V button battery for keeping the time when the board is powered off, and a 32.768Khz crystal. This chip also has an extra feature: it can output a waveform at various configurable frequencies on a spare pin. The circuit uses this feature to flash an LED at 1Hz. Because it was so simple to do, this output is also fed into the non maskable interrupt pin on the 6809.  When the interrupt routine runs it simply increments an "uptime" value, stored in memory.  This is my first use of interrupts on the 6809, and was surprisingly simple to do.  This uptime count can be displayed using a simple new command, cunningly called "u".  Because a 16bit value for seconds is only enough to count up for a day or so, the uptime count is actually stored as a 32bit value, enough for a bit more then a 100 years!  Unfortunately the "u" command is not especially friendly, and simply outputs the uptime value in hex.

Interestingly this RTC stores all values in binary coded decimal, making display a virtual no-op since I already have routines for displaying hex digits. Setting the clock is currently done by preparing a block of memory in the following format (all values are bytes in BCD):

Seconds, Minutes, Hours, Day, Date, Month, Year, Output control

And sending it up to the IC.

Day is from 01 to 07 and is simply incremented at midnight. The choice of the start of the week is arbitrary, but I will use Sunday. Output control sets what the output pin should do. $10 is the magic value which enables the 1Hz pulse. Higher frequencies are also possible as well as simply turning the output pin on and off but this seems less useful.

I have written a special command for obtaining and printing the time. It somewhat crudely borrows the I2C load command subroutine to send a specific I2C request which has the effect of dumping the clock values into a memory buffer. This is then formatted for printing, the most complicated bit being to turn the numbers 01 to 07 into a day name (Sun to Sat).

The code, for anyone perverted enough to want to see, is on github as usual.

The following screenshot shows the monitor being used to load some memory with a buffer containing the current time, then the time being loaded across the I2C bus, and finally the time is retrieved and printed using the showtime command, "t". Notice that when storing the buffer to the I2C RTC IC the I2C slave address $d0 is used, along with the 8bit start address $00.  This is because the load and store commands can deal with 8 and 16bit addresses.

And here is a capture obtained when the "t" command is run.  You can easily see the seconds, minutes, hours, etc being outputted from the RTC IC.

I have loads of potential next ideas, but not enough time:
  1. First I feel I'm reaching the limits of what a09, the 6809 assembler is useful for. I need to split the monitor into multiple files and structure it better, but it does not seem very good at larger programs. Thankfully there are other assemblers out there. Hopefully it will be easier to port my code to a more sophisticated assembler.
  2. I need to make the breadboard more reliable. Often I have to hit the reset switch several times before sensible text comes across the terminal. I'm not sure if this is a real problem with my circuit or just down to flakey wiring inherent in breadboards. Either way it needs to be fixed.
  3. I have a GI AY-3-8912 soundchip I would love to get making noises. This is the same chip used in the 128Kbyte ZX Spectrum, a computer I played with back in the good old days.
  4. I have one other I2C chip I could get working, a MCP23017 16 bit I/O port. Not terribly interesting but could be cool to make some LEDs flash.
  5. I have some 8x8 bicolour LED matrices I could turn into a display. One idea is to use an AVR with I2C in slave mode to drive the LEDs, perhaps with intelegence so it can scroll text etc. The 6809 would send high level commands like "scroll this message" etc. This sounds quite hard, but could be a lot of fun.
  6. For something simpler, I could use some 7 segment displays, perhaps driven from the 16 bit I/O expander mentioned above.
So many ideas but not enough time...