如题,我在使用CH341进行I2C通讯,使用PmBus协议,但是在读取较多数据时经常卡死,4个6个字节短一点的都可以,多了的话如20个25个都不行了。其他情况如读写字节或字均正常,请问有什么解决办法吗?
您好,可以把完整测试代码发下,我们协助您看下是否为代码调用问题导致。此外,您反馈的卡死是API发生阻塞吗?如是,此时设备管理器中设备状态是否正常?
你好,下面是我的C#代码
public SmbusStatus BlockRead(byte cmd, ref byte[] data, byte dataLen, bool bPec) { byte[] txBuffer = new byte[10]; byte[] rxBuffer = new byte[512]; byte txLength = 0; byte rxLength = bPec ? (byte)(dataLen + 2): (byte)(dataLen + 1); try { // Write SlaveAddress and Cmd, ReadAddress will be sent automatically to fetch data txBuffer[txLength++] = NodeId; txBuffer[txLength++] = cmd; Marshal.Copy(txBuffer, 0, ptrTx, txLength); if (!CH341StreamI2C_Delay(DevIndex, txLength, ptrTx, rxLength, ptrRx, 0, 0, 60, 0))// 动态延时 { return SmbusStatus.I2cError; } else if(rxLength > 2)// CNT + DATA + PEC { Marshal.Copy(ptrRx, rxBuffer, 0, rxLength); // validate CRC if (bPec) { byte[] crcBuffer = new byte[256]; crcBuffer[0] = NodeId; crcBuffer[1] = cmd; crcBuffer[2] = (byte)(NodeId + 1); crcBuffer[3] = rxBuffer[0];//data cnt for (uint i = 0; i < rxBuffer[0]; i++) { crcBuffer[4 + i] = rxBuffer[i+1]; } if (rxBuffer[rxLength-1] != (byte)CRC(CRC_8, crcBuffer, 0, (byte)(4 + dataLen))) { return SmbusStatus.PecError; } } // return received byte, except cnt and rec for (uint i = 0; i < dataLen; i++) { data[i] = rxBuffer[1 + i]; } return SmbusStatus.Successful; } else { return SmbusStatus.I2cError; } } catch { return SmbusStatus.I2cError; } }
我在使用您之前提供的博客中的CH341StreamI2C_Delay代码后返回FALSE,多读取几次之后API卡死,需要重新插拔CH341设备才能退出GUI。
在我第一次调用读取函数时可以用逻辑分析仪看到收发的数据,仿真查看嵌入式设备的回复正常,但CH341回读回来的数据似乎是错的
以下是正常回复数据:
抱歉,上面的数据解析似乎是RIGOL的逻辑分析功能解析数据时错位了,请帮我看下API卡死的问题就好,谢谢。
您好,可以把完整的代码工程发到我们技术邮箱,我们工程师同步试下。邮箱地址:tech@wch.cn。
好的,感谢,我在这儿同步贴下我的CH341StreamI2C_Delay代码,这通讯搞得确实头疼,只剩下这一个问题没解决了。
#define CH341_TX_LEN_MAX 32 //每包数据最多发送的数据长度,最大32 inline UCHAR CH341_DELAY_US(PUCHAR mBuf, UCHAR uDelay) { UCHAR i = 0; while (uDelay > mCH341A_CMD_I2C_STM_DLY) { mBuf[i++] = mCH341A_CMD_I2C_STM_US | mCH341A_CMD_I2C_STM_DLY; // 延时15微秒 uDelay -= mCH341A_CMD_I2C_STM_DLY; } if (uDelay > 0) mBuf[i++] = mCH341A_CMD_I2C_STM_US | (uDelay & 0x0f); // 填入剩余的延迟 return i; } inline UCHAR CH341_DELAY_MS(PUCHAR mBuf, UCHAR uDelay) { UCHAR i = 0; while (uDelay > mCH341A_CMD_I2C_STM_DLY) { mBuf[i++] = mCH341A_CMD_I2C_STM_MS | mCH341A_CMD_I2C_STM_DLY; // 延时15毫秒 uDelay -= mCH341A_CMD_I2C_STM_DLY; } if (uDelay > 0) mBuf[i++] = mCH341A_CMD_I2C_STM_MS | (uDelay & 0x0f); // 填入剩余的延迟 return i; } BOOL WINAPI CH341StreamI2C_Delay( ULONG iIndex, // 指定CH341设备序号 ULONG iWriteLength, // 准备写出的数据字节数 PVOID iWriteBuffer, // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位 ULONG iReadLength, // 准备读取的数据字节数 PVOID oReadBuffer, // 指向一个缓冲区,返回后是读入的数据 UCHAR iWriteDataDelay, // 连续写数据之间的延时,单位US,数值范围:0~15 UCHAR iAddrDelay1, // 写地址到读地址之间的延时,单位US,数值范围:0~15 UCHAR iAddrDelay2, // 读地址和读数据的延时,单位US,数值范围:0~15 UCHAR iReadDataDelay) // 连续读数据之间的延时,单位US,数值范围:0~15 { UCHAR mBuffer[mDEFAULT_COMMAND_LEN + mDEFAULT_COMMAND_LEN / 8]; UCHAR mBufferTemp[mDEFAULT_COMMAND_LEN + mDEFAULT_COMMAND_LEN / 8]; ULONG i, j, mLength; PUCHAR mWrBufTemp,mWrBuf; // 内存分配 mLength = max(iWriteLength, iReadLength); if (mLength > mMAX_BUFFER_LENGTH) return (FALSE); if (mLength <= mDEFAULT_BUFFER_LEN)// 不超过默认缓冲区长度 { mWrBuf = (PUCHAR)mBuffer; mWrBufTemp = (PUCHAR)mBufferTemp; } else // 超过则需要另外分配内存 { mWrBuf = (PUCHAR)LocalAlloc(LMEM_FIXED, mMAX_COMMAND_LENGTH + mMAX_COMMAND_LENGTH / 8); // 分配内存 mWrBufTemp = (PUCHAR)LocalAlloc(LMEM_FIXED, mMAX_COMMAND_LENGTH + mMAX_COMMAND_LENGTH / 8); // 分配内存 if ((mWrBuf == NULL) || (mWrBufTemp == NULL)) return (FALSE); // 分配内存失败 } // 填充发送数据 i = 0; //mWrBufTemp[i++] = mCH341A_CMD_I2C_STREAM; // 命令码,从次字节开始为I2C命令流 mWrBufTemp[i++] = mCH341A_CMD_I2C_STM_STA; // 产生起始位 // 填入发送数据 j = 0; while (j < iWriteLength) { // 填入一个字节的数据 mWrBufTemp[i++] = (UCHAR)(mCH341A_CMD_I2C_STM_OUT | 0x01); // 输出数据,位5-位0为长度 mWrBufTemp[i++] = *((PUCHAR)iWriteBuffer + j++); // 复制地址 i += CH341_DELAY_US(&mWrBufTemp[i], iWriteDataDelay); } if (iReadLength > 0) { // 填入读地址与写地址之间的延迟 i += CH341_DELAY_US(&mWrBufTemp[i], iAddrDelay1); // 填入读地址 mWrBufTemp[i++] = mCH341A_CMD_I2C_STM_STA; // 产生起始位 mWrBufTemp[i++] = (UCHAR)(mCH341A_CMD_I2C_STM_OUT | 0x01); // 输出数据,位5-位0为长度 mWrBufTemp[i++] = *(PUCHAR)iWriteBuffer | 0x01; // I2C目标设备地址,最低位为1则进行读操作 // 填入读地址与读数据之间的延迟 i += CH341_DELAY_US(&mWrBufTemp[i], iAddrDelay2); // 填入读数据 for (j = 0; j < (iReadLength - 1); j++) { mWrBufTemp[i++] = (UCHAR)(mCH341A_CMD_I2C_STM_IN | 1); // 输入数据,位5-位0为长度 i += CH341_DELAY_US(&mWrBufTemp[i], iReadDataDelay); } // 填入最后一个字节并发送无应答 mWrBufTemp[i++] = (UCHAR)mCH341A_CMD_I2C_STM_IN; // 输入数据,位5-位0为长度 i += CH341_DELAY_US(&mWrBufTemp[i], iReadDataDelay); } // 填入结束命令 mWrBufTemp[i++] = mCH341A_CMD_I2C_STM_STO; // 产生停止位 mWrBufTemp[i++] = mCH341A_CMD_I2C_STM_END; // 当前包提前结束 // 复制临时数组到发送数组,并按照指定长度分割 mLength = i;// 待分配字节数 i = 0;// mWrBufTemp序列号 j = 0;// mWrBuf序列号 while (mLength > (CH341_TX_LEN_MAX - 1)) { mWrBuf[j++] = mCH341A_CMD_I2C_STREAM; // 命令码,从次字节开始为I2C命令流 memcpy(&mWrBuf[j], &mWrBufTemp[i], (CH341_TX_LEN_MAX - 1)); // 填充所有数据 j += (CH341_TX_LEN_MAX - 1); i += (CH341_TX_LEN_MAX - 1); mLength -= (CH341_TX_LEN_MAX - 1); } // 复制最后的不足指定长度的数据 mWrBuf[j++] = mCH341A_CMD_I2C_STREAM; // 命令码,从次字节开始为I2C命令流 memcpy(&mWrBuf[j], &mWrBufTemp[i], mLength); // 填充所有数据 j += mLength; mLength = 0; // Write & Read if (iReadLength) i = CH341WriteRead(iIndex, j, mWrBuf, CH341_TX_LEN_MAX, (iReadLength + CH341_TX_LEN_MAX - 1) / CH341_TX_LEN_MAX, &mLength, oReadBuffer); // 执行数据流命令,先输出再输入 else i = CH341WriteData(iIndex, mWrBuf, &j); // 写出数据块 // 检测是否读回所需数据 if (i && mLength != iReadLength) i = FALSE; // 释放分配的内存 if (mWrBuf != mBuffer) { LocalFree(mWrBuf); LocalFree(mWrBufTemp); } return (i); }
这两天测试下来发现确实是CH341WriteRead这个API有BUG,每次点击读数据时,前32个数据发送和读取均正常,从第二个数据流开始时钟信号发送的越来越晚,到第三次重新读数据第二个数据流干脆就没有时钟信号产生了。
我试过更换CH341DLL.LIB库版本也没有用,所以想请教下有没有什么解决办法?或者能提供CH341WriteRead的源码以便我自行修改吗?
真的很急,谢谢!
联系方式:个人信息保护,已隐藏
补贴
增加了读数据复位操作以防卡死
// 检测是否读回所需数据 if (i && mLength != iReadLength) { CH341ResetRead(iIndex);//复位读操作以防下次读数据卡死 i = FALSE; }
未加CH341ResetRead复位读操作前第一个信号流能读到数据,第二个信号流读不到。
加上之后在复位之后CH341才正常产生了时钟信号,后续第二个信号流的数据用示波器抓到了
您好,工程师已添加您微信,后续有问题可随时沟通。