# SimulateI2C **Repository Path**: ZhengJH2017/simulate-i2c ## Basic Information - **Project Name**: SimulateI2C - **Description**: 这是一个通用的模拟I2C驱动,可以在任何带GPIO的MCU上移植使用。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2021-08-20 - **Last Updated**: 2025-08-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: I2C, 单片机, SoftI2C, stm32, mcu ## README 这是一个通用的模拟I2C协议,理论上可以在任何具有GPIO接口的MCU上移植。原理是利用GPIO的输入和输出功能实现I2C的协议模拟。 ## 现在开始 ### 移植步骤 - 把代码加进工程里 - 填写与机器和操作系统相关的宏定义及函数 以下为步骤的详细内容 ### 把代码加进工程里 把工程里SoftI2C下的五个文件包含进工程里 ``` └─SoftI2C i2c_platform.c i2c_platform.h i2c_sw.c i2c_sw.h typedef.h ``` ### 修改与机器和操作系统相关的宏定义及函数 移植的过程只需要根据自己的平台(特定的机器和操作系统)修改i2c_platform.c和i2c_platform.h两个文件。 #### 编辑平台硬件信息 首先是修改i2c_platform.h文件。通常情况下,只需要分别针对I/O模式,时钟线, 数据线这三处地方,添加宏定义的实现代码即可。 即如下代码处。 ``` //i/o mode #define SCL_MOD_OUT #define SDA_MOD_OUT #define SDA_MOD_IN //clock #define SCL //P01 #define SCL_H 1//P01 = 1 #define SCL_L 0//P01 = 0 //data #define SDA 0 //P00 #define SDA_H 1//P00 = 1 #define SDA_H_IN //P00 = 1 #define SDA_L 0//P00 = 0 #define SDA_L_IN //P00 = 0 ``` 如下是针对STC15系列的51 CPU架构的MCU的**示例**代码 ``` //i/o mode #define SCL_MOD_OUT SETBITFIELD(P3M1, 5, 0); \ SETBITFIELD(P3M0, 5, 1) #define SDA_MOD_OUT SETBITFIELD(P3M1, 4, 0); \ SETBITFIELD(P3M0, 4, 1) #define SDA_MOD_IN SETBITFIELD(P3M1, 4, 0); \ SETBITFIELD(P3M0, 4, 0) //clock #define SCL P35//P01 #define SCL_H SCL = 1//P01 = 1 #define SCL_L SCL = 0//P01 = 0 //data #define SDA P34 //P00 #define SDA_H SDA = 1//P00 = 1 #define SDA_H_IN SDA = 1//P00 = 1 #define SDA_L SDA = 0//P00 = 0 #define SDA_L_IN SDA = 0//P00 = 0 ``` #### 编辑依赖于操作系统的函数 其实是修改i2c_platform.c, 这里有一个依赖于当前嵌入式系统(RTOS or Supper Loop)的函数I2CDelay( ),如下 ``` void I2CDelay(uint8_t us) { //with us delay function of your flatform /* while(us--) { delay_us(1); } */ } ``` 如下是一个具体的实现**示例** ``` void I2CDelay(uint8_t us) { //with us delay function of your flatform while(us--) { delay_us(5); } } ``` 另一个就是依赖于当前嵌入式系统实现的延时函数ReadTsu( ) ``` void ReadTsu(void) { //according to the actual situation, this time is generally not required, that is, set to 0. /* delay_us(time to set-up); */ } ``` 如下是两个具体的实现**示例** 示例一 ``` void ReadTsu(void) { //according to the actual situation, this time is generally not required, that is, set to 0. //delay_ms(time to set-up); return; } ``` 示例二 ``` void ReadTsu(void) { //according to the actual situation, this time is generally not required, that is, set to 0. //delay_ms(time to set-up); delay_us(10); } ``` 以上就是移植需要做的所有工作了,正常情况下,已经可以使用该驱动了。 ## 使用 使用时,只需要在写的地方调用 ``` WriteRegWithDataLen(uint8_t chip_addr, uint8_t* reg_addr, uint8_t addr_size, uint8_t* Data, uint8_t size) ``` 而在需要读的地方调用 ``` ReadRegWithDataLen(uint8_t chip_addr, uint8_t* reg_addr, uint8_t addr_size, uint8_t *pData, uint8_t size) ``` 就可以了。 chip_addr,是芯片的I2C的带写bit的地址,比如一个芯片的地址是0x60,一般在规格书里描述如下 **1100 000 w/r** 则此处应该取0xC0,即0x60左移1bit得到的地址。 reg_addr,是芯片内部寄存器地址。 addr_size,是芯片内部寄存器地址的大小,按字节计算。 Data or pData, 是打算写进去或者读出来的数据。 size,是读或写的数据的长度。 如下,是一个简单的示例代码 ``` int main(int argc, char *argv[]) { unsigned char data[1] = {1}; WriteRegWithDataLen(0x96, NULL, 0, data, 1); ReadRegWithDataLen(0x96, NULL, 0, data, 1); system("PAUSE"); return 0; } ``` ## FAQ ### ReadTsu()函数是干嘛用的? I2C模块或者芯片在发送了读指令后,返回数据有个建立时间(Time for data set-up),这个时间在某些非标准的模块或者芯片里会有一个比较长的延时,需要加上这个延时函数,来完成正常通讯。 ### 为什么我的读函数没有Re-Start状态时序? 大多数没有带寄存器地址的读方式,是不发送Re-Start的,如果你的芯片或者模块需要这个状态时序,请写信给我,我会实现它。同样,大多数带寄存器地址的读方式,是有Re-Start的,如果你遇到不带这个状态的芯片或者模块,也请写信给我,我会实现它。 如果还有其他的疑问或者好的建议,请发送到zhengjianhua@cdtech.cn,我会尽量回复您。 ## License SimulateI2C is released under the [MIT license](LICENSE.txt).

ZhengJH Logo hengJH & Cdtech's Logo