31 Replies Latest reply on Aug 18, 2019 4:56 PM by clem57

    BBB - I2C notes

    shabaz

      Note: 15th August 2019: The I2C library described here has been wrapped up into a single general-purpose I/O library which includes I2C and SPI. See here:

      BeagleBone Black (BBB) and PocketBeagle I/O (GPIO), SPI and I2C Library for C – 2019 Edition

       

      I2C turns out to be fairly easy and straightforward on the BBB.

      There are three I2C busses (I2C0, I2C1, I2C2), of which I2C2 is easily usable (Expansion port P9, see table 11 in the SRM).

       

      I2C0: used for some on-board components (HDMI, EEPROM, power mgmt). Not brought out to any connector/header

      I2C1: entirely free to use, but doesn't seem to be enabled

      I2C2: entirely free to use, and works

       

      NOTE: In software, the busses are numbered differently; I2C2 is identified as 1. I2C0 is identified as 0.

       

      The C code in the zip file attached worked for me on I2C2 bus (it's just a tidied version of various other peoples code, with some wrapper

      functions to make life easier).

       

       

      // bus=1 for interface I2C2 on BBB

      // returns handle to be used in remainder functions

      // addr is a 7-bit value (so, for example the BMP085-0330SBO008BMP085-0330SBO008 datasheet specifies

      // 0xEE, so we need to right-shift by 1 and use 0x77 for this function)

      int i2c_open(unsigned char bus, unsigned char addr);

      // These functions return -1 on error, otherwise return the number of bytes read/written:

      int i2c_write(int handle, unsigned char* buf, unsigned char length);

      int i2c_read(int handle, unsigned char* buf, unsigned char length);

      int i2c_write_read(int handle,

                         unsigned char addr_w, unsigned char *buf_w, unsigned int len_w,

                         unsigned char addr_r, unsigned char *buf_r, unsigned int len_r);

      int i2c_write_ignore_nack(int handle,

                                unsigned char addr_w, unsigned char* buf, unsigned int length);

      int i2c_read_no_ack(int handle,

                          unsigned char addr_r, unsigned char* buf, unsigned int length);

      int i2c_write_byte(int handle, unsigned char val);

      int i2c_read_byte(int handle, unsigned char* val);

      // These functions return -1 on error, otherwise return 0 on success

      int i2c_close(int handle);

      // Provides an inaccurate delay (may be useful for waiting for ADC etc).

      // The maximum delay is 999msec

      int delay_ms(unsigned int msec);

       

       

      By default the bus appears to run at 100kbps approx.

      I created a test program using the nearest I2C device at hand (which happened to be a BMP085 temperature and pressure sensor),

      it is part of the zip contents.

       

       

      root@beaglebone:~# ./a.out

      Temperature is 23.6 degrees C

      Pressure is 101.027 kPa

       

       

      I noticed it is not possible to control the power mgmt ic using the code, since presumably it is a protected resource.

       

      I2C2 on Expansion header P9:

      pin 19: SCL

      pin 20: SDA

      pin 1: 0V

      pin 3: 3.3V

       

       

      bbb-i2c2.jpg

       

      Message was edited by: Shabaz  - added more recent v3 which adds a couple more functions, and added a precompiled library (put it in /usr/lib and put the header in /usr/include)

        • Re: BBB - I2C notes

          shabaz wrote:

           

          NOTE: In software, the busses are numbered differently; I2C2 is identified as 1. I2C0 is identified as 0.

          That will be due to the order of registration with the kernels i2c subsystem. I expect that when the hardware's I2C1 is enabled then it will get /dev/i2c-1 and I2C2 will move to /dev/i2c-2

           

          You'll be able to work out which one is which by looking at the targets of the symlinks in /sys/bus/i2c/devices

           

          root@beaglebone:/sys/bus/i2c/devices# ls -l i2c*

          lrwxrwxrwx 1 root root 0 Jan  2 04:52 i2c-0 -> ../../../devices/ocp.2/44e0b000.i2c/i2c-0/

          lrwxrwxrwx 1 root root 0 Jan  2 04:52 i2c-1 -> ../../../devices/ocp.2/4819c000.i2c/i2c-1/

           


            • Re: BBB - I2C notes
              shabaz

              That's good to know! So we'll be able to enumerate at the start of code.

              Hopefully a build will appear with the full 400kHz speed too.

               

              I had to update the code a little bit (attached) with a new function (repeated start capability - needed for some I2C devices).

               

              // To perform a 'repeated start' use the i2c_write_read function which can write some

              // data and then immediately read data without a stop bit in between.

              int i2c_write_read(int handle,

                                 unsigned char addr_w, unsigned char *buf_w, unsigned int len_w,

                                 unsigned char addr_r, unsigned char *buf_r, unsigned int len_r);

                • Re: BBB - I2C notes

                  shabaz wrote:

                   

                  Hopefully a build will appear with the full 400kHz speed too.

                  fairly sure you could do that yourself if you wanted to

                   

                  i2c@4819c000 {

                                          compatible = "ti,omap4-i2c";

                                          #address-cells = <0x1>;

                                          #size-cells = <0x0>;

                                          ti,hwmods = "i2c3";

                                          reg = <0x4819c000 0x1000>;

                                          interrupts = <0x1e>;

                                          status = "okay";

                                          pinctrl-names = "default";

                                          pinctrl-0 = <0x4>;

                                          clock-frequency = <0x186a0>;

                                          linux,phandle = <0x24>;

                   

                  haven't tried it, but 0x186a0 is 100kHz, so seems it would work. You'd need to check that anything else on the bus will be happy at a higher frequency too - you probably don't want to annoy the PMIC for example..

                   

                  The reg = <0x4819c000 line in there is also your key to the data in /sys so you can see how it all ties together.

                   

                  Devicetree seems like a bit of a pain, especially if it's going to be the only interface to setting up pinmux, but on the other hand there's lots of other stuff in there for you to tweak and it doesn't require a full kernel recompile the way platform data in previous kernels did.


              • Re: BBB - I2C notes
                shabaz

                The code is updated to v3 (I've left the older version there for now, I'll delete it in a few days), to add a couple more functions for I2C-like devices that don't actually ack at all (which is technically against the I2C spec).

                • Re: BBB - I2C notes
                  jrychter

                  Hi — for those who find this post and might be looking for a way to send longer I2C that include restarts, I've written a tiny Linux library that allows you to send arbitrary sequences with restarts and reads, using the Bus Pirate convention/notation. See Lsquaredc (L²C) for details.

                    • Re: BBB - I2C notes
                      shabaz

                      Hi Jan,

                       

                      The code above supports restarts too, I've been using this code for close to a year. Not sure what bus pirate convention is, but the code above (in my opinion) follows closely what the I2C bus is doing, in it's naming of functions and parameters, i.e. how chip manufacturers may specify behavior.

                       

                      Anyway, choice is good.

                        • Re: BBB - I2C notes
                          jrychter

                          I'm sorry, I should have been clearer in my description. Your code is OK and works fine, as long as all you need is a single restart in a transaction.


                          The main difference is that lsquaredc has a *single* API call to perform any kind of I2C sequence, including multiple writes, reads and restarts. Sequences are specified like this:


                          {0x38, 0x16, I2C_RESTART, 0x39, I2C_READ, I2C_READ, I2C_READ};

                           

                          you then call i2c_send_sequence() providing a buffer that can store 3 bytes, and you get your data back. The sequence may include many restarts (up to 42).

                            • Re: BBB - I2C notes

                              Any kind of I2C sequence?  Ok, I need to have a sequence length of 1, how do I do that with your code ?

                               

                              This is on real hardware where an I2C transaction needs to be exactly one byte on the wire.

                               

                              Ten bit addressing? PEC?  You seem to fall quite a way short of your 'any' claim.

                                • Re: BBB - I2C notes
                                  jrychter

                                  Guys, chill down. I am not here to compete, and my code has a proud price tag of 0. If you like it, use it. If you don't, don't use it. I just thought it would be useful to people.

                                   

                                  I have better things to do than pursue arguments of this kind.

                                    • Re: BBB - I2C notes

                                      Nobody is arguing.  The choice you made of how to answer what should have been a straightforward question is perhaps more interesting than any real answer.

                                       

                                      FWIW, I'm unlikely to ever use either piece of code, neither meet my needs.

                                      • Re: BBB - I2C notes
                                        shabaz

                                        Hi Jan,

                                         

                                        Your original post did seem to imply that restarts were missing from the above code - not great when I want people to incrementally improve the code over time. I did not go through the NXP spec and implement all, I took the approach that I would test with the devices I use, and add over time, since this is not a rocket-science thing. As it is, I've used it with "I2C-like' devices too, like cameras.

                                         

                                        I really don't understand what you're saying, the code above is free too - and has no restrictions to do with acknowledgement either, unlike your code (not saying it is a bad thing though) - so to be fair it is "more free".

                                         

                                        I hope people do use your code (Especially if they are familiar with this bus pirate convention which I really didn't think was a convention - I2C has been around before Bus Pirate was even conceived). Just you could have expressed it in a better way.

                                          • Re: BBB - I2C notes
                                            jalpashah

                                            Hi,

                                            I have written the c code for I2C for interfacing the SHT11(temp sensor) and PIC24F16KA102. but i m not getting the acknowledge from the SHT11 when i m writing through pic.  i m pasting my code here. plz let me know where i m doing wrong if possible. thanks

                                             

                                            #include <stdint.h>

                                            #include <xc.h>

                                            #include "main.h"

                                             

                                            #include <p24Fxxxx.h>

                                            // ICD Pin Placement Select (EMUC/EMUD share PGC3/PGD3)

                                            //_FICD(ICS_PGx1)

                                            /*

                                            * File:   main.c

                                            * Author: jalpa

                                            *

                                            * Created on 7 February, 2014, 10:11 AM

                                            */

                                            /*******************************************************************************************************************/

                                            //Include section

                                             

                                            /*******************************************************************************************************************/

                                            #include "p24F16KA102.h"

                                             

                                            #include "spi.h"

                                            #include "i2c.h"

                                            #include <timer.h>

                                             

                                             

                                             

                                            /*******************************************************************************************************************/

                                            //Define section

                                            /*******************************************************************************************************************/

                                            // for input use  PORTBbits.RB7  notation and for output pin use LATBbits.LATB2.

                                            //#define CE LATBbits.LATB2    //pin-6  // CE output pin, PORTB bit 2

                                             

                                             

                                            //#define SDA_pin LATBbits.LATB9    //pin-22// Clock output pin, PORTB bit 11

                                            //#define SCL_pin LATBbits.LATB8

                                            //

                                            //#define SDA_dir TRISBbits.TRISB9

                                            //#define SCL_dir TRISBbits.TRISB8

                                             

                                            /*******************************************************************************************************************/

                                             

                                            //Declaration of variable

                                            /*******************************************************************************************************************/

                                            unsigned char msb;

                                            unsigned char lsb;

                                            unsigned char status;

                                             

                                            /*******************************************************************************************************************/

                                            //Declaration of functions

                                             

                                            /*******************************************************************************************************************/

                                            void i2c_init();

                                            void reset_i2c_bus();

                                            void delay_us(unsigned short us);

                                            void i2c_start();

                                            void i2c_restart();

                                            char i2c_send_byte(int data);

                                            char i2c_read_byte();

                                             

                                             

                                            //#define SCK_dir TRISBbits.TRISB8

                                             

                                             

                                             

                                            /*******************************************************************************************************************/

                                            //Main function

                                            /*******************************************************************************************************************/

                                             

                                            void main(void)

                                            {

                                             

                                             

                                             

                                               // AD1PCFGbits.PCFG0=1;

                                               // AD1PCFGbits.PCFG1=1;

                                             

                                               AD1PCFG =0XFF;

                                               //SCK_dir=0;

                                              

                                               i2c_init();

                                               i2c_start();

                                               delay_us(2000);

                                               status = i2c_send_byte(0x03);

                                               delay_us(10000);

                                               msb= i2c_read_byte();

                                               lsb= i2c_read_byte();

                                               reset_i2c_bus();

                                             

                                             

                                             

                                               

                                             

                                            }

                                             

                                             

                                             

                                             

                                             

                                            void i2c_init()

                                            {

                                                int temp;

                                                I2C1BRG = 157;

                                                I2C1CONbits.I2CEN = 0;

                                                I2C1CONbits.DISSLW = 1;

                                                I2C1CONbits.I2CEN= 1;

                                                temp = I2C1RCV;

                                                reset_i2c_bus();

                                             

                                            }

                                             

                                            void reset_i2c_bus()  // reset is used before and after a packet i sent through the bus (release both sda and scl)

                                            {

                                             

                                                int x=0;

                                             

                                                I2C1CONbits.PEN= 1;

                                             

                                                while(I2C1CONbits.PEN)

                                                {

                                             

                                                    delay_us(1);

                                                    x++;

                                                    if(x>20) break;

                                             

                                                }

                                             

                                                I2C1CONbits.RCEN=0;

                                                IFS1bits.MI2C1IF=0;

                                                I2C1STATbits.IWCOL=0;

                                                I2C1STATbits.BCL=0;

                                             

                                                delay_us(10);

                                               

                                            }

                                             

                                            void i2c_start(void)

                                            {

                                               int x = 0;

                                               I2C1CONbits.ACKDT = 0;    //Reset any previous Ack

                                               delay_us(10);

                                               I2C1CONbits.SEN = 1;    //Initiate Start condition

                                               Nop();

                                             

                                               //the hardware will automatically clear Start Bit

                                               //wait for automatic clear before proceding

                                               while (I2C1CONbits.SEN)

                                               {

                                                  delay_us(1);

                                                  x++;

                                                  if (x > 20)

                                                  break;

                                               }

                                                delay_us(2);

                                            }

                                             

                                             

                                            void i2c_restart(void)

                                            {

                                               int x = 0;

                                             

                                               I2C1CONbits.RSEN = 1;    //Initiate restart condition

                                               Nop();

                                             

                                               //the hardware will automatically clear restart bit

                                               //wait for automatic clear before proceding

                                               while (I2C1CONbits.RSEN)

                                               {

                                                  delay_us(1);

                                                  x++;

                                                  if (x > 20)    break;

                                               }

                                             

                                               delay_us(2);

                                            }

                                             

                                             

                                            char i2c_send_byte(int data)

                                            {

                                               int i;

                                             

                                               while (I2C1STATbits.TBF) { }

                                               IFS1bits.MI2C1IF = 0; // Clear Interrupt

                                               I2C1TRN = data; // load the outgoing data byte

                                             

                                               // wait for transmission

                                               for (i=0; i<500; i++)

                                               {

                                                  if (!I2C1STATbits.TRSTAT) break;

                                                  delay_us(1);

                                             

                                                  }

                                                  if (i == 500) {

                                                  return(1);

                                               }

                                             

                                               // Check for NO_ACK from slave, abort if not found

                                               if (I2C1STATbits.ACKSTAT == 1)

                                               {

                                                  reset_i2c_bus();   //error

                                                  return(1);

                                               }

                                             

                                               delay_us(2);

                                               return(0);

                                            }

                                             

                                            char i2c_read_byte(void)    //does not reset bus!!!

                                            {

                                               int i = 0;

                                               char data = 0;

                                             

                                               //set I2C module to receive

                                               I2C1CONbits.RCEN = 1;

                                             

                                               //if no response, break

                                               while (!I2C1STATbits.RBF)

                                               {

                                                  i++;

                                                  if (i > 2000) break;

                                               }

                                             

                                               //get data from I2CRCV register

                                               data = I2C1RCV;

                                             

                                               //set ACK to high

                                               I2C1CONbits.ACKEN = 1;

                                             

                                               //wait before exiting

                                               delay_us(10);

                                             

                                               //return data

                                               return data;

                                            }

                                             

                                             

                                             

                                            void delay_us(unsigned short us)

                                            {

                                              unsigned short i;

                                             

                                              // TIMER1 Period = PR1 x 2 x Tosc x Prescale second

                                              // TIMER1 Period = 145 x 2 x 1/32000000 x 1 = 9 us // practically coming 12us

                                             

                                              PR1=145;                      // Maximum Counter

                                              T1CON=0x0000;            // TIMER1 Off, Prescale 1:1 using the internal clock

                                              for (i=0; i < us;i++) {

                                                TMR1=0;                     // Reset TIMER1 Counter

                                                IFS0bits.T1IF=0;            // Clear TIMER1 Interrupt Flag

                                                T1CONbits.TON=1;            // Turn On TIMER1

                                                while(IFS0bits.T1IF != 1);  // Wait until TMR1 > PR1 (Overflow)

                                                T1CONbits.TON=0;            // Turn Off TIMER1

                                              }

                                             

                                            }

                                • Re: BBB - I2C notes

                                  Hi Shabaz, first of all, I would thank you for the library.

                                  I'm trying communicate my BBB with a OV7670 cam through the I2C bus.

                                  I'm using the "i2c_write_ignore_nack" function, because the SCCB (Camera protocol) in despite of be a I2C clone, don't have NACK or ACK.

                                  I analyzed with the oscilloscope, but I don't know why the BBB just send the address, and never other data.

                                   

                                   

                                  Here is my code:

                                   

                                   

                                  #include <unistd.h>

                                  #include "i2cfunc.h"

                                   

                                  int main(int argc, char *argv[])

                                  {

                                    int handle

                                    unsigned char buf[10];

                                   

                                    buf[0] = 0x02;

                                    buf[1] = 0x80;

                                   

                                    handle = i2c_open(1, 42);

                                    sleep(1);

                                    i2c_write_ignore_nack(handle, 42, buf, 2);

                                    sleep(1);

                                    i2c_close(handle);

                                   

                                    return 0;

                                  }

                                   

                                   

                                  Thanks!!

                                    • Re: BBB - I2C notes
                                      shabaz

                                      Hi Igor,

                                       

                                      It's been a while since I looked at this code, but I did get the i2c_write_ignore_nack function to work as needed, to interface to a camera, it was OV9655 so a different model,

                                      but the protocol should be similar.

                                      The camera code is at the blog post here, file "camctrl.c" in the zip file has the procedure to use this with the OV camera, in function cam_init I seem to do almost exactly what you did, so I can't see

                                      why it didn't work for you : (

                                      Maybe you can spot a difference, or try to use my file as a quick test to see if you get further.

                                      Let me know how you get on.

                                       

                                      EDIT: Also, make sure you've recompiled this i2c library, and not using the pre-compiled library in the zip file, since it is so old now. (I guess you've compiled it anyway).

                                        • Re: BBB - I2C notes

                                          Thank you for the quick reply!

                                          Your cam project is really cool, I'm trying to do something like this!

                                          I had recompiled the I2C library... The problem should be another thing.

                                          I will try run your project here. I have a few questions:

                                          You use some external push-pull resistor in the I2C bus?

                                          What Linux distribution you used?

                                          For this I2C communication you edit some system operation file, or used a specific device tree overlay?

                                            • Re: BBB - I2C notes
                                              shabaz

                                              Hi Igor,

                                               

                                              From memory I'm fairly sure I did use external pull-ups, something like 2.2k resistors. I used Angstrom at that time.

                                              Debian should be fine too. But as mentioned in the comments in the thread, you may want to double-check the

                                              I2C interface number (but I think you checked with an oscilloscope anyway, so that should be fine).

                                              I didn't make any device tree changes as far as I can recall, but some of the comments in the thread above suggest that it is

                                              possible to do this, to change the frequency to 400kHz if desired.

                                              I hope this helps!

                                              Also, when you get your camera working, I hope you can publish the details, since it will be more recent than my notes.

                                                • Re: BBB - I2C notes

                                                  Hey shabaz,

                                                  I managed to establish I2C communication, the problem was the slave.

                                                  My project is now running. However the captured images are horribles (veeery dark and with some noises) and I will try to configure the camera to capture better pictures.

                                                  If I have a lot of difficulties I may contact you through your cam post...

                                                   

                                                  Thank you about the library and the tips!

                                                    • Re: BBB - I2C notes
                                                      shabaz

                                                      Hi Igor,

                                                       

                                                      That's great to hear. Regarding the images, if this is a module from (say) ebay, then a lot of them have ignored the datasheet recommendations concerning supply decoupling, and it makes a significant difference. Also, any lengthy wiring will cause issues, and you may need to use a scope to ensure signals look good (i.e. stuff not visible with a logic analyzer). Basically this type of project probably does need a PCB, my crude prototype using wiring only just worked. Also, the buffer was fairly essential for the device I used. But, it could also partly be due to configuration too as you say.

                                                        • Re: BBB - I2C notes

                                                          Unfortunately my module is a typical and cheap ebay module, lol ... And I'm still not using PCB, but I am using very short cables, and put some extra capacitors on the module power supply...

                                                          Regarding the buffers, I haven't used them, because my module outputs 2.7 volts, do you think this can be a problem?

                                                            • Re: BBB - I2C notes
                                                              shabaz

                                                              Hi Igor,

                                                               

                                                              I think it might be a problem. I couldn't get it to work without the buffer (from memory, it was a long time ago : (

                                                              But, it could have been due to bad termination causing poor waveforms, it wasn't really worth me investigating at the time because my wiring was quite bad without a PCB : ( If you have access to an oscilloscope, I'd recommend it is used.

                                                              Regarding the capacitors, if your datasheet specifies (say) 1nF ceramics (I think mine did) then it is essential to place them on the module itself, probably soldered on top of any existing 100nF or similar capacitor. It was extremely critical in my case, it refused to work without it.

                                                  • Re: BBB - I2C notes
                                                    n8ers

                                                    Igor,

                                                     

                                                    Perhaps this article might help:

                                                    Omnivison ov3640 i2c sccb | The Code Artist

                                                     

                                                    Looks like there are a few quirks to the sccb interface.

                                                     

                                                    -Nate

                                                      • Re: BBB - I2C notes

                                                        Hi Nate!

                                                         

                                                        I had already managed to solve my problem, my project is already operating, with 80% of implementas features, I'm still working on it, but either way I found it very interesting article from the link you gave me.

                                                         

                                                        Thank you so much.

                                                    • Re: BBB - I2C notes
                                                      andyedtec

                                                      Hello, thank you for posting this code I have had it working.

                                                       

                                                      I'm trying to re write this code in  QT creator could anybody offer me any help as my C++ is

                                                      limited.

                                                       

                                                      Should I start a new thread or is it ok to post here.

                                                       

                                                      Thanks Andy

                                                      • Re: BBB - I2C notes
                                                        andyedtec

                                                        How do I use the repeated start function?

                                                         

                                                        Andy

                                                          • Re: BBB - I2C notes
                                                            shabaz

                                                            Hi Andrew, it's in the comments (pasted below):

                                                             

                                                            I had to update the code a little bit (attached) with a new function (repeated start capability - needed for some I2C devices).

                                                             

                                                            // To perform a 'repeated start' use the i2c_write_read function which can write some

                                                            // data and then immediately read data without a stop bit in between.

                                                            int i2c_write_read(int handle,

                                                                               unsigned char addr_w, unsigned char *buf_w, unsigned int len_w,

                                                                               unsigned char addr_r, unsigned char *buf_r, unsigned int len_r);

                                                          • Re: BBB - I2C notes
                                                            clem57

                                                            Thanks for a great work...