2024年最全STM32硬件I2C与软件模拟I2C详解,助力程序员正确成长
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以戳这里获取
3.STM32随机读取EEPROM内部任何地址的数据
4.STM32随机顺序读取EEPROM内部任何地址的数据
EEPROM一共有256个字节对应的地址为(0~255)
当读取到最后一个字节,也就是255地址,第256个字节,在读取又会从头(第一个字节数据)开始读取。
六.硬件I2C读写EEPROM实验
实验目的
STM32作为主机向从机EEPROM存储器写入256个字节的数据
STM32作为主机向从机EEPROM存储器读取写入的256个字节的数据
读写成功亮绿灯,读写失败亮红灯
实验原理
原理图

实物图

编程要点
(1) 配置通讯使用的目标引脚为开漏模式;
(2) 编写模拟 I2C 时序的控制函数;
(3) 编写基本 I2C 按字节收发的函数;
(4) 编写读写 EEPROM 存储内容的函数;
(5) 编写测试程序,对读写数据进行校验。
两个引脚PB6,PB7都要配置成复用的开漏输出
这里有一个注意的点,你配置成输出模式,并不会影响引脚的输入功能
详情请看——>GPIO端口的八种工作模式
源码
i2c_ee.h
前面理论已经讲得已经很详细了,直接上代码叭!!
#ifndef \_\_IIC\_EE\_H
#define \_\_IIC\_EE\_H
#include "stm32f10x.h"
#include <stdio.h>
//IIC1
#define EEPROM\_I2C I2C1
#define EEPROM\_I2C\_CLK RCC\_APB1Periph\_I2C1
#define EEPROM\_I2C\_APBxClkCmd RCC\_APB1PeriphClockCmd
#define EEPROM\_I2C\_BAUDRATE 400000
// IIC1 GPIO 引脚宏定义
#define EEPROM\_I2C\_SCL\_GPIO\_CLK (RCC\_APB2Periph\_GPIOB)
#define EEPROM\_I2C\_SDA\_GPIO\_CLK (RCC\_APB2Periph\_GPIOB)
#define EEPROM\_I2C\_GPIO\_APBxClkCmd RCC\_APB2PeriphClockCmd
#define EEPROM\_I2C\_SCL\_GPIO\_PORT GPIOB
#define EEPROM\_I2C\_SCL\_GPIO\_PIN GPIO\_Pin\_6
#define EEPROM\_I2C\_SDA\_GPIO\_PORT GPIOB
#define EEPROM\_I2C\_SDA\_GPIO\_PIN GPIO\_Pin\_7
//STM32自身地址1 与从机设备地址不相同即可(7位地址)
#define STM32\_I2C\_OWN\_ADDR 0x6f
//EEPROM设备地址
#define EEPROM\_I2C\_Address 0XA0
#define I2C\_PageSize 8
//等待次数
#define I2CT\_FLAG\_TIMEOUT ((uint32\_t)0x1000)
#define I2CT\_LONG\_TIMEOUT ((uint32\_t)(10 \* I2CT\_FLAG\_TIMEOUT))
/\*信息输出\*/
#define EEPROM\_DEBUG\_ON 0
#define EEPROM\_INFO(fmt,arg...) printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
#define EEPROM\_ERROR(fmt,arg...) printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM\_DEBUG(fmt,arg...) do{\
if(EEPROM\_DEBUG\_ON)\
printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",\_\_LINE\_\_, ##arg);\
}while(0)
void I2C\_EE\_Config(void);
void EEPROM\_Byte\_Write(uint8\_t addr,uint8\_t data);
uint32\_t EEPROM\_WaitForWriteEnd(void);
uint32\_t EEPROM\_Page\_Write(uint8\_t addr,uint8\_t \*data,uint16\_t Num_ByteToWrite);
uint32\_t EEPROM\_Read(uint8\_t \*data,uint8\_t addr,uint16\_t Num_ByteToRead);
void I2C\_EE\_BufferWrite(uint8\_t\* pBuffer,uint8\_t WriteAddr, uint16\_t NumByteToWrite);
#endif /\* \_\_IIC\_EE\_H \*/
i2c_ee.c
#include "i2c\_ee.h"
//设置等待时间
static __IO uint32\_t I2CTimeout = I2CT_LONG_TIMEOUT;
//等待超时,打印错误信息
static uint32\_t I2C\_TIMEOUT\_UserCallback(uint8\_t errorCode);
void I2C\_EE\_Config(void)
{
GPIO_InitTypeDef GPIO_InitStuctrue;
I2C_InitTypeDef I2C_InitStuctrue;
//开启GPIO外设时钟
EEPROM\_I2C\_GPIO\_APBxClkCmd(EEPROM_I2C_SCL_GPIO_CLK|EEPROM_I2C_SDA_GPIO_CLK,ENABLE);
//开启IIC外设时钟
EEPROM\_I2C\_APBxClkCmd(EEPROM_I2C_CLK,ENABLE);
//SCL引脚-复用开漏输出
GPIO_InitStuctrue.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_InitStuctrue.GPIO_Pin=EEPROM_I2C_SCL_GPIO_PIN;
GPIO_InitStuctrue.GPIO_Speed=GPIO_Speed_50MHz;
GPIO\_Init(EEPROM_I2C_SCL_GPIO_PORT,&GPIO_InitStuctrue);
//SDA引脚-复用开漏输出
GPIO_InitStuctrue.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStuctrue.GPIO_Pin = EEPROM_I2C_SDA_GPIO_PIN;
GPIO_InitStuctrue.GPIO_Speed=GPIO_Speed_50MHz;
GPIO\_Init(EEPROM_I2C_SDA_GPIO_PORT,&GPIO_InitStuctrue);
//IIC结构体成员配置
I2C_InitStuctrue.I2C_Ack=I2C_Ack_Enable;
I2C_InitStuctrue.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_InitStuctrue.I2C_ClockSpeed=EEPROM_I2C_BAUDRATE;
I2C_InitStuctrue.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_InitStuctrue.I2C_Mode=I2C_Mode_I2C;
I2C_InitStuctrue.I2C_OwnAddress1=STM32_I2C_OWN_ADDR;
I2C\_Init(EEPROM_I2C,&I2C_InitStuctrue);
I2C\_Cmd(EEPROM_I2C,ENABLE);
}
//向EEPROM写入一个字节
void EEPROM\_Byte\_Write(uint8\_t addr,uint8\_t data)
{
//发送起始信号
I2C\_GenerateSTART(EEPROM_I2C,ENABLE);
//检测EV5事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR);
//发送设备写地址
I2C\_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
//检测EV6事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR);
//发送要操作设备内部的地址
I2C\_SendData(EEPROM_I2C,addr);
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR);
I2C\_SendData(EEPROM_I2C,data);
//检测EV8\_2事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED )==ERROR);
//发送停止信号
I2C\_GenerateSTOP(EEPROM_I2C,ENABLE);
}
//向EEPROM写入多个字节
uint32\_t EEPROM\_Page\_Write(uint8\_t addr,uint8\_t \*data,uint16\_t Num_ByteToWrite)
{
I2CTimeout = I2CT_LONG_TIMEOUT;
//判断IIC总线是否忙碌
while(I2C\_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(1);
}
//重新赋值
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送起始信号
I2C\_GenerateSTART(EEPROM_I2C,ENABLE);
//检测EV5事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(2);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送设备写地址
I2C\_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
//检测EV6事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(3);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送要操作设备内部的地址
I2C\_SendData(EEPROM_I2C,addr);
//检测EV8事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(4);
}
while(Num_ByteToWrite)
{
I2C\_SendData(EEPROM_I2C,\*data);
I2CTimeout = I2CT_FLAG_TIMEOUT;
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(5);
}
Num_ByteToWrite--;
data++;
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//检测EV8\_2事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(6);
}
//发送停止信号
I2C\_GenerateSTOP(EEPROM_I2C,ENABLE);
return 1;
}
//向EEPROM读取多个字节
uint32\_t EEPROM\_Read(uint8\_t \*data,uint8\_t addr,uint16\_t Num_ByteToRead)
{
I2CTimeout = I2CT_LONG_TIMEOUT;
//判断IIC总线是否忙碌
while(I2C\_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(1);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送起始信号
I2C\_GenerateSTART(EEPROM_I2C,ENABLE);
//检测EV5事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(7);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送设备写地址
I2C\_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
//检测EV6事件等待从机应答
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(8);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送要操作设备内部存储器的地址
I2C\_SendData(EEPROM_I2C,addr);
//检测EV8事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(9);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送起始信号
I2C\_GenerateSTART(EEPROM_I2C,ENABLE);
//检测EV5事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(10);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送设备读地址
I2C\_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Receiver);
//检测EV6事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(10);
}
while(Num_ByteToRead--)
{
//是否是最后一个字节,若是则发送非应答信号
if( Num_ByteToRead==0)
{
//发送非应答信号
I2C\_AcknowledgeConfig(EEPROM_I2C,DISABLE);
//发送停止信号
I2C\_GenerateSTOP(EEPROM_I2C,ENABLE);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//检测EV7事件
while( I2C\_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED )==ERROR)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(10);
}
\*data=I2C\_ReceiveData(EEPROM_I2C);
data++;
}
//重新开启应答信号
I2C\_AcknowledgeConfig(EEPROM_I2C,ENABLE);
return 1;
}
void I2C\_EE\_BufferWrite(uint8\_t\* pBuffer,uint8\_t WriteAddr, uint16\_t NumByteToWrite)
{
u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
//I2C\_PageSize=8
Addr = WriteAddr % I2C_PageSize;
count = I2C_PageSize - Addr;
NumOfPage = NumByteToWrite / I2C_PageSize;
NumOfSingle = NumByteToWrite % I2C_PageSize;
/\* 写入数据的地址对齐,对齐数为8 \*/
if(Addr == 0)
{
/\* 如果写入的数据个数小于8 \*/
if(NumOfPage == 0)
{
EEPROM\_Page\_Write(WriteAddr, pBuffer, NumOfSingle);
EEPROM\_WaitForWriteEnd();
}
/\* 如果写入的数据个数大于8 \*/
else
{
//按页写入
while(NumOfPage--)
{
EEPROM\_Page\_Write(WriteAddr, pBuffer, I2C_PageSize);
EEPROM\_WaitForWriteEnd();
WriteAddr += I2C_PageSize;
pBuffer += I2C_PageSize;
}
//不足一页(8个)单独写入
if(NumOfSingle!=0)
{
EEPROM\_Page\_Write(WriteAddr, pBuffer, NumOfSingle);
EEPROM\_WaitForWriteEnd();
}
}
}
/\*写的数据的地址不对齐\*/
else
{
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / I2C_PageSize;
NumOfSingle = NumByteToWrite % I2C_PageSize;
if(count != 0)
{
EEPROM\_Page\_Write(WriteAddr, pBuffer, count);
EEPROM\_WaitForWriteEnd();
WriteAddr += count;
pBuffer += count;
}
while(NumOfPage--)
{
EEPROM\_Page\_Write(WriteAddr, pBuffer, I2C_PageSize);
EEPROM\_WaitForWriteEnd();
WriteAddr += I2C_PageSize;
pBuffer += I2C_PageSize;
}
if(NumOfSingle != 0)
{
EEPROM\_Page\_Write(WriteAddr, pBuffer, NumOfSingle);
EEPROM\_WaitForWriteEnd();
}
}
}
uint32\_t EEPROM\_WaitForWriteEnd(void)
{
I2CTimeout = I2CT_FLAG_TIMEOUT;
do
{
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送起始信号
I2C\_GenerateSTART(EEPROM_I2C,ENABLE);
//检测EV5事件
while( I2C\_GetFlagStatus(EEPROM_I2C,I2C_FLAG_SB )==RESET)
{
if((I2CTimeout--) == 0) return I2C\_TIMEOUT\_UserCallback(10);
}
I2CTimeout = I2CT_FLAG_TIMEOUT;
//发送设备写地址
I2C\_Send7bitAddress(EEPROM_I2C,EEPROM_I2C_Address,I2C_Direction_Transmitter);
}while( (I2C\_GetFlagStatus(EEPROM_I2C,I2C_FLAG_ADDR )==RESET) && (I2CTimeout--) );
//发送停止信号
I2C\_GenerateSTOP(EEPROM_I2C,ENABLE);
return 1;
}
static uint32\_t I2C\_TIMEOUT\_UserCallback(uint8\_t errorCode)
{
/\* Block communication and all processes \*/
EEPROM\_ERROR("I2C 等待超时!errorCode = %d",errorCode);
return 0;
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "./i2c/i2c\_ee.h"
#include <string.h>
#include "usart.h"
#define SOFT\_DELAY Delay(0x0FFFFF);
void Delay(__IO u32 nCount);
//声明I2C测试函数
uint8\_t I2C\_EE\_Test(void);
int main(void)
{
//初始化IIC
I2C\_EE\_Config();
//初始化USART
Usart\_Config();
//初始化LED
LED\_GPIO\_Config();
printf("\r\nIIC读写EEPROM测试实验\r\n");
//读写成功亮绿灯,失败亮红灯
if( I2C\_EE\_Test()==1 )
{
LED\_G(NO);
}
else
{
LED\_R(NO);
}
while(1)
{
;
}
}
uint8\_t I2C\_EE\_Test(void)
{
uint8\_t ReadData[256]={0};
uint8\_t WriteDdta[256]={0};
uint16\_t i;
//初始化写入数组
for(i=0;i<256;i++)
{
WriteDdta[i]=i;
}
//向EEPROM从地址为0开始写入256个字节的数据
I2C\_EE\_BufferWrite(WriteDdta,0,256);
//等待EEPROM写入数据完成
EEPROM\_WaitForWriteEnd();
//向EEPROM从地址为0开始读出256个字节的数据
EEPROM\_Read(ReadData,0,256);
for (i=0; i<256; i++)
{
if(ReadData[i] != WriteDdta[i])
{
EEPROM\_ERROR("0x%02X ", ReadData[i]);
EEPROM\_ERROR("错误:I2C EEPROM写入与读出的数据不一致\n\r");
return 0;
}
printf("0x%02X ", ReadData[i]);
if(i%16 == 15)
printf("\n\r");
}
EEPROM\_INFO("I2C(AT24C02)读写测试成功\n\r");
return 1;
}
void Delay(__IO uint32\_t nCount) //简单的延时函数
{
for(; nCount != 0; nCount--);
}
重点讲一下,如何解决以下页写入问题,实现连续写入
现在来解释代码中下图函数如何解决问题
如果地址对齐:
如果地址不对齐:
实验效果
七.软件模式I2C协议
实验目的
STM32作为主机向从机EEPROM存储器写入256个字节的数据
STM32作为主机向从机EEPROM存储器读取写入的256个字节的数据
读写成功亮绿灯,读写失败亮红灯
实验原理
软件模式I2C由我们CPU来控制引脚产生I2C时序,所以我们随便选引脚都可以,不过你选择的引脚肯定要连接到通信的EEPROM的SCL,SDA引脚上。这里是用了PC12,PC11充当主机STM32SCL,SDA引脚。






value应该初始化为0,我忘了sorry
源码
i2c_gpio.h
#ifndef \_I2C\_GPIO\_H
#define \_I2C\_GPIO\_H
#include "stm32f10x.h"
#define EEPROM\_I2C\_WR 0 /\* 写控制bit \*/
#define EEPROM\_I2C\_RD 1 /\* 读控制bit \*/
#define EEPROM\_GPIO\_PORT\_I2C GPIOB
#define EEPROM\_RCC\_I2C\_PORT RCC\_APB2Periph\_GPIOB
#define EEPROM\_I2C\_SCL\_PIN GPIO\_Pin\_6
#define EEPROM\_I2C\_SDA\_PIN GPIO\_Pin\_7
/\*当 STM32 的 GPIO 配置成开漏输出模式时,它仍然可以通过读取
GPIO 的输入数据寄存器获取外部对引脚的输入电平,也就是说它同时具有浮空输入模式的
功能\*/
#define EEPROM\_I2C\_SCL\_1() EEPROM\_GPIO\_PORT\_I2C->BSRR |= EEPROM\_I2C\_SCL\_PIN /\* SCL = 1 \*/
#define EEPROM\_I2C\_SCL\_0() EEPROM\_GPIO\_PORT\_I2C->BRR |= EEPROM\_I2C\_SCL\_PIN /\* SCL = 0 \*/
#define EEPROM\_I2C\_SDA\_1() EEPROM\_GPIO\_PORT\_I2C->BSRR |= EEPROM\_I2C\_SDA\_PIN /\* SDA = 1 \*/
#define EEPROM\_I2C\_SDA\_0() EEPROM\_GPIO\_PORT\_I2C->BRR |= EEPROM\_I2C\_SDA\_PIN /\* SDA = 0 \*/
#define EEPROM\_I2C\_SDA\_READ() ((EEPROM\_GPIO\_PORT\_I2C->IDR & EEPROM\_I2C\_SDA\_PIN)!=0 ) /\* 读SDA口线状态 \*/
void i2c\_Start(void);
void i2c\_Stop(void);
void i2c\_Ack(void);
void i2c\_NAcK(void);
uint8\_t i2c\_WaitAck(void);
void i2c\_SendByte(uint8\_t data);
uint8\_t i2c\_ReadByte(void);
uint8\_t i2c\_CheckDevice(uint8\_t Address);
#endif /\* \_I2C\_GPIO\_H \*/
i2c_gpio.c
#include "i2c\_gpio.h"
#include "stm32f10x.h"
void I2c\_gpio\_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC\_APB2PeriphClockCmd(EEPROM_RCC_I2C_PORT, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO\_Init(EEPROM_GPIO_PORT_I2C, &GPIO_InitStructure);
/\* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 \*/
i2c\_Stop();
}
static void i2c\_Delay(void)
{
uint8\_t i;
for(i=0;i<10;i++)
{
}
}
void i2c\_Start(void)
{
EEPROM\_I2C\_SCL\_1();
EEPROM\_I2C\_SDA\_1();
i2c\_Delay();
EEPROM\_I2C\_SDA\_0();
i2c\_Delay();
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
}
void i2c\_Stop(void)
{
EEPROM\_I2C\_SDA\_0();
EEPROM\_I2C\_SCL\_1();
i2c\_Delay();
EEPROM\_I2C\_SDA\_1();
i2c\_Delay();
}
void i2c\_Ack(void)
{
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
EEPROM\_I2C\_SDA\_0();
i2c\_Delay();
EEPROM\_I2C\_SCL\_1();
i2c\_Delay();
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
EEPROM\_I2C\_SDA\_1();
i2c\_Delay();
}
void i2c\_NAcK(void)
{
EEPROM\_I2C\_SDA\_1();
i2c\_Delay();
EEPROM\_I2C\_SCL\_1();
i2c\_Delay();
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
}
uint8\_t i2c\_WaitAck(void)
{
uint8\_t ret;
EEPROM\_I2C\_SDA\_1();
EEPROM\_I2C\_SCL\_1();
i2c\_Delay();
if( EEPROM\_I2C\_SDA\_READ() )
{
ret=1;
}
else
{
ret=0;
}
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
return ret;
}
void i2c\_SendByte(uint8\_t data)
{
uint8\_t i;
for(i=0;i<8;i++)
{
if( data&0x80 )
{
EEPROM\_I2C\_SDA\_1();
}
else
{
EEPROM\_I2C\_SDA\_0();
}
i2c\_Delay();
EEPROM\_I2C\_SCL\_1();
i2c\_Delay();
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
if( i==7 )
{
EEPROM\_I2C\_SDA\_1();
i2c\_Delay();
}
data=data<<1;
}
}
uint8\_t i2c\_ReadByte(void)
{
uint8\_t value=0;
uint8\_t i;
for(i=0;i<8;i++)
{
value=value<<1;
EEPROM\_I2C\_SCL\_1();
i2c\_Delay();
if( EEPROM\_I2C\_SDA\_READ() )
{
value++;
}
EEPROM\_I2C\_SCL\_0();
i2c\_Delay();
}
return value;
}
uint8\_t i2c\_CheckDevice(uint8\_t Address)
{
uint8\_t ucACK;
I2c\_gpio\_config();
i2c\_Start();
i2c\_SendByte(Address|EEPROM_I2C_WR);
ucACK=i2c\_WaitAck();
i2c\_Stop();
return ucACK;
}
i2c_ee.h
#ifndef \_I2C\_EE\_H
#define \_I2C\_EE\_H
#include "stm32f10x.h"
#define EEPROM\_DEV\_ADDR 0xA0 /\* 24xx02的设备地址 \*/
#define EEPROM\_PAGE\_SIZE 8 /\* 24xx02的页面大小 \*/
#define EEPROM\_SIZE 256 /\* 24xx02总容量 \*/
uint8\_t ee\_Checkok(void);
uint8\_t ee\_ReadByte( uint8\_t \*pReaddata,uint16\_t Address,uint16\_t num );
uint8\_t ee\_WriteByte( uint8\_t \*Writepdata,uint16\_t Address,uint16\_t num );
uint8\_t ee\_WaitStandby(void);
uint8\_t ee\_WriteBytes(uint8\_t \*_pWriteBuf, uint16\_t _usAddress, uint16\_t _usSize);
uint8\_t ee\_ReadBytes(uint8\_t \*_pReadBuf, uint16\_t _usAddress, uint16\_t _usSize);
uint8\_t ee\_Test(void) ;
#endif /\* \_I2C\_EE\_H\*/
i2c_ee.c
#include "i2c\_ee.h"
#include "i2c\_gpio.h"
//检测EEPORM是否忙碌
uint8\_t ee\_Checkok(void)
{
if(i2c\_CheckDevice(EEPROM_DEV_ADDR)==0)
{
return 1;
}
else
{
i2c\_Stop();
return 0;
}
}
//检测EEPROM写入数完成
uint8\_t ee\_WaitStandby(void)
{
uint32\_t wait_count = 0;
while(i2c\_CheckDevice(EEPROM_DEV_ADDR))
{
//若检测超过次数,退出循环
if(wait_count++>0xFFFF)
{
//等待超时
return 1;
}
}
//等待完成
return 0;
}
//向EEPROM写入多个字节
uint8\_t ee\_WriteBytes(uint8\_t \*_pWriteBuf, uint16\_t _usAddress, uint16\_t _usSize)
{
uint16\_t i,m;
uint16\_t addr;
addr=_usAddress;
for(i=0;i<_usSize;i++)
{
//当第一次或者地址对齐到8就要重新发起起始信号和EEPROM地址
//为了解决8地址对齐问题
if(i==0 || (addr % EEPROM_PAGE_SIZE)==0 )
{
//循环发送起始信号和EEPROM地址的原因是为了等待上一次写入的一页数据\
写入完成
for(m=0;m<1000;m++)
{
//发送起始地址
i2c\_Start();
//发送设备写地址
i2c\_SendByte(EEPROM_DEV_ADDR|EEPROM_I2C_WR);
//等待从机应答
if( i2c\_WaitAck()==0 )
{
break;
}
}
//若等待的1000次从机还未应答,等待超时
if( m==1000 )
{
goto cmd_fail;
}
//EEPROM应答后发送EEPROM的内部存储器地址
i2c\_SendByte((uint8\_t)addr);
//等待从机应答
if( i2c\_WaitAck()!=0 )
{
goto cmd_fail;
}
}
//发送数据
i2c\_SendByte(_pWriteBuf[i]);
//等待应答
if( i2c\_WaitAck()!=0 )
{
goto cmd_fail;
}
//写入地址加1
addr++;
}
i2c\_Stop();
return 1;
cmd_fail:
i2c\_Stop();
return 0;
}
uint8\_t ee\_ReadBytes(uint8\_t \*_pReadBuf, uint16\_t _usAddress, uint16\_t _usSize)
{
uint16\_t i;
i2c\_Start();
i2c\_SendByte(EEPROM_DEV_ADDR|EEPROM_I2C_WR);
if( i2c\_WaitAck()!=0 )
{
goto cmd_fail;
}
i2c\_SendByte((uint8\_t)_usAddress);
if( i2c\_WaitAck()!=0 )
{
goto cmd_fail;
}
i2c\_Start();
i2c\_SendByte(EEPROM_DEV_ADDR|EEPROM_I2C_RD);
if( i2c\_WaitAck()!=0 )
{
goto cmd_fail;
}
for(i=0;i<_usSize;i++)
{
_pReadBuf[i]=i2c\_ReadByte();
/\* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack \*/
if (i != _usSize - 1)
{
// i2c\_NAcK(); /\* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) \*/
i2c\_Ack(); /\* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) \*/
}
else
{
i2c\_NAcK(); /\* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) \*/
}
}
i2c\_Stop();
return 1;
cmd_fail:
i2c\_Stop();
return 0;
}
uint8\_t ee\_Test(void)
{
uint16\_t i;
uint8\_t write_buf[EEPROM_SIZE];
uint8\_t read_buf[EEPROM_SIZE];
/\*-----------------------------------------------------------------------------------\*/
if (i2c\_CheckDevice(EEPROM_DEV_ADDR) == 1)
{
/\* 没有检测到EEPROM \*/
printf("没有检测到串行EEPROM!\r\n");
return 0;
}
/\*------------------------------------------------------------------------------------\*/
/\* 填充测试缓冲区 \*/
for (i = 0; i < EEPROM_SIZE; i++)
{
write_buf[i] = i;
}
/\*------------------------------------------------------------------------------------\*/
if (ee\_WriteBytes(write_buf, 0, EEPROM_SIZE) == 0)
{
printf("写EEPROM出错!\r\n");
return 0;
}
else
{
printf("写EEPROM成功!\r\n");
}
/\*-----------------------------------------------------------------------------------\*/
if (ee\_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++)
{
if(read_buf[i] != write_buf[i])
{
printf("0x%02X ", read_buf[i]);
printf("错误:EEPROM读出与写入的数据不一致");
return 0;
}
printf(" %02X", read_buf[i]);
if ((i & 15) == 15)
{
printf("\r\n");
}
}
printf("EEPROM读写测试成功\r\n");
return 1;
}
main
#include "stm32f10x.h"


**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**
printf("写EEPROM成功!\r\n");
}
/\*-----------------------------------------------------------------------------------\*/
if (ee\_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++)
{
if(read_buf[i] != write_buf[i])
{
printf("0x%02X ", read_buf[i]);
printf("错误:EEPROM读出与写入的数据不一致");
return 0;
}
printf(" %02X", read_buf[i]);
if ((i & 15) == 15)
{
printf("\r\n");
}
}
printf("EEPROM读写测试成功\r\n");
return 1;
}
main
#include "stm32f10x.h"
[外链图片转存中...(img-iOLQcz0T-1715530888210)]
[外链图片转存中...(img-XPEcuprk-1715530888210)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**
作者:普通网友