Hi everyone, I've an udoo quad and would like to connect a device using i2c protocol: I saw on the pinout diagram that pin labelled 20 and 21 should be SDA and SCL of an i2c port. Reading other threads I've seen people trying to use that port, but anyone mentioned how did they enable that port. How can I use that port without using the device tree editor (I don't have a screen attached) or how can I use the device tree editor from my computer and not the udoo itself? Thanks, Daniele
If your Udoo is connected to a network (through wifi or wired) you can use vnc to setup a remote desktop session and do the device tree editor stuff. https://www.udoo.org/docs/Basic_Setup/Remote_Desktop_(VNC).html
Linux has some tools to check the i2c bus. You could use python with smbus library to communicate with your i2c device. Has the manufacturer of your i2c device no programming examples? See also the following page as a reference, skip the device specific first parts https://www.acmesystems.it/user_i2c
Yes I have the data sheet for a couple of I2C Devices. I will start with a simple EE-PROM for a write and a read. If you know of any Python code that would be great. I think you have answered my question about the Library. Is this uncommon that one would use the Linux host side for I2C Communication? Are most users using the Arduino for the I2C? I will give that a shot. THANKS~!
Normally you use the Arduino part for Real Time reading sensors and controlling actuators, motors etc based on the input of the sensors (I/O). The communication protocol could be I2C if these devices are I2C. But you could also use general binary input/output (just reading a switch open/closed, switching LED on/off etc) directly. You normally use the Linux part for non real-time dependant processes like hosting webserver, sending data to the cloud, communicating with the www, user interface etc and communicate through serial with the Arduino part. The linux part performance of I/O is heavily influenced by it's load (it has to do a lot of different things). So if for example you are reading a sensor and want to control a LED based on this switch as fast as possible and within a certain time frame you use the Arduino part. Of the reaction time is not that important then you can also use the Linux part.
For controlling or troubleshooting i2c devices from the Linux shell, install the package "i2c-tools" if you don't already have it (apt-get install i2c-tools). This pulls in the commands "i2cdetect", "i2cget", "i2cset" and "i2cdump". For all of these you have to give the bus number, e.g. "-y 0" for the /dev/i2c-0 bus mappable to pins 20 and 21. A good first step is just to scan the bus for available devices. An example from my home Udoo which has 9 i2c devices sharing the bus: Code: root@imp:/# i2cdetect -y 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- 1d -- -- 20: -- -- -- -- -- -- 26 27 -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- 41 -- -- 44 45 -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- 75 76 77 i2cdump is useful for reading the contents of small serial EEPROMs such as 24LC02's. For example, reading out the monitor's EDID on a PC (bus numbers will vary by motherboard & video card, but the 0x50 i2c address should remain the same): Code: senin ~ # i2cdump -y 4 0x50 No size specified (using byte-data access) 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 ff ff ff ff ff ff 00 10 ac 7b a0 4c 50 48 30 ........??{?LPH0 10: 2c 19 01 04 a5 34 20 78 3a ee 95 a3 54 4c 99 26 ,????4 x:???TL?& 20: 0f 50 54 a1 08 00 81 40 81 80 a9 40 b3 00 d1 c0 ?PT??.?@???@?.?? 30: 01 01 01 01 01 01 28 3c 80 a0 70 b0 23 40 30 20 ??????(<??p?#@0 40: 36 00 06 44 21 00 00 1a 00 00 00 ff 00 37 57 46 6.?D!..?.....7WF 50: 47 31 35 41 51 30 48 50 4c 0a 00 00 00 fc 00 44 G15AQ0HPL?...?.D 60: 45 4c 4c 20 55 32 34 31 32 4d 0a 20 00 00 00 fd ELL U2412M? ...? 70: 00 32 3d 1e 53 11 00 0a 20 20 20 20 20 20 00 84 .2=?S?.? .? 80: 00 ff ff ff ff ff ff 00 10 ac 7b a0 4c 50 48 30 ........??{?LPH0 90: 2c 19 01 04 a5 34 20 78 3a ee 95 a3 54 4c 99 26 ,????4 x:???TL?& a0: 0f 50 54 a1 08 00 81 40 81 80 a9 40 b3 00 d1 c0 ?PT??.?@???@?.?? b0: 01 01 01 01 01 01 28 3c 80 a0 70 b0 23 40 30 20 ??????(<??p?#@0 c0: 36 00 06 44 21 00 00 1a 00 00 00 ff 00 37 57 46 6.?D!..?.....7WF d0: 47 31 35 41 51 30 48 50 4c 0a 00 00 00 fc 00 44 G15AQ0HPL?...?.D e0: 45 4c 4c 20 55 32 34 31 32 4d 0a 20 00 00 00 fd ELL U2412M? ...? f0: 00 32 3d 1e 53 11 00 0a 20 20 20 20 20 20 00 84 .2=?S?.? .? i2cget and i2cget read & write registers (respectively) one byte at a time, and can work with almost any type of i2c chip, so long as timing isn't critical and you have a register map on hand. Here's an example script I use to switch power on and off to some rarely-used external USB backup drives, using a custom relay board driven by a Microchip MCP23008 i2c I/O-expander chip. I use two leftover output bits from this chip to control an unrelated device, hence the bitwise AND and OR operations to avoid disturbing those pins: Code: #!/bin/bash #+ 8 = 3G /w #+ 4 = 4G /y #+ 2 = 2G /s #+ 1 = 3G /u # 0x40 = main-power latching relay; 0x80 = invert polarity ADDR=0x27 # MCP23008 i2c address DLY=.2 CUR=`i2cget -y 0 $ADDR 0x0a` PV=$[ $CUR & 0x30 ] # PV control bits to be preserved: # 0x10 = inhibit DCDC; 0x20 = force-on DCDC if [ .$1 = .off ]; then i2cset -y 0 $ADDR 0x00 0x00; sleep $DLY # 0 -> IODIR; all pins as output i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | 0x40 ];`; sleep $DLY i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | 0xc0 ];`; sleep $DLY i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | 0x40 ];`; sleep $DLY i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | 0x00 ];` elif [ .$1 = .on ]; then i2cset -y 0 $ADDR 0x00 0x00; sleep $DLY i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | 0x80 ];`; sleep $DLY i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | 0x00 ];` elif [ .$1 = . ]; then head $0 |grep '^#+' echo echo $CUR else i2cset -y 0 $ADDR 0x0a `printf '0x%x' $[ $PV | $1 ];` fi
Here's an example of how to control an i2c device on the Linux side from the C language, either plain C or C++. This is a little command-line tool I wrote for sending messages to an HD44780-type LCD character display, connected behind an MCP23017 I/O expander with the pinout given in the #define statements up top. My LCD has a 3-color RGB backlight, so the first parameter is taken as a 3-bit (0-7) decimal number to set the backlight color. This is modeled after some Python code published at Adafruit. Code: // jnh - based on Adafruit Adafruit_CharLCD_IPclock_example.py // I2C/MCP23017 version, 2016-09-17 #include <stdio.h> #include <stdlib.h> #include <linux/i2c-dev.h> #include <error.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int addr = 0x26; /* 23017 chip I2C addr */ char *i2cbus= "/dev/i2c-0"; #define D4 1 // 23017 GPB0 #define D5 2 // 23017 GPB1 #define D6 4 // 23017 GPB2 #define D7 8 // 23017 GPB3 #define BK_R 16 // 23017 GPB4 #define BK_G 32 // 23017 GPB5 #define BK_B 64 // 23017 GPB6 #define RS 128 // 23017 GPB7 #define EN_HI 0xFF // 23017 GPA7 #define EN_LO 0x7F // 23017 GPA7 #define IODIRA 0x00 // 23017 bit directions for GPIO-A #define IODIRB 0x01 // 23017 bit directions for GPIO-A #define OLATA 0x14 // 23017 output latch: GPIO-A #define OLATB 0x15 // 23017 output latch: GPIO-B // commands #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 // flags for display entry mode ( | LCD_ENTRYMODESET = 0x04 ); #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // flags for display on/off control ( | DISPLAYCONTROL = 0x08 ) #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // flags for display/cursor shift ( | LCD_CURSORSHIFT = 0x10 ) #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // flags for function set ( | LCD_FUNCTIONSET = 0x20 ) #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 int i2c_fd, gpb, bklt=0; unsigned char displaycontrol=0, displayfunction=0, displaymode=0; wr_reg(unsigned char reg, unsigned char val) { unsigned char buf[4]; buf[0]=reg; buf[1]=val; if ((write(i2c_fd, buf, 2)) != 2) { fprintf(stderr,"Error writing 0x%x to reg 0x%x @ addr 0x%x on %s\n",val,reg,addr,i2cbus); exit(1); } } pulse_en () { wr_reg(OLATA,EN_HI); usleep(2); wr_reg(OLATA,EN_LO); usleep(1); wr_reg(OLATA,EN_HI); usleep(2); } write1nibble(unsigned char nib) { wr_reg(OLATB,(bklt | (nib & 0xF)) ); // RS=high-bit => 0 usleep(20); pulse_en(); usleep(40); } writenibbles(unsigned char value, char mode) { unsigned char nib; //fprintf(stderr,"sending %X / %d]\n",value, mode); /* high nibble first */ nib = (value >> 4); wr_reg(OLATB,(bklt | (nib & 0xF) | (mode ? 0x80 : 0x00)) ); usleep(20); pulse_en(); usleep(20); nib = (value & 0x0f); wr_reg(OLATB,(bklt | (nib & 0xF) | (mode ? 0x80 : 0x00)) ); usleep(20); pulse_en(); usleep(100); } message (char *msg) { int j; for (j=0; (msg[j]!=0); j++) { if ((msg[j] == '\n' || msg[j] == '\r')) writenibbles(0xC0,0); /* next line */ else writenibbles(msg[j],1); } } main(int argc, char **argv) { int i,s; if (argc < 2) { fprintf(stderr,"usage: %s <bkcolor>\n",argv[0]); fprintf(stderr,"usage: %s <bkcolor> \"message\"\n",argv[0]); fprintf(stderr," -OR- %s <bkcolor> <charnum1> <param2>...\n",argv[0]); exit(1); } bklt=((7 ^ atoi(argv[1])) << 4); if ((i2c_fd = open(i2cbus, O_RDWR)) < 0) { fprintf(stderr,"Failed to open i2c port %s\n",i2cbus); exit(1); } if (ioctl(i2c_fd, I2C_SLAVE, addr) < 0) { fprintf(stderr,"Unable to get bus access to talk to addr %x on %s\n",addr,i2cbus); exit(1); } wr_reg (IODIRA,0x7F); // 0xFF & ~EN; wr_reg (IODIRB,0x00); // all GPIO-B pins to output if (argc == 2) { wr_reg(OLATB,bklt); exit(0); } /* init: enter & exit 8-bit mode to reset possible nibble misalignment... */ write1nibble(0x3); usleep(5000); write1nibble(0x3); usleep(5000); write1nibble(0x3); usleep(1000); write1nibble(0x2); usleep(1000); writenibbles(LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS,0); usleep(50); writenibbles(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF,0); // 0x0C usleep(50); writenibbles(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT,0); // 0x06 usleep(50); writenibbles(LCD_CLEARDISPLAY,0); usleep(3200); if (argc > 3) { s=atoi(argv[2]); for (i=s;i<(s+16);i++) writenibbles(i,1); writenibbles(0xC0,0); for (i=(s+16);i<(s+32);i++) writenibbles(i,1); } else message (argv[2]); close(i2c_fd); return 0; }
As one more Linux i2c C-language example that involves reading a sensor rather than controlling something, here's a short utility I wrote for pulling voltage and current (shunt) measurements from a pair of TI INA219 power monitoring chips, and multiplying them to calculate wattage. Code: #include <stdio.h> #include <stdlib.h> #include <linux/i2c-dev.h> #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int fd; int addr1 = 0x44; int addr2 = 0x45; char *i2cbus= "/dev/i2c-0"; unsigned char buf[32]; // Buffer for i2c data being read/written int read_register(int addr, unsigned char reg, unsigned char mask, unsigned char sh) { short value; if ((fd = open(i2cbus, O_RDWR)) < 0) { fprintf(stderr,"Failed to open i2c port\n"); exit(1); } if (ioctl(fd, I2C_SLAVE, addr) < 0) { fprintf(stderr,"Unable to get bus access to talk to INA219 at %X\n",addr); exit(1); } buf[0] = reg; if ((write(fd, buf, 1)) != 1) { fprintf(stderr,"Error sending register address %d to %X\n",reg,addr); exit(1); } if (read(fd, buf, 2) != 2) { fprintf(stderr,"Unable to read from register %d on %X\n",reg,addr); exit(1); } close(fd); if (mask != 0 ) buf[1] &= mask; value = ( (buf[0] << 8) | buf[1]); if (sh != 0 ) return (int)(value << sh); else return (int)(value); } int read_register_trig(int addr, unsigned char reg, unsigned char mask, unsigned char sh) { short value; if ((fd = open(i2cbus, O_RDWR)) < 0) { fprintf(stderr,"Failed to open i2c port\n"); exit(1); } if (ioctl(fd, I2C_SLAVE, addr) < 0) { fprintf(stderr,"Unable to get bus access to talk to INA219 at %X\n",addr); exit(1); } // trigger conversion: buf[0] = 0; // config reg buf[1] = 0b00001111; // upper 8 bits buf[2] = 0b11111111; // lower 8 bits if ((write(fd, buf, 3)) != 3) { fprintf(stderr,"Error writing to config register\n"); exit(1); } usleep(68000); buf[0] = reg; if ((write(fd, buf, 1)) != 1) { fprintf(stderr,"Error sending register address %d to %X\n",reg,addr); exit(1); } if (read(fd, buf, 2) != 2) { fprintf(stderr,"Unable to read from register %d on %X\n",reg,addr); exit(1); } close(fd); if (mask != 0 ) buf[1] &= mask; value = ( (buf[0] << 8) | buf[1]); if (sh != 0 ) return (int)(value << sh); else return (int)(value); } void init_conf_cal(int addr, unsigned int Cal) { if ((fd = open(i2cbus, O_RDWR)) < 0) { fprintf(stderr,"Failed to open i2c port\n"); exit(1); } if (ioctl(fd, I2C_SLAVE, addr) < 0) { fprintf(stderr,"Unable to get bus access to talk to INA219 at %X\n",addr); exit(1); } // set config register // bit 15: (0) RESET // bit 14: (0) n/c // bit 13: (0) 0=16V range, 1=32V // bit 12/11: (01) PG1/PG0: Shunt voltage gain // 00 = 1, +/- 40mV fullscale // -> 01 = 2, +/- 80mV fullscale 80mV/.01ohm = 8A FS // 10 = 4, +/-160mV fullscale // 11 = 8, +/-320mV fullscale // bits 10,9,8,7: (1111) BADC: bus ADC resolution, samples to average // bit 6,5,4,3: (1111) SADC: shunt ADC resolution, samples to average // bits 2,1,0: (111) Op Mode: Shunt & Bus, continuous // 011 = shunt & bus, triggered buf[0] = 0; // config reg buf[1] = 0b00001111; // upper 8 bits buf[2] = 0b11111111; // lower 8 bits if ((write(fd, buf, 3)) != 3) { fprintf(stderr,"Error writing to config register\n"); exit(1); } // CALIBRATION REGISTER (REG 5) // ============================= // VBUS_MAX = 32V (set above) // VSHUNT_MAX = 0.08V (gain 8) // RSHUNT = 0.01 ohm // MaxPossible_I = VSHUNT_MAX / RSHUNT = 8.0A // MaxExpected_I = 8.0A, same // MinLSB = MaxExpected_I/32767 = .000244148 // MaxLSB = MaxExpected_I/4096 = .001953125 // CurrrentLSB = MinLSB for now = .00024414807580797753 // Cal = trunc (0.04096 / (Current_LSB * RSHUNT)) // Cal = 16776 // Cal = 16776; buf[0] = 5; // calibration register buf[1] = ((Cal & 0xFF00) >> 8); buf[2] = (Cal & 0xFF); if ((write(fd, buf, 3)) != 3) { fprintf(stderr,"Error writing to config register\n"); exit(1); } // Calculate Power LSB: // PowerLSB = 20 * CurrentLSB // PowerLSB = .0048829615161595506 (4.89 mW per bit) // // Compute max current & shunt voltage values before overflow // // Max_Current_Overflow = min(CurrentLSB*32767,MaxPossible_I) = 8.0 A // // Max_ShuntVoltage = Max_Current_Overflow * RSHUNT = .08V // (clamp to VSHUNT_MAX if greater) // // Compute max power // MaxPower = Max_Current_Overflow * VBUS_MAX // MaxPower = 8.0A * 32V = 256.0W close(fd); } main (int argc, char **argv) { int r,v; if ( (argc == 3) && (argv[1][0]=='-') && (argv[1][1]=='i') ) { init_conf_cal(addr1,atoi(argv[2])); init_conf_cal(addr2,atoi(argv[2])); fprintf(stderr,"Initialization complete\n"); } else if ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='d') ) { r=0; printf("a%X r%d = %d",addr1,r,read_register_trig(addr1,r,0,0)); putchar('\t'); printf("a%X r%d = %d",addr2,r,read_register_trig(addr2,r,0,0)); putchar('\n'); for(r=1;r<=5;r++) { printf("a%X r%d = %d",addr1,r,read_register(addr1,r,0,0)); putchar('\t'); printf("a%X r%d = %d",addr2,r,read_register(addr2,r,0,0)); putchar('\n'); } } else if ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='v') ) { printf("V_a: %5.3f\tV_b: %5.3f\n", (float)read_register_trig(addr2,2,0xF8,0)*1/2000, (float)read_register_trig(addr1,2,0xF8,0)*1/2000); } else if ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='a') ) { printf("A_a: %5.3f\tA_b: %5.3f\n", (float)read_register_trig(addr2,1,0,2)*8/32767, (float)read_register_trig(addr1,1,0,2)*8/32767); } else if ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='A') ) { printf("A_a: %5.3f\tA_b: %5.3f\n", (float)read_register_trig(addr2,4,0,2)*2/32767, (float)read_register_trig(addr1,4,0,2)*2/32767); } else if ( ( argc == 1 ) || \ ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='p') ) ) { printf("P_a: %5.3f\tP_b: %5.3f\n", (float)read_register_trig(addr2,3,0,0)*20*8/32767, (float)read_register_trig(addr1,3,0,0)*20*8/32767); } else if ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='w') ) { float v1,v2,a1,a2; char cf; v1=(float)read_register_trig(addr2,2,0xF8,0)*1/2000; v2=(float)read_register_trig(addr1,2,0xF8,0)*1/2000; a1=(float)read_register(addr2,4,0,2)*2/32767; a2=(float)read_register(addr1,4,0,2)*2/32767; if (a1 > 0 && a2 > 0) cf=(v1>v2 ? 'X' : 'x'); else if (a1 > 0) cf=(v1>v2 ? 'V' : 'v'); else cf=(v1>v2 ? 'B' : 'b'); printf("%4.2f%c %3.1f", (v1>v2 ? v1 : v2), cf, (a1*v1 + a2*v2)); } else if ( (argc == 2) && (argv[1][0]=='-') && (argv[1][1]=='W') ) { float v1,v2,p1,p2,a1,a2; v1=(float)read_register_trig(addr2,2,0xF8,0)*1/2000; v2=(float)read_register_trig(addr1,2,0xF8,0)*1/2000; a1=(float)read_register(addr2,4,0,2)*2/32767; a2=(float)read_register(addr1,4,0,2)*2/32767; p1=(float)read_register(addr2,3,0,0)*20*8/32767; p2=(float)read_register(addr1,3,0,0)*20*8/32767; printf("%4.2f%c %4.2f %4.2f [%4.2f %4.2f]", (v1>v2 ? v1 : v2), (v1>v2 ? 'V' : 'v'), a1*v1,a2*v2,p1,p2); } else if ( (argc == 2) && (argv[1][0]>='0') && (argv[1][0]<='9') ) { r = atoi(argv[1]); if (r >= 0 && r <= 5) { printf("a%X r%d = %d",addr1,r,read_register_trig(addr1,r,0,0)); putchar('\t'); printf("a%X r%d = %d",addr2,r,read_register_trig(addr2,r,0,0)); putchar('\n'); } else { fprintf(stderr,"Invalid register.\n"); exit(1); } } else { fprintf(stderr,"Usage: %s <-i | -d | -[vaApw] | reg_number, 0-5>\n",argv[0]); exit(1); } exit(0); }