I2C 外设在单次事务后失败

问题描述 投票:0回答:1

我正在使用 这个 库,使用 Beaglebone Black 上的 PRU 通过 I2C 总线对一些传感器进行采样。似乎只有第一个事务工作正常,之后库似乎只尝试从寄存器地址 0xFF 读取,然后 SCL 线被拉低(请参阅逻辑分析仪捕获的附加图像)。我已经在他们的 github 上提出了一个问题,但我并不期待在那里得到答案,因为存储库已经很多年没有更新了,而且作者自 2019 年以来就没有在 GitHub 上活跃过。

在尝试自己查找问题时,我决定将寄存器地址和接收到的数据写入共享内存中,以使用 prudebug 进行检查。对于此测试,我尝试读取寄存器 0x00 和 0x03。有趣的是,它将正确的寄存器地址写入内存(见图),但只有第一个返回值是正确的(0xEA)。写入共享内存中接收数据缓冲区的值为0xFF,这是没有意义的。从逻辑分析仪可以看出,0xFF是它尝试读取的地址。

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

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;
}
c embedded i2c beagleboneblack
1个回答
0
投票

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

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;
}

此外,延迟功能是完全错误的。它不会延迟请求的微秒,并且一旦打开优化就会被编译器优化掉。我通过将其更改为修复了它:

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);
    };
}
© www.soinside.com 2019 - 2024. All rights reserved.