CH341的I2C接口编程说明

article/2025/11/7 2:03:04

 CH341的I2C接口特性:(CH347为CH341的升级版,支持更高速的I2C速度,支持ACK检测以及I2C时钟延展等功能)

1、支持I2C速度20K/100K/400K/750K;

2、默认不支持设备的ACK应答监测,即忽略ACK状态;强制支持需修改软件;

引脚序号功能说明
24SCL
23SDA

 Windows系统I2C通讯接口函数

HANDLE	WINAPI	CH341OpenDevice(  // 打开CH341设备,返回句柄,出错则无效ULONG			iIndex );  // 指定CH341设备序号,0对应第一个设备VOID	WINAPI	CH341CloseDevice(  // 关闭CH341设备ULONG			iIndex );  // 指定CH341设备序号BOOL	WINAPI	CH341SetStream(  // 设置串口流模式ULONG			iIndex,  // 指定CH341设备序号ULONG			iMode );  // 指定模式,见下行
// 位1-位0: I2C接口速度/SCL频率, 00=低速/20KHz,01=标准/100KHz(默认值),10=快速/400KHz,11=高速/750KHz
// 位2:     SPI的I/O数/IO引脚, 0=单入单出(D3时钟/D5出/D7入)(默认值),1=双入双出(D3时钟/D5出D4出/D7入D6入)
// 位7:     SPI字节中的位顺序, 0=低位在前, 1=高位在前
// 其它保留,必须为0BOOL	WINAPI	CH341ReadI2C(  // 从I2C接口读取一个字节数据ULONG			iIndex,  // 指定CH341设备序号UCHAR			iDevice,  // 低7位指定I2C设备地址UCHAR			iAddr,  // 指定数据单元的地址PUCHAR			oByte );  // 指向一个字节单元,用于保存读取的字节数据BOOL	WINAPI	CH341WriteI2C(  // 向I2C接口写入一个字节数据ULONG			iIndex,  // 指定CH341设备序号UCHAR			iDevice,  // 低7位指定I2C设备地址UCHAR			iAddr,  // 指定数据单元的地址UCHAR			iByte );  // 待写入的字节数据BOOL	WINAPI	CH341StreamI2C(  // 处理I2C数据流,2线接口,时钟线为SCL引脚,数据线为SDA引脚(准双向I/O),速度约56K字节ULONG			iIndex,  // 指定CH341设备序号ULONG			iWriteLength,  // 准备写出的数据字节数PVOID			iWriteBuffer,  // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位ULONG			iReadLength,  // 准备读取的数据字节数PVOID			oReadBuffer );  // 指向一个缓冲区,返回后是读入的数据BOOL	WINAPI	CH341ReadEEPROM(  // 从EEPROM中读取数据块,速度约56K字节ULONG			iIndex,  // 指定CH341设备序号EEPROM_TYPE		iEepromID,  // 指定EEPROM型号ULONG			iAddr,  // 指定数据单元的地址ULONG			iLength,  // 准备读取的数据字节数PUCHAR			oBuffer );  // 指向一个缓冲区,返回后是读入的数据BOOL	WINAPI	CH341WriteEEPROM(  // 向EEPROM中写入数据块ULONG			iIndex,  // 指定CH341设备序号EEPROM_TYPE		iEepromID,  // 指定EEPROM型号ULONG			iAddr,  // 指定数据单元的地址ULONG			iLength,  // 准备写出的数据字节数PUCHAR			iBuffer );  // 指向一个缓冲区,放置准备写出的数据

如上API接口函数,根据不同的业务场景可以选用不同的函数。

CH341ReadI2C: 适用于I2C设备地址固定7位,单次读取1个字节。

CH341WriteI2C: 适用于I2C设备地址固定7位,单次写入1个字节。

CH341StreamI2C: 适用于多字节的设备地址,或设备地址后紧跟寄存器地址,或连续的多字节读写。

CH341ReadIEEPROM,CH341WriteEEPROM: 适用于直接操作EEPROM存储器件。

操作流程:

CH341StreamI2C 函数说明

iWriteLength:I2C Write的字节长度

iWriteBuffer:I2C Write的缓冲区内容,该缓冲区内容会经过SDA信号线对外输出首字节地址通常是设备地址及读写位。如设备地址是0x50,I2C写操作时首字节为:0x50 << 1 = 0xA0,I2C读操作时首字节为:0x50 << 1 | BIT(0) = 0xA1。

iReadLength:I2C Read的字节长度

oReadBuffer:API成功返回后,其内容是从SDA信号线上采集的数据

示例1:EEPROM 24C256的设备地址是:0x50, 从其3200H开始的地址读取6字节的数据。

UCHAR OutBuf[3] = {0xA0, 0x32, 0x00};
UCHAR InBuf[6];CH341StreamI2C(0, 3, OutBuf, 6, inBuf);

对应CH341PAR软件操作:

示例2:EEPROM 24C256的设备地址是:0x50, 从其3200H开始的地址写入2个字节的数据,内容0x11,0x22。

UCHAR OutBuf[5];OutBuf[0] = 0xA0;
OutBuf[1] = 0x32;
OutBuf[2] = 0x00;
OutBuf[3] = 0x11;
OutBuf[4] = 0x22;CH341StreamI2C(0, 5, OutBuf, 0, NULL);

对应CH341PAR软件操作:

 对应I2C总线时序如下:(未连接真正的EEPROM器件,忽略红色NACK标识)

升级的I2C接口函数

默认库函数提供的I2C函数不支持设备ACK的应答检测,此外有些外设需要在I2C的地址和数据或数据和数据之间插入一定的延迟delay,来满足时序上的要求。此类需求,可参考如下 API的实现。

I2C Start 和 I2C Stop

BOOL	WINAPI	IIC_IssueStart(ULONG			iIndex )  // 指定CH341设备序号
{UCHAR	mBuffer[ mCH341_PACKET_LENGTH ];ULONG	mLength;mBuffer[ 0 ] = mCH341A_CMD_I2C_STREAM;  // 命令码mBuffer[ 1 ] = mCH341A_CMD_I2C_STM_STA;  // 产生起始位mBuffer[ 2 ] = mCH341A_CMD_I2C_STM_END;  // 当前包提前结束mLength = 3;return( CH341WriteData( iIndex, mBuffer, &mLength ) );  // 写出数据块
}BOOL	WINAPI	IIC_IssueStop(ULONG			iIndex )  // 指定CH341设备序号
{UCHAR	mBuffer[ mCH341_PACKET_LENGTH ];ULONG	mLength;mBuffer[ 0 ] = mCH341A_CMD_I2C_STREAM;  // 命令码mBuffer[ 1 ] = mCH341A_CMD_I2C_STM_STO;  // 产生停止位mBuffer[ 2 ] = mCH341A_CMD_I2C_STM_END;  // 当前包提前结束mLength = 3;return( CH341WriteData( iIndex, mBuffer, &mLength ) );  // 写出数据块
}

I2C Write 1个字节并检查应答

BOOL	WINAPI	IIC_OutByteCheckAck(  // 输出一字节数据并检查应答是否有效ULONG			iIndex,  // 指定CH341设备序号UCHAR			iOutByte )  // 准备写出的数据
{UCHAR	mBuffer[ mCH341_PACKET_LENGTH ];ULONG	mLength, mInLen;mBuffer[ 0 ] = mCH341A_CMD_I2C_STREAM;  // 命令码mBuffer[ 1 ] = mCH341A_CMD_I2C_STM_OUT;  // 输出数据,位5-位0为长度,0长度则只发送一个字节并返回应答mBuffer[ 2 ] = iOutByte;  // 数据mBuffer[ 3 ] = mCH341A_CMD_I2C_STM_END;  // 当前包提前结束mLength = 4;mInLen = 0;if ( CH341WriteRead( iIndex, mLength, mBuffer, mCH341A_CMD_I2C_STM_MAX, 1, &mInLen, mBuffer ) ) {  // 执行数据流命令,先输出再输入if ( mInLen && ( mBuffer[ mInLen - 1 ] & 0x80 ) == 0 ) return( TRUE );  // 返回的数据的位7代表ACK应答位,ACK=0有效}return( FALSE );
}

 I2C 发送设备地址,等待设备应答(可用于检测设备是否连接,并工作)

BOOL    WINAPI  CH341CheckDev(  //检查I2C设备是否连接ULONG			iIndex,     // 指定CH341设备序号UCHAR           iDevAddr    //设备地址)
{UCHAR buf ;buf = (iDevAddr<<1);IIC_IssueStart(0);if( IIC_OutByteCheckAck(0,buf) ){IIC_IssueStop(0);return TRUE;}else{IIC_IssueStop(0);return FALSE;}
}

 CH341StreamI2C_Delay(CH341StreamI2C接口函数的升级版) 可指定I2C连续写数据之间的延时,写地址和读地址之间的延时,读地址和读数据之间的延时,连续读数据之间的延迟。


BOOL WINAPI CH341StreamI2C_Delay(ULONG iIndex,          // 指定CH341设备序号ULONG iWriteLength,    // 准备写出的数据字节数PVOID iWriteBuffer,    // 指向一个缓冲区,放置准备写出的数据,首字节通常是I2C设备地址及读写方向位ULONG iReadLength,     // 准备读取的数据字节数PVOID oReadBuffer,     // 指向一个缓冲区,返回后是读入的数据UCHAR iWriteDataDelay, // 连续写数据之间的延时,单位US,数值范围:0~15UCHAR iAddrDelay1,     // 写地址到读地址之间的延时,单位US,数值范围:0~15UCHAR iAddrDelay2,     // 读地址和读数据的延时,单位US,数值范围:0~15UCHAR iReadDataDelay)  // 连续读数据之间的延时,单位US,数值范围:0~15
{UCHAR mBuffer[mDEFAULT_COMMAND_LEN + mDEFAULT_COMMAND_LEN / 8];ULONG i, j, k, mLength;PUCHAR mWrBuf;UCHAR iWriteTimes;mLength = max(iWriteLength, iReadLength);if (mLength > mMAX_BUFFER_LENGTH)return (FALSE);if (mLength <= mDEFAULT_BUFFER_LEN)mWrBuf = (PUCHAR)mBuffer;                                                               // 不超过默认缓冲区长度else {                                                                                      // 超过则需要另外分配内存mWrBuf = (PUCHAR)LocalAlloc(LMEM_FIXED, mMAX_COMMAND_LENGTH + mMAX_COMMAND_LENGTH / 8); // 分配内存if (mWrBuf == NULL)return (FALSE); // 分配内存失败}i = 0;mWrBuf[i++] = mCH341A_CMD_I2C_STREAM;  // 命令码mWrBuf[i++] = mCH341A_CMD_I2C_STM_STA; // 产生起始位if (iWriteLength) {for (j = 0; j < iWriteLength;) {mLength = mCH341_PACKET_LENGTH - i % mCH341_PACKET_LENGTH; // 当前包剩余长度,<mCH341A_CMD_I2C_STM_MAXif (mLength <= 3) {while (mLength--)mWrBuf[i++] = mCH341A_CMD_I2C_STM_END; // 当前包提前结束mLength = mCH341_PACKET_LENGTH;}if (mLength >= mCH341_PACKET_LENGTH) {mWrBuf[i++] = mCH341A_CMD_I2C_STREAM; // 新包的命令码mLength = mCH341_PACKET_LENGTH - 1;}mLength--; // 去掉尾部的提前结束码if (mLength > 3 * (iWriteLength - j))mLength = 3 * (iWriteLength - j); // 本次输出有效数据长度iWriteTimes = mLength / 3;for (k = 0; k < iWriteTimes; k++) {mWrBuf[i++] = (UCHAR)(mCH341A_CMD_I2C_STM_OUT | 0x01); // 输出数据,位5-位0为长度mWrBuf[i++] = *((PUCHAR)iWriteBuffer + j++);           // 复制数据mWrBuf[i++] = mCH341A_CMD_I2C_STM_US | (iWriteDataDelay & 0x0f);mLength -= 3;}}}if (iReadLength) {mLength = mCH341_PACKET_LENGTH - i % mCH341_PACKET_LENGTH; // 当前包剩余长度,<mCH341A_CMD_I2C_STM_MAXif (mLength <= 3) {while (mLength--)mWrBuf[i++] = mCH341A_CMD_I2C_STM_END; // 当前包提前结束mLength = mCH341_PACKET_LENGTH;}if (mLength >= mCH341_PACKET_LENGTH)mWrBuf[i++] = mCH341A_CMD_I2C_STREAM; // 新包的命令码mWrBuf[i++] = mCH341A_CMD_I2C_STM_US | (iAddrDelay1 & 0x0f);if (iWriteLength > 1) {                                 // 先输出mWrBuf[i++] = mCH341A_CMD_I2C_STM_STA;              // 产生起始位mWrBuf[i++] = (UCHAR)(mCH341A_CMD_I2C_STM_OUT | 1); // 输出数据,位5-位0为长度mWrBuf[i++] = *(PUCHAR)iWriteBuffer | 0x01;         // I2C目标设备地址,最低位为1则进行读操作} else if (iWriteLength) {                              // 输出一字节后直接输入i--;mWrBuf[i++] = *(PUCHAR)iWriteBuffer | 0x01; // I2C目标设备地址,最低位为1则进行读操作}mWrBuf[i++] = mCH341A_CMD_I2C_STM_US | (iAddrDelay2 & 0x0f); // 延时10微秒for (j = 1; j < iReadLength;) {mLength = mCH341_PACKET_LENGTH - i % mCH341_PACKET_LENGTH; // 当前包剩余长度,<mCH341A_CMD_I2C_STM_MAXif (mLength <= 1) {if (mLength)mWrBuf[i++] = mCH341A_CMD_I2C_STM_END; // 当前包提前结束mLength = mCH341_PACKET_LENGTH;}if (mLength >= mCH341_PACKET_LENGTH)mWrBuf[i++] = mCH341A_CMD_I2C_STREAM; // 新包的命令码mLength = iReadLength - j >= mCH341A_CMD_I2C_STM_MAX ? mCH341A_CMD_I2C_STM_MAX : 1; // 本次输入有效数据长度mWrBuf[i++] = (UCHAR)(mCH341A_CMD_I2C_STM_IN | mLength);                            // 输入数据,位5-位0为长度j += mLength;if (mLength >= mCH341A_CMD_I2C_STM_MAX) {                 // 当前包将满mWrBuf[i] = mCH341A_CMD_I2C_STM_END;                  // 当前包提前结束i += mCH341_PACKET_LENGTH - i % mCH341_PACKET_LENGTH; // 跳过当前包剩余部分}mWrBuf[i++] = mCH341A_CMD_I2C_STM_US | (iReadDataDelay & 0x0f); // 延时10微秒}mLength = mCH341_PACKET_LENGTH - i % mCH341_PACKET_LENGTH; // 当前包剩余长度,<mCH341A_CMD_I2C_STM_MAXif (mLength <= 1) {if (mLength)mWrBuf[i++] = mCH341A_CMD_I2C_STM_END; // 当前包提前结束mLength = mCH341_PACKET_LENGTH;}if (mLength >= mCH341_PACKET_LENGTH)mWrBuf[i++] = mCH341A_CMD_I2C_STREAM; // 新包的命令码mWrBuf[i++] = mCH341A_CMD_I2C_STM_IN;     // 输入数据,只接收一个字节并发送无应答}mLength = mCH341_PACKET_LENGTH - i % mCH341_PACKET_LENGTH; // 当前包剩余长度,<mCH341A_CMD_I2C_STM_MAXif (mLength <= 1) {if (mLength)mWrBuf[i++] = mCH341A_CMD_I2C_STM_END; // 当前包提前结束mLength = mCH341_PACKET_LENGTH;}if (mLength >= mCH341_PACKET_LENGTH)mWrBuf[i++] = mCH341A_CMD_I2C_STREAM; // 新包的命令码mWrBuf[i++] = mCH341A_CMD_I2C_STM_STO;    // 产生停止位mWrBuf[i++] = mCH341A_CMD_I2C_STM_END;    // 当前包提前结束mLength = 0;if (iReadLength)j = CH341WriteRead(iIndex, i, mWrBuf, mCH341A_CMD_I2C_STM_MAX, (iReadLength + mCH341A_CMD_I2C_STM_MAX - 1) / mCH341A_CMD_I2C_STM_MAX, &mLength, oReadBuffer); // 执行数据流命令,先输出再输入elsej = CH341WriteData(iIndex, mWrBuf, &i); // 写出数据块if (j && mLength != iReadLength)j = FALSE;if (mWrBuf != mBuffer)LocalFree(mWrBuf); // 如果是分配的内存则释放return (j);
}

演示示意图,红色部分为作用间隔时间。

如上为CH341的I2C功能使用说明,其他平台上Linux和Android系统上接口函数均保持类似,可直接参考移植。

注:如果对I2C功能有更高要求,可选用增强版的CH347芯片来实现。链接:

高速USB转JTAG/SPI/I2C/UART/GPIO应用_PC技术小能手的博客-CSDN博客


http://chatgpt.dhexx.cn/article/jhpmRntr.shtml

相关文章

I2C 接口总结

I2C 接口总结 一、I2C 总线基本知识点 i2c总线是 Philips 公司首先推出的一种两线制串行传输总线。它由一根数据线&#xff08;SDA&#xff09;和一根 时钟线&#xff08;SDL&#xff09;组成。 所有主机在SCL 线上产生它们自己的时钟来传输I2C 总线上的报文数据&#xff0c;只…

通俗易懂:I2c总线

I2c: 简介 来源&#xff1a;I2C总线是一种简单、双向二线制同步串行总线。只有两条线作用&#xff1a;器件间近距离经常性数据通信&#xff08;器件间&#xff1a;是在一块板子之内&#xff09;比如hi3518ev200和外部的sensor传感器接口就是使用i2c总线的下图是使用i2c接口的OL…

I2C 接口介绍(物理层and协议层)

1. I2C 协议简介 I2C(Inter-Integrated Circuit) 协议是由 Philips 公司开发的&#xff0c;由于它具备引脚少、硬件实现简单、可扩展性强、不需要如 USART、CAN 的外部收发设备等特点&#xff0c;现在被广泛地使用在系统内多个集成电路 (I2C) 间的通信。根据I2C 总线协议版本 2…

【InterFace】I2C 总线详述

背景 昨天出去了一趟&#xff0c;人问&#xff1a;你熟悉什么&#xff1f;答&#xff1a;软硬件。结果&#xff0c;好尴尬。 硬件一般都做什么内容&#xff1f; 答&#xff1a;电源设计。 能说下升压、降压电路是怎么实现的吗&#xff1f; 答&#xff1a;不能。&#xff08;Ps…

I2C接口及时序

1. I2C学习要点 1.有2条双向串行线&#xff0c;一条数据线SDA&#xff0c;一条时钟线SCL 如果只做master SCL可以只是输出 SDA在PAD上一定是inout pin&#xff0c;当然转为数字信号时可以分为两组 inout SDA&#xff1b; input sda_i; output sda_o; wire sda_oe…

I2C详解(二)

7-bit寻址数据传输 常见的传输方向及格式有如下两种&#xff1a; 主机写数据-从机接收&#xff0c;传输方向不变 要进行数据写入从机&#xff0c;首先主机发送START条件从机地址R/W0(写操作&#xff0c;设置为0)&#xff0c;从机读取到该地址后回应ACK&#xff0c;主机将继续…

I2C的理解

一&#xff1a;I2C是什么&#xff1f; 它就是为了让机器之间能够相互明白的一种通信协议&#xff0c;本质上所有的通信协议例如I2C&#xff0c;SPI&#xff0c;USART等与让人类相互交流的中文&#xff0c;英文等没区别。前者是让机器之间能相互理解&#xff0c;后者让人类相互理…

I2C总线

目录 一、简介 二、通讯协议 2.1、物理层 2.2、协议层 2.2.1、数据有效性规定 2.2.2、起始信号与停止信号 2.2.3、响应 2.2.4、总线的寻址方式 一、简介 I2C&#xff08;Inter&#xff0d;Integrated Circuit&#xff09;&#xff1a;内部集成电路。由飞利浦公司开发的…

I2C接口总结

1 I2C总线的特点 I2C总线最主要的优点是其简单性和有效性。由于接口直接在组件之上&#xff0c;因此I2C总线占用的空间非常小&#xff0c;减少了电路板的空间和芯片管脚的数量&#xff0c;降低了互联成本。总线的长度可高达25英尺&#xff0c;通常最大频率为400Khz&#xff0c;…

关于I2C总线

两根线数据线SDA和时序线SCL&#xff0c;这让我想到了火线和零线。 总线上任何一个IC器件输出低电平都会使总线拉低&#xff0c;是线与的关系。 支持多主和主从工作方式 有效性&#xff1a;只有时钟线变成低电平数据线才能变化。 I2C的初始化应包括&#xff0c;起始信号&…

一篇短文让你彻底理解什么是I2C通信

一、I2C协议 I2C 也叫 IIC&#xff08;Inter&#xff0d;Integrated Circuit&#xff09;总线&#xff0c;是一种由PHILIPS公司在80年代开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。它是半双工通信方式。 我们首先阅读以下这个例子&#xff0c;这会方便后…

I2C总线详解

1.1 I2C总线知识 1.1.1 I2C总线物理拓扑结构 I2C总线在物理连接上非常简单&#xff0c;分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制&#xff0c;来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态…

【i2c协议介绍】

文章目录 协议简单介绍五种速度模式master/slave和transmitter/receiver关系第一种情况&#xff1a;master作为transmitter&#xff0c;slave作为receiver第二种情况&#xff1a;当master作为receiver&#xff0c;slave作为transmitter i2c基本信号start产生stop信号数据传输有…

I2C详解(一)

I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体&#xff08;现被NXP收购&#xff09;开发的两线时串行总线&#xff0c;常用于微控制器与外设之间的连接。要想了解详细I2C协议&#xff0c;目前最新的I2C标准协议是2014年第6版本&#xff0c;如下&#xff1a; I2…

I2C详细介绍

1、I2C总线具有两根双向信号线&#xff0c;一根是数据线SDA&#xff0c;另一根是时钟线SCL 2、IIC总线上可以挂很多设备&#xff1a;多个主设备&#xff0c;多个从设备&#xff08;外围 设备&#xff09;。上图中主设备是两个单片机&#xff0c;剩下的都是从设备。 3、多主机…

OLED显示屏I2C接口

简介 本文将介绍两种不同尺寸的OLED显示屏&#xff1a;0.96寸和0.91寸。他们都是4引脚I2C接口的&#xff0c;如下图所示&#xff1a; 注&#xff1a;经过项目测试发现&#xff0c;本文介绍的驱动方式和代码也完全适用于 1.54寸SSD1309主控的1.54寸OLED和1.29寸CH1115主控的OLE…

关于I2C接口的介绍

I 2C:Inter-Integrated Circuit 内部集成电路,应该翻译为集成电路总线,IC的正确读法为“I方C”。 这种通信协议由Philip最早提出,一种由数据线SDA和时钟线SCL两根信号线组成的串行通信总线,具体规范可以在NXP网站找到v2.1。不像UART没有CLOCK线,不能实现同步,I2C和SPI都…

【原创】【I2C】I2C介绍

一、这是个什么玩意 I2C首先它也是一个通信接口&#xff0c;同理通信接口就是用于模块之间的通信的&#xff0c;同SPI接口学习思路一样&#xff0c;首先明白它是一个接口&#xff0c;然后再扣它是一个怎么样的接口。 I2C(Inter&#xff0d;Integrated Circuit)总线是由Philips公…

I2C接口

一、I2C总线协议内容 1. I2C总线引脚定义 SDA (I2C数据引脚) CLK (I2C数据引脚) 2. I2C总线物理连接 I2C总线物理连接如下图所示&#xff0c;SDA和CLK连接线上连有两个上拉电阻&#xff0c;当总线空闲时&#xff0c;两根线均为高电平。连到总线上的任一器件输出的低电平&am…

“如何成为阿里云P8架构师?“ ”当然是考取阿里云新版ACE认证啊”

**简介&#xff1a;**阿里云新版云计算架构师ACE认证全面重构上线&#xff01;为建立云计算生态领域含金量第一的专家级人才标准和认证体系&#xff0c;影响泛云生态高层次技术人才&#xff0c;阿里云历时一年&#xff0c;组织近百位专家&#xff0c;对云计算架构师ACE认证进行…