I’m working on launching a high-altitude balloon later this year with a Raspberry Pi serving as its flight computer. The Raspberry Pi is an excellent tool because it allows you to do most common tasks at a higher level of abstraction than other MCU platforms. However, it lacks at least one of the major conveniences of MCU’s like the AVR that I’m accustomed to working with - the analog-to-digital converter (ADC). In this article, I’ll describe one solution to the missing ADC, albeit a little complex. For this project, I’m using an ATTinyx61 to serve as the ADC, communicating with the RPi as a slave on the I2C bus.

Why use an AVR as an ADC?

Before settling on this solution, I evaluated a few other possibilities:

  • MCP3008 is an 8-channel 10-bit ADC that comes in a DIP package. It is an SPI-only device which is fine; but the protocol that it uses requires a fair bit of bit-banging. I want to run the flight computer software in Python; so I was a little skeptical of doing this kind of low-level transction at the level.
  • ADS7830 is an 8 channel 8-bit ADC; but it’s available ony in a TSSOP-16 package. One of my goals was to build the daughter board(s) as inexpensively as possible. If I have to fabricate a custom board, then I miss that goal.
  • Gertboard is a great board for prototyping ideas; but I need something that I can send up to 100,000 feet with minimal weigh.

I’m sure there a tons of others; but in the end (a) I couldn’t invest more time in finding the rare DIP packaged, 10 bit 8 channel ADC on the I2C bus. So why not make our own?! I know AVR; so we’ll just turn an AVR into our ADC’s.

Read more »

What the hell is really going on in I2C anyway?

If you’re used to working at the “Arduino-level” of knowledge, then you know the I2C is a two-wire peripheral interface, etc. etc. If you already know that level, but want to know more about how it works at the physical bit layer, read on. This tutorial is meant to help you understand what’s going on without the higher level of abstraction that libraries like Wire.h exposes.

For the purposes of this tutorial, we’re going to temporarily ignore the situation where there are multiple masters on the bus. Instead, we’ll assume a single master and one or more slaves. This allows us to pose the following question:

Who drives the clock - master, slave, or both?

In our typical simplified case, it’s the master that drives the clock.

Since I2C is described as a two-wire interface, what are the “two wires”?

There are indeed only two signals: SDA or serial data and the clock SCL or serial clock. The narrower bus is convenient but it requires considerable orchestration between slaves and master. The rest of this tutorial is all about this coordination.

Why are pullup resistors generally required on the I2C lines?

This is because when any device on the bus pulls the line low, the line must be low at each of the drops. Similarly, a high logic level condition on the bus requires that all devices stop drinving it.

As a brief, but important, aside, the SDA and SCL pins must be implemented as open collector outputs. Open collector schema

When the the external side of the output transistor, the collector, is connected to a pullup resistor, the pin is at a logic high when the transistor is turned off. When the transistor is turned on, the output voltage falls to a low logic level. This is why pullup resistors are use on these lines.

Starting and stopping a transfer

Since we’re talking about a bus, this is about data transfer. Since for practical reasons, the data transferred across the bus is finite, there must be a start and stop condition to bookend the interaction. Unsurprisingly, these are the START and STOP conditions.

The START condition is defined when the SCL line is high and the SDA line transitions from high to low. There is no other moment in the protocol where this is true. Normally state transitions of the SDA line occur only when the SCL line is low.

Similarly, there is a STOP condition that does not occur otherwise during data transfer. This happens when the SDA transitions from low to high while the SCL is high.

Data transfer on the bus

Every unit of data that is sent on the bus is 8 bits wide. The master is responsible for initiating and stopping the data transfer by asserting the conditions described above. At the beginning of a transfer, the master lower SDA while SCL is high, creating a START condition on the bus. Thereafter, it sends transfers 7 bits of slave address on SDA clocked via SCL. The last (least significant) bit in the stream is the read/write bit, where read is a logic 1 and write is a logic 0.

Up until now, all of the work is being done by the master. Now the master expects a response. In this case, the master lowers SCL a ninth and final time. This is when the slave that responds to the transferred address must respond by lowering SDA. This is an ACK.

Take a look at Figure 1 to visualize a typical write sequence from master to slave:

![Figure 1(i2c-master-write.001.png)

Reading data from the slave

When the master device wishes to read data from a slave, it follows the same general protocol as that for a write. First it generates a start condition followed by seven bits of address data, the followed by a read bit. This comprises a 9 bit preamble that address the device of interest. Then, the slave device sends an ACK to acknowledge the receipt of the address and read/write designator. This is immediately followed by 8 bits of data. So far it’s almost just like the situation with the master-to-slave write described above. But now, it’s the master, not the slave that most ACK the data. Each 8 bit byte of data transfered from slave to master is punctuated by an ACK from the master. That is each byte until the last one. When the master needs no more data, it sends a NACK which is the equivalent of the telling the slave device to “shut up.” We could visualize it like Figure 2.

Figure 2

How can I possibly diagnose problems on the bus?

I started this tutorial by describing a goal of taking your knowledge about the I2C/TWI bus beyond the Wire.h level. But with that comes the usual challenges presented by getting closer to silicon. My preferred approach to dealing with this is the logic analyzer. I use the Intronix Logicport analyzer I can’t say enough good things about this device although it is expensive. A less expensive alternative for listening to the I2C (and other) buses is the Bus Pirate. I have no direct experience with the Bus Pirate - but I understand the principle and it seems sound.

To give you a sample of what bus analysis looks like in the context of what we’ve been talking about, take a look at Figure 3:

Figure 3

At a glance, you can see exactly what is happening on the bus. For example, you can see the waveforms that mark the START condition. (You do remember what defines a START condition right? No? It’s when the master drives SDA low while SCL is high.) Then we see

That’s for part I. Hope that helps take one layer of mystery out I2C.