I2C2 not working with ADS1115-BeagleboneBlack

Hi so i have an issue, and I have tried debugging it for quite some time now but still no luck.

What I am trying to do is connect my beaglebone black with adafruit ads1115 16 bit gravity module. I want to use my PRU1 to drive the adc module and they both communicate through i2c2. The readings that come back from the adc then get stored into a shared memory between the linux and the pru and my python code then reads from that shared memory. I implemented the register values and everything from the bealgbone manual and the ads1115 manual, and wrote a small code for the driver and the main code, but I don’t know what I am doing wrong, code wise or setting wise that fails this communication.

I read somewhere that I need to completely disconnect my i2c2 from linnux so my pru can claim that and I created a small overlay for that, but it still doesn’t work.

I also read, that I need to set my i2c2 pin 19 and 20 in mode 3 and my overlay does that, but when I check manually using the devmem2 commands the values of my pin 19 and 20 I still get 0x37 which is mode 7 and I don’t know why its doing that.

I connected my scl and sda lines with an oscilloscope to see if I get any clock but I get no clock at all, the lines are always high at 3.3v

Morevoer, my adc module works since when I use the linux inbuilt commands such as i2cget I see that the i2c 2 bus is connected to a 0x48 register device. There are not many good resources out there either, and I have scavenged the whole internet but couldn’t find anything that would help me work this i2c module.

Can someone please have a look at my code or tell me what I might be doing wrong, which causes this issue. Any help would be appreciated pleaseee. thanks so much

PRU_MAIN.C code:

#include <stdint.h>
#include <pru_cfg.h>
#include "pru_driver.h"

#define PRU_SHARED_MEM 0x00010000

void main(void) {
    // Enable OCP master port
    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

    volatile uint32_t* shared_mem = (volatile uint32_t*)PRU_SHARED_MEM;

    init_I2C();

    // Configure ADS1115: AIN0, single-shot, 2.048V, 128SPS
    I2C_Write(0x48, 0x01, 0xC583);
    __delay_cycles(2000000);  // Wait ~8ms

    // Read conversion register
    uint16_t adc = I2C_Read_Register(0x48, 0x00);

    // Store result in PRU shared memory
    shared_mem[0] = adc;

    while (1) {
        __delay_cycles(1000000);
    }
}

PRU_Driver.c code:


#include "pru_driver.h"

// Initialize I2C2 module
void init_I2C(void) {
    I2C_CON = 0;                   // Disable I2C
    I2C_SYSC = (1 << 1);           // Soft reset
    while (!(I2C_SYSS & 0x1));     // Wait for reset done

    I2C_PSC = 23;                  // Prescaler for 2MHz
    I2C_SCLL = 13;                 // SCL low time

    I2C_SCLH = 15;                 // SCL high time

    I2C_IRQENABLE_SET = 0x00;      // Disable interrupts
    I2C_CON = I2C_EN;              // Enable I2C module
    while (!(I2C_SYSS & 0x1));     // Confirm ready
}

// Write 2-byte data to I2C slave
void I2C_Write(uint8_t addr, uint8_t reg, uint16_t data) {
    while (I2C_IRQSTATUS_RAW & (1 << 12)); // Wait for bus free

    I2C_SA = addr;
    I2C_CNT = 3; // reg + 2 bytes

    I2C_CON = I2C_EN | I2C_MST | I2C_TRX | I2C_STT;

    while (!(I2C_IRQSTATUS_RAW & (1 << 4)));
    I2C_DATA = reg;
    I2C_IRQSTATUS = (1 << 4);

    while (!(I2C_IRQSTATUS_RAW & (1 << 4)));
    I2C_DATA = (data >> 8) & 0xFF;
    I2C_IRQSTATUS = (1 << 4);

    while (!(I2C_IRQSTATUS_RAW & (1 << 4)));
    I2C_DATA = data & 0xFF;
    I2C_CON |= I2C_STP;
    I2C_IRQSTATUS = (1 << 4);

    while (I2C_IRQSTATUS_RAW & (1 << 12));
    I2C_IRQSTATUS = 0xFFFF;
}

// Read 2 bytes from a register
uint16_t I2C_Read_Register(uint8_t addr, uint8_t reg) {
    uint16_t val = 0;

    while (I2C_IRQSTATUS_RAW & (1 << 12));
    I2C_SA = addr;
    I2C_CNT = 1;
    I2C_CON = I2C_EN | I2C_MST | I2C_TRX | I2C_STT | I2C_STP;

    while (!(I2C_IRQSTATUS_RAW & (1 << 4)));
    I2C_DATA = reg;
    I2C_IRQSTATUS = (1 << 4);

    while (!(I2C_IRQSTATUS_RAW & (1 << 2)));
    I2C_IRQSTATUS = (1 << 2);

    I2C_SA = addr;
    I2C_CNT = 2;
    I2C_CON = I2C_EN | I2C_MST | I2C_STT | I2C_STP;

    while (!(I2C_IRQSTATUS_RAW & (1 << 3)));
    val = (I2C_DATA << 8);
    I2C_IRQSTATUS = (1 << 3);

    while (!(I2C_IRQSTATUS_RAW & (1 << 3)));
    val |= I2C_DATA;
    I2C_IRQSTATUS = (1 << 3);

    while (I2C_IRQSTATUS_RAW & (1 << 12));
    I2C_IRQSTATUS = 0xFFFF;

    return val;
}

PRU_Driver.h file


#ifndef PRU_DRIVER_H
#define PRU_DRIVER_H

#include <stdint.h>

#define I2C_BASE_ADDR       0x4819C000  // I2C2 base address

#define I2C_REVNB_LO        (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x00))
#define I2C_SYSC            (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x90))
#define I2C_SYSS            (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x94))
#define I2C_CNT             (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x98))
#define I2C_DATA            (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x9C))
#define I2C_CON             (*(volatile uint32_t*)(I2C_BASE_ADDR + 0xA4))
#define I2C_SA              (*(volatile uint32_t*)(I2C_BASE_ADDR + 0xAC))
#define I2C_PSC             (*(volatile uint32_t*)(I2C_BASE_ADDR + 0xB0))
#define I2C_SCLL            (*(volatile uint32_t*)(I2C_BASE_ADDR + 0xB4))
#define I2C_SCLH            (*(volatile uint32_t*)(I2C_BASE_ADDR + 0xB8))
#define I2C_BUF             (*(volatile uint32_t*)(I2C_BASE_ADDR + 0xA8))
#define I2C_IRQSTATUS_RAW   (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x24))
#define I2C_IRQSTATUS       (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x28))
#define I2C_IRQENABLE_SET   (*(volatile uint32_t*)(I2C_BASE_ADDR + 0x2C))

#define I2C_EN   (1 << 15)
#define I2C_MST  (1 << 10)
#define I2C_TRX  (1 << 9)
#define I2C_STP  (1 << 1)
#define I2C_STT  (1 << 0)

void init_I2C(void);
void I2C_Write(uint8_t addr, uint8_t reg, uint16_t data);
uint16_t I2C_Read_Register(uint8_t addr, uint8_t reg);

#endif

As fun as it may be to hammer out a lot of PRU code,
you need to focus on the PINMUX issue first.

There’s tons of examples on how to operate that at:
Device tree FAQ - BeagleBoard

this is the only part of your post that indicates that you tried to test one piece at a time… crawl → walk → run …

first prove your connection to the adc with /usr/sbin/i2cget and /usr/sbin/i2cset … not just that you see the address (0x48) but that you can read and write to your registers

second ) adapt your ‘c’ code to run on the linux processor … this sidesteps any ‘pru’ configuration issues. In no way have you demonstrated that you know how to configure the ‘pru’ nor the pins to work with IIC.

third ) once you get your ‘c’ code vetted on the linux processor … try to get a ‘simple’ pru program running… like blinking a LED. I don’t have any idea WHY you want to drive the adc from a pru, but it is your project and I am only making suggestions.

fourth ) are you sure that the IIC bus is even exposed to the pru? I’ve only done a few IIC projects, but all have been driven from linux. NO CLUE what advantage is gained by running ‘c’ code on the pru to drive the adc…

EDIT: show me where chatgpt is wrong: https://chatgpt.com/share/67f3ee31-8fc0-8005-ba6c-a6b830ac2512

good luck,
gomer

Thanks everyone for your replies, but i found the solution.
You have to enable the clock in the Power Reset Clock management of the beaglebone otherwise all the hardware peripherals remain in an idle state, so I enabled my clock for i2c and it worked!