I2C是一种串行通讯总线,由于只有串行数据线SDA和串行时钟线SCL两个总线而被广泛使用。

I2C软件模拟通信的本质是用芯片上任意两个引脚模拟I2C通信,也就是说通过控制任意两个引脚电平的高低变化来模拟I2C时序。写代码之前,需要非常熟悉I2C时序图。

准备基础知识:

如下I2C时序图,I2C总线在空闲状态下SCL和SDA都保持高电平。

I2C总线在传送数据过程中共有三种类型信号:开始信号、结束信号和应答信号。

开始信号:SCL为高电平时,SDA 由高向低时,开始传送数据。

结束信号:SCL为高电平时,SDA 由低向高时,结束传送数据。

应答信号:在一个字节传输的8个时钟后的第9个时钟期间,接收器必须回送一个应答位(ACK)给发送器。

本项目所用芯片STM32F103RCT6,最小系统自带I2C存储器AT24C02,原理图如下:

由于I2C可以接多个主设备,多个从设备(外围设备),因此进行通讯是需要进行器件寻址,以此分辨是于哪个设备进行通讯,每个设备地址唯一。

EEPROM器件寻址:起始条件使能芯片读写操作后,EEPROM都要求有8位的器件地址信息。该器件地址信息的LSB为读/写操作选择位,高为读操作,低为写操作。若比较器件地址一致,EEPRON输出应答立答“0”。如果不一致,则返向待机状态。

AT24C系列E2PROM芯片地址的固定部分为1010。设备地址需要看芯片引脚的具体连接情况,本项目中1,2,3引脚均接地,则A2,A1,A0三位均为0,因此写地址为10100000,即0xA0;读地址为10100001,即0xA1。

要点:

模拟I2C是GPIO应设置为通用开漏输出或通用推挽输出,电平每跳变一次,需要加延时函数。

设备地址需要看芯片引脚的具体连接情况,数据地址的长度根据芯片不同而不同。

SCL时钟电平为低时,才可以改换SDA数据线的电平,SCL为高时SDA必须保持稳定。SDA数据在SCL时钟为低电平时准备好,在SCL上升沿的过程发送出去。

在写完8位数据之后,将要读取应答信号,这里也就是要SDA将从输出状态变为输入状态。

接收器拉低SDA线表示应答,并在应答脉冲期间保持稳定的低电平。当主器件作接收器时,必须发出数据传输结束的信号给发送器,即它在最后一个字节之后的应答脉冲期间不会产生应答信号(不拉低SDA)。这种情况下,发送器必须释放SDA线为高以便主器件产生停止条件。

在写读写程序的时候根据SDA时序及要求进行。也就是说必须看明白下面两个图。

主器件在EEPROM收到每个数据后都应答“0”。最后由主器件发送停止条件,终止写序列。

EEPROM接收器件地址和数据字地址并产生应答ACK,主器件就产生一个重复的起始条件。主器件发送器件地址(读/写选择位为“1”,读操作),EEPROM应答ACK,并随时钟发送数据,主器件不发送“0”时,产生停止条件。

时序混乱会导致程序无法运行,多写或者少写数据,因此每一步的高低电平时序都要谨慎思考。

模拟I2C传送的数据很慢,比硬件I2C要慢很多,而且占用CPU资源,而硬件IIC速度效率高,但用法复杂,不够稳定,容易产生卡死,各有利弊,但工程中常用硬件I2C。

I2C软件模拟EEPROM步骤:

1、配置所需要的宏

2、配置时钟

3、相关GPIO初始化

4、SDA输入输出方向配置

5、I2C起始信号及I2C结束信号

6、应答信号及非应答信号的产生

7、设置等待应答信号

8、主机发送数据

9、主机接收数据

10、EEPROM写入一个数据

11、EEPROM读出一个数据

12、EEPROM写入定长数据

13、EEPROM读出定长数据

14、EEPROM写入数组数据

15、EEPROM读出数组数据

16、EEPROM检查是否正常

代码如下:项目I2C模拟引脚用PB6,PB7。

//main.h  宏定义#ifndef _I2C_H_
#define _I2C_H_
#include "stm32f10x.h"#define I2C_SCL    GPIO_Pin_6
#define I2C_SDA    GPIO_Pin_7
#define GPIO_I2C   GPIOB
#define I2C_SCL_H  GPIO_SetBits(GPIO_I2C, I2C_SCL)
#define I2C_SCL_L  GPIO_ResetBits(GPIO_I2C, I2C_SCL)
#define I2C_SDA_H  GPIO_SetBits(GPIO_I2C, I2C_SDA)
#define I2C_SDA_L  GPIO_ResetBits(GPIO_I2C, I2C_SDA)  #define AT24C02       255
#define EE_TYPE   AT24C02void RCC_Init(void);
void I2C_GPIO_Config(void);
void I2C_OUT(void);
void I2C_IN(void);
void I2C_Start(void);
void I2C_Stop(void);
void Send_Byte(uint8_t data);
uint8_t Read_Byte(uint8_t ack);
uint8_t I2C_Wait_ACK(void);
void I2C_ACK(void);
void I2C_NACK(void);uint8_t AT24C02_ReadOneByte(uint16_t ReadAddr);
void AT24C02_WriteOneByte(u16 WriteAddr,u8 DataToWrite);
void AT24C02_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len);
uint32_t AT24C02_ReadLenByte(uint16_t ReadAddr,uint8_t Len);
void AT24C02_Write(uint16_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite);
void AT24C02_Read(uint16_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead);
uint8_t AT24C02_Check(void);#endif
//main.c#include "sys.h"
#include "stm32f10x.h"
#include "string.h"
#include "stdio.h"
#include "main.h"
#include "tools.h"//时钟配置
void RCC_Init()
{    ErrorStatus HSEStartUpStatus;                    RCC_DeInit();                                    RCC_HSEConfig(RCC_HSE_ON);                       HSEStartUpStatus=RCC_WaitForHSEStartUp();       if(HSEStartUpStatus==SUCCESS)                   {FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);           FLASH_SetLatency(FLASH_Latency_2);                            RCC_HCLKConfig(RCC_SYSCLK_Div1);                                RCC_PCLK1Config(RCC_HCLK_Div2);                                 RCC_PCLK2Config(RCC_HCLK_Div1);                                RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);            }   else{/*do nothing*/}RCC_PLLCmd(ENABLE); while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET)                  {}RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);                          while (RCC_GetSYSCLKSource()!=0x08)          {}
}//GPIO初始化
void I2C_GPIO_Config()
{       GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);               GPIO_InitStruct.GPIO_Pin=I2C_SCL|I2C_SDA;                                 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_OD;                         GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;                     GPIO_Init(GPIOC,&GPIO_InitStruct);                                  I2C_SCL_H;I2C_SDA_H;  }//SDA输出
void I2C_OUT()
{      GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=I2C_SDA;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;  GPIO_Init(GPIOC,&GPIO_InitStructure);I2C_SCL_H;I2C_SDA_H;
}//SDA输入
void I2C_IN()
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin=I2C_SDA;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;  GPIO_Init(GPIOC,&GPIO_InitStructure);
}//起始信号
void I2C_Start(void)
{I2C_OUT();       I2C_SDA_H;I2C_SCL_H;DelayUS(4);   I2C_SDA_L; DelayUS(4);I2C_SCL_L;      }//停止信号
void I2C_Stop(void)
{I2C_OUT(); I2C_SCL_L;I2C_SDA_L;                 DelayUS(4); I2C_SCL_H; I2C_SDA_H;DelayUS(4); }//产生应答信号
void I2C_ACK(void)
{I2C_SCL_L;I2C_OUT(); I2C_SDA_L;DelayUS(2); I2C_SCL_H;DelayUS(2); I2C_SCL_L;}//产生非应答信号
void I2C_NACK(void)
{I2C_SCL_L;   I2C_OUT();I2C_SDA_H;DelayUS(2); I2C_SCL_H;DelayUS(2); I2C_SCL_L;}//等待应答信号
uint8_t I2C_Wait_ACK()  {int time=0;  I2C_IN();    I2C_SDA_H;DelayUS(4); I2C_SCL_H;DelayUS(4); while(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))  {time++;if(time>250)  {I2C_Stop();return 1;}}I2C_SCL_L;I2C_OUT();return 0;
}//发送数据
void Send_Byte(uint8_t data)
{uint8_t i;I2C_OUT();I2C_SCL_L;DelayUS(2); for(i=0;i<8;i++)  {if((data&0x80)>>7){I2C_SDA_H;}else{I2C_SDA_L;data <<= 1; DelayUS(2);I2C_SCL_H;DelayUS(2);}                 I2C_SCL_L;DelayUS(2); }}//接收数据
uint8_t Read_Byte(uint8_t ack)
{uint8_t cnt;uint8_t rev_Data;  I2C_IN();I2C_SCL_L;DelayUS(2); I2C_SDA_H;for(cnt=0;cnt<8;cnt++){I2C_SCL_H; DelayUS(2); rev_Data<<=1;if(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA)) {rev_Data++;I2C_SCL_L;DelayUS(2); }}if(ack==0)  {I2C_NACK();}else                 {I2C_ACK();  }return rev_Data;}//读单个字节
uint8_t AT24C02_ReadOneByte(uint16_t ReadAddr)
{                 uint8_t temp=0;                                                                               I2C_Start();  if(EE_TYPE>2047)            {Send_Byte(0XA0);             I2C_Wait_ACK();Send_Byte(ReadAddr>>8);       //I2C_Wait_ACK();         }else{ Send_Byte(0XA0+((ReadAddr/256)<<1)); }               I2C_Wait_ACK();Send_Byte(ReadAddr%256); I2C_Wait_ACK();    I2C_Start();            Send_Byte(0XA1);      I2C_Wait_ACK(); temp=Read_Byte(0);     I2C_Stop();    return temp;
}//写单个字节
void AT24C02_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
{                                                                                            I2C_Start();  if(EE_TYPE>2047){Send_Byte(0XA0);    I2C_Wait_ACK(); Send_Byte(WriteAddr>>8);  }else{Send_Byte(0XA0+((WriteAddr/256)<<1));   }    I2C_Wait_ACK();    Send_Byte(WriteAddr%256);   I2C_Wait_ACK();                                                        Send_Byte(DataToWrite);                     I2C_Wait_ACK();                     I2C_Stop();   DelayMS(10);
}//写入定长数据
void AT24C02_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len)
{   uint8_t t;for(t=0;t<Len;t++){AT24C02_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);}
}//读取定长数据
uint32_t AT24C02_ReadLenByte(uint16_t ReadAddr,uint8_t Len)
{   uint8_t t;uint32_t temp=0;for(t=0;t<Len;t++){temp<<=8;temp+=AT24C02_ReadOneByte(ReadAddr+Len-t-1);                        }return temp;
}//读数组数据
void AT24C02_Read(uint16_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead)
{while(NumToRead){*pBuffer++=AT24C02_ReadOneByte(ReadAddr++);  NumToRead--;}
}//写入数组数据
void AT24C02_Write(uint16_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite)
{while(NumToWrite--){AT24C02_WriteOneByte(WriteAddr,*pBuffer);WriteAddr++;pBuffer++;}
}//检查AT24C02是否正常uint8_t AT24C02_Check(void)
{uint8_t temp;temp=AT24C02_ReadOneByte(255);  if(temp==0X55){return 0;       }       else{AT24C02_WriteOneByte(255,0X55);temp=AT24C02_ReadOneByte(255);   if(temp==0X55){return 0;}}return 1;
}uint8_t  readDate[10]={0};
uint8_t  writeDate[8]={1,2,3,4,5,6,7,8};int main()
{uint8_t i=0;RCC_Init();I2C_GPIO_Config();AT24C02_Check();AT24C02_Write(0,writeDate,10);for(i=0;i<10;i++){readDate[i]=writeDate[i];}AT24C02_Read(0,readDate,10);DelayMS(500);while(1){}
}       

实验结果:

通过程序模拟输出数组的数据,输出正确。

I2C软件模拟EEPROM通讯实验相关推荐

  1. CORE-ESP32C3|eink|墨水屏日历+时间日期+温度显示|I2C软件模拟| LuatOS-SOC接口|官方demo|学习(14):墨水屏动态日历+oled日期显示+ AHT10测温模组

    目录 参考博文 源于网友oled+eink+aht10项目 源代码修改及复现说明 主要修改 显示效果 ​编辑硬件准备 软件版本 日志及soc下载工具 软件使用 接线说明 天气显示屏 硬件接线 温度采集 ...

  2. 使用ensp软件模拟DNS服务器实验

    DNS服务器介绍: DNS服务器(Domain Name Server,域名服务器)是进行域名和与之相对应的IP地址进行转换的服务器.它起到将人类易于记忆的域名映射到相应的机器可识别的IP地址的作用. ...

  3. STM32 软件模拟IIC

    0.系列目录 STM32 软件模拟IIC STM32 使用DMP库处理MPU6050数据 STM32 MPU6050与匿名上位机通讯(V2.6版) 1.IIC通讯过程 SCL和SDA在空闲时候均为高电 ...

  4. IIC软件模拟-读写EEPROM

    这里写目录标题 1.IIC简介 2. I2C 基本读写过程 2.1.主机写数据到从机 2.2.主机由从机中读数据 2.3.读和写数据 2.4.地址及数据方向 2.5.响应信号 3.软件模拟I2C 4. ...

  5. STM32模拟I2C时序读写EEPROM精简版

    平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线+外部EEPROM(不需要上拉电阻) 工程介绍:主要文件在USER组中,bsp_i2c_ee.c,bsp_i2c_ee.h ...

  6. 关于stm32通信协议:软件模拟SPI、软件模拟I2C的总结(fishing_8)

    趁着帮老师代上嵌入式实验课的机会,又重新熟悉了一遍stm32的通信协议:串口协议.SPI协议.I2C协议.RS485协议.大概半年前,是过了一遍的,但也只停留于读了遍代码,跑了下例程,最近又过了一遍( ...

  7. STC8A8K64单片机关于AT24C04基本读写操作(包含硬件I2C与软件模拟I2C)

    实验:AT24C04基本读写操作 步骤及现象:在下载程序前,选择stc-isp的IRC频率:12MHz. 程序下载完成后,在串口助手界面,HEX模式下,选择波 特率9600,然后点击"打开串 ...

  8. 【正点原子STM32连载】 第四十五章 FLASH模拟EEPROM实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

    第四十五章 FLASH模拟EEPROM实验 STM32本身没有自带EEPROM,但是STM32具有IAP(在应用编程)功能,所以我们可以把它的FLASH当成EEPROM来使用.本章,我们将利用STM3 ...

  9. I2C接口的EEPROM烧写软件介绍以及源码下载

    本软件可以通过Ginkgo I2C适配器对EEPROM芯片进行读写数据,当然也可以自己修改程序用于自己的硬件. 软件功能简介: 支持多通道操作,硬件模式支持2个通道,软件模式支持8个通道. 可以根据不 ...

最新文章

  1. hibernate 和 mybatis 的区别
  2. ad 原理图差分线_Altium差分线如何从原理图到PCB布线?
  3. java9新特性 2017_Java 9 ← 2017,2019 → Java 13 ,来看看Java两年来的变化
  4. Spring Boot 使用 AOP 实现页面自适应
  5. Qt从入门到放弃_0x01:建立项目
  6. 接口规范 6. 查看在线用户
  7. 《软件测试技术大全:测试基础 流行工具 项目实战(第3版)》—第1章1.2节软件测试的发展...
  8. Linux命令之awk:高级输入输出(四)
  9. C#基础12:嵌套类、匿名类与密封类
  10. 每一个都能笑抽的39个奇葩代码注释
  11. mysql 流浪,流浪汉机器 – 如何找出mysql用户名/密码
  12. gis利器之Gdal(二)shp数据读取
  13. IDEA 关于两个分支代码合并的操作
  14. ssm实现记住账号密码(cookie)
  15. 数据结构之——关键路径
  16. python namedtuple用法_详解Python中namedtuple的使用
  17. CE实现植物大战僵尸之阳光篇
  18. OpenVINO使用OpenCL内存执行,避免拷贝
  19. 移动机器人 | 手势识别
  20. idea出现Can not set int field xxx to java.lang.Long 错误

热门文章

  1. 牛客竞赛语法入门班-循环结构习题代码(1)
  2. 钉钉开发第三方H5微应用入门详细教程[ISV][免登流程][授权码][HTTP回调推送][识别用户身份][获取用户信息]...
  3. phpadmin安装到mysql中_apach+mysql+php+phpadmin安装!(for windows)
  4. abap 编号_ABAP——编码规范
  5. PDF文件如何转换成转图片?分享两种实现方法
  6. HIT 软件构造Lab1 思考
  7. AS3隐藏特性—Sprite对象的尺寸
  8. 在面试过程中面试官经常提到的问题
  9. 第43讲 Android Camera2 API AF自动对焦 第二部分
  10. 2021年春季学期期末统一考试品牌管理 试题