在线时间38 小时
UID3158301
注册时间2017-7-3
NXP金币16
该用户从未签到
中级会员
- 积分
- 463
- 最后登录
- 2023-2-23
|
本帖最后由 imetro 于 2018-5-15 22:53 编辑
2018/5/15更新I2C DMA使用方法,见第3楼。
----------原帖内容----------
刚刚调好了一个LPC54114的I2C的bug,因为这个问题困扰了一天时间,现将遇到的问题和解决方法做个记录,希望对各位有所帮助。
先说说问题吧。在我的项目中需要用到一个心率测量模块MAX30102,该模块使用I2C总线与主机通信,在此之前需要对MAX30102初始化。但是,在初始化的过程中却遇到了以下情况:
1. 有时候程序会卡在对于MSTPENDING位的检测中。在SDK的代码中大量使用了I2C_PendingStatusWait()函数,而该函数的作用就是判断MSTPENDING位是否被置位。函数代码如下:
- static uint32_t I2C_PendingStatusWait(I2C_Type *base)
- {
- uint32_t status;
- #if I2C_WAIT_TIMEOUT
- uint32_t waitTimes = I2C_WAIT_TIMEOUT;
- #endif
- do
- {
- status = I2C_GetStatusFlags(base);
- #if I2C_WAIT_TIMEOUT
- } while (((status & I2C_STAT_MSTPENDING_MASK) == 0) && (--waitTimes));
- if (waitTimes == 0)
- {
- return kStatus_I2C_Timeout;
- }
- #else
- } while ((status & I2C_STAT_MSTPENDING_MASK) == 0);
- #endif
- /* Clear controller state. */
- I2C_MasterClearStatusFlags(base, I2C_STAT_MSTARBLOSS_MASK | I2C_STAT_MSTSTSTPERR_MASK);
- return status;
- }
复制代码
2. 有时候程序可以继续执行,但MSTSTATE的值表明传输有误。检查可以发现,MSTSTATE的值有时候为0x3,这表示在发送从机地址时没有应答,但地址肯定是正确的。
3. 进一步分析可以发现,在LPC54114和MAX30102上电后第一次运行时,第一次写入是正确的(包括写从机地址、写内存地址等步骤),但后续的读写会出现上述错误。
遇到这种情况,第一个想到的就是用逻辑分析仪来看看波形(忘了截图了,不好意思)。第一次写入的波形看起来似乎没有问题,但后续的读写在写从机地址这一步确实会返回NAK。那么,问题应该出在第一次写入中。仔细检查可以发现,在第一次写入后的STOP信号出现了两次,难道这就是问题的原因?不过我记得我好像没有这么做呀,是不是SDK做了什么?想到这里,我赶紧回过头去看我的读写代码:
- static uint8_t max30102_read_reg(uint8_t addr)
- {
- uint8_t tmp;
-
- I2C_MasterStart(I2C1, 0x57, kI2C_Write);
- tmp = addr;
- I2C_MasterWriteBlocking(I2C1, &tmp, 1, kI2C_TransferNoStopFlag);
- I2C_MasterRepeatedStart(I2C1, 0x57, kI2C_Read);
- I2C_MasterReadBlocking(I2C1, &tmp, 1, 0);
- I2C_MasterStop(I2C1);
-
- return tmp;
- }
复制代码- static void max30102_write_reg(uint8_t addr, uint8_t data)
- {
- uint8_t tmp;
-
- I2C_MasterStart(I2C1, 0x57, kI2C_Write);
- tmp = addr;
- I2C_MasterWriteBlocking(I2C1, &tmp, 1, kI2C_TransferNoStopFlag);
- tmp = data;
- I2C_MasterWriteBlocking(I2C1, &tmp, 1, 0);
- I2C_MasterStop(I2C1);
- }
复制代码
在我的代码中,确实在最后发送了STOP信号,但是看起来并没有调用两次。难道是调用的I2C_MasterReadBlocking()和I2C_MasterWriteBlocking()函数有问题?我可是照着例程打的呀。看来这两个函数的最后一个参数一定有什么玄机。赶紧又跑去看SDK的相关代码,这一看终于发现了问题:
- status_t I2C_MasterWriteBlocking(I2C_Type *base, const void *txBuff, size_t txSize, uint32_t flags)
- {
- ...
- if ((status & (I2C_STAT_MSTARBLOSS_MASK | I2C_STAT_MSTSTSTPERR_MASK)) == 0)
- {
- if (!(flags & kI2C_TransferNoStopFlag))
- {
- /* Initiate stop */
- base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK;
- status = I2C_PendingStatusWait(base);
- if (status == kStatus_I2C_Timeout)
- {
- return kStatus_I2C_Timeout;
- }
- }
- }
- ...
- }
复制代码- status_t I2C_MasterReadBlocking(I2C_Type *base, void *rxBuff, size_t rxSize, uint32_t flags)
- {
- ...
- if ((flags & kI2C_TransferNoStopFlag) == 0)
- {
- /* initiate NAK and stop */
- base->MSTCTL = I2C_MSTCTL_MSTSTOP_MASK;
- status = I2C_PendingStatusWait(base);
- if (status == kStatus_I2C_Timeout)
- {
- return kStatus_I2C_Timeout;
- }
- }
- ...
- }
复制代码
可以看到,这两处代码是整个函数唯一用到flags这个参数的地方,且参数均只用到了kI2C_TransferNoStopFlag,且它们的作用相同:在flags为0时终止传输并发送STOP信号(当然I2C_MasterReadBlocking()函数还包括对最后一次读操作返回NAK)。按照我的代码,在max30102_read_reg()和max30102_write_reg()的结尾处确实会发送两次STOP信号,而这导致了MCU和MAX30102无法正确通信。至此问题得到解决,将两个多余的I2C_MasterStop()函数去掉就正常了。
这个问题解决得比较艰难,一是没有认识到多次发送STOP信号可能会造成问题,二是该问题可能会导致MCU和MAX30102都无法再正常工作,难以分开考察(现在看来情况1像是MCU的I2C状态机出错,而情况2像是MAX30102进入了某种锁定状态),好在最后通过逻辑分析和SDK代码阅读发现了问题。最后,建议修改SDK中demo的相关代码,以免造成不必要的困扰。最后贴一段SDK的代码来说明问题,其中最后一个I2C_MasterStop是应该去掉的:
- ...
- if (kStatus_Success == I2C_MasterStart(EXAMPLE_I2C_MASTER, I2C_MASTER_SLAVE_ADDR_7BIT, kI2C_Write))
- {
- /* subAddress = 0x01, data = g_master_txBuff - write to slave.
- start + slaveaddress(w) + subAddress + length of data buffer + data buffer + stop*/
- reVal = I2C_MasterWriteBlocking(EXAMPLE_I2C_MASTER, &deviceAddress, 1, kI2C_TransferNoStopFlag);
- if (reVal != kStatus_Success)
- {
- return -1;
- }
- reVal = I2C_MasterRepeatedStart(EXAMPLE_I2C_MASTER, I2C_MASTER_SLAVE_ADDR_7BIT, kI2C_Read);
- if (reVal != kStatus_Success)
- {
- return -1;
- }
- reVal = I2C_MasterReadBlocking(EXAMPLE_I2C_MASTER, g_master_rxBuff, I2C_DATA_LENGTH - 1, 0);
- if (reVal != kStatus_Success)
- {
- return -1;
- }
- reVal = I2C_MasterStop(EXAMPLE_I2C_MASTER);
- if (reVal != kStatus_Success)
- {
- return -1;
- }
- }
- ...
复制代码
|
评分
-
查看全部评分
|