AVR basics: using the I2C bus #2 – transmitting

All this LCD loveliness with just two wires. Who wouldn’t want that?

So in the first post in this series, we looked at how to configure the bus speed for I2C. Now we’ve got that out of the way, let’s start sending stuff over the bus.

The I2C bus is quite complex – at least compared to bog-standard, UART-based serial or SPI. Fortunately, Atmel has done a lot of the hard work for you via a handful of registers. You just need to write the right values to these, and read the results, with the AVR hardware taking care of the nitty-gritty.

Sending a message

Here’s a very simple outline of what happens when you send something over I2C. And remember that, for this series, we’re talking about using the AVR chip, such as my beloved ATMEGA328P, as a bus master and some other willing device as a slave.

All communications are initiated by the master.

Communications are byte-oriented, which is just a fancy way of saying each command issued over the bus is usually a byte long and data is managed in byte-size chunks. When you read about I2C you will also see lots of talk about start conditions, stop conditions, acknowledge bits and no-acknowledge bits. These are all managed via single bits of data; but don’t worry – you don’t have to set or otherwise mess with these bits yourself. The AVR handles that for you when you use its registers. You just have to be aware that they’re happening.

One concept that it’s useful to understand is that all information on the I2C bus is set up when the clock line is low. (Again, you don’t have to worry about doing this, but it is handy to know it’s happening.) In other words, each bit of information – whether it’s for a command byte or data byte – is set high or low on the SDA line when the clock (SCL) is in the low position. That bit of data is then read when SCL is high.

Why is this important? Because it explains how those start and stop conditions are handled. In those cases, the bit is set when the SCL clock line is high. So the fact that the SDA data line transitions from high to low or vice versa when the SCL line is high is a signal to all involved that this is a special kind of bit, not a data bit. Its actual significance depends on when it happens.

Bear in mind also that I2C lines, both SDA and SCL are held high – usually by pull-up resistors – when idle. So I2C lines are ‘active low’. You will often see in the literature, such as the microcontroller’s datasheet, that a register bit is ‘cleared’ by writing a 1 to it because 1 is the default condition.

The full package

Let’s look at sending an entire message. This will be a fairly simple one consisting of writing a one-byte value to a register on the slave device, and in this example we’ll write the value 0xE2 to a register at address 0x09.

First we need to get the I2C bus into the start condition. When the master starts an I2C communication it brings the SDA line low. Because the SCL line is still in its idle position of high, this is a signal that things are about to kick off. The clock then starts pulsing and we are in what’s known as a start condition.

[Click to see larger version]

Next we send a byte consisting of the sum of two values – the address of the slave device we want to talk to (known as SLA) and a one-bit value that tells the slave whether this is a read (R) or write (W) operation. This byte, then, is often referred to as SLA+R or SLA+W.

The address uses the top 7 bits of the byte. You’ll see this referred to as a ‘7-bit address’ and that can be confusing. Normally, if you think of something being a 7-bit value it means it’s comprised of bits 0-6. But an I2C address isn’t like that. It’s actually comprised of bits 1-7. This leaves the least significant bit, bit 0, free for other things and also means that I2C addresses are always even numbers.

(I should add at this point that here we’re only dealing with the most common I2C versions in which 7-bit addresses are used; there is also a version with 10-bit addresses spread over two bytes.)

Bit 0 is used to tell the slave whether this is a read (bit set to 1) or write (bit set to 0). So the value of this byte is: slave_address + mode. Let’s say our I2C device has an address of 0x6C then the value of this byte will be:

For writing (SLA+W): 0x6C + 0 = 0x6C
For reading (SLA+R): 0x6C + 1 = 0x6D

So, that first byte consists of one of those values and means that only the device with that address will respond to what’s about to come down the I2C bus – until, that is, a stop condition is encountered (we’ll get to that).

After each byte is sent from the master, the master and slave swap transmitter and receiver functions for one bit and the slave sends back one bit. It does this by taking the SDA line low or allowing it to return high. This is the acknowledge bit and is set to 0 for ‘acknowledge’ (ACK) or 1 for ‘no acknowledge’ (NOT ACK). It’s up to your code how you handle this. We’ll touch on this some more when we talk about the AVR’s registers.

The next byte sent from the master is typically a register value, also commonly known as a ‘command’ byte. Selecting a register is your way of telling the slave device what you want it to do. For example, with a real-time clock chip I’m playing with right now (the DS3107), sending the value 0x05 tells it that you want to write or read the month value. Again, this byte is followed by an acknowledge bit.

It could end there. With some devices, simply selecting a register achieves the effect you’re after. Other times, you will send one or more data bytes. Let’s take the example of a 24 x 4 LCD display I like to use. Sending the command (register) byte of 0 tells the display you want to write to the screen. You then send as many bytes as you want to write characters, control the backlighting, set the cursor position and so on. So a transmission might consist of several, even dozens of bytes all prefaced by just one address and one register byte.

Finally, the master signals the end of the transmission – the ‘stop condition’ by holding SDA low, allowing SCL to go high where it stays, and then allowing SDA to go high.

Sounds complicated, doesn’t it? Luckily, the implementation of all this is largely taken care of by the AVR microcontroller. All you need to do is make judicious use of its registers. So we’ll look at those in the next post..


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.