I2C外围设备在单个事务后失败

xam8gpfp  于 9个月前  发布在  其他
关注(0)|答案(1)|浏览(173)

我正在使用this库通过Beaglebone Black上的PRU通过I2C总线对一些传感器进行采样。似乎只有第一个事务工作正常,之后库似乎只尝试从寄存器地址0xFF读取,然后SCL线被拉低(见所附的图像捕获的逻辑分析仪)。我已经打开了一个问题,他们的github,但我不期待那里有答案,因为存储库已经多年没有更新,作者自2019年以来就没有在GitHub上活跃过。
x1c 0d1x的数据
在尝试自己查找问题时,我决定将寄存器地址和接收到的数据写入共享内存中,以使用prudebug进行检查。对于此测试,我尝试读取寄存器0x 00和0x 03。有趣的是,它将正确的寄存器地址写入内存(见图),但只有第一个返回值是正确的(0xEA)。写入共享内存中的接收数据缓冲区的值是0xFF,从逻辑分析仪可以看出,0xFF是它试图读取的地址。



我不知道我在这里做错了什么,但任何帮助都将不胜感激!我的主要代码如下所示,库函数可以在链接库here中找到。

uint8_t curr_data_idx = 0;
uint8_t tmp = 0;
uint8_t reg = 0x00;
volatile uint8_t * buf = ((volatile uint8_t*)(RESERVED_SMEM_ADDR + 32));

int main(void)
{

    // enable OCP
    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

    pru_i2c_driver_DelayMicros(500000);

    uint8_t res = 0;
    res = pru_i2c_driver_Init(1);
    uint8_t saddr = 0x68;

    pru_i2c_driver_ReadReg(1, saddr, reg, &tmp);
    buf[curr_data_idx] = tmp;
    pru_i2c_driver_DelayMicros(1000);
    curr_data_idx++;

    reg = 0x03;
    pru_i2c_driver_ReadReg(1, saddr, reg, &tmp);
    buf[curr_data_idx] = tmp;
    pru_i2c_driver_DelayMicros(1000);
    curr_data_idx++;
   
    __halt();
    return 0;
}

字符串

ht4b089n

ht4b089n1#

经过一些繁琐的调试,我发现了给定代码的问题;原来库中给出的延迟是不够的,库未能清除指示发生错误的位标志。因此,第一个事务将正常发生,但当试图从RX FIFO读取数据时,AERR标志将被置位,因为当由于延迟不够而试图读取时FIFO仍然是空的。然后,当试图执行第二次读取时,错误标志仍然被设置,从RX FIFO读取的数据实际上是以前的值。在事务之间增加几微秒的延迟并确保处理发生的任何错误后,一切都工作正常。我重写的库:

uint8_t pru_i2c_driver_ReadBytes(
                  const uint8_t i2cDevice, 
                  const uint8_t address, 
                  const uint8_t reg, 
                  const uint8_t bytes, 
                  uint8_t* buffer, 
                  i2c_flags_t* flags
)
{
    uint8_t tries = 0;
_init_section:
    if(!pru_i2c_initialized[i2cDevice-1]) {
        if(!pru_i2c_driver_Init(i2cDevice)) {
            *flags |= I2C_FAILED_INIT;
            if(tries++ < MAX_TRIES){
                pru_i2c_driver_DelayMicros(1);
                goto _init_section;
            }
            return 0;
        }
    }
    tries = 0;

_write_init:
    if (!pru_i2c_driver_WaitBB(i2cDevice))
    {
        *flags |= I2C_FAILED_WAIT_BB;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(1);
            goto _write_init;
        }
        return 0;
    }
    tries = 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; // EN/MST/TRX/STT
    pru_i2c_driver_DelayMicros(7);

_write_start:
    if (!pru_i2c_driver_WaitXRDY(i2cDevice))
    {
        *flags |= I2C_FAILED_WAIT_XRDY;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(1);
            goto _write_start;
        }
        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))
    {
        *flags |= I2C_FAILED_WAIT_ARDY;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(1);
            goto _write_start;
        }
        return 0;
    }

    pru_i2c_driver_DelayMicros(5);
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_ARDY = 0b1;

    if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR)
    {
        *flags |= I2C_AERR_WRITE;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(1);
            goto _write_start;
        }
        *flags |= I2C_AERR_WRITE_UNRESOLVED;
        return 0;
    }
    if(CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
    {
        *flags |= I2C_NACK_DETECTED;
        CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK = 0b1;
        return 0;
    }

    tries = 0;
_read_init:
    // read data
    CT_I2C[i2cDevice-1]->I2C_CNT_bit.I2C_CNT_DCOUNT = bytes; // bytes to receive
    CT_I2C[i2cDevice-1]->I2C_CON = 0x8403; // EN/MST/STP/STT
    pru_i2c_driver_DelayMicros(20);

_read_start:
    // wait data
    if (!pru_i2c_driver_WaitRRDY(i2cDevice))
    {
        *flags |= I2C_FAILED_WAIT_RRDY;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(1);
            goto _read_start;
        }
        return 0;
    }

    pru_i2c_driver_DelayMicros(10);

    int8_t count;

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

        pru_i2c_driver_DelayMicros(10);

        if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR)
        {
            CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_AERR = 0b1;
            if(tries++ < MAX_TRIES){
                pru_i2c_driver_DelayMicros(10);
                goto _looptop;
            }
            return 0;

        }
        if (CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK)
        {
            *flags |= I2C_NACK_DETECTED;
            CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_NACK = 0b1;
            return 0;
        }

    _wait_next_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))
        {
            *flags |= I2C_FAILED_WAIT_RRDY;
            if(tries++ < MAX_TRIES){
                pru_i2c_driver_DelayMicros(10);
                goto _wait_next_data;
            }
            return 0;
        }

        tries = 0;
    }

_transaction_end:
    // wait for access ready
    if (!pru_i2c_driver_WaitARDY(i2cDevice))
    {
        *flags |= I2C_FAILED_WAIT_ARDY;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(10);
            goto _transaction_end;
        }
        return 0;
    }

_wait_bus_free:
    // wait for bus free
    // wait data
    if (!pru_i2c_driver_WaitBF(i2cDevice))
    {
        *flags |= I2C_FAILED_WAIT_BF;
        if(tries++ < MAX_TRIES){
            pru_i2c_driver_DelayMicros(10);
            goto _wait_bus_free;
        }
        return 0;
    }

    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 = 0b1;
    CT_I2C[i2cDevice-1]->I2C_IRQSTATUS_RAW_bit.I2C_IRQSTATUS_RAW_RRDY = 0b1;

    return count;
}

字符串
此外,delay函数是明显错误的。它没有延迟所请求的微秒,并且在优化打开后立即被编译器优化掉。我通过将其更改为:

void DelayMicros(uint8_t micros){
    // Factor of 29 gives most accurate timings verified with logic analyzer
    uint16_t cycles = ((uint16_t)micros * 29u);
    uint16_t i = 0;
#pragma MUST_ITERATE(29, , 29)
#pragma UNROLL(1)
    for (i = 0; i < cycles; i++)
    {
        __delay_cycles(1);
    };
}

相关问题