This shows you the differences between two versions of the page.
chumbyhackerboard:i2c [2010/09/02 20:29] binkl Change address 58 to 29 so that the wiki text matches the i2cfail.gif |
chumbyhackerboard:i2c [2016/01/28 18:05] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== i2c twiddler ====== | ||
- | First, you'll need to have a toolchain installed so make sure you go back and install it! | ||
- | |||
- | Sean Cross wrote a great i2c twiddler-tool. This allows you to poke and peek at i2c chips right from the command line. Nice! | ||
- | |||
- | Scroll down and copy his i2c C code. Open up your terminal to the CHB and type in **mkdir /mnt/storage/dev** (or whatever place you want to store your code), then **cd /mnt/storage/dev** and finally **cat > i2c.c** into the terminal and hit return. | ||
- | Then paste in the code and finish by typing **Control-D** | ||
- | |||
- | {{:chumbyhackerboard:i2ccopy.gif|}} | ||
- | |||
- | Then compile the code with **gcc** by typing in **gcc -o i2c i2c.c** to create the **i2c** executable. Then run it and make sure you get the response below | ||
- | |||
- | {{:chumbyhackerboard:i2ccompile.gif|}} | ||
- | |||
- | ====== Reading the MMA7455L accelerometer ====== | ||
- | |||
- | There's a Freescale +-2G to +-8G 3-access accelerometer on the CHB for you to play with, lets get some readings. First off, we need to know what the i2c address is. [[http://www.freescale.com/files/sensors/doc/data_sheet/MMA7455L.pdf|Open up the datasheet]] and look for the section called "i2c Slave Address" | ||
- | |||
- | {{:chumbyhackerboard:mma7455addr.gif|}} | ||
- | |||
- | **$1D** means hexadecimal 0x1D which is the same as decimal 29. Great! Lets read byte #0 from the accelerometer by typing in **./i2c r 29 0** | ||
- | |||
- | {{:chumbyhackerboard:i2cfail.gif|}} | ||
- | |||
- | ?? We got an error that the register was not readable. This means the chip could not be found on the i2c bus. :( But then we rememer that i2c addresses are 7 bits long and are transmitted in the upper bits of an 8-bit byte. So we actually need to shift the address up by 1 bit. That's easy to do, though, just multiply by 2 to get i2c address **58** | ||
- | |||
- | {{:chumbyhackerboard:i2cok.gif|}} | ||
- | |||
- | Rock! Now we need to figure out what registers we can read, looking at the datasheet we see: | ||
- | |||
- | {{:chumbyhackerboard:mma7455reg.gif|}} | ||
- | |||
- | There's a lot of stuff! Lets start with one we know is going to work, like **$0D** (hex 0x0D = dec 13) | ||
- | |||
- | {{:chumbyhackerboard:mmareg13.gif|}} | ||
- | |||
- | It in fact returns the value 0x1D which we know is the i2c address | ||
- | |||
- | ====== Acceleromate! ====== | ||
- | |||
- | OK so now we want to get that XYZ data, right? Looking at the register file it seems like the first 6 bytes are used for 10-bit readings, but we can get single 8 bit readings from registers number 6, 7 and 8 | ||
- | |||
- | If you're careful you can read those registers while having a friend gently shake the board, you'll see different values returned | ||
- | |||
- | {{:chumbyhackerboard:mmai2ctest.gif|}} | ||
- | |||
- | However, wouldn't it be great if you didn't need a friend to shake the board while you pressed Up-arrow & Return? Lets edit Sean's code. To begin we will put **#define**'s in for the address and registers | ||
- | |||
- | <code> | ||
- | // The 'raw' 7 bit address shifted up | ||
- | #define MMA7455_I2CADDR (0x1D * 2) | ||
- | |||
- | // The registers to read! | ||
- | #define MMA7455_XOUT8 6 | ||
- | #define MMA7455_YOUT8 7 | ||
- | #define MMA7455_ZOUT8 8 | ||
- | </code> | ||
- | |||
- | Then replace the **main()** function with our own which is shorter and only reads those registers to print out the values | ||
- | |||
- | <code> | ||
- | int main(int argc, char **argv) { | ||
- | int i2c_file; | ||
- | int8_t x, y, z; // the readings are 8 bits and signed! | ||
- | |||
- | // Open a connection to the I2C userspace control file. | ||
- | if ((i2c_file = open(I2C_FILE_NAME, O_RDWR)) < 0) { | ||
- | perror("Unable to open i2c control file"); | ||
- | exit(1); | ||
- | } | ||
- | |||
- | // ignore arguments! | ||
- | |||
- | while (1) { | ||
- | | ||
- | // read X and Y and Z from the register | ||
- | if( get_i2c_register(i2c_file, MMA7455_I2CADDR, MMA7455_XOUT8, &x) || | ||
- | get_i2c_register(i2c_file, MMA7455_I2CADDR, MMA7455_YOUT8, &y) || | ||
- | get_i2c_register(i2c_file, MMA7455_I2CADDR, MMA7455_ZOUT8, &z) ) { | ||
- | |||
- | printf("Unable to read register!\n"); | ||
- | return -1; | ||
- | } | ||
- | |||
- | printf("X = %d\tY = %d\tZ = %d\n", x, y, z); | ||
- | } | ||
- | |||
- | close(i2c_file); | ||
- | return 0; | ||
- | } | ||
- | </code> | ||
- | |||
- | Note the while() loop, and that we read all three registers and stick the results into 8-bit signed variables. Then we printf() 'em all and loop again. | ||
- | |||
- | You can grab all of the code below | ||
- | |||
- | Stick the code in a new file called **mma7455.c** by copying and pasting as before. Then compile by running **gcc -o mma7455 mma7455.c** and run with **./mma7455**. Now shake it! | ||
- | |||
- | {{:chumbyhackerboard:shaken.gif|}} | ||
- | |||
- | There you go! Now you can talk to the accelerometer to get motion data, and this code is easily adaptable for any i2c chip you may want to use. Enjoy! | ||
- | |||
- | |||
- | ====== Basic i2c twiddler Code ====== | ||
- | |||
- | <file> | ||
- | /* | ||
- | This software uses a BSD license. | ||
- | |||
- | Copyright (c) 2010, Sean Cross / chumby industries | ||
- | All rights reserved. | ||
- | |||
- | Redistribution and use in source and binary forms, with or without | ||
- | modification, are permitted provided that the following conditions | ||
- | are met: | ||
- | |||
- | * Redistributions of source code must retain the above copyright | ||
- | notice, this list of conditions and the following disclaimer. | ||
- | * Redistributions in binary form must reproduce the above copyright | ||
- | notice, this list of conditions and the following disclaimer in the | ||
- | documentation and/or other materials provided with the | ||
- | distribution. | ||
- | * Neither the name of Sean Cross / chumby industries nor the names | ||
- | of its contributors may be used to endorse or promote products | ||
- | derived from this software without specific prior written | ||
- | permission. | ||
- | |||
- | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
- | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
- | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
- | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
- | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
- | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
- | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | ||
- | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | ||
- | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
- | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY | ||
- | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
- | POSSIBILITY OF SUCH DAMAGE. | ||
- | |||
- | */ | ||
- | |||
- | #include <stdio.h> | ||
- | #include <linux/i2c.h> | ||
- | #include <linux/i2c-dev.h> | ||
- | #include <fcntl.h> | ||
- | #include <stdlib.h> | ||
- | #include <unistd.h> | ||
- | #include <sys/ioctl.h> | ||
- | #include <string.h> | ||
- | |||
- | |||
- | #define I2C_FILE_NAME "/dev/i2c-0" | ||
- | #define USAGE_MESSAGE \ | ||
- | "Usage:\n" \ | ||
- | " %s r [addr] [register] " \ | ||
- | "to read value from [register]\n" \ | ||
- | " %s w [addr] [register] [value] " \ | ||
- | "to write a value [value] to register [register]\n" \ | ||
- | "" | ||
- | |||
- | static int set_i2c_register(int file, | ||
- | unsigned char addr, | ||
- | unsigned char reg, | ||
- | unsigned char value) { | ||
- | |||
- | unsigned char outbuf[2]; | ||
- | struct i2c_rdwr_ioctl_data packets; | ||
- | struct i2c_msg messages[1]; | ||
- | |||
- | messages[0].addr = addr; | ||
- | messages[0].flags = 0; | ||
- | messages[0].len = sizeof(outbuf); | ||
- | messages[0].buf = outbuf; | ||
- | |||
- | /* The first byte indicates which register we'll write */ | ||
- | outbuf[0] = reg; | ||
- | |||
- | /* | ||
- | * The second byte indicates the value to write. Note that for many | ||
- | * devices, we can write multiple, sequential registers at once by | ||
- | * simply making outbuf bigger. | ||
- | */ | ||
- | outbuf[1] = value; | ||
- | |||
- | /* Transfer the i2c packets to the kernel and verify it worked */ | ||
- | packets.msgs = messages; | ||
- | packets.nmsgs = 1; | ||
- | if(ioctl(file, I2C_RDWR, &packets) < 0) { | ||
- | perror("Unable to send data"); | ||
- | return 1; | ||
- | } | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | static int get_i2c_register(int file, | ||
- | unsigned char addr, | ||
- | unsigned char reg, | ||
- | unsigned char *val) { | ||
- | unsigned char inbuf, outbuf; | ||
- | struct i2c_rdwr_ioctl_data packets; | ||
- | struct i2c_msg messages[2]; | ||
- | |||
- | /* | ||
- | * In order to read a register, we first do a "dummy write" by writing | ||
- | * 0 bytes to the register we want to read from. This is similar to | ||
- | * the packet in set_i2c_register, except it's 1 byte rather than 2. | ||
- | */ | ||
- | outbuf = reg; | ||
- | messages[0].addr = addr; | ||
- | messages[0].flags = 0; | ||
- | messages[0].len = sizeof(outbuf); | ||
- | messages[0].buf = &outbuf; | ||
- | |||
- | /* The data will get returned in this structure */ | ||
- | messages[1].addr = addr; | ||
- | messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/; | ||
- | messages[1].len = sizeof(inbuf); | ||
- | messages[1].buf = &inbuf; | ||
- | |||
- | /* Send the request to the kernel and get the result back */ | ||
- | packets.msgs = messages; | ||
- | packets.nmsgs = 2; | ||
- | if(ioctl(file, I2C_RDWR, &packets) < 0) { | ||
- | perror("Unable to send data"); | ||
- | return 1; | ||
- | } | ||
- | *val = inbuf; | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int main(int argc, char **argv) { | ||
- | int i2c_file; | ||
- | |||
- | // Open a connection to the I2C userspace control file. | ||
- | if ((i2c_file = open(I2C_FILE_NAME, O_RDWR)) < 0) { | ||
- | perror("Unable to open i2c control file"); | ||
- | exit(1); | ||
- | } | ||
- | |||
- | |||
- | if(argc > 3 && !strcmp(argv[1], "r")) { | ||
- | int addr = strtol(argv[2], NULL, 0); | ||
- | int reg = strtol(argv[3], NULL, 0); | ||
- | unsigned char value; | ||
- | if(get_i2c_register(i2c_file, addr, reg, &value)) { | ||
- | printf("Unable to get register!\n"); | ||
- | } | ||
- | else { | ||
- | printf("Register %d: %d (%x)\n", reg, (int)value, (int)value); | ||
- | } | ||
- | } | ||
- | else if(argc > 4 && !strcmp(argv[1], "w")) { | ||
- | int addr = strtol(argv[2], NULL, 0); | ||
- | int reg = strtol(argv[3], NULL, 0); | ||
- | int value = strtol(argv[4], NULL, 0); | ||
- | if(set_i2c_register(i2c_file, addr, reg, value)) { | ||
- | printf("Unable to get register!\n"); | ||
- | } | ||
- | else { | ||
- | printf("Set register %x: %d (%x)\n", reg, value, value); | ||
- | } | ||
- | } | ||
- | else { | ||
- | fprintf(stderr, USAGE_MESSAGE, argv[0], argv[0]); | ||
- | } | ||
- | |||
- | |||
- | close(i2c_file); | ||
- | |||
- | |||
- | return 0; | ||
- | } | ||
- | </file> | ||
- | |||
- | ====== MMA7455L reader ====== | ||
- | <file> | ||
- | /* | ||
- | This software uses a BSD license. | ||
- | |||
- | Copyright (c) 2010, Sean Cross / chumby industries & Limor Fried / adafruit industries (we are both industrious people, eh?) | ||
- | |||
- | All rights reserved. | ||
- | |||
- | Redistribution and use in source and binary forms, with or without | ||
- | modification, are permitted provided that the following conditions | ||
- | are met: | ||
- | |||
- | * Redistributions of source code must retain the above copyright | ||
- | notice, this list of conditions and the following disclaimer. | ||
- | * Redistributions in binary form must reproduce the above copyright | ||
- | notice, this list of conditions and the following disclaimer in the | ||
- | documentation and/or other materials provided with the | ||
- | distribution. | ||
- | * Neither the name of Sean Cross / chumby industries nor the names | ||
- | of its contributors may be used to endorse or promote products | ||
- | derived from this software without specific prior written | ||
- | permission. | ||
- | |||
- | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
- | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
- | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
- | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
- | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
- | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
- | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | ||
- | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | ||
- | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
- | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY | ||
- | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
- | POSSIBILITY OF SUCH DAMAGE. | ||
- | */ | ||
- | |||
- | #include <stdio.h> | ||
- | #include <linux/i2c.h> | ||
- | #include <linux/i2c-dev.h> | ||
- | #include <fcntl.h> | ||
- | #include <stdlib.h> | ||
- | #include <unistd.h> | ||
- | #include <sys/ioctl.h> | ||
- | #include <string.h> | ||
- | |||
- | // The 'raw' 7 bit address shifted up | ||
- | #define MMA7455_I2CADDR (0x1D * 2) | ||
- | |||
- | // The registers to read! | ||
- | #define MMA7455_XOUT8 6 | ||
- | #define MMA7455_YOUT8 7 | ||
- | #define MMA7455_ZOUT8 8 | ||
- | |||
- | |||
- | #define I2C_FILE_NAME "/dev/i2c-0" | ||
- | |||
- | static int get_i2c_register(int file, | ||
- | unsigned char addr, | ||
- | unsigned char reg, | ||
- | unsigned char *val) { | ||
- | unsigned char inbuf, outbuf; | ||
- | struct i2c_rdwr_ioctl_data packets; | ||
- | struct i2c_msg messages[2]; | ||
- | |||
- | /* | ||
- | * In order to read a register, we first do a "dummy write" by writing | ||
- | * 0 bytes to the register we want to read from. This is similar to | ||
- | * the packet in set_i2c_register, except it's 1 byte rather than 2. | ||
- | */ | ||
- | outbuf = reg; | ||
- | messages[0].addr = addr; | ||
- | messages[0].flags = 0; | ||
- | messages[0].len = sizeof(outbuf); | ||
- | messages[0].buf = &outbuf; | ||
- | |||
- | /* The data will get returned in this structure */ | ||
- | messages[1].addr = addr; | ||
- | messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/; | ||
- | messages[1].len = sizeof(inbuf); | ||
- | messages[1].buf = &inbuf; | ||
- | |||
- | /* Send the request to the kernel and get the result back */ | ||
- | packets.msgs = messages; | ||
- | packets.nmsgs = 2; | ||
- | if(ioctl(file, I2C_RDWR, &packets) < 0) { | ||
- | perror("Unable to send data"); | ||
- | return 1; | ||
- | } | ||
- | *val = inbuf; | ||
- | |||
- | return 0; | ||
- | } | ||
- | |||
- | |||
- | int main(int argc, char **argv) { | ||
- | int i2c_file; | ||
- | int8_t x, y, z; // the readings are 8 bits and signed! | ||
- | |||
- | // Open a connection to the I2C userspace control file. | ||
- | if ((i2c_file = open(I2C_FILE_NAME, O_RDWR)) < 0) { | ||
- | perror("Unable to open i2c control file"); | ||
- | exit(1); | ||
- | } | ||
- | |||
- | // ignore arguments! | ||
- | |||
- | while (1) { | ||
- | | ||
- | // read X and Y and Z from the register | ||
- | if( get_i2c_register(i2c_file, MMA7455_I2CADDR, MMA7455_XOUT8, &x) || | ||
- | get_i2c_register(i2c_file, MMA7455_I2CADDR, MMA7455_YOUT8, &y) || | ||
- | get_i2c_register(i2c_file, MMA7455_I2CADDR, MMA7455_ZOUT8, &z) ) { | ||
- | |||
- | printf("Unable to read register!\n"); | ||
- | return -1; | ||
- | } | ||
- | |||
- | printf("X = %d\tY = %d\tZ = %d\n", x, y, z); | ||
- | } | ||
- | |||
- | close(i2c_file); | ||
- | return 0; | ||
- | } | ||
- | </file> |