CH341 BlockRead卡死

如题,我在使用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回读回来的数据似乎是错的

    以下是正常回复数据:

1698392915655654.png

1698392915872838.png

1698392915696848.png




抱歉,上面的数据解析似乎是RIGOL的逻辑分析功能解析数据时错位了,请帮我看下API卡死的问题就好,谢谢。


1698393968380616.png

1698393969153387.png



您好,可以把完整的代码工程发到我们技术邮箱,我们工程师同步试下。邮箱地址: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才正常产生了时钟信号,后续第二个信号流的数据用示波器抓到了


您好,工程师已添加您微信,后续有问题可随时沟通。


只有登录才能回复,可以选择微信账号登录