This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM3358: I2C on PRU Breaks Without Particular Delay

Part Number: AM3358


Hi there,

I've recently been working with I2C on the AM3358's PRU. For prototyping purposes, I've been working with a PocketBeagle and trying to communicate with an MMA8453 accelerometer attached to the I2C-2 bus. As a starting point, I have been using pru_i2c_driver.c from the LinuxDroneLab. This library accesses the AM3358's I2C peripheral from the PRUs. Although this post is about problems I've run into with the LinuxDroneLab library, I believe the problem I am encountering is more closely related to the AM3358 processor than the library itself. However, please let me know if this post would be better placed in a different forum.

While I was immediately able to write to registers using the LinuxDroneLab library, I was unable to read registers. Below, I have included the functions from the LinuxDroneLab that I'm using to read registers / bytes. The code below is identical to the code in the repository linked above with the exception of four comments I've added (which are explained below).

uint8_t pru_i2c_driver_ReadBytes(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bytes, uint8_t* buffer)
{
    if(!pru_i2c_initialized[i2cDevice-1]) {
        if(!pru_i2c_driver_Init(i2cDevice)) {
            return 0;
        }
    }

    if (!pru_i2c_driver_WaitBB(i2cDevice))
    {
        return 0;
    }

    CT_I2C[i2cDevice-1]->I2C_SA_bit.I2C_SA_SA = address; // 7 bit address
    CT_I2C[i2cDevice-1]->I2C_CNT_bit.I2C_CNT_DCOUNT = 1; // 1 byte to transmit
    CT_I2C[i2cDevice-1]->I2C_CON = 0x8601; // MST/TRX/STT
    /*-------------------------------------BELOW HERE 1------------------------------------*/
    pru_i2c_driver_DelayMicros(7);

    if (!pru_i2c_driver_WaitXRDY(i2cDevice))
    {
        return 0;
    }
    // write register to read
    CT_I2C[i2cDevice-1]->I2C_DATA = reg;
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_XRDY = 0b1;

    // wait access to registers
    if (!pru_i2c_driver_WaitARDY(i2cDevice))
    {
        return 0;
    }
    pru_i2c_driver_DelayMicros(4);

    if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR
            | CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
    {
        return 0;
    }

    // read data
    CT_I2C[i2cDevice-1]->I2C_CNT_bit.I2C_CNT_DCOUNT = bytes; // bytes to reveive
    /*-------------------------------------ABOVE HERE 1------------------------------------*/
    CT_I2C[i2cDevice-1]->I2C_CON = 0x8403; // MST/STP/STT
    /*-------------------------------------BELOW HERE 2------------------------------------*/
    pru_i2c_driver_DelayMicros(12);
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_ARDY = 0b1;

    // wait data
    if (!pru_i2c_driver_WaitRRDY(i2cDevice))
    {
        return 0;
    }

    uint8_t count;
    for (count = 0; count < bytes; count++)
    {
        /*-------------------------------------ABOVE HERE 2------------------------------------*/
        // read byte
        buffer[count] = CT_I2C[i2cDevice-1]->I2C_DATA;

        // require next data
        CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_RRDY = 0b1;

        // wait data
        if (!pru_i2c_driver_WaitRRDY(i2cDevice))
        {
            return 0;
        }
        pru_i2c_driver_DelayMicros(1);

        if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR
                | CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
        {
            return 0;
        }
    }

    // wait for access ready
    if (!pru_i2c_driver_WaitARDY(i2cDevice))
    {
        return 0;
    }

    // wait for bus free
    // wait data
    if (!pru_i2c_driver_WaitBF(i2cDevice))
    {
        return 0;
    }

    return count;
}

uint8_t pru_i2c_driver_ReadReg(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t* buffer)
{
    return pru_i2c_driver_ReadBytes(i2cDevice, address, reg, 1, buffer);
}

As I mentioned, the above code was not working for me. In particular, using the above code, I was able to read a register once, and then all subsequent reads using pru_i2c_driver_ReadReg would return 0 (indicating a failed read attempt).

However, I have found a way to make the code work. Inside pru_i2c_driver_ReadBytes, if I add two of the following delays -- one anywhere between the sections I've commented BELOW HERE 1 and ABOVE HERE 1 and another between the sections BELOW HERE 2 and ABOVE HERE 2 -- the code works:

__delay_cycles(100000);

From my experiments, the number of cycles of the first delay can be less than 100000 -- I've found that anything above 7000 generally works, but at around 6000 cycles the code starts misbehaving again. Similarly, for the second delay, it seems like anything above 25000 cycles works. But, in the end, it seems like the code requires some kind of delay before and after writing 0x8403 to I2C_CON.

This issue seems very peculiar to me. I was wondering if anyone had some insight as to why adding __delay_cycles anywhere in the aforementioned regions is able to fix the code. I've looked at the AM3358's TRM, and the code above (without the __delay_cycles) seems to follow the suggested guidelines for I2C. Unfortunately, I do not have the proper equipment to actually read the I2C signals as they are being transmitted. Nonetheless, from my tests, adding this __delay_cycles makes the code work just as I need for the project I am working on. However, I was hoping to learn more about the reason why this works in case I need to make changes in the future. Please let me know if there is any additional information I can provide. Thank you in advance!

P.S. Somethings I've noticed from my experiments that may be useful:

  • If an I2C write is performed before my I2C reads, I cannot successfully read any bytes. The I2C write, however, will perform successfully. It also seems like I am able to perform multiple I2C writes without any issues.
  • Right below the ABOVE HERE 1 label, a value of 0x8403 is written to I2C_CON. Interestingly, if I try reading I2C_CON after this write, one of two things will happen depending on if I use the __delay_cycles "hack." With the hack, the value I read from I2C_CON is 0x8000. If I do not use the hack, the value I read from I2C_CON is 0x8403. In other words, I read back the value I write to I2C_CON only in the case when the code doesn't work.
  • From some preliminary tests, it seems that the I2C_IRQSTATUS_RAW register plays an important role in this issue. In the for loop in pru_i2c_driver_ReadBytes, I've tried checking the value of I2C_IRQSTATUS_RAW. With the hack (i.e. the code working), I2C_IRQSTATUS_RAW is equal to 0x11C at the start of the for loop. Without the hack, though, I2C_IRQSTATUS_RAW is equal to 0x111C at the start of the for loop. In other words, without the hack, the bus busy (BB) bit is not being cleared. I believe that this bit should be cleared when 0x8403 is written to I2C_CON (because 0x8403 makes STT high in I2C_CON while BB is high in I2C_IRQSTATUS_RAW, which should trigger a restart condition). So, it seems like the restart condition is only being triggered if I include the aforementioned __delay_cycles. This, however, is just a hypothesis from miscellaneous tests I've performed.
  • I've only tried single byte reads so far (i.e. reading one register value).
  • I've attached the code I've been using to test. My apologies for the file being so long -- I haven't gotten around to getting the PRU linker working properly, so I've essentially just copied the pru_i2c_driver.c code into my main file. This library code ends at line 409.

/*
 * pru_i2c_driver.c
 *
 *  Created on: 04 mar 2018
 *      Author: andrea
 */
#include "include/pru_i2c.h"
#include "include/pru_i2c_driver.h"

#define CLKACTIVITY_I2C_FCLK            24
#define CLKACTIVITY_L4LS_GCLK           8
#define MAX_CYCLES_WAITING              200000

volatile pruI2c* CT_I2C[2] = { &CT_I2C1, &CT_I2C2};
uint32_t* CM_PER_I2C_CLKCTRL[2] = {(uint32_t*) 0x44E00048, (uint32_t*) 0x44E00044};

uint8_t pru_i2c_initialized[2] = {0,0};

void pru_i2c_driver_DelayMicros(uint8_t micros)
{
    uint16_t cycles = micros * 100;
    uint16_t i = 0;
    for (i = 0; i < cycles; i++)
    {
    };
}

uint8_t pru_i2c_driver_WaitBB(uint8_t i2cDevice)
{
    uint32_t ticks = 0;
    while (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_BB)
    {
        ticks++;
        if (ticks > MAX_CYCLES_WAITING)
        {
            return 0;
        }
    }
    return 1;
}
uint8_t pru_i2c_driver_WaitBF(uint8_t i2cDevice)
{
    uint32_t ticks = 0;
    while (!CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_BF)
    {
        ticks++;
        if (ticks > MAX_CYCLES_WAITING)
        {
            return 0;
        }
    }
    return 1;
}
uint8_t pru_i2c_driver_WaitXRDY(uint8_t i2cDevice)
{
    uint32_t ticks = 0;
    while (!CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_XRDY)
    {
        ticks++;
        if (ticks > MAX_CYCLES_WAITING)
        {
            return 0;
        }
    }
    return 1;
}
uint8_t pru_i2c_driver_WaitRRDY(uint8_t i2cDevice)
{
    uint32_t ticks = 0;
    while (!CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_RRDY)
    {
        ticks++;
        if (ticks > MAX_CYCLES_WAITING)
        {
            return 0;
        }
    }
    return 1;
}
uint8_t pru_i2c_driver_WaitARDY(uint8_t i2cDevice)
{
    uint32_t ticks = 0;
    while (!CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_ARDY)
    {
        ticks++;
        if (ticks > MAX_CYCLES_WAITING)
        {
            return 0;
        }
    }
    return 1;
}

uint8_t pru_i2c_driver_WaitRDONE(uint8_t i2cDevice)
{
    uint32_t ticks = 0;
    while (!CT_I2C[i2cDevice-1]->I2C_SYSS_bit.I2C_SYSS_RDONE)
    {
        ticks++;
        if (ticks > MAX_CYCLES_WAITING)
        {
            return 0;
        }
    }
    return 1;
}

uint8_t pru_i2c_driver_ReadBytes(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bytes, uint8_t* buffer)
{
    if(!pru_i2c_initialized[i2cDevice-1]) {
        if(!pru_i2c_driver_Init(i2cDevice)) {
            return 0;
        }
    }

    if (!pru_i2c_driver_WaitBB(i2cDevice))
    {
        return 0;
    }

    CT_I2C[i2cDevice-1]->I2C_SA_bit.I2C_SA_SA = address; // 7 bit address
    CT_I2C[i2cDevice-1]->I2C_CNT_bit.I2C_CNT_DCOUNT = 1; // 1 byte to transmit
    CT_I2C[i2cDevice-1]->I2C_CON = 0x8601; // MST/TRX/STT
    pru_i2c_driver_DelayMicros(7);

    if (!pru_i2c_driver_WaitXRDY(i2cDevice))
    {
        return 0;
    }
    // write register to read
    CT_I2C[i2cDevice-1]->I2C_DATA = reg;
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_XRDY = 0b1;

    // wait access to registers
    if (!pru_i2c_driver_WaitARDY(i2cDevice))
    {
        return 0;
    }
    pru_i2c_driver_DelayMicros(4);

    if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR
            | CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
    {
        return 0;
    }

    // read data
    CT_I2C[i2cDevice-1]->I2C_CNT_bit.I2C_CNT_DCOUNT = bytes; // bytes to reveive
    CT_I2C[i2cDevice-1]->I2C_CON = 0x8403; // MST/STP/STT
    pru_i2c_driver_DelayMicros(12);
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_ARDY = 0b1;

    // wait data
    if (!pru_i2c_driver_WaitRRDY(i2cDevice))
    {
        return 0;
    }

    uint8_t count;
    for (count = 0; count < bytes; count++)
    {
        // read byte
        buffer[count] = CT_I2C[i2cDevice-1]->I2C_DATA;

        // require next data
        CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_RRDY = 0b1;

        // wait data
        if (!pru_i2c_driver_WaitRRDY(i2cDevice))
        {
            return 0;
        }
        pru_i2c_driver_DelayMicros(1);

        if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR
                | CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
        {
            return 0;
        }
    }

    // wait for access ready
    if (!pru_i2c_driver_WaitARDY(i2cDevice))
    {
        return 0;
    }

    // wait for bus free
    // wait data
    if (!pru_i2c_driver_WaitBF(i2cDevice))
    {
        return 0;
    }

    return count;
}

uint8_t pru_i2c_driver_WriteBytes(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bytes, uint8_t* buffer)
{
    if(!pru_i2c_initialized[i2cDevice-1]) {
        if(!pru_i2c_driver_Init(i2cDevice)) {
            return 0;
        }
    }
    if (!pru_i2c_driver_WaitBB(i2cDevice))
    {
        return 0;
    }

    CT_I2C[i2cDevice-1]->I2C_SA_bit.I2C_SA_SA = address; // 7 bit address
    CT_I2C[i2cDevice-1]->I2C_CNT_bit.I2C_CNT_DCOUNT = bytes + 1; // 1 byte to transmit
    CT_I2C[i2cDevice-1]->I2C_CON = 0x8603; // MST/TRX/STT/STP
    pru_i2c_driver_DelayMicros(7);

    if (!pru_i2c_driver_WaitXRDY(i2cDevice))
    {
        return 0;
    }
    // write register to read
    CT_I2C[i2cDevice-1]->I2C_DATA = reg;
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_XRDY = 0b1;
    pru_i2c_driver_DelayMicros(1);
    if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR
            | CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
    {
        return 0;
    }

    uint8_t count;
    for (count = 0; count < bytes; count++)
    {
        pru_i2c_driver_WaitXRDY(i2cDevice);
        CT_I2C[i2cDevice-1]->I2C_DATA = buffer[count];
        CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_XRDY = 0b1;
        pru_i2c_driver_DelayMicros(1);
        if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR
                | CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
        {
            return 0;
        }
    }

    // wait for access ready
    if (!pru_i2c_driver_WaitARDY(i2cDevice))
    {
        return 0;
    }
    pru_i2c_driver_DelayMicros(6);

    // wait for bus free
    // wait data
    if (!pru_i2c_driver_WaitBF(i2cDevice))
    {
        return 0;
    }

    // serve?
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_ARDY = 0b1;
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_XRDY = 1;
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_RRDY = 1;
    return count;
}

uint8_t pru_i2c_driver_ReadReg(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t* buffer)
{
    return pru_i2c_driver_ReadBytes(i2cDevice, address, reg, 1, buffer);
}
uint8_t pru_i2c_driver_WriteReg(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t value)
{
    return pru_i2c_driver_WriteBytes(i2cDevice, address, reg, 1, &value);
}


void pru_i2c_driver_Set400KHz(uint8_t i2cDevice)
{
    // prescaler
    CT_I2C[i2cDevice-1]->I2C_PSC = 0x04; // 24MHz
    /*
     * tLow = (SCLL +7)*83ns
     * 83ns is the time period at 48MHz/4(il PSC),
     * tLow = (1000000000ns/400000Hz)/2) is the time period (in ns) at low signal on SCL
     * SCLL = tLow/83ns -7
     * SCLL = 1250/83 -7
     * SCLL is like 8.06 (rounded)
     */
    CT_I2C[i2cDevice-1]->I2C_SCLL = 0x09; // from linux setting ... check linux driver for calc
    /*
     * tHigh = (SCLH +5)*83ns
     * 83ns is the time period at 48MHz/4,
     * tHigh = (1000000000ns/400000Hz)/2) is the time period (in ns) at high signal on SCL
     * SCLH = tHigh/83ns -5
     * SCLH = 1250/83 -5
     * SCLH is like 10 (rounded)
     */
    CT_I2C[i2cDevice-1]->I2C_SCLH = 0x03; // from linux setting ... check linux driver for calc
}

void pru_i2c_driver_Set100KHz(uint8_t i2cDevice)
{
    // prescaler
    CT_I2C[i2cDevice-1]->I2C_PSC = 0x0B; // 12MHz
    /*
     * tLow = (SCLL +7)*83ns
     * 83ns is the time period at 12MHz,
     * tLow = (1000000000ns/100000Hz)/2) is the time period (in ns) at low signal on SCL
     * SCLL = tLow/83ns -7 = 53,241
     * SCLL = 1250/83 -7
     * SCLL is like 53 (rounded)
     */
    CT_I2C[i2cDevice-1]->I2C_SCLL = 0xD;
    /*
     * tHigh = (SCLH +5)*83ns
     * 83ns is the time period at 12MHz,
     * tHigh = (1000000000ns/100000Hz)/2) is the time period (in ns) at high signal on SCL
     * SCLH = tHigh/83ns - 5 = 55,25
     * SCLH = 1250/83 - 5
     * SCLH is like 55 (rounded)
     */
    CT_I2C[i2cDevice-1]->I2C_SCLH = 0xF;
}

/*******************************************************************
 * C O N F I G U R A T I O N   O F   I 2 C   A N D   C L O C K S   *
 *******************************************************************/
uint8_t pru_i2c_driver_Init(uint8_t i2cDevice) {
//    uint32_t * CM_PER_L4LS_CLKSTCTRL = (uint32_t *) 0x44E00000;
//    (*CM_PER_L4LS_CLKSTCTRL) = ((*CM_PER_L4LS_CLKSTCTRL)
//            | (1 << CLKACTIVITY_I2C_FCLK) | (1 << CLKACTIVITY_L4LS_GCLK))
//            & 0xFFFFFFFC; // CLKTRCTRL = 0x00

    /*
     * FROM: https://e2e.ti.com/support/arm/sitara_arm/f/791/p/458311/1659097
     * CM_PER_I2C2_CLKCTRL must enabled;
     */
    (*CM_PER_I2C_CLKCTRL[i2cDevice-1]) = 2;

    // SYSC system control register
    CT_I2C[i2cDevice-1]->I2C_SYSC_bit.I2C_SYSC_AUTOIDLE = 0b0; // AutoIdle disabled
    CT_I2C[i2cDevice-1]->I2C_SYSC_bit.I2C_SYSC_ENAWAKEUP = 0b0; // wakeup disabled
    CT_I2C[i2cDevice-1]->I2C_SYSC_bit.I2C_SYSC_IDLEMODE = 0b01; // no idleMode
    CT_I2C[i2cDevice-1]->I2C_SYSC_bit.I2C_SYSC_CLKACTIVITY = 0b00; // cutoff interface ocp clock and functional system clock

    pru_i2c_driver_Set400KHz(i2cDevice);

    // I2C_BUF as default: DMA disabled and buffer tx/rx lenght = 1

    // enable i2c2
    CT_I2C[i2cDevice-1]->I2C_CON_bit.I2C_CON_I2C_EN = 0b1;
    if(pru_i2c_driver_WaitRDONE(i2cDevice)) {
        pru_i2c_initialized[i2cDevice-1]=1;
        return 1;
    }
    return 0;
}

uint8_t pru_i2c_driver_ReadBit(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bitPos, uint8_t* buffer) {
    uint8_t b;
    uint8_t count = pru_i2c_driver_ReadReg(i2cDevice, address, reg, &b);
    *buffer = b & (1 << bitPos);
    return count;
}
uint8_t pru_i2c_driver_WriteBit(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bitPos, uint8_t value) {
    uint8_t b;
    pru_i2c_driver_ReadReg(i2cDevice, address, reg, &b);
    b = ((value) ? (b | (1 << bitPos)) : (b & ~(1 << bitPos)));
    return pru_i2c_driver_WriteReg(i2cDevice, address, reg, b);
}
uint8_t pru_i2c_driver_ReadBits(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bitPos, uint8_t numBits, uint8_t* buffer) {
    uint8_t count, b;
    if ((count = pru_i2c_driver_ReadReg(i2cDevice, address, reg, &b)) != 0) {
        uint8_t mask = ((1 << numBits) - 1) << (bitPos - numBits + 1);
        b &= mask;
        b >>= (bitPos - numBits + 1);
        *buffer = b;
    }
    return count;
}
uint8_t pru_i2c_driver_WriteBits(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint8_t bitPos, uint8_t numBits, uint8_t value) {
    uint8_t b;
    if (pru_i2c_driver_ReadReg(i2cDevice, address, reg, &b) != 0) {
        uint8_t mask = ((1 << numBits) - 1) << (bitPos - numBits + 1);
        value <<= (bitPos - numBits + 1); // shift data into correct position
        value &= mask; // zero all non-important bits in data
        b &= ~(mask); // zero all important bits in existing byte
        b |= value; // combine data with existing byte
        return pru_i2c_driver_WriteReg(i2cDevice, address, reg, b);
    } else {
        return 0;
    }
}

uint16_t pru_i2c_driver_ReadWord(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint16_t* buffer) {
    uint8_t msblsb[2];
    if(pru_i2c_driver_ReadBytes(i2cDevice, address, reg, 2, msblsb)) {
        ((uint8_t*)buffer)[0] = msblsb[1]; // lsb first
        ((uint8_t*)buffer)[1] = msblsb[0]; // msb second
        return 1;
    }
    return 0;
}
uint16_t pru_i2c_driver_WriteWord(uint8_t i2cDevice, uint8_t address, uint8_t reg, uint16_t value) {
    uint8_t* value8 = (uint8_t*)(&value);
    uint8_t lsbmsb[2] = {0};
    lsbmsb[0] = value8[1]; // msb first
    lsbmsb[1] = value8[0]; // lsb first
    return pru_i2c_driver_WriteBytes(i2cDevice, address, reg, 2, lsbmsb);
}

#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"
#include "prugpio.h"
#include <pru_uart.h>
#include <string.h>

volatile register uint32_t __R30;
volatile register uint32_t __R31;
// From: http://git.ti.com/pru-software-support-package/pru-software-support-package/trees/master/examples/am335x/PRU_Hardware_UART

/* The FIFO size on the PRU UART is 16 bytes; however, we are (arbitrarily)
 * only going to send 8 at a time */
#define FIFO_SIZE	16
#define MAX_CHARS	8

void uart_init() {
    /*** INITIALIZATION ***/

	/* Set up UART to function at 115200 baud - DLL divisor is 104 at 16x oversample
	 * 192MHz / 104 / 16 = ~115200 */
	CT_UART.DLL = 104;
	CT_UART.DLH = 0;
	CT_UART.MDR = 0x0;

	/* Enable Interrupts in UART module. This allows the main thread to poll for
	 * Receive Data Available and Transmit Holding Register Empty */
	CT_UART.IER = 0x7;

	/* If FIFOs are to be used, select desired trigger level and enable
	 * FIFOs by writing to FCR. FIFOEN bit in FCR must be set first before
	 * other bits are configured */
	/* Enable FIFOs for now at 1-byte, and flush them */
	CT_UART.FCR = (0x8) | (0x4) | (0x2) | (0x1);
	//CT_UART.FCR = (0x80) | (0x4) | (0x2) | (0x01); // 8-byte RX FIFO trigger

	/* Choose desired protocol settings by writing to LCR */
	/* 8-bit word, 1 stop bit, no parity, no break control and no divisor latch */
	CT_UART.LCR = 3;

	/* Enable loopback for test */
	CT_UART.MCR = 0x00;

	/* Choose desired response to emulation suspend events by configuring
	 * FREE bit and enable UART by setting UTRST and URRST in PWREMU_MGMT */
	/* Allow UART to run free, enable UART TX/RX */
	CT_UART.PWREMU_MGMT = 0x6001;

	/*** END INITIALIZATION ***/
}

void uart_end() {
    /* Disable UART before halting */
	CT_UART.PWREMU_MGMT = 0x0;

	/* Halt PRU core */
	__halt();
}

void uart_send(const char* buffer) {
	uint8_t tx;
    uint8_t cnt = 0;

	/*** SEND SOME DATA ***/

	/* Let's send/receive some dummy data */
    while(1) {
        /* Load character, ensure it is not string termination */
        if ((tx = buffer[cnt++]) == '\0')
            break;
        CT_UART.THR = tx;

        /* Wait for TX FIFO to be empty */
        while (!((CT_UART.FCR & 0x2) == 0x2));
    }

	/*** DONE SENDING DATA ***/
}

char* itoa(int val, int base){
	static char buf[32] = {0};

	int i = 30;

	if(val == 0) {
		buf[i--] = '0';
		return &buf[i + 1];
	}

	for(; val && i ; --i, val /= base) {
	buf[i] = "0123456789abcdef"[val % base];
	}

	return &buf[i+1];

}

void main(void)
{    
    char str[50];
    uint8_t ret, data;
    
    uart_init();
    uart_send("-------------------------------PROGRAM START-------------------------------\r\n\n");
    uart_send("Pins are configured.\r\n\n");

    pru_i2c_driver_Init(2);
    //pru_i2c_driver_WriteReg(2, 0x1C, 0x2A, 0x01);

    // Try to read Reg 0x0D (WHO_AM_I) three times and display results over UART
    ret = pru_i2c_driver_ReadReg(2, 0x1C, 0x0D, &data);
    strcpy(str, "Value: ");
    strcat(str, itoa(data, 10));
    strcat(str, " | Return: ");
    strcat(str, itoa(ret, 10));
    strcat(str, "\r\n\n");
    uart_send(str);

    ret = pru_i2c_driver_ReadReg(2, 0x1C, 0x0D, &data);
    strcpy(str, "Value: ");
    strcat(str, itoa(data, 10));
    strcat(str, " | Return: ");
    strcat(str, itoa(ret, 10));
    strcat(str, "\r\n\n");
    uart_send(str);

    ret = pru_i2c_driver_ReadReg(2, 0x1C, 0x0D, &data);
    strcpy(str, "Value: ");
    strcat(str, itoa(data, 10));
    strcat(str, " | Return: ");
    strcat(str, itoa(ret, 10));
    strcat(str, "\r\n\n");
    uart_send(str);
    /*while(1) {

	__delay_cycles(100000000);
    }*/

    uart_end();
}

  • Hello,

    We can only support questions about TI software on the forums, not LinuxDroneLab software. So we will be limited in the assistance we can offer around debugging their code.

    Some general thoughts:

    * Are you running Linux on the ARM core? If so, are you enabling or disabling the I2C node in the Linux device tree? If another core (i.e., PRU) is controlling a peripheral you want to make sure that peripheral is disabled in Linux. That way the ARM does not try to do anything wacky to the peripheral the other core is using. Note that the peripheral's clocks may need to be manually enabled in this case, since Linux does not turn on the clocks for peripherals that it thinks are off.

    * Potentially unrelated, but if you are running TI-RTOS on the ARM core, we provide an RTOS driver and PRU firmware for the RTOS ARM core to control the PRU as if it was another I2C instance. See the rtos i2c driver documentation for more information.

    * If you need to refer to an example I2C driver, the TI RTOS driver for ARM cores might be helpful. It is in the AM335x RTOS SDK under pdk_xxx/packages/ti/drv/i2c. I expect AM335x to use src/v1.

    Regards,

    Nick