这里写目录标题

  • 1.IIC简介
  • 2. I2C 基本读写过程
    • 2.1.主机写数据到从机
    • 2.2.主机由从机中读数据
    • 2.3.读和写数据
    • 2.4.地址及数据方向
    • 2.5.响应信号
  • 3.软件模拟I2C
  • 4.软件模拟IIC实现过程
    • 4.1.IIC开启信号
    • 4.2.IIC结束信号
    • 4.3.主机向I2C总线设备发送8bit数据
    • 4.4.主机读取8bit数据
    • 4.5.CPU产生一个时钟,并读取器件的ACK应答信号
    • 4.6.应答信号
    • 4.7.非应答信号
    • 4.8.发送设备地址(SLAVE_ADDRESS)
    • 4.9.信号逻辑配置完成**
  • 5.EEPROM芯片
  • 6.通过I2C协议向EEPROM写入数据。
  • 编程要点
    • 6.1.判断EEPROM是否正常
    • 6.2.读取指定地址处开始的数据
  • 6.3.向串行EEPROM指定地址写入若干数据
    • 6.4.EEPROM数据擦除
  • 6.工程文件

1.IIC简介

IIC和EEPROM详解
IIC总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。它是由数据线 SDA时钟 SCL 构成的串行总线,可发送和接收数据。
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号
开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IIC 在接收到 8bit 数据后,向发送数据的IIC 发出特定的低电平脉冲,表示已收到数据

时序图
开始信号:当SCL时钟线为高电平,SDA IN由电平向低电平跳变时,说明IIC传输开始。
结束信号:当SCL时钟线为高电平,SDA IN由电平向高电平跳变时,说明IIC传输结束。
数据有效:当SCL数据输出为高电平时,SDA的数据有效,相反则数据无效。

2. I2C 基本读写过程

注:
阴影是主机发送数据到从机。
数据由从机发送数据到主机

1、S 表示由主机的 I2C 接口产生的传输起始信号 (S),这时连接到 I2C 总线上的所有从机都会 接收到这个信号
起始信号产生后,所有从机就开始等待主机紧接下来广播的从机地址信号 (SLAVE_ADDRESS)。 在 I2C总线上,每个设备的地址都是唯一的,当主机广播的地址与某个设备地址相同时,这个设 备就被选中了,没被选中的设备将会忽略之后的数据信号
2、在地址位之后,是传输方向的选择位,该位为 0 时,表示后面的数据传输方向是由主机传输至从机,即主机向从机写数据。该位为 1 时,则相反,即主机由从机读数据
3.从机接收到匹配的地址后,主机或从机会返回一个应答 或非应答 信号,只有接收到应答信号后,主机才能继续发送或接收数据
4、主机开始正式向从机传输数据,数据包的大小为 8 位,主机每发送完一个字节数据,都要等待从机应答信号,重复这个过程,可以向从机传输 N 个数据,这个 N 没有大小限制。当数据传输结束时,主机向从机发送一个**停止传输信号 **,表示不再传输数据。

2.1.主机写数据到从机


S(开始信号) ->SLAVE ADDRESS(主机发送从机的地址)->W(选择主机写数据到从机)->A(从机发送应答信号到主机) ->DATA(主机发送到从机的数据)… -> !A(非应答信号) ->P(结束信号)

2.2.主机由从机中读数据


S(开始信号) ->SLAVE ADDRESS(主机发送从机的地址)->R(选择主机读数据到从机)->A(从机发送应答信号到主机) ->DATA(主机收到从机的数据)->A(主机应答信号) ->… -> !A(非应答信号) ->P(结束信号)

2.3.读和写数据


S(开始信号) ->SLAVE ADDRESS(主机发送从机的地址)->R/!W(读或者写)->A(从机发送应答信号到主机) ->DATA(主机收到从机的数据)->A/!A(主机应答信号或者非应答信号) ->Sr(主机发送重复起始信号)->… -> !A(非应答信号) ->P(结束信号)

2.4.地址及数据方向

I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA 信号线发送设备地址(SLAVE_ADDRESS) 来查找从机。I2C 协议规定设备地址可以是 7 位或 10 位。实际中 7 位的地址应用比较广泛。紧紧跟设备地址的一个数据位用来表示数据传输方向,它是数据方向位 (R/),第 8位或第 11 位。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。


读数据方向时,主机会释放对 SDA 信号线的控制,由从机控制 SDA 信号线,主机接收信号,写数据方向时SDA 由主机控制,从机接收信号。

2.5.响应信号

I2C 的数据和地址传输都带响应。响应包括“应答 (ACK)”和“非应答 (NACK)”两种信号。作为数据接收端时,当设备 (无论主从机) 接收到 I2C 传输的一个字节数据或地址后,若希望对方继续发送数据,则需要向对方发送“应答 (ACK)”信号,发送方会继续发送下一个数据;若接收端希望结束数据传输,则向对方发送“非应答 (NACK)”信号,发送方接收到该信号后会产生一个停止信号,结束信号传输。

发送端需要数据接收端发送响应信号时,需要把SDA数据总线的控制权交给接收端,即拉高数据总线

数据接收端回高电平表示非应答信号,低电平表示应答信号。

时钟线SCL拉高电平,会使数据接收端回复一个应答信号。

3.软件模拟I2C

如果我们直接控制 STM32 的两个 GPIO 引脚,分别用作 SCL 及 SDA,按照上述信号的时序要求,直接像控制 LED 灯那样控制引脚的输出 (若是接收数据时则读取 SDA 电平),就可以实现 I2C 通讯。同样,所以只要遵守协议,就是标准的通讯,不管如何实现它,由于直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态,所以称之为“软件模拟协议”方式。

4.软件模拟IIC实现过程

4.1.IIC开启信号

void I2C_Start(void) //IIC开启信号
{/* 当SCL高电平时,SDA出现高电平向低电平跳变 启动信号 */SDA_H;SCL_H;Delay_us(2);SDA_L;Delay_us(2);SCL_L;Delay_us(2);
}

4.2.IIC结束信号

void I2C_Stop(void)
{/* 当SCL高电平时,SDA出现低电平向高电平跳变 停止信号 */SDA_L;SCL_H;Delay_us(2);SDA_H;
}

4.3.主机向I2C总线设备发送8bit数据

void I2C_SendByte(uint8_t _Byte)
{uint8_t i;/* 先发送字节的高位bit7 ,即先广播从机的地址*/  for (i = 0; i < 8; i++){      if (_Byte & 0x80){SDA_H;}else{SDA_L;}Delay_us(2);SCL_H;Delay_us(2);SCL_L;if (i == 7){SDA_H; //主机写完8bit数据都要释放数据总线,由从机控制数据总线,回复应答或者非应答信号}_Byte <<= 1;  /* 左移一个bit */Delay_us(2);}
}

4.4.主机读取8bit数据

uint8_t I2C_ReadByte(void)
{uint8_t i;uint8_t value;/* 读到第1个bit为数据的bit7,即高位往地位读 */value = 0;for (i = 0; i < 8; i++){value <<= 1;SCL_H;   //时钟线为高时,数据有效Delay_us(2);if (SDA_READ)  //读取数据线的状态{value++;   //如果读到的数据为1 往value的最低位写1}SCL_L;Delay_us(2);}return value;
}

4.5.CPU产生一个时钟,并读取器件的ACK应答信号

uint8_t I2C_WaitAck(void)
{uint8_t re;SDA_H;  /* 主机释放SDA总线,把数据总线控制器交给从机 */Delay_us(2);SCL_H;   /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */Delay_us(2);if (SDA_READ)  /* 读取SDA口线状态 */{re = 1;  //非应答信号}else{re = 0;   //应答信号}SCL_L;Delay_us(2);return re;
}

4.6.应答信号

void I2C_Ack(void)
{SDA_L; // SDA = 0, 表示应答信号Delay_us(2);SCL_H;    Delay_us(2);SCL_L;Delay_us(2);SDA_H;    // CPU释放SDA总线,控制权交给数据发送方
}

4.7.非应答信号

void I2C_NAck(void)
{SDA_H; // SDA = 0, 表示非应答信号Delay_us(2);SCL_H;   //时钟线拉高,等待接收端回复响应信号 Delay_us(2);SCL_L;Delay_us(2);
}

4.8.发送设备地址(SLAVE_ADDRESS)

uint8_t I2C_CheckDevice(uint8_t _Address)
{uint8_t _Ack;I2C_GPIO_Config();        //数据线和时钟线配置I2C_Start();     // 发送启动信号 /* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */I2C_SendByte(_Address | EEPROM_I2C_WR);_Ack= I2C_WaitAck();   /* 检测设备的ACK应答 */I2C_Stop();         //发送停止信号 return _Ack;
}

4.9.信号逻辑配置完成**

.C文件

#include "software.h"
#include "stm32f10x.h"
static void I2C_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  /* 打开GPIO时钟 */GPIO_InitStructure.GPIO_Pin = SCL_PIN | SDA_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;      /* 开漏输出 */GPIO_Init(GPIO_x, &GPIO_InitStructure);/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */I2C_Stop();
}void I2C_Start(void)
{/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */SDA_H;SCL_H;Delay_us(2);SDA_L;Delay_us(2);SCL_L;Delay_us(2);
}void I2C_Stop(void)
{/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */SDA_L;SCL_H;Delay_us(2);SDA_H;
}void I2C_SendByte(uint8_t _ucByte)
{uint8_t i;/* 先发送字节的高位bit7 */for (i = 0; i < 8; i++){     if (_ucByte & 0x80){SDA_H;}else{SDA_L;}Delay_us(2);SCL_H;Delay_us(2);SCL_L;if (i == 7){SDA_H; // 释放总线}_ucByte <<= 1;   /* 左移一个bit */Delay_us(2);}
}uint8_t I2C_ReadByte(void)
{uint8_t i;uint8_t value;/* 读到第1个bit为数据的bit7 */value = 0;for (i = 0; i < 8; i++){value <<= 1;SCL_H;Delay_us(2);if (SDA_READ){value++;}SCL_L;Delay_us(2);}return value;
}uint8_t I2C_WaitAck(void)
{uint8_t re;SDA_H;  /* CPU释放SDA总线 */Delay_us(2);SCL_H;  /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */Delay_us(2);if (SDA_READ)  /* CPU读取SDA口线状态 */{re = 1;}else{re = 0;}SCL_L;Delay_us(2);return re;
}void I2C_Ack(void)
{SDA_L; /* CPU驱动SDA = 0 */Delay_us(2);SCL_H;   /* CPU产生1个时钟 */Delay_us(2);SCL_L;Delay_us(2);SDA_H; /* CPU释放SDA总线 */
}void I2C_NAck(void)
{SDA_H; /* CPU驱动SDA = 1 */Delay_us(2);SCL_H;   /* CPU产生1个时钟 */Delay_us(2);SCL_L;Delay_us(2);
}uint8_t I2C_CheckDevice(uint8_t _Address)
{uint8_t ucAck;I2C_GPIO_Config();       /* 配置GPIO */I2C_Start();        /* 发送启动信号 *//* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */I2C_SendByte(_Address |I2C_WR);ucAck = I2C_WaitAck();   /* 检测设备的ACK应答 */I2C_Stop();         /* 发送停止信号 */return ucAck;
}

.h文件

#ifndef _I2C_GPIO_H_
#define _I2C_GPIO_H_
#include "delay.h"
#include "stm32f10x.h"#define I2C_WR  0       /* 写控制bit */#define I2C_RD  1       /* 读控制bit *//* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */#define GPIO_x   GPIOB         /* GPIO端口 */#define SCL_PIN     GPIO_Pin_6          /* 连接到SCL时钟线的GPIO */#define SDA_PIN     GPIO_Pin_7          /* 连接到SDA数据线的GPIO */#define SCL_H     GPIO_SetBits(GPIO_x, GPIO_Pin_6)      /* SCL = 1 */#define SCL_L     GPIO_ResetBits(GPIO_x, GPIO_Pin_6)      /* SCL = 0 */#define SDA_H     GPIO_SetBits(GPIO_x, GPIO_Pin_7)        /* SDA = 1 */#define SDA_L     GPIO_ResetBits(GPIO_x, GPIO_Pin_7)      /* SDA = 0 */#define SDA_READ  GPIO_ReadInputDataBit(GPIO_x, GPIO_Pin_7)   /* 读SDA口线状态 */
void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(uint8_t _ucByte);
uint8_t I2C_ReadByte(void);
uint8_t I2C_WaitAck(void);
void I2C_Ack(void);
void I2C_NAck(void);
uint8_t I2C_CheckDevice(uint8_t _Address);
#endif

5.EEPROM芯片


24C02 (EEPROM)芯片

本实验板中的 EEPROM 芯片 (型号:AT24C02) 的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。EEPROM 芯片的设备地址一共有 7位,其中高 4 位固定为:1010 b,低 3 位则由 A0/A1/A2 信号线的电平决定,见图EEPROM 设备地址 ,图中的 R/W 是读写方向位,与地址无关。

按照我们此处的连接,A0/A1/A2 均为 0,所以 EEPROM 的 7 位设备地址是:101 0000b,即 0x50。
由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当 R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1时,表示读方向,加上 7 位地址,其值为“0xA0”,常称该值为“读地址”。
EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当引脚为低电平时,可写入数据,我们直接接地,不使用写保护功能。

6.通过I2C协议向EEPROM写入数据。

编程要点

(1) 配置通讯使用的目标引脚为开漏模式;
(2) 使能GPIO的时钟;
(3) 配置 GPIO口的模式、速率等参数;
(4) 编写基本 I2C 按字节收发的函数;
(5) 编写读写 EEPROM 存储内容的函数;
(6) 编写测试程序,对读写数据进行校验。

6.1.判断EEPROM是否正常

uint8_t _Check(void)
{if (I2C_CheckDevice(EEPROM_DEV_ADDR) == 0){return 1;}else{/* 失败后,切记发送I2C总线停止信号 */I2C_Stop();      return 0;}
}

6.2.读取指定地址处开始的数据

//参数_usAddress : 起始地址
//  _Size : 数据长度,单位为字节
//* _pReadBuf : 存放读到的数据的缓冲区指针
uint8_t ReadBytes(uint8_t *_pReadBuf, uint16_t Address, uint16_t _Size)
{uint16_t i;// 采用串行EEPROM随即读取指令序列,连续读取若干字节 I2C_Start(); //1.开启IIC信号I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);   //2.发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 if (I2C_WaitAck() != 0)   //3.等待ACK响应{goto cmd_fail;  /* EEPROM器件无应答 */}I2C_SendByte((uint8_t)Address);// 4.发送字节地址if (I2C_WaitAck() != 0)  //    5.等待ACK {goto cmd_fail; /* EEPROM器件无应答 */}I2C_Start(); //6.重新启动I2C总线,开始读取数据      I2C_SendByte(EEPROM_DEV_ADDR | I2C_RD);// 7.发起控制字节,高7bit是地址,bit0是读写控制位if (I2C_WaitAck() != 0)    //发送ACK响应{goto cmd_fail;    /* EEPROM器件无应答 */}      for (i = 0; i < _Size; i++) //9.循环读取数据 {_pReadBuf[i] = I2C_ReadByte();   //每次读1个字节 // 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack if (i != _Size - 1){I2C_Ack();  // 产生Ack应答}else{I2C_NAck(); // 最后1个字节读完后,产生Nack非应答信号}}   I2C_Stop();  //10.停止信号return 1; /* 执行成功 */
cmd_fail: //命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 I2C_Stop();   // 发送I2C总线停止信号 return 0;
}

6.3.向串行EEPROM指定地址写入若干数据

参数:
Address : 起始地址
_Size : 数据长度,单位为字节
_pWriteBuf : 存放读到的数据的缓冲区指针

uint8_t _WriteBytes(uint8_t *WriteBuf, uint16_t Address, uint16_t _Size)
{uint16_t i,m;uint16_t Addr;    /* 写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。page size = 8,一次只能写入8bit数据*/Addr = Address; for (i = 0; i < _Size; i++){// 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 if ((i == 0) || (Addr & (EEPROM_PAGE_SIZE - 1)) == 0){I2C_Stop();// 1.发停止信号,启动内部写操作 for (m = 0; m < 1000; m++)  //通过检查器件应答的方式,判断内部写操作是否完成{               I2C_Start();            //2.发起I2C总线启动信号     I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);     //2.发起控制字节,高7bit是地址,bit0是读写控制位    if (I2C_WaitAck() == 0)//第3步:发送一个时钟,等待响应{break;}}if (m  == 1000){goto cmd_fail;   // EEPROM器件写超时 }I2C_SendByte((uint8_t)Addr);        // 第4步:发送字节地址SLAVE_ADDRESSif (I2C_WaitAck() != 0)       // 第5步:等待ACK响应{goto cmd_fail;    // EEPROM器件无应答 }}I2C_SendByte(WriteBuf[i]); // 7.开始写入数据 if (I2C_WaitAck() != 0)// 8.发送ACK{goto cmd_fail;   // EEPROM器件无应答 }Addr++;   //地址增1      }I2C_Stop();  //I2C停止信号return 1;cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */I2C_Stop();    // 发送I2C总线停止信号 return 0;
}

6.4.EEPROM数据擦除

擦除就算往EEPROM所有单元写1的过程

void _Erase(void)
{uint16_t i;uint8_t buf[EEPROM_SIZE];/* 填充缓冲区 */for (i = 0; i < EEPROM_SIZE; i++){buf[i] = 0xFF;}// 写EEPROM, 起始地址 = 0,数据长度为 256 if (_WriteBytes(buf, 0, EEPROM_SIZE) == 0){printf("擦除eeprom出错!\r\n");return;}else{printf("擦除eeprom成功!\r\n");}
}

.C文件

#include "iic_analog.h"
#include "software.h"
#include "usart.h" uint8_t _Check(void)
{if (I2C_CheckDevice(EEPROM_DEV_ADDR) == 0){return 1;}else{I2C_Stop();                // 失败后,切记发送I2C总线停止信号 return 0;}
}uint8_t ReadBytes(uint8_t *_pReadBuf, uint16_t Address, uint16_t _Size)
{uint16_t i;// 采用串行EEPROM随即读取指令序列,连续读取若干字节 I2C_Start(); //1.开启IIC信号I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);   //2.发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 if (I2C_WaitAck() != 0)   //3.等待ACK响应{goto cmd_fail;  /* EEPROM器件无应答 */}I2C_SendByte((uint8_t)Address);// 4.发送字节地址if (I2C_WaitAck() != 0)  //    5.等待ACK {goto cmd_fail; /* EEPROM器件无应答 */}I2C_Start(); //6.重新启动I2C总线,开始读取数据      I2C_SendByte(EEPROM_DEV_ADDR | I2C_RD);// 7.发起控制字节,高7bit是地址,bit0是读写控制位if (I2C_WaitAck() != 0)    //发送ACK响应{goto cmd_fail;    /* EEPROM器件无应答 */}      for (i = 0; i < _Size; i++) //9.循环读取数据 {_pReadBuf[i] = I2C_ReadByte();   //每次读1个字节 // 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack if (i != _Size - 1){I2C_Ack();  // 产生Ack应答}else{I2C_NAck(); // 最后1个字节读完后,产生Nack非应答信号}}   I2C_Stop();  //10.停止信号return 1; /* 执行成功 */
cmd_fail: //命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 I2C_Stop();   // 发送I2C总线停止信号 return 0;
}uint8_t _WriteBytes(uint8_t *WriteBuf, uint16_t Address, uint16_t _Size)
{uint16_t i,m;uint16_t Addr;    /* 写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。page size = 8,一次只能写入8bit数据*/Addr = Address; for (i = 0; i < _Size; i++){// 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 if ((i == 0) || (Addr & (EEPROM_PAGE_SIZE - 1)) == 0){I2C_Stop();// 1.发停止信号,启动内部写操作 for (m = 0; m < 1000; m++)  //通过检查器件应答的方式,判断内部写操作是否完成{               I2C_Start();            //2.发起I2C总线启动信号     I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR);     //2.发起控制字节,高7bit是地址,bit0是读写控制位    if (I2C_WaitAck() == 0)//第3步:发送一个时钟,等待响应{break;}}if (m  == 1000){goto cmd_fail;   // EEPROM器件写超时 }I2C_SendByte((uint8_t)Addr);        // 第4步:发送字节地址SLAVE_ADDRESSif (I2C_WaitAck() != 0)       // 第5步:等待ACK响应{goto cmd_fail;    // EEPROM器件无应答 }}I2C_SendByte(WriteBuf[i]); // 7.开始写入数据 if (I2C_WaitAck() != 0)// 8.发送ACK{goto cmd_fail;   // EEPROM器件无应答 }Addr++;   //地址增1      }I2C_Stop();  //I2C停止信号return 1;cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */I2C_Stop();    // 发送I2C总线停止信号 return 0;
}void _Erase(void)
{uint16_t i;uint8_t buf[EEPROM_SIZE];/* 填充缓冲区 */for (i = 0; i < EEPROM_SIZE; i++){buf[i] = 0xFF;}// 写EEPROM, 起始地址 = 0,数据长度为 256 if (_WriteBytes(buf, 0, EEPROM_SIZE) == 0){printf("擦除eeprom出错!\r\n");return;}else{printf("擦除eeprom成功!\r\n");}
}

.h文件

#ifndef __I2C_H_
#define __I2C_H_#include "stm32f10x.h"
#include "delay.h"#define EEPROM_DEV_ADDR         0xA0        //EEPROM的设备地址
#define EEPROM_PAGE_SIZE         8            /* 24xx02的页面大小 */
#define EEPROM_SIZE                256            /* 24xx02总容量 */uint8_t _Check(void);
uint8_t ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize);
uint8_t _WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize);
void _Erase(void);#endif
main.c
#include "main.h"int main(void){   uint16_t i;uint8_t write_buf[EEPROM_SIZE];uint8_t read_buf[EEPROM_SIZE];/* 串口初始化 */Usart1_Init(115200);if (_Check() == 0){printf("没有检测到串行EEPROM!\r\n");            return 0;}for (i = 0; i < EEPROM_SIZE; i++)  //写入数据{      write_buf[i] = i;}if (_WriteBytes(write_buf, 0, EEPROM_SIZE) == 0){printf("写EEPROM出错!\r\n");return 0;}else{     printf("写EEPROM成功!\r\n");}Delay_ms(1000);        //写完之后需要适当的延时再去读,不然会出错if (ReadBytes(read_buf, 0, EEPROM_SIZE) == 0){printf("读EEPROM出错!\r\n");return 0;}else{        printf("读EEPROM成功,数据如下:\r\n");}for (i = 0; i < EEPROM_SIZE; i++){printf(" %02X", read_buf[i]);if ((i & 15) == 15){printf("\r\n");   }       }printf(":EEPROM读写测试成功\r\n");while(1);
}

6.工程文件

点击这里

IIC软件模拟-读写EEPROM相关推荐

  1. IIC控制设计读写EEPROM

    IIC控制设计 本博文完全参考小梅哥(下面是链接) IIC协议详解+Uart串口读写EEPROM IIC模块是比较好理解,但个人觉得比较难实现.这里的读为随机读数据,即自己可以指定读取那一个数据.若不 ...

  2. 基于51和Protues仿真的82C55A 软件模拟读写时序

    仿真图: 源代码: /*************** writer:shopping.w ******************/ #include <reg52.h> #include & ...

  3. STM32 Cube MX 之hal库软件模拟IIC 可直接移植使用

    此为软件模拟IIC,可以直接移植到HAL库使用..h文件需要自己做函数声明这里就不再放出,如有问题大家可以讨论. 使用的时候只需要更改SDA 和SCL引脚的宏定义就可以移植使用,当然IIC协议其实就是 ...

  4. STM32硬件IIC读写EEPROM

    前面一篇写了软件模拟IIC读写EEPROM. 本篇介绍硬件IIC读写EEPROM.平台是STM32F103+AT24C04N.SDA和SCL接5K上拉电阻到3.3v. 首先介绍AT24C04N的基本特 ...

  5. IO口软件模拟IIC

    一.IIC时序 IIC(Inter-Integrated Circuit, 内部集成电路)总线是飞利浦公司开发的两线式串行总线,用于短距离传输,常用语微控制器及其外围设备.它是由数据线SDA和时钟线S ...

  6. IIC协议的软件模拟实现程序

    项目场景:基于STM32F407实现GPIO软件模拟IIC驱动EEPROM 背景:工作中用到了EEPROM用于存储配置信息,需要对EEPROM进行读.写功能的实现. 硬件:使用的EEPROM型号为 B ...

  7. I2C软件模拟EEPROM通讯实验

    I2C是一种串行通讯总线,由于只有串行数据线SDA和串行时钟线SCL两个总线而被广泛使用. I2C软件模拟通信的本质是用芯片上任意两个引脚模拟I2C通信,也就是说通过控制任意两个引脚电平的高低变化来模 ...

  8. 软件模拟SPI时序实现25Q64读写操作

    软件模拟SPI时序实现25Q64读写操作 单片机采用SPI/IIC通讯协议访问外围电子模块如:显示屏.EEPROM.FLASH.各种电子传感器等等越来越多,掌握SPI/IIC通讯协议访问外设非常必要. ...

  9. STM32F103单片机软件模拟IIC并读取TMP112数字温度传感器

    本文利用STM32F103系列单片机读取TMP112数字温度传感器的温度信息,TMP112数字传感器采用IIC总线协议通信.STM32自身含有硬件IIC资源,分别是PB6-->SCL.PB7-- ...

最新文章

  1. SQL2005CLR函数扩展-正则表达式
  2. 手机摇一摇效果-html5
  3. tcp的无延时发送_腾讯网红程序员,详解带宽、延时、吞吐率、PPS 这些都是啥?...
  4. 服务器远程重新做系统吗,服务器远程做系统安装系统
  5. MySQL两个字段都建立索引 使用情况
  6. JAR包命令解压以及再命令打包
  7. hibernate组件映射
  8. 关系运算符 与 逻辑运算符
  9. JAVA 二叉树面试题
  10. 安全测试(初测)报告
  11. Spring系列缓存注解@Cacheable @CacheEvit @CachePut 使用姿势介绍
  12. “创新实践”项目介绍2:《3D点云中的汽车检测》
  13. centos7如何安装视频播放器
  14. CS224W-07:图神经网络二
  15. 价值1.35亿美元的BUG
  16. SPI通信拓扑如何选择?
  17. 界面劫持与反界面劫持
  18. oracle dba 创建视图,拥有dba权限的情况下创建视图报ORA-01031: insufficient privileg错误...
  19. 有序有重复、有序无重复、无序无重复、无序有重复区别详解及Python实现
  20. 下载B站或者其他网页视频

热门文章

  1. winform如何实现将数据库数据加载到树上
  2. python读取yaml文件
  3. pandas添加、修改dataframe中index的列名
  4. java数据结构基础名词解释
  5. c-free5.0运行程序错误_web前端之异常/错误监控
  6. php查询mongo数据库效率,2000000万数据库 MongoDB 查询速度慢
  7. php/js互传cookie中文乱码的问题
  8. 基于QQ服务器JavaMail邮箱SSL密码第三方发送邮件
  9. [ExtJS5学习笔记]第十五节 Extjs5表格显示不友好?panel的frame属性在作怪
  10. web前端(滚动条样式)