Dangerous Prototypes

Other projects => Past projects => Open 7400 Competition => Topic started by: schazamp on October 15, 2011, 06:02:01 am

Title: (pseudo)Entry: Reading I2C temp sensor without uC
Post by: schazamp on October 15, 2011, 06:02:01 am
This was going to be my entry, but I have not yet been successful in getting it working.  I got a long way, and learned a lot, and I think the principle is sound.  Therefore, I wanted to post my design and results, in hopes that someone might make a suggestion and I might learn where the problem is, even if it doesn't count for a proper entry.

My project is to use logic devices to read a TC74 temperature sensor.  The general idea is to have some kind of master clock (from 3 of the 74HC04's 6 inverters), manipulate the clock to drive the SDA and SCL lines, and then clock data from a sequence of PISO shift registers (75HC165) to drive the I2C bus commands (start, device ID, write, set read address, start, device ID, read) and then, based on a series of counters, clock the response from the device off the I2C bus into a SIPO register (74HC595), and use that to drive some 74HC4511s and 7-segment displays.

I am utterly new at trying to use the 7400 series chips for anything more ambitious than simple gate logic and shifting, and am much more comfortable in the realm of software.  However, I thought the bus exchange (do some protocol stuff, write out the "read" address, and read the data response from the device) was simple enough to model with discrete logic.  For a good chunk of the logic, I am using a CPLD (Xilinx CoolRunnerII, on the DP development board).  This allowed me to prototype my design and investigate it in the simulator, iterating in a REPL much faster than the one that involves writing it on paper, ordering ICs, and breadboarding it (repeat until it works).

Driving the I2C bus signals

For some background the I2C interface uses two open drain lines, SCL (for clock) and SDA (for data, sampled on the rising edge of SCL).  Since the 165s also clock data on the rising edge of the clock signal, I needed to have my serial data clock (SCAc) one-quarter phase offset from the SCL.  This ensures that the bits are shifted onto the bus while SCL is low. 

To accomplish this, I use a single master clock running at twice the rate of SCL or SDAc.  The master clock (MSTR) goes through a divide-by-two to drive SDAc, and is inverted (MSTRi) before going through a divide-by-two to drive SCL.

Code: [Select]
For example, to put "0 1 0 0" onto the bus:
SCL:  ___/^^^___/^^^___/^^^___/^^^
SDA:  ^^_______/^^^^^^^______________
SDAc: _ _/^^^___/^^^___/^^^___/^^^___
MSTR:  _/^_/^_/^_/^_/^_/^_/^_/^_/
MSTRi: /^_/^_/^_/^_/^_/^_/^_/^_/^
SCL:  ___/^^^___/^^^___/^^^___/^^^
SCL is in there twice to see how it relates to SDA and also to MSTRi.
[attachment=2]

I2C protocol oddities

The I2C protocol indicates the beginning of a transmission by sending a "start" signal, by the transmitter driving the data line low while the clock line is high (rather than changing data while clock is low).  Since this is outside the "normal" PISO sequence, I had to add several buffer stages between the output of the SCL divide-by-two and the SCL bus (see figure 0).

Another oddity is the "ack", where the sender relinquishes the bus for one bit after transmitting each 8 bits, allowing the receiver to pull the bus low to acknowledge.  This was easy to deal with, I just leave the bus high for one cycle (just as if I were writing another 1) and ignore whatever the receiver sends for an ack.

The final oddity is the "stop" signal, where the transmitter indicates that the transaction is complete, and the bus is available for someone else to start a transaction.  This is done by letting the SDA go from low to high after while SCL is high.  I hadn't figured out how to deal with this, probably by some kind of trickiness after the counting system determines (by count of clock cycles) that everything should be finished.

Timing mechanism

The other part of managing the I2C transaction is controlling the timing, so my PISOs send the right amount of data, and then the SIPO listens at the right moment (to read just the data from the sensor), and also so the system can determine when the transaction should be complete, and reset itself in preparation for the next transaction.  I had thought about using a pair of 555 circuits, an astable for the clock, and a monostable to drive the clock for just the right about of time, but I then thought that it would be easier to get exactly the right results by actually counting the clock pulses.  Fortunately, the CPLD has a number of counting primitives available.
[attachment=0]
At a high level, the timing design needs to work something like this (see figure 1).  When the device is triggered, the master counter (CB8CE) is enabled, which starts counting the master clock (which is always running).  The trigger (a debounced momentary switch) drives a latch (upper left, FDCP), so that once the device is triggered, it runs until completion irrespective of the trigger state.  A binary comparator (lower left, COMP8) compares the total pulse count to a hard-coded number, and when true, drives a disable latch (lower right, FDCP), that causes the system to remain in a disabled state until the next trigger.  The latches are using D flip-flops with asynchronous clear and preset, with no input or clock.  When CLR is high, Q is latched to 0, when PRE is high (and CLR is low), Q is latched to 1.  The RESET signal goes to clear all the flip-flops and counters elsewhere in the system.

The other part of the timing system is how to determine when to have the SIPO start clocking data from the bus into its register.  I handle this the same way, by having a comparator with a hard-coded count for when to start the serial data (a fixed number of clock cycles from start, after all the control stuff has been shifted from the PISOs onto the bus) latch a SERIAL_ENABLE signal.  There's another comparator that has a hard-coded count for when to *stop* the serial data, which resets the SERIAL_ENABLE latch.  If the counts are set up correctly, the the SIPO will be enable just long enough to get the 8 bits of the temperature from the sensor.

Master Clock Signal

The clock I am using is made of 3 NAND inverters, configured to run at about 20KHz (measured with the logic analyzer).  It looks something like this:
Code: [Select]
---|>o---|>o---|>o------> MSTR_CLK
|            |      |
{R2}        = C1  {R1}
|            |      |
|_____________________

The frequency, supposedly, is determined by f = 1 / (2.2R1 * C1) (where R2 = 10*R1)
R1 = 39K
R2 = 390K
C1 = 470pF

I was shooting for around 100KHz, which is the slower I2C bus speed supported by the TC74, but I figured slower probably wouldn't hurt, and would be easier to trace with the logic analyzer.  Note that with the dividers, the actual I2C bus speed will be half of the speed generated by this clock (or less), so, roughly 10KHz.

Displaying the Result

Once I have the result of the temperature reading in the SIPO, I need to display it.  Since the SIPO has an 8-bit binary output, but the 4511s I had to drive the 7-segment displays take only BCD input, I needed to find a binary to BCD converter.  Fortunately, I found something close, implemented in verilog, released under the GPLv2.  It was actually a full binary to 3-digit BCD 7-segment driver, but as an exercise, I stripped it down to just its 8-bit-binary to 3 digit bcd output (2x4-bit and 1x2-bit, to show up to 255).  See this forum topic: viewtopic.php?f=56&t=2843 (http://dangerousprototypes.com/forum/viewtopic.php?f=56&t=2843) for a little more detail. 

Misc

In order to simulate the system, I wanted to model the 74HC165s, to make sure I had the right grasp of how to use them.  I couldn't find any ready-made models to use for CPLD, but fortunately, there was a logical block-diagram and timing sequence example in the datasheet.  I was able to implement the 74HC165 with its logical elements in the schematic view, and then abstract that into a higher-level component to use in the full system.  I'll attach it, along with the other interesting files, to a follow-on post.  I write more soon about my physical implementation, and my findings, and maybe someone can suggest a solution.
Title: Re: (pseudo)Entry: Reading I2C temp sensor without uC
Post by: ian on October 15, 2011, 02:23:28 pm
Wow. Intense project and great writeup. I hope you eventually get it going.
Title: Re: (pseudo)Entry: Reading I2C temp sensor without uC
Post by: schazamp on October 15, 2011, 06:32:02 pm
Thank you, Ian.  Maybe it was too ambitious for a first 7400 series project, I feel that having no experience debugging clocked logic and timings has been quite an impediment to my debugging, but the whole point of this is to learn something useful, and that at least I can consider a success.

I wanted to write up a bit about implementing the 74HC165 in the CPLD, since building and simulating it was very instructive and helpful for implementing the other parts of the design.

Implementing the 74HC165 in the CPLD

The 74HC165 was the most promising looking Parallel-In-Serial-Out (PISO) shift register I could find, but in order to make sure it would work the way I needed to in my circuit, I wanted to simulate it and step through its behavior.  I tried using the Falstad Circuit Simulator (http://www.falstad.com/circuit/ (http://www.falstad.com/circuit/)), but it didn't really have the primitives I needed (specifically, the set / reset latch).  Having just implemented the binary-to-bcd converter on the CoolRunnerII CPLD, I remembered seeing the primitives available in the schematic-entry symbol library.

How it works

Figure 0 is the block diagram of the implementation.  There are 8 stages, each a D-type flip-flops with asynchronous set and reset, with a serial input (so the devices can be daisy-chained) and normal and inverting serial outputs.  The clock and clock-inhibit inputs are ORed together, and there is one other input, a SHIFT/~LOAD control.  We can understand the operation by looking at a single stage.

[attachment=0]

When SH/~LD is HIGH, the register is in "SHIFT" mode.  The signal is inverted, and by driving one of the inputs LOW to the AND gates driving the PRESET and CLEAR inputs to the flip-flop, both remain LOW, and so the DATA input to the flip-flop is transferred to the output on the rising edge of the CLOCK signal.  When the SH/~LD is LOW, the register is in "LOAD" mode.  With one of each of the AND inputs HIGH, the other (driven by the parallel-loadable input pin for the stage) determines the output of the flip flop.  If input is LOW, the AND for PRESET is low, and the AND output for CLEAR is HIGH (because the input is inverted going into the AND).  This drives the output of the flip-flop LOW.  If the input is HIGH, it drives the output of the PRESET AND gate HIGH, and the CLEAR AND gate LOW, which sets the output of the flip-flop HIGH.  Since PRESET and CLEAR are asynchronous, the flip-flops will ignore any in DATA or clock signals as long as either PRESET or CLEAR is asserted.

Simulation Results

[attachment=2]

The datasheet contains a sequence diagram that illustrates the behavior (see Figure 1), and I used this as a way to confirm that the CPLD implementation was correct.  As you can see in Figure 2, the timing matches up.

[attachment=1]

Using this simulated PISO, I was able to confirm the whole design showed the expected output, from the timing of the counters, to the signals on SCK and SDA that I was expecting.  The next writeup will show some more details of the breadboard implementation, and have some conjecture about what isn't quite right with the circuit yet.
Title: Re: (pseudo)Entry: Reading I2C temp sensor without uC
Post by: schazamp on October 18, 2011, 03:45:32 am
Layout
Here is an image of my implementation so far.  I had hooked everything up, including the sensor, but when I ran into trouble, I backed off a bit, to aid debugging.

[attachment=2]

In the lower right is the power supply (3.3V for the CPLD, and 5V because I thought I wanted that for the displays, but I'm running everything at 3.3V at the moment.

I'm using two of my Bus Pirate Breakout Board (three in stock in the free PCB drawer:  http://dangerousprototypes.com/store/in ... ucts_id=66 (http://dangerousprototypes.com/store/index.php?main_page=product_info&cPath=1&products_id=66)).  I only have one bus pirate (viewtopic.php?f=28&t=1734 (http://dangerousprototypes.com/forum/viewtopic.php?f=28&t=1734)), but for this project I needed both a JTAG programmer for the CPLD and a logic analyzer.  So, the breakout board on the right is wired up for the BusPirate XSVF Player firmware (http://dangerousprototypes.com/docs/Bus ... SVF_player (http://dangerousprototypes.com/docs/Bus_Pirate_JTAG_XSVF_player)), and the one on the left is wired up as a logic analyzer.  That way, all I need to do to use one or the other (after swapping the firmware -- ugh) is hook up the 10-pin cable without worrying that all the connections are right each time.  I should just get another JTAG programmer, or a better logic analyzer, but this seemed to get the job done. 

In the top center is the 7-segment display circuit, with the 74HC4511 BCD 7-segment display drivers, the displays themselves, and the wiring to the BCD output of the CPLD breakout.  In the center is the CPLD breakout itself.  The whole project wouldn't fit on the CPLD (too many PISOs, not enough resources), so I couldn't use that for the entire thing.  I did end up using most of the pins for various debugging outputs (more on that later).  The trigger switch is smack in the middle, in the least convenient place. 

Below the trigger is a clock made from 3 out of the 6 inverters available on the 74HC04.  It pretty consistently produces 20kHz output (measured by the logic analyzer.  To the left of the clock is the temp sensor, all alone, and some MOSFETs that will convert the high/low signal from the PISO registers to open drain.

Finally, all along the far left, are the PISO registers.  The bottom one is on the "end", so its contents will be the first to be shifted out.  This entire bank of ICs is unconnected from the rest of the circuit, I wanted to make sure I was getting good clock and SH/LD output before I tried using them or programming in all the bits I need to send.

Operation

So I know that the Bus Pirate logic analyzer is hardly high end, and I might have better luck with a special-purpose device, but it is all I have.  Maybe I'm using it wrong.  See Figure 1 for some output:
[attachment=1]
This capture was triggered on channel 0, capturing at 100 kHz.
Channel 0 is the TRIGGER signal, driven by a momentary switch, which I tried to debounce with a 100nF cap, but you can still see some noise there.
Channel 1 is the SDA_CLK_OUT signal, the clock to drive the PISO bank.
Channel 2 is the RESET signal, which is the inverted, latched TRIGGER.  This should be true, except during the timed interval controlled by TRIGGER and the MASTER DISABLE
Channel 3 is the MSTR_DISABLE signal.  This should be driven high by the master reset latch, set when the MSTR_CLK_IN has seen 24 pulses after the TRIGGER is first set high.
Channel 4 is the MSTR_CLK_IN signal.

Here are some of my questions:
* The logic analyzer capture is triggered on the momentary switch coming in on channel 0, however, I never see the actual transition of the switch from low to high.  There is a noticeable delay between engaging the button and having the "waiting for the trigger" mode light on the BP go out.  Is there some way to have this trigger more responsive?  Or am I out of luck?  Because the whole system is meant to execute within a well-defined (but extremely small) sequence of pulses after the trigger, it's hard to make sense of the stuff I get from the logic analyzer if I miss the window that I'm interested in (though I know what I would expect to see after the trigger sequence is done, and this is not it).
* I know breadboards have tons of stray capacitance, and that can cause timing issues, but I have little experience dealing with it directly.  At the speed I'm working with (20kHz), is that likely to be part of my problem?
* The I2C bus (and particularly, this temperature sensor) can run at 100kHz.  I have been assuming that, at 20kHz, there is plenty of time between clock signals for the CPLD to do its thing (though I don't have the optional external oscillator installed on that board), and I have also been assuming that the temperature sensor will respond properly to bus commands, even if they are slower than 100kHz.  Is this a reasonable assumption? 

The next thing I can think of to try would be slowing the MSTR_CLK_IN waaaaaay down (10s of Hz) to see if I can get the logic analyzer to respond to the trigger in time to capture the part of the sequence I'm really interested it.  Am I missing something obvious about how to trigger the capture?  Is there a better way to set up the capture to get what I want?

I would really appreciate any advice or help about where I may be going wrong, here.  If anyone's really interested, I've attached a zip with the CPLD project, though I'm not sure how much use it will be.

( ! ) Fatal error: Uncaught exception 'Elk_Exception' with message 'Please try again. If you come back to this error screen, report the error to an administrator.' in /var/www/dangerousprototypes/forum/sources/database/Db-mysql.class.php on line 696
( ! ) Elk_Exception: Please try again. If you come back to this error screen, report the error to an administrator. in /var/www/dangerousprototypes/forum/sources/database/Db-mysql.class.php on line 696
Call Stack
#TimeMemoryFunctionLocation
10.01082111280session_write_close ( )...(null):0
20.01112242872ElkArte\sources\subs\SessionHandler\DatabaseHandler->write( )...(null):0
30.01122243648Database_MySQL->query( ).../DatabaseHandler.php:119
40.05662382384Database_MySQL->error( ).../Db-mysql.class.php:273