IIC软件模拟-读写EEPROM
这里写目录标题
- 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相关推荐
- IIC控制设计读写EEPROM
IIC控制设计 本博文完全参考小梅哥(下面是链接) IIC协议详解+Uart串口读写EEPROM IIC模块是比较好理解,但个人觉得比较难实现.这里的读为随机读数据,即自己可以指定读取那一个数据.若不 ...
- 基于51和Protues仿真的82C55A 软件模拟读写时序
仿真图: 源代码: /*************** writer:shopping.w ******************/ #include <reg52.h> #include & ...
- STM32 Cube MX 之hal库软件模拟IIC 可直接移植使用
此为软件模拟IIC,可以直接移植到HAL库使用..h文件需要自己做函数声明这里就不再放出,如有问题大家可以讨论. 使用的时候只需要更改SDA 和SCL引脚的宏定义就可以移植使用,当然IIC协议其实就是 ...
- STM32硬件IIC读写EEPROM
前面一篇写了软件模拟IIC读写EEPROM. 本篇介绍硬件IIC读写EEPROM.平台是STM32F103+AT24C04N.SDA和SCL接5K上拉电阻到3.3v. 首先介绍AT24C04N的基本特 ...
- IO口软件模拟IIC
一.IIC时序 IIC(Inter-Integrated Circuit, 内部集成电路)总线是飞利浦公司开发的两线式串行总线,用于短距离传输,常用语微控制器及其外围设备.它是由数据线SDA和时钟线S ...
- IIC协议的软件模拟实现程序
项目场景:基于STM32F407实现GPIO软件模拟IIC驱动EEPROM 背景:工作中用到了EEPROM用于存储配置信息,需要对EEPROM进行读.写功能的实现. 硬件:使用的EEPROM型号为 B ...
- I2C软件模拟EEPROM通讯实验
I2C是一种串行通讯总线,由于只有串行数据线SDA和串行时钟线SCL两个总线而被广泛使用. I2C软件模拟通信的本质是用芯片上任意两个引脚模拟I2C通信,也就是说通过控制任意两个引脚电平的高低变化来模 ...
- 软件模拟SPI时序实现25Q64读写操作
软件模拟SPI时序实现25Q64读写操作 单片机采用SPI/IIC通讯协议访问外围电子模块如:显示屏.EEPROM.FLASH.各种电子传感器等等越来越多,掌握SPI/IIC通讯协议访问外设非常必要. ...
- STM32F103单片机软件模拟IIC并读取TMP112数字温度传感器
本文利用STM32F103系列单片机读取TMP112数字温度传感器的温度信息,TMP112数字传感器采用IIC总线协议通信.STM32自身含有硬件IIC资源,分别是PB6-->SCL.PB7-- ...
最新文章
- SQL2005CLR函数扩展-正则表达式
- 手机摇一摇效果-html5
- tcp的无延时发送_腾讯网红程序员,详解带宽、延时、吞吐率、PPS 这些都是啥?...
- 服务器远程重新做系统吗,服务器远程做系统安装系统
- MySQL两个字段都建立索引 使用情况
- JAR包命令解压以及再命令打包
- hibernate组件映射
- 关系运算符 与 逻辑运算符
- JAVA 二叉树面试题
- 安全测试(初测)报告
- Spring系列缓存注解@Cacheable @CacheEvit @CachePut 使用姿势介绍
- “创新实践”项目介绍2:《3D点云中的汽车检测》
- centos7如何安装视频播放器
- CS224W-07:图神经网络二
- 价值1.35亿美元的BUG
- SPI通信拓扑如何选择?
- 界面劫持与反界面劫持
- oracle dba 创建视图,拥有dba权限的情况下创建视图报ORA-01031: insufficient privileg错误...
- 有序有重复、有序无重复、无序无重复、无序有重复区别详解及Python实现
- 下载B站或者其他网页视频
热门文章
- winform如何实现将数据库数据加载到树上
- python读取yaml文件
- pandas添加、修改dataframe中index的列名
- java数据结构基础名词解释
- c-free5.0运行程序错误_web前端之异常/错误监控
- php查询mongo数据库效率,2000000万数据库 MongoDB 查询速度慢
- php/js互传cookie中文乱码的问题
- 基于QQ服务器JavaMail邮箱SSL密码第三方发送邮件
- [ExtJS5学习笔记]第十五节 Extjs5表格显示不友好?panel的frame属性在作怪
- web前端(滚动条样式)