In the case above there is one bit you can choose, this means you can place 2 of these devices on the I2c bus: one with the pin
to ground (gives a "0" in the device address) and one with the pin to the + (gives a "1" in the device address).
The device addresses of the one with the pin to the "+" are as in the first example, the one with the pin to ground
has the I2c addresses $A0 (write) and A1 (read).
It is also possible that, if a device address looks like above, that the device uses more than one I2c read and write address.
This is e.g. the case with devices like eeproms with a lot of memory inside. The different I2c read/write addresses allow then to e.g.
select different memory banks or functions within one device.
Please see the datasheet of a device for its I2c device address(es).
Properties of the I2c protocol are as follows:
- Start & Stop: Every I2c transfer begins with a I2cStart and ends with an I2cStop
Both are special transitions on the I2c bus which do not involve data transfer.
They only make the slaves aware of what the I2c master is doing.
- Slave addressing: The first byte (put on the bus by the master) after an I2cStart is always an I2c (write or read) slave device address.
All slaves look to this, but only the addressed one will do someting with the actions to follow (until an I2cStop occurs). -
- Writing data to a slave: All bytes put on the bus by the master during after an I2c write address are transmitted to the addressed lave
and read in by it.
All bytes are acknowledged by the slave.
- Reading data from a slave: After an I2c read address, all bytes to read are sent by the addressed slave and read in by the master.
The master should acknowledge every byte received except the last one.
So, here we go: suppose we want to send 3 bytes to the device with device (write) address $A0, then the code looks as follows:
I2C_Init(100000); // Initialise I2c at a clock speed of 100 Khz, only to be done once!
...
I2C_Start(); // Issue I2C start signal
I2c_Wr($A0); // Send I2c Write Slave Address
I2c_Wr(Byte1); // write first byte
I2c_Wr(Byte2); // write second byte
I2c_Wr(Byte2); // write third byte
I2C_Stop();
Reading is as easy (example reads also 3 bytes):
I2C_Init(100000); // Initialise I2c at a clock speed of 100 Khz, only to be done once!
...
I2C_Start(); // Issue I2C start signal
I2c_Wr($A1); // Send I2c Read Slave Address
Byte1 := I2c_Rd(true); // read first byte and give acknowledge
Byte2 := I2c_Rd(true); // read second byte and give acknowledge
Byte3 := I2c_Rd(false); // read last byte and give no acknowledge !!!!
I2C_Stop();
Keep in mind that, if the master read from a slave, the last byte read should not be acknowledged!
Important: It is entirely up to the slave device to handle and interprete the bytes written to it,
and to decide what bytes to sent to the master when read by it.
Most I2c slave devices are much more complex than only having the ability to e.g. receive or send 3 bytes with a fixed meaning, so here comes the next
chapter to handle that. So, knowing this, the code above will probably be not applicable for most actual devices!,
see however the next chapter. :-)
The issues discussed so far are "standard" I2c, they hold for every I2c slave device. What follows is device dependant,
and the datasheet of the device at hand should be looked in to see what the data send/received exactly means.
"Almost standard" is the following behaviour: A lot of I2c devices work internally with some kind of adressing with a "pointer"
which holds the address of the place (or register) in the I2c device that must receive data during a master write,
and must sent its content during a master read operation.
Also, in most devices (but again, see the datasheet), the "pointer" is "autoincremental": it is incremented after a byte is written to
or read from the device, resulting in a socalled "sequentially write" or "sequentially read".
In most cases of I2c slave devices, the "internal address" range is 0..$FF, and so 1 byte suffices to represent the internal address.
Also in most cases the pointer value is set to the value of the first byte after a device write address.
So, for this type of devices (with internal address pointer and auto increment) the code would look as follows:
I2C_Init(100000); // Initialise I2c at a clock speed of 100 Khz, only to be done once!
...
I2C_Start(); // Issue I2C start signal
I2c_Wr($A0); // Send I2c Write Slave Address
I2c_Wr(RegAddress) // <-- added: send the address of the register you want to write to (here "RegAddress")
I2c_Wr(Byte1); // write first byte to [RegAddress]
I2c_Wr(Byte2); // write second byte to [RegAddress + 1]
I2c_Wr(Byte2); // write third byte to [RegAddress + 2]
I2C_Stop();
Reading is a little bit more difficult, the pointer value has to be written before the read operation (example reads also 3 bytes):
I2C_Init(100000); // Initialise I2c at a clock speed of 100 Khz, only to be done once!
...
I2C_Start(); // Issue I2C start signal
I2c_Wr($A0); // <-- added: Send I2c Write Slave Address
I2c_Wr(RegAddress) // <-- added: send the address of the register you want to read from (here "RegAddress")
I2C_Repeated_Start; // <-- added: new (repeated) start condition: the following byte should be interpreted as address
I2c_Wr($A1); // Send I2c Read Slave Address
Byte1 := I2c_Rd(true); // read first byte from [RegAddress] and give acknowledge
Byte2 := I2c_Rd(true); // read second byte from [RegAddress + 1] and give acknowledge
Byte3 := I2c_Rd(false); // read last byte from [RegAddress + 2] and give no acknowledge
I2C_Stop();
As you can see, the "pointer" value is written before the read slave address is sent.
Important:
- there is NO I2c_stop between the write and the read operation
- the I2c read slave address is preceded by a "Repeated Start", again a special I2c condition change of the lines.
Sometimes, e.g. when the PIC has no hardware for I2c, or if that hardware is used by some other peripheral, one is obliged to use
the Soft I2c library of mE. With this software the I2c bus is driven completely by software.
The Soft I2c library functions are:
procedure Soft_I2C_Config(var port : byte; const SDA, SCL : byte);
procedure Soft_I2C_Start;
function Soft_I2C_Read(ack : byte) : byte;
function Soft_I2C_Write(data : byte) : byte;
procedure Soft_I2C_Stop;
As you can see, the I2c_Init routine has been replaced by Soft_I2C_Config which only defines both pins to be used as SDA and SCL lines.
It is not possible here to select the clock speed.
I2c_Start and are both replaced by Soft_I2C_Start.
I2c_Stop is replaced by Soft_I2c_Stop, and
I2c_Wr and I2c_Rd are replaced by Soft_I2C_Write and Soft_I2C_Read respectively.
Example:
Soft_I2C_Config(PORTC, 1, 0); // Initialise Soft I2c: SDA is PORTC.1 and SCL is PORTC.0, only to be done once!
...
Soft_I2C_Start(); // Issue I2C start signal
Soft_I2c_Write($A0); // Send I2c Write Slave Address
Soft_I2c_Write(RegAddress) // send the address of the register you want to read from (here "RegAddress")
Soft_I2C_Start; // new start condition: the following byte should be interpreted as address
Soft_I2c_Write($A1); // Send I2c Read Slave Address
Byte1 := Soft_I2c_Read(true); // read first byte from [RegAddress] and give acknowledge
Byte2 := Soft_I2c_Read(true); // read second byte from [RegAddress + 1] and give acknowledge
Byte3 := Soft_I2c_Read(false);// read last byte from [RegAddress + 2] and give no acknowledge
Soft_I2C_Stop();
-------------------------------------------