# i2c on beaglebone



## comarius (Sep 27, 2019)

Hi,

Anyone have some iic code to write to i2c under freeBSD ?
My i2c display is detected at 0x0c.


```
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic2: 0x3c



root@s2wcom:/home/s2wcom/oled # devinfo | grep iic
      iichb0
        iicbus0
          iic0
      iichb1
        iicbus1
          iic1
      iichb2
        iicbus2
          iic2
```

The i2c write was taken from various sources and the most 'reliable' match I found was
the code donw the thread.
I don't know if I am doing this correct. All I could find was writing one byte at the time.



```
int _i2c_write(int slave, int idev, const unsigned char* pb,
               unsigned char offset, size_t bytes)
{
    int8_t buf[2];
    struct iic_msg msg;
    struct iic_rdwr_data rdwr;

    for(int i=0;i<bytes;i++)
    {
        buf[0] = i;
        buf[1] = pb[i];
        msg.slave = slave<<1;
        msg.flags = 0;
        msg.len = 2;
        msg.buf = buf;

        rdwr.msgs = &msg;
        rdwr.nmsgs = 1;

        if ( ioctl(idev, I2CRDWR, &rdwr) < 0 ) // hang bang !
        {
            perror("I2CRDWR");
            return(-1);
        }
    }

    return(0);
}
```

I also tried, but I guess is not the right way



```
int _i2c_write(int slave, int idev, const unsigned char* pb,
               unsigned char offset, size_t bytes)
{
    int8_t buf[LARGE_ENOUGH];
    struct iic_msg msg;
    struct iic_rdwr_data rdwr;

        buf[0] = 0;
        memcpy(buf+1,pb,bytes);
        msg.slave = slave<<1;
        msg.flags = 0;
        msg.len = 1+bytes;
        msg.buf = buf;

        rdwr.msgs = &msg;
        rdwr.nmsgs = 1;

        if ( ioctl(idev, I2CRDWR, &rdwr) < 0 ) // hang bang !
        {
            perror("I2CRDWR");
            return(-1);
        }
    return(0);
}
```


----------



## obsigna (Sep 27, 2019)

I use I2C on the BeagleBone Black for addressing the 12bit DAC chip MCP4725 and the 16bit ADC chip ADS1115. I use an Op Amp circuit for scaling the output voltages of the DAC (max. 3.3 V) to my needs, here 10 V, and another circuit for clipping and clamping the input voltage for it not pass the rates of the ADC, here 2 V.

The code for testing the DAC is:

```
//  MCP4725.c
//  MCP4725 DAC Device I2C Cammandline Interface

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <fcntl.h>
#include <dev/iicbus/iic.h>

#define Vcc 10.0

double sqr(double x)
{
   return x*x;
}

int main(int argc, const char *argv[])
{
   double Vout;

   if (argc < 2 || (Vout = strtod(argv[1], NULL)) < 0.0 || Vcc < Vout)
   {
      printf("Usage: %s <DAC Output in V> [p(ermanent]\n", argv[0]);
      return 0;
   }

   int dac = open("/dev/iic1", O_RDWR);
   if (dac < 0)
   {
      perror("DAC Open");
      return -1;
   }

   uint16_t u = lround(Vout/Vcc*0x0FFF);
   uint8_t  output[3] = {(argc > 2 && *argv[2] == 'p') ? 0x60 : 0x40, u >> 4, (u&0x0F) << 4};
   uint8_t  result[2] = {};
   uint8_t  addr = 0x62 << 1;
   struct iic_msg msgs[2] = {{addr | IIC_M_WR, IIC_M_WR, 3*sizeof(uint8_t), output},
                             {addr | IIC_M_RD, IIC_M_RD, 2*sizeof(uint8_t), result}};
   struct iic_rdwr_data data = {msgs, 1};

   for (int i = 0; 1; i++)
   {
      usleep(10000);

      double U = Vout*log(sqr(sin(6.283185307e-3*i)) + 1)/0.6931471806;
      u = lround(U/Vcc*0x0FFF);
      output[0] = 0x40;
      output[1] = u >> 4;
      output[2] = (u&0x0F) << 4;

      if (ioctl(dac, I2CRDWR, &data) < 0)
      {
         perror("DAC I2CRDWR");
         return -1;
      }
   }

   close(dac);
   return 0;
}
```

The code for testing the ADC is:

```
//  ADS1115.c
//  ADS1115 ADC Device I2C Interface Testing

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dev/iicbus/iic.h>

int main(int argc, char *const argv[])
{
   int iic = open("/dev/iic1", O_RDWR);
   if (iic < 0)
   {
      perror("/dev/iic1 Open");
      return -1;
   }

   uint8_t addr = 0x48 << 1;
   uint8_t config[3] = {1, 0b10000100, 0b10000011};
   uint8_t cnvreg[1] = {0};
   uint8_t result[2] = {};

   struct iic_msg msgs[3] = {{addr|IIC_M_WR, IIC_M_WR, 3, config},
                             {addr|IIC_M_WR, IIC_M_WR, 1, cnvreg},
                             {addr|IIC_M_RD, IIC_M_RD, 2, result}};

   struct iic_rdwr_data config_msg = {&msgs[0], 2};
   if (ioctl(iic, I2CRDWR, &config_msg) < 0)
   {
      perror("/dev/iic1 I2CRDWR");
      close(iic);
      return -1;
   }

   struct iic_rdwr_data convert_msg = {&msgs[2], 1};
   for (int i = 0; i < 10; i++)
   {
      usleep(100000);

      if (ioctl(iic, I2CRDWR, &convert_msg) < 0)
      {
         perror("/dev/iic1 I2CRDWR");
         close(iic);
         return -1;
      }

      union {uint8_t b[2]; int16_t v;} value = {.b = {result[1], result[0]}};
      printf("%hd = %.3f mV\n", value.v, value.v*2048.0/0x7FFF);
   }

   close(iic);
   return 0;
}
```


----------



## comarius (Sep 27, 2019)

Thank you very much. Really appreciate.
My new write  function:



```
int _i2c_write(int slave, int idev, const uint8_t* pb, size_t bytes)
{
        uint8_t local[128];

        memcpy(local,pb,bytes); // ??!!  get rid of and use the pass-in pointer

        uint8_t addr = slave << 1;
        uint8_t result[2] = {0,0};

        struct iic_msg msgs[3] = {{addr|IIC_M_WR, IIC_M_WR, bytes, local},
                                 {addr|IIC_M_RD, IIC_M_RD, 2, result}};

        struct iic_rdwr_data data = {msgs, 1};
        printf("writing %d bytes at %d  ", bytes, local[0]);
        if (ioctl(idev, I2CRDWR, &data) < 0)
        {
                perror("/dev/iic2 I2CRDWR");
                return -1;
        }

        return 0;
}
```

Is working, no hang, writes are OK. I can see them with the shitty 14$
oscilloscope (GND->SCL hot->SDA mimic 2 channels) . I cant see the timings
with this cheap thing. Still the display is blank.








I need further investigation. Wiring are OK because form Linux works





I'll come with details if I can find the issue.


----------



## comarius (Sep 27, 2019)

*RESOLVED*

Me bad  .I had code #ifdef __FreeBSD__ / #elif __linux__ left out for 
the LCD init sequence.
Here it is working perfect. THX Again.


----------



## obsigna (Sep 28, 2019)

I do not see this kind of messages. Did you specify some interrupt settings in your I2C device tree overlay? Here is mine:


```
/dts-v1/;
/plugin/;

/ {
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

    part-number = "I2C1";
    version = "0001";
    exclusive-use = "i2c1", "P9.17", "P9.18";
};

&am33xx_pinmux {
    i2c1_pins: pinmux_i2c1_pins {
        pinctrl-single,pins = <0x158 0x32 0x15c 0x32>;
    };
};

&i2c1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_pins>;
    clock-frequency = <400000>;
    #address-cells = <0x1>;
    #size-cells = <0x0>;
};
```

This would be compiled using the following command:
`dtc -I dts -O dtb -b0 -@ -o /boot/dtbo/am335x-boneblack-i2c1.dtbo am335x-boneblack-i2c1.dtso`


----------

