Netcruzer Library API  V2.03
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups Pages
I2C Serial Data Ports

Netcruzer boards have 2 to 3 I2C ports. They are all interrupt based, meaning once a message is written to the queue, it is processed in the background by an interrupt service routine. Functions are provided for querying the status of a transmission, and reading data read from the I2C port.

For details on "Serial Data Port", see the nz_serI2C.h Documentation.

Details

This module implements an I2C Master, and provides functions for communicating with slave devices. All I2C communication is done in the background via I2C interrupts. Transmit and receive Queues (Circular Buffers) are used for writing and reading data to and from I2C devices.

Writing to the I2C Port

To sent data to an I2C device, the message is written to the Transmit Queue (Circular Buffer) by using the provided i2c1Xxx to i2c3Xxx functions, like i2c1Write() for example. To get status of transmission, see Status of I2C Transmission section below. The Transmit Queue is monitored in the background via the I2C interrupts, and if it contains data, is written out via the I2C bus. There are a couple of different ways of doing this (I2C 1 functions shown, replace 1 with 2 or 3 for other I2C ports):

For example, to write two bytes (0x80 and 0xA7) to a device with address 0x60, the following code can be used.

i2c1BeginTransmission(0x60);
i2c1Write(0x80);
i2c1Write(0xA7);
i2c1EndTransmission();

Alternatively the i2c1WriteMessage() function can be used to send an I2C message. The example above can be replaced by the following code.

BYTE msg[] = {0x80, 0xA7};
i2c1WriteMessage(0x60, msg, sizeof(msg));

or

i2c1WriteMessage(0x60, (BYTE*)"\x80\xA7", sizeof(msg)); //Write Byte 1=0x80, Byte2=0xA7 to slave at 0x60

Using the i2c1WriteAsciiEscString() function, the following code can be used:

i2c1WriteAsciiEscString("6080A7");

The i2c1WriteAsciiEscString() function uses "ASCII Format, with Escape Sequences" (Control Characters). This format uses 2 upper case characters to represent a single hex character. Strings must be enclosed within single quotation marks('). Lower case characters 'a' to 'z' are used to represent "Control characters". See ASCII Format, with Escape Sequence for details. The following Control Characters are defined:

This method of writing I2C messages is particularly well suited for writing messages that contain text strings. For example, to write a string to a "LCD2S Serial I2C Display", the following code can be used:

//Write to LCD2S at address 0x50, 0x80=Write Text command, 0x0C=Clear Screen command, 0x0A=Go to next line command
i2c1WriteAsciiEscString("50800C'Hello'0A'World'");

For additional examples, see examples on netcruzer.com.

Reading from the I2C Port

When reading data from an I2C device, the data will be read in the background via the I2C interrupts. The destination of the read data is determined by the read functions used:

The i2c1IsBusy() or i2c1GetStatusWait() functions can be used to check when the background transmission has finished (and if successful), and the read data is available.

The following example show how to use i2c1Read() to read data:

BYTE c;
i2c1BeginTransmission(0x50); //Start transmission to slave at address 0x50
i2c1Write(0xD0); //Write 0xD0 = address of register to read
i2c1Read(10); //Read 10 byte.
i2c1EndTransmission(); //End transmission, and start sending message in background
//Wait till transmission finished (background via interrupt), and check if successful
if (i2c1GetStatusWait(0)==0) {
//Get all available data from default Receive Circular Buffer (might be less than requested!).
while (i2c1HasData()) {
c = i2c1GetByte(); //Get next available byte
}
}

The following example show how to use i2c1ReadToArray() to read data to a BYTE array:

BYTE buf[2];
i2c1BeginTransmission(0x50); //Start transmission to slave at address 0x50
i2c1Write(0xD0); //Write 0xD0 = address of register to read
i2c1ReadToArray(buf, 2); //Read 2 bytes to buf
i2c1EndTransmission(); //End transmission, and start sending message in background
//Wait till transmission finished (background via interrupt), and get status
if (i2c1GetStatusWait(0)==0) {
//Message Successful. Read data available in buf[0] and buf[1].
}

The i2c1ReadSlaveReg() function replaces the code shown above with a single function.

BYTE buf[2];
//Read 2 bytes from register 0x0D of slave at address 0x50, and save result in "buf" array
i2c1ReadSlaveReg(0x50, 0x0D, buf, 2);
//Wait till transmission finished (background via interrupt), and get status
if (i2c1GetStatusWait(0)==0) {
//Message Successful. Read data available in buf[0] and buf[1].
}

Each I2C device actually has two addresses. The even address is used to write to it, and the odd address to read from it. Bit 0 (R/W bit) of the address byte is used to determine if a read or write command has to be executed. If 1, a I2C read is executed. To read, set bit 0 of the address. For example, accessing address 0x20 will write to the node, and address 0x21 will read from it.

Status of I2C Transmission

The status of an I2C transmission can be obtained by a couple of different methods.

Method 1 (pre transmission status):
The functions for adding data to the I2C Transmit Queue (Circular Buffer) all return 0 if successful. If any of them return a non-zero value, the Transmit Queue if probably full. At this stage the message has NOT been sent on the bus yet, so no bus error messages will be returned (slave not responding with ACK for example). These functions include:
i2c1BeginTransmission(), i2c1Write(), i2c1WriteString(), i2c1EndTransmission()....
To save checking the returned value of each function, only the value returned by i2c1EndTransmission() can be checked, for example:

i2c1BeginTransmission(0x50); //Start transmission to slave at address 0x50
i2c1Write(0xD0); //Write 0xD0
//End transmission, and start sending message in background. Returns 0 if ERROR!
if (i2c1EndTransmission()!=0) {
//Message Successfully added to transmit Queue.
}

Using this method of error checking does NOT detect if there is no slave on the bus with the given address. It however has the advantage of not having to wait for the transmission to finish!

Method 2 (post transmission status):
If additional status information is required, the code has to wait till the transmission is finished(is done in the background via I2C interrupts). Once finished, the i2c1GetStatus() function can be used to get the status of the transmission. The i2c1GetStatusWait() function will wait till finished, and then return the status. For example:

i2c1BeginTransmission(0x50); //Start transmission to slave at address 0x50
i2c1Write(0xD0); //Write 0xD0
i2c1EndTransmission(); //End transmission, and start sending message in background
//Wait till transmission finished (background via interrupt), and check if successful
if (i2c1GetStatusWait(0)==0) {
//Message Successfully added to transmit Queue, and sent to slave!
}

The status of the last 4 transmissions are stored, and can be requested by passing the address of the slave in the "adr" parameter of the i2c1GetStatus() function. Passing 0 to this function will return the status of the last transmission. This function is useful when sending an I2C message, and checking the status at a later stage. If other tasks also send I2C messages to other slaves, this function can be used to be sure to get the status of the desired message.

Port Independent I2C Functions

In stead of using the normal port specific i2c1Xxx(), i2c2Xxx() and i2c3Xxx() functions, the port independent i2cXxx() versions can be used. The same functions are available, but each has an additional parameter specifying the port to use. The first parameter of each function is a pointer to the I2C_INFO structure for the desired I2C port. The I2C1_INFO, I2C2_INFO and I2C3_INFO defines can be used. These functions can be used to optimize performance, seeing that they are a bit more efficient than the port specific versions. Internally, the port specific function call the port indepenent functions.

For example, the i2cWrite() function is the port independent version of the i2c1Write() function. Using the normal function to write 100 to I2C bus 1 looks like this:

i2c1Write(100);

Using port independent functions look like this:

i2cWrite(I2C1_INFO, 100);

or

I2C_INFO* objI2C = I2C1_INFO;
i2cWrite(objI2C, 100);

Advanced - Using Circular Buffers

Internally Circular Buffers are used for queueing transmit data, and storing received data. In stead of using the normal i2cXxx functions (like i2cBeginTransmisstion(), i2cWrite()...), data can be directly written to, and read from the Circular Buffer. The buffers are configured as Binary Format, with Escape Sequence. This means special "escape sequences"(control characters) are contained in the buffer, along with the normal data. See Binary Format, with Escape Sequence for details.

The CIRBUF_TX_I2Cn and CIRBUF_RX_I2Cn constants (where n is port number) can be used with the Circular Buffer functions to address the required transmit or receive I2C buffers. For example, to write a byte to I2C port 1's transmit buffer:

cbufPutByte(CIRBUF_TX_I2C1, 0x6A);

To send a message via the I2C port, it is written to the I2C transmit buffer. This buffer is processed in the background, and all data added to it is send via the I2S port. Special "escape sequences" (control characters) are used for putting Start, Stop and Repeated Start conditions on the bus. The '^' is the default "escape character". The "escape sequences" are:

Escape
Sequence
Description
^^ Will cause a single '^' character to be sent on the I2C port
^s Put START on bus. A second 's' control character (anywhere in string) before a 'p' control character will cause a REPEATED START condition.
^p Put STOP on bus
^rn Read 'n' number of bytes(n=0-254). A value of 255 means read till slave does not ACK. Read bytes are written to receive circular buffer.
^rn^ua Same as '^rn', but writes read bytes to given address in "User Memory", where 'a' is a value from 0 to 255.
^rn^ts Same as '^rn', but writes read bytes to given Serial Data Port Numbers, where 's' is the "Serial Data Port Buffer ID".
^Rnbb Read 'n' number of bytes(n=0-254), and writes read bytes to given address, 'bb' is address pointer.
^Lna Read 'n' number of bytes(n=0-254), and writes read bytes to given registered Circular Buffers, 'a' is OR'ed bits representing registered Circular Buffers

For the '^s' Escape Sequence (Start), the next byte in the buffer MUST be the slave address! Even addresses are used to write to the slave, and odd addresses to read from them.

For the '^r' Escape Sequence (Read), the next byte in the buffer MUST be the number of bytes to read! It can only be used inside a message with START and STOP conditions.

Examples

Following are some examples for using Circular Buffers for I2C Communication.

cbufPutControlChar(CIRBUF_TX_I2C1, 's'); //I2C Start - use 's' control character
cbufPutByte(CIRBUF_TX_I2C1, 0x6A); //I2C address - MUST follow I2C Start
cbufPutByte(CIRBUF_TX_I2C1, 0x52); //Data
cbufPutControlChar(CIRBUF_TX_I2C1, 'p'); //I2C Stop - use 'p' control character

Writes 0x52 to an I2C device with address 6A, via I2C port 1. The following characters will be added to the buffer
'^', 's', 0x5A, 0x52, '^', 'p'

cbufPutControlChar(CIRBUF_TX_I2C1, 's'); //I2C Start - use 's' control character
cbufPutByte(CIRBUF_TX_I2C1, 0x50); //I2C address - MUST follow I2C Start
cbufPutString(CIRBUF_TX_I2C1, "Hello"); //Data
cbufPutControlChar(CIRBUF_TX_I2C1, 'p'); //I2C Stop - use 'p' control character

Writes "Hello" to an I2C device with address 50, via I2C port 2. The following characters will be added to the buffer
'^', 's', 0x50, 'H', 'e', 'l', 'l', 'o', '^', 'p'

Advanced - Using ASCII Commands

As described in the previous section, Circular Buffers (configured as Binary Format, with Escape Sequence) are used for writing and reading data. It describes how to write and read binary data to the Circular Buffers. As an alternative, this same data can be written and read in ASCII format using the cbufPutAsciiEscString() function. This function will decode the given ASCII format to the "Binary Format, with Escape Sequence" format of the buffer. See ASCII Format, with Escape Sequence for details on the format of the ASCII string.

In short, the ASCII string uses 2 upper case characters to represent a single hex character. Strings must be enclosed within single quotation marks('). Lower case characters 'a' to 'z' are used to represent "Control characters".

Examples

Following are some examples for using ASCII commands for communicating I2C devices.

Writing 0x52 to an I2C device with address 6A, via I2C port 1

cbufPutAsciiEscString(CIRBUF_TX_I2C1, 0, "s6A52p"); //Start, Address 0x6A, Data 0x52, Stop

The following characters will be added to the buffer
'^', 's', 0x6A, 0x52, '^', 'p'

Writing "Hello" to an I2C device with address 50, via I2C port 2

cbufPutAsciiEscString(CIRBUF_TX_I2C1, 0, "s50'Hello'p"); //Start, Address 0x50, String "Hello", Stop

The following characters will be added to the buffer
'^', 's', 0x50, 'H', 'e', 'l', 'l', 'o', '^', 'p'