This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
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] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== i2c twiddler ====== | + | ====== Moved! ====== |
- | First, you'll need to have a toolchain installed so make sure you go back and install it! | + | This tutorial has moved to [[http://learn.adafruit.com/chumby-hacker-board/i2c-sensor|http://learn.adafruit.com/chumby-hacker-board/i2c-sensor]] |
- | + | ||
- | 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> | + |