The problem about I2C communication
Ann
Posts: 9
I want to realize to read three axis data of gyro by using Activity Board. The Example is shown as following.
Example: The I2C address of gyro is 105. The I2C addresses of gyro register which send data is 0x20 and the data is 0x1f. The address of register which receive data from I2C bus is 0X2B and the data would be saved in MSB. However, I cant read any data. Is there any mistakes in my program? Hope sb could slove it.
The following is completed code.
Example: The I2C address of gyro is 105. The I2C addresses of gyro register which send data is 0x20 and the data is 0x1f. The address of register which receive data from I2C bus is 0X2B and the data would be saved in MSB. However, I cant read any data. Is there any mistakes in my program? Hope sb could slove it.
The following is completed code.
#include "simpletools.h" // Include simpletools header
#include "simplei2c.h"
#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23
int main() // Main function
{
int x, y, z; // Declare x, y, & z axis variables
pause(1000);
int Addr = 105; // I2C address of gyro
i2c *bus = i2c_newbus(3, 2, 0); // New I2C bus SCL=P3, SDA=P2
i2c_out(bus,Addr,CTRL_REG1,16,0x1f,16); //open all axis
i2c_out(bus,Addr,CTRL_REG3,16,0x08,16); //Enable control ready signal
i2c_out(bus,Addr,CTRL_REG4,16,0x80,16); //Set scale (500 deg/sec)
pause(100);
while(1) // Repeat indefinitely
{
print("%c", HOME);
int MSB;
int LSB;
i2c_in(bus,Addr,0x29,8,MSB,8);
i2c_in(bus,Addr,0x28,8,LSB,8);
x = ((MSB << 8) | LSB);
i2c_in(bus,Addr,0x2B,8,MSB,8);
i2c_in(bus,Addr,0x2A,8,LSB,8);
y = ((MSB << 8) | LSB);
i2c_in(bus,Addr,0x2D,8,MSB,8);
i2c_in(bus,Addr,0x2C,8,LSB,8);
z = ((MSB << 8) | LSB);
print("\nx=%d, y=%d, z=%d%c\n", x, y, z, CLREOL); //Display raw compass values
waitcnt(CLKFREQ/2+CNT);
}
}
#include "simplei2c.h"
#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23
int main() // Main function
{
int x, y, z; // Declare x, y, & z axis variables
pause(1000);
int Addr = 105; // I2C address of gyro
i2c *bus = i2c_newbus(3, 2, 0); // New I2C bus SCL=P3, SDA=P2
i2c_out(bus,Addr,CTRL_REG1,16,0x1f,16); //open all axis
i2c_out(bus,Addr,CTRL_REG3,16,0x08,16); //Enable control ready signal
i2c_out(bus,Addr,CTRL_REG4,16,0x80,16); //Set scale (500 deg/sec)
pause(100);
while(1) // Repeat indefinitely
{
print("%c", HOME);
int MSB;
int LSB;
i2c_in(bus,Addr,0x29,8,MSB,8);
i2c_in(bus,Addr,0x28,8,LSB,8);
x = ((MSB << 8) | LSB);
i2c_in(bus,Addr,0x2B,8,MSB,8);
i2c_in(bus,Addr,0x2A,8,LSB,8);
y = ((MSB << 8) | LSB);
i2c_in(bus,Addr,0x2D,8,MSB,8);
i2c_in(bus,Addr,0x2C,8,LSB,8);
z = ((MSB << 8) | LSB);
print("\nx=%d, y=%d, z=%d%c\n", x, y, z, CLREOL); //Display raw compass values
waitcnt(CLKFREQ/2+CNT);
}
}

Comments
Normally, address is the upper 7 bits of the byte, and the last bit is reserved for read/write. 105d is 1101001b, so it looks like might want to shift that left one place and add in a r/w bit.
On a slightly related note I have a L3GD20 driver here that can serve as inspiration (it uses C++ and a different I2C system, but the higher level protocol is the same).
byte MSB, LSB;
MSB = readI2C(0x29);
LSB = readI2C(0x28);
x = ((MSB << 8) | LSB);
MSB = readI2C(0x2B);
LSB = readI2C(0x2A);
y = ((MSB << 8) | LSB);
MSB = readI2C(0x2D);
LSB = readI2C(0x2C);
z = ((MSB << 8) | LSB);
How to port this program to used on Activity Board?And the gyro is L3G4200D.
The documentation function descriptions say this about the I2C address:
i2cAddr 8 bit device address. This is the 7-bit I2C address and read/write bit.
To me, this is an opportunity for improvement and should say:
i2cAddr 8 bit device address. This is the 7-bit I2C address on bits 7..1 and the read/write bit on bit 0.
What it means is the I2C address used in the functions is a left shift by 1 address. Other systems use the address as stated in the data sheet. For example an EEPROM address is 0x50. In the Parallax world that address gets translated by the programmer to 0xA0 (0x50 left shift by 1).
The top level documentation is the key to understanding simple libraries. It provides a navigation point to find all library docs.
In my installation, the documentation starts at Documents\SimpleIDE\Learn\Simple Libraries Index.html
For example: file:///C:/Users/Steve/Documents/SimpleIDE/Learn/Simple Libraries Index.html
Simple Libraries
https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries Index.html
i2c_in
https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries/Utility/libsimpletools/html/simpletools_8h.html#a3716fa497e2d94ab9ab80c30b3109480
i2c_out
https://propsideworkspace.googlecode.com/hg/Learn/Simple Libraries/Utility/libsimpletools/html/simpletools_8h.html#a8e388b4d227694772970f0ee51ad2c31
#include "simpletools.h" // Include simpletools header#include "simplei2c.h" #define CTRL_REG1 0x20 #define CTRL_REG2 0x21 #define CTRL_REG3 0x22 #define CTRL_REG4 0x23 int main() // Main function { unsigned char x16, y16, z16; // Declare x, y, & z axis variables pause(1000); int Addr = 105; // I2C address of gyro int ack = 0; int nack = 1; uint8_t data[1]; //i2c compass; //i2c *bus = i2c_newbus(3, 2, 0); // New I2C bus SCL=P3, SDA=P2 i2c bus; i2c_open(&bus,3,2,0); i2c_start(&bus); ack = i2c_writeByte(&bus, CTRL_REG1); //open all axis ack = i2c_writeData(&bus,0x1f,1); ack = i2c_writeByte(&bus, CTRL_REG3); //Enable control ready signal ack = i2c_writeData(&bus,0x08,1); ack = i2c_writeByte(&bus, CTRL_REG4); ack = i2c_writeData(&bus,0x80,1); //Set scale (500 deg/sec) //i2c_out(bus,Addr,CTRL_REG1,1,0x1f,1); //open all axis //i2c_out(bus,Addr,CTRL_REG3,1,0x08,1); //Enable control ready signal //i2c_out(bus,Addr,CTRL_REG4,1,0x80,1); //Set scale (500 deg/sec) pause(100); while(1) // Repeat indefinitely { print("%c", HOME); int MSB; int LSB; //i2c_open(&bus,3,2,0); i2c_start(&bus); ack = i2c_writeByte(&bus, 0x29); ack = i2c_readData(&bus, data, 1); MSB = data; ack = i2c_writeByte(&bus, 0x28); ack = i2c_readData(&bus, data, 1); LSB = data; i2c_stop(&bus); //i2c_in(bus,Addr,0x29,8,MSB,8); //i2c_in(bus,Addr,0x28,8,LSB,8); x16 = ((MSB << 8) | LSB); i2c_start(&bus); ack = i2c_writeByte(&bus, 0x2B); ack = i2c_readData(&bus, data, 1); MSB = data; ack = i2c_writeByte(&bus, 0x2A); ack = i2c_readData(&bus, data, 1); LSB = data; i2c_stop(&bus); //i2c_in(bus,Addr,0x2B,8,MSB,8); //i2c_in(bus,Addr,0x2A,8,LSB,8); y16 = ((MSB << 8) | LSB); i2c_start(&bus); ack = i2c_writeByte(&bus, 0x2D); ack = i2c_readData(&bus, data, 1); MSB = data; ack = i2c_writeByte(&bus, 0x2C); ack = i2c_readData(&bus, data, 1); LSB = data; i2c_stop(&bus); //i2c_in(bus,Addr,0x2D,8,MSB,8); //i2c_in(bus,Addr,0x2C,8,LSB,8); z16 = ((MSB << 8) | LSB); int x, y, z; int *px, *py, *pz; px = &x; py = &y; pz = &z; *px = x16; *py = y16; *pz = z16; print("\nx=%d, y=%d, z=%d%c\n", x, y, z, CLREOL); //Display raw compass values waitcnt(CLKFREQ/2+CNT); } }The ee_writeBuffer and ee_readBuffer functions show how to write basic read/write code.
There are simpler ways to do this, but I wanted it to be complete, useful, and non-destructive.
--Steve
/* * EEPROM Test program demonstrates using simplei2c library. */ #include "simpletools.h" #include "fdserial.h" // forward declarations int ee_writeBuffer(i2c *bus, int devaddr, int address, unsigned char *buffer, int length); int ee_readBuffer( i2c *bus, int devaddr, int address, unsigned char *buffer, int length); #define PAGELEN 128 #define COUNT 16*(1024/PAGELEN)*PAGELEN // N*(...) KB #define TESTADDR 0x4000 // set to 0 for testing lower 16KB of EEPROM #define EEADDR 0xA0 // eeprom i2c address 0x50 << 1 #define VERBOSE 0 // don't print all debug info // the pattern generator so we don't end up with the same pattern in every page. #define VAL(n) (((n) + (n)/(PAGELEN)) & 0xff) int main(void) { int n; // index variable int val; // temporary variable int fail = 0; // failure counter int addrOffset = TESTADDR; unsigned char buffer[PAGELEN+1]; // buffer for testing fdserial *ser; // we need fdserial because we want to use rxTime. i2c eebus; // the i2c bus var will be passed as &eebus pointer. extern terminal *dport_ptr; // this is the default serial port pointer. // some simpletext libraries need more time for terminal startup. waitcnt(CLKFREQ/2+CNT); putLine("Check EEPROM"); // switch from simpleserial to fdserial simpleterm_close(); ser = fdserial_open(31,30,0,115200); dport_ptr = ser; // make sure this is the default port // let the user choose to write EEPROM putStr("Press 'y' within 2 seconds to write EEPROM address "); putHex(addrOffset); putLine("."); // check for user input val = fdserial_rxTime(ser, 2000); // open the I2C bus with SCL driven so it works with all Parallax boards. i2c_open(&eebus, 28, 29, 1); if('y' == val) { // Erase EEPROM before write to ensure write was effective on multiple write passes. putLine("\nErasing EEPROM"); for(n = 0; n < PAGELEN; n++) { // fill buffer with 0xFF's buffer[n % PAGELEN] = 0xFF; } // Go from 0 to COUNT (+1) to ensure last "sector" is erased. for(n = 0; n < COUNT+1; n++) { if(n % PAGELEN == 0) { ee_writeBuffer(&eebus, EEADDR, ((n-PAGELEN)/PAGELEN)*PAGELEN+addrOffset, buffer, PAGELEN); } } putLine("\nWriting EEPROM"); // n < COUNT+1 ensures last "sector" is written. for(n = 0; n < COUNT+1; n++) { if(n > 0 && n % PAGELEN == 0) { // this is only called whenever we hit a page boundary after filling the buffer. ee_writeBuffer(&eebus, EEADDR, ((n-PAGELEN)/PAGELEN)*PAGELEN+addrOffset, buffer, PAGELEN); } // get new and mostly unique pattern for the buffer. val = buffer[n % PAGELEN] = VAL(n); // if verbosity is enabled dump. mostly for debug purposes. if(VERBOSE) { if (n % 16 == 0) { // do this every 16th byte to have a nice display putStr("\nADDR "); writeHexDigits(ser, n+addrOffset, 4); putChar(':'); } putChar(' '); writeHexDigits(ser, val, 2); } } } // finally, do the read test. putLine("\nReading EEPROM"); fail = 0; // this time we want to read the buffer before checking. // it means we don't want n < COUNT+1 for(n = 0; n < COUNT; n++) { if(n % PAGELEN == 0) { ee_readBuffer(&eebus, EEADDR, n+addrOffset, buffer, PAGELEN); } val = buffer[n%PAGELEN]; // check for bad value. if(1 && val != VAL(n)) { if(!fail) { putStr("\nError at address: "); writeHexDigits(ser, n+addrOffset, 4); putStr(" Expected "); writeHexDigits(ser, VAL(n), 2); putStr(" Recieved "); writeHexDigits(ser, val, 2); putLine("\nThis program must write EEPROM at least once for test pass."); } fail++; } if (VERBOSE) { if(n % 16 == 0) { waitcnt(CLKFREQ/10+CNT); putStr("\nADDR "); writeHexDigits(ser, n+addrOffset, 4); putChar(':'); } putChar(' '); writeHexDigits(ser, val, 2); } } if(fail) { putStr("\nTest errors: "); putDec(fail); } putLine("\nTest Complete."); return 0; } int ee_writeBuffer(i2c *bus, int devaddr, int address, unsigned char *buffer, int length) { int result = 0; int upaddr = ((address >> 16) & 0x07) << 1; //print("\nee_writeBuffer: Dev %02x Addr %05x Count %x\n", devaddr, address, length); i2c_start(bus); i2c_writeByte(bus, devaddr | upaddr); i2c_writeByte(bus, (address >> 8) & 0xff); i2c_writeByte(bus, address & 0xff); result = i2c_writeData(bus, buffer, length); i2c_stop(bus); do { waitcnt(CLKFREQ/1000+CNT); } while(i2c_poll(bus, devaddr)); return result; } int ee_readBuffer(i2c *bus, int devaddr, int address, unsigned char *buffer, int length) { int result = 0; int upaddr = ((address >> 16) & 0x07) << 1; //print("\nee_readBuffer : Dev %02x Addr %05x Count %x\n", devaddr, address, length); i2c_start(bus); i2c_writeByte(bus, devaddr | upaddr); i2c_writeByte(bus, (address >> 8) & 0xff); i2c_writeByte(bus, (address) & 0xff); i2c_start(bus); i2c_writeByte(bus, devaddr | 1); result = i2c_readData(bus, buffer, length); i2c_stop(bus); return result; }Alright, we'll use i2c_in and i2c_out. First step would be to make sure the device can reply with its own ID with i2c_in. Please run this and let me know if you get the "Device ID confirmed..." message.
#include "simpletools.h" // Include simpletools header unsigned char addrW = 0xd2; // I2C write address unsigned char addrR = 0xd3; // I2C read address unsigned char devId = 0x0f; // Device ID register unsigned char reply; // Single byte reply i2c *bus; // Bus for gyro int main() // main function { bus = i2c_newbus(5, 4, 0); // Set I2C bus SCL=P5, SDA=P4 i2c_in(bus, addrR, &devId, 1, &reply, 1); // Can device ID itself? if(reply == 0b11010011) // Is device communicating? print("Device ID confirmed: %b.\n", reply); else print("Error, wrong device ID: %b\n", reply); }#include "simpletools.h" // Include simpletools header unsigned char addrW = 0xd2; // I2C write address unsigned char addrR = 0xd3; // I2C read address // L3G4200D register addresses & commands. // See device datasheet section 7 for more info. unsigned char devId = 0x0f; // Device ID unsigned char ctrl1 = 0x20; // Control reg 1 unsigned char cfg1 = 0b00011111; // 100 Hz, 25 cutoff, power up, axes enabled unsigned char ctrl2 = 0x21; unsigned char ctrl3 = 0x22; unsigned char cfg3 = 0b00001000; // Enable data polling (I2_DRDY) unsigned char ctrl4 = 0x23; unsigned char cfg4 = 0b10000000; // Block until read, big endian unsigned char status = 0x27; unsigned char xL = 0x28; // Reg for x low byte // Next 5 bytes xH, yL, yH, zL, xH unsigned char reply; // Single byte reply char xyz[6]; // XYZ data array. int x, y, z; // Axis variables i2c *bus; // Declare I2C bus int main() // main function { bus = i2c_newbus(5, 4, 0); // New I2C bus SCL=P5, SDA=P4 int n; // Setup + count bytes n = i2c_out(bus, addrW, &ctrl3, 1, &cfg3, 1); n += i2c_out(bus, addrW, &ctrl4, 1, &cfg4, 1); n += i2c_out(bus, addrW, &ctrl1, 1, &cfg1, 1); if(n != 9) // Check bytes { print("Bytes should be 9, but was %d.", n); while(1); } while(1) // Main loop { int ready = 0; // Wait until ready while(!ready) { i2c_in(bus, addrR, &status, 1, &ready, 1); ready = 1 & (ready >>= 3); } for(int i = 0; i < 6; i++) // Get axis bytes { int regAddr = xL + i; i2c_in(bus, addrR, ®Addr, 1, &xyz[i], 1); } // Bytes to int x = (int) (short) ((xyz[1] << 8) + xyz[0]) / 114; y = (int) (short) ((xyz[3] << 8) + xyz[2]) / 114; z = (int) (short) ((xyz[5] << 8) + xyz[4]) / 114; printf("%c x = %d, y = %d, zL = %d%c\n\n", // Display values HOME, x, y, z, CLREOL); pause(250); } }