我正在使用 这个 库,使用 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;
}
经过一些繁琐的调试,我发现了给定代码的问题;事实证明,库中给出的延迟不够,并且库未能清除指示发生错误的位标志。结果,第一个事务将正常发生,但当尝试从 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);
};
}