基于stm32使用IIC读写EEPROM

目录

一、IIC

1、简介

2、IIC总线结构

3、总线协议 

3.1、起始和停止条件

3.2、数据的传输

3.3、信号应答

二、24C02

1、简介

2、芯片引脚

3、写操作

3.1、字节写

 3.2、页写入

 4、读操作

4.1、当前地址读

4.2、指定地址读

三、实操部分

1.使用CubuMX进行配置(基于stm32zgt6)

1.1、生成工程

 1.2、编写24C02读写函数

2.使用软件进行模拟时序

总结



一、IIC

1、简介

IC总线是一种串行通信协议,也称为“I²C”或“TWI(Two Wire Interface)”。IIC总线由Philips Semiconductors(飞利浦半导体)公司于1980年推出,用于连接集成电路芯片及其它数字电路。它采用两根线串行传输数据(一根双向的数据线SDA和一根双向的时钟线SCL),可以连接多个设备,具有高效、简单、灵活的优点。

在IIC总线中,每一个设备都有一个唯一的地址,数据的传输是由主设备发起的,从设备进行响应。主设备可以向从设备发送读取或写入命令,也可以向多个设备广播命令,读取或写入多个设备的数据。另外,IIC总线还支持多主设备的操作,可以实现多个主设备对同一总线进行控制。总之,IIC总线是一种常用的通信协议,可以满足不同类型的集成电路芯片之间的数据传输需求。

2、IIC总线结构

3、总线协议 

3.1、起始和停止条件

起始条件:SCL在高电平期间,SDA由高电平变为低电平。

停止条件:SCL在高电平期间,SDA由低电平变为高电平 。

3.2、数据的传输

发送字节:SCL低电平期间,主机将数据位依次放到SDA线上,然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化。连续循环八次即可传输一个字节(高位先行)

接收字节SCL低电平期间,从机将数据位依次放到SDA线上,然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化 。连续循环八次即可读取一个字节(高位先行)

接收和发送的区别主要是在于是主机在操作数据线还是从机在操控,如果是从机要操控的话,主机要先释放数据线(也就是置为高电平)。

3.3、信号应答

发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。

接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)。

二、24C02

1、简介

        24C02是一种串行电子存储器,其容量为2K位(256字节,24Cxx中的xx表示有多少Kbit,02Kbit=256Byte)。该存储器采用I2C总线协议进行通讯,可以通过SCL和SDA两条信号线进行读写操作。它广泛应用于模拟电子系统、数字电子系统和微处理器系统中,如计算机主板、电视机、手机、汽车电子等领域。由于它有低功耗、可靠性高等优点,因此在嵌入式系统和智能卡等领域也得到了广泛的应用

2、芯片引脚

VCC、GND就不用介绍了吧。

SCL是时钟信号线,SDA是数据信号线,用于实现IIC时序进行相应的读写操作。

WP是写保护引脚,顾名思义使能的时候就保护起来不让写入数据。

A0-A2是地址输入引脚,每个芯片都有自身的地址,这样才能实现IIC通信选取芯片,24C02的地址(共八位)前四位是固定的1010,后三位的就是有A0-A2决定的,假如全部接的是GND那么地址就是1010000。最后一位由所需操作决定,是读操作还是写操作。

3、写操作

3.1、字节写

写入一个字节:起始条件--->器件地址(第八位为写)--->写入的地址(接收应答)--->写入的数据(接收应答)--->停止条件

 3.2、页写入

 4、读操作

4.1、当前地址读

读取一个字节:起始条件--->器件地址--->接收读取到的数据(发送应答)--->停止条件 

4.2、指定地址读

指定地址读:起始条件--->器件地址(写)--->想读取的地址--->起始条件--->器件地址(读)--->读取数据(给应答)--->停止条件

这么操作的原因在EEPROM中是有一个类似于指针的东西指向地址的,我们需要用写的方式将指针指向要读取的地址然后再进行读取的切换进行读取该地址上的数据。

三、实操部分

1.使用CubuMX进行配置(基于stm32zgt6)

1.1、生成工程

选择板子上相应的引脚,此时是黄色的说明还没有使能。

 

通过这一步骤使能之后,引脚就会变为绿色。

IIC配置,默认配置即可。(最好是配置一个USART1进行printf的重定向,这样方便打印结果)

生成工程之后就会出现这些HAL库封装的函数,函数很多,有需要用到的时候再去了解即可。

 1.2、编写24C02读写函数

 写入字节函数

#define  AT24C02_READ_ADDRESS   0xA1
#define  AT24C02_WRITE_ADDRESS  0xA0

void AT24C02_Write_Byte(uint16_t MemAddress,uint8_t *pData,uint16_t Size)
{
    while(Size--)
    {
        while(HAL_I2C_Mem_Write(&hi2c1, AT24C02_WRITE_ADDRESS, MemAddress,I2C_MEMADD_SIZE_8BIT,pData,1,10) != HAL_OK){};
        MemAddress++;
        pData++;
    }
}

HAL_I2C_Mem_Write()参数说明:

hi2c:I2C的句柄

DevAddress:设备地址

MemAddress:设备的内存地址

MemAddSize:设备的内存地址的大小

pData:要写入的数据

Size:写入数据的大小

Timeout:最大超时时间

读取字节函数

void AT24C02_Read_Byte(uint16_t MemAddress,uint8_t *pData,uint16_t Size)
{
    while(HAL_I2C_Mem_Read(&hi2c1,AT24C02_READ_ADDRESS,MemAddress, I2C_MEMADD_SIZE_8BIT,pData, Size, 10) != HAL_OK){};
}

HAL_I2C_Mem_Read()参数说明:

hi2c:I2C句柄

DevAddress:设备的地址

MemAddress:设备内存的地址

MemAddSize:设备内存的大小

pData:读取数据的接收

Size:读取的数据大小

Timeout:超时时间

main函数调用

int main(void)
{
  uint8_t WBuf[50]="this is eeprom test!\r\n";
  uint8_t RBuf[50];

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART1_UART_Init();

  HAL_Delay(20);
  printf("the is eeprom test\n");
  HAL_Delay(20);
  AT24C02_Write_Byte(0,WBuf,sizeof(WBuf));
  printf("eeprom write success!!!\r\n");
  HAL_Delay(20);
	
  while (1)
  {
	AT24C02_Read_Byte(0,RBuf,sizeof(RBuf));
	printf("READ: %s\r\n",RBuf);
	HAL_Delay(1000);
  }
}

2.使用软件进行模拟时序

自定义iic.c和iic.h文件(此步骤省略)

.h文件

#ifndef _iic_H
#define _iic_H

#include "system.h"

/*  IIC_SCL时钟端口、引脚定义 */
#define IIC_SCL_PORT 			  GPIOB   
#define IIC_SCL_PIN 			  (GPIO_PIN_8)
#define IIC_SCL_PORT_RCC		__HAL_RCC_GPIOB_CLK_ENABLE()

/*  IIC_SDA时钟端口、引脚定义 */
#define IIC_SDA_PORT 			  GPIOB  
#define IIC_SDA_PIN 			  (GPIO_PIN_9)
#define IIC_SDA_PORT_RCC		__HAL_RCC_GPIOB_CLK_ENABLE()


//IO操作函数	 
#define IIC_SCL    PBout(8) //SCL
#define IIC_SDA    PBout(9) //SDA	 
#define READ_SDA   PBin(9)  //输入SDA

//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口				 
void IIC_Start(void);				//发送IIC开始信号
void IIC_Stop(void);	  			//发送IIC停止信号
void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
u8 IIC_Read_Byte(u8 ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
void IIC_Ack(void);					//IIC发送ACK信号
void IIC_NAck(void);				//IIC不发送ACK信号


#endif

.c文件

/*******************************************************************************
* 函 数 名         : IIC_Init
* 函数功能		   : IIC初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    
  IIC_SCL_PORT_RCC;   //使能时钟
  IIC_SDA_PORT_RCC;

  GPIO_InitStructure.Pin=IIC_SCL_PIN;
  GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
  GPIO_InitStructure.Pull=GPIO_PULLUP;          //上拉
  GPIO_InitStructure.Speed=GPIO_SPEED_FAST;     //快速
  HAL_GPIO_Init(IIC_SCL_PORT,&GPIO_InitStructure);
  GPIO_InitStructure.Pin=IIC_SDA_PIN;
  HAL_GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
  
  IIC_SDA=1;  //默认状态
  IIC_SCL=1;  
}

/*******************************************************************************
* 函 数 名         : SDA_OUT
* 函数功能		   : SDA输出配置   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void SDA_OUT(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	GPIO_InitStructure.Pin=IIC_SDA_PIN;
  GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;  //推挽输出
  GPIO_InitStructure.Pull=GPIO_PULLUP;          //上拉
  GPIO_InitStructure.Speed=GPIO_SPEED_FAST;     //快速
  HAL_GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : SDA_IN
* 函数功能		   : SDA输入配置	   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void SDA_IN(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	GPIO_InitStructure.Pin=IIC_SDA_PIN;
  GPIO_InitStructure.Mode=GPIO_MODE_INPUT;  
  GPIO_InitStructure.Pull=GPIO_PULLUP;          //上拉
  GPIO_InitStructure.Speed=GPIO_SPEED_FAST;     //快速
  HAL_GPIO_Init(IIC_SDA_PORT,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : IIC_Start
* 函数功能		   : 产生IIC起始信号   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(5);  //延时少量时间,防止翻转过快
 	IIC_SDA=0;    //SCL高电平期间,SDA由高变低
	delay_us(6);  //同理
	IIC_SCL=0;    //钳住I2C总线,准备发送或接收数据 
}	

/*******************************************************************************
* 函 数 名         : IIC_Stop
* 函数功能		   : 产生IIC停止信号   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//保证SDA处于低电平
 	IIC_SCL=1; 
	delay_us(6); 
	IIC_SDA=1;//发送I2C总线结束信号,SCL高电平期间SDA由低变高
	delay_us(6);							   	
}

/*******************************************************************************
* 函 数 名         : IIC_Wait_Ack
* 函数功能		   : 等待应答信号到来   
* 输    入         : 无
* 输    出         : 1,接收应答失败
        			 0,接收应答成功
*******************************************************************************/
u8 IIC_Wait_Ack(void)
{
	u8 tempTime=0;
	SDA_IN();      //SDA设置为输入  
	IIC_SDA=1;     //释放总线
	delay_us(1);	   
	IIC_SCL=1;
	delay_us(1);	 
	while(READ_SDA)
	{
		tempTime++;
		if(tempTime>250)//判断是否超时
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 

/*******************************************************************************
* 函 数 名         : IIC_Ack
* 函数功能		   : 产生ACK应答  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Ack(void)
{
	IIC_SCL=0;//确保SCL初始在低电平
	SDA_OUT();
	IIC_SDA=0;//将SDA拉低
	delay_us(2);
	IIC_SCL=1;//在SCL由高变低的期间,SDA低电平表示有应答
	delay_us(5);
	IIC_SCL=0;
}

/*******************************************************************************
* 函 数 名         : IIC_NAck
* 函数功能		   : 产生NACK非应答  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;//步骤与上同理,只是发送的是1
	delay_us(2);
	IIC_SCL=1;
	delay_us(5);
	IIC_SCL=0;
}	

/*******************************************************************************
* 函 数 名         : IIC_Send_Byte
* 函数功能		   : IIC发送一个字节 
* 输    入         : txd:发送一个字节
* 输    出         : 无
*******************************************************************************/		  
void IIC_Send_Byte(u8 txd)
{                        
  u8 t;   
	SDA_OUT(); 	    
  IIC_SCL=0;//拉低时钟开始数据传输
  for(t=0;t<8;t++)
  {              
    if((txd&0x80)>0) //0x80  1000 0000
      IIC_SDA=1;
    else
      IIC_SDA=0;
    txd<<=1; 	     //高位先发,发完就将低位移上来
    delay_us(2);   //对TEA5767这三个延时都是必须的
    IIC_SCL=1;     //在SCL由低变高的期间,SDA是什么状态就表示什么数据
    delay_us(2); 
    IIC_SCL=0;	
    delay_us(2);
  }	 
} 

/*******************************************************************************
* 函 数 名         : IIC_Read_Byte
* 函数功能		   : IIC读一个字节 
* 输    入         : ack=1时,发送ACK,ack=0,发送nACK 
* 输    出         : 应答或非应答
*******************************************************************************/  
u8 IIC_Read_Byte(u8 ack)
{
	u8 i,receive=0;
	SDA_IN();//SDA设置为输入
  for(i=0;i<8;i++ )
	{
    IIC_SCL=0; 
    delay_us(2);
		IIC_SCL=1;
    receive<<=1;
    if(READ_SDA)receive++;   
		delay_us(1); 
  }					 
  if (!ack)
      IIC_NAck();//发送nACK
  else
      IIC_Ack(); //发送ACK   
  return receive;
}

注意!!!在HAL库中没有封装us级的延时,所以需要自己去封装。剩余24c02部分自行封装以及使用。


总结

本人是个菜鸟,如有错误,请指出,谢谢!

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: STM32 I2CEEPROM跨页是指在使用STM32的I2C总线与EEPROM进行通信时,当需要入的数据跨越了EEPROM的页边界时的处理方式。EEPROM是一种可擦非易失性存储器,通常将数据按页(page)进行存储,每页包含固定数量的字节。 当需要向EEPROM中的一个页入数据时,可以使用I2C总线发送一组连续的入命令。但当需要跨页入数据时,就需要分为多个步骤来完成: 1. 发送入命令:首先,通过I2C总线发送一个入命令,指定要入的EEPROM地址。 2. 入数据至页边界:将将要入的数据按页边界切割,将数据的第一部分EEPROM的当前页。 3. 切换至下一页:如果需要入的数据跨越了页边界,就需要切换到下一页。通过I2C总线发送一个新的入命令,指定下一页的地址。 4. 入剩余数据:将剩余的数据入新的页中。 需要特别注意的是,在跨页入时,需要保持I2C总线的稳定。在切换页的过程中,可能会出现一些延迟。为了确保稳定性,可以在发送入命令之后,等待一段时间,以确保EEPROM已切换到新的页。 总结起来,STM32 I2CEEPROM跨页,需要将要入的数据按页边界切割,并在切换页时保持I2C总线的稳定性。 ### 回答2: STM32是一种微控制器,具有许多输入/输出接口(I/O接口),其中包括IIC(Inter-Integrated Circuit)接口。EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种常用的非易失性存储器。 在STM32中,使用IICEEPROM可以通过跨页的方式实现。跨页是一种技术,允许在一次传输中向EEPROM的多个页中连续入数据。具体步骤如下: 1. 配置STM32IIC接口模块。首先,需要设置相关寄存器以启用IIC接口,并配置通信速率和其他参数。 2. 初始化EEPROM。在进行IIC之前,需要初始化EEPROM。这可能包括发送一些特定的命令和设置寄存器。 3. 选择入的页。要进行跨页,需要选择要入的起始页。可以使用特定的命令和地址选择页。在接收到页选择命令后,EEPROM将准备好接收数据。 4. 传输数据。将待入的数据传输到IIC接口,并将其发送到EEPROM。在跨页操作中,数据会自动从一个页传输到下一个页。可以通过循环迭代来传输更多数据。 5. 等待入完成。在数据传输完毕后,需要等待EEPROM完成入操作。通常,EEPROM会有一个内部标志位来指示入是否完成。可以通过查询此标志位来等待入操作的完成。 6. 关闭IIC接口。在完成操作后,需要关闭IIC接口,以便其他设备可以使用该接口。 请注意,具体的实现细节可能会因不同的STM32系列和EEPROM型号而有所不同。因此,建议参考相应的STM32EEPROM芯片的数据手册,以获取更详细的信息和代码示例。 ### 回答3: IIC(Inter-Integrated Circuit)是一种串行通信协议,常被用来连接微控制器与外部设备。在STM32微控制器中,使用IIC总线可以实现对EEPROM(Electrically Erasable Programmable Read-Only Memory)的操作。 对于EEPROM的跨页操作,即在入数据时,需要跨越EEPROM的页边界进行入。通常,EEPROM内部的数据存储以页为单位分割,每一页包含多个字节的数据。当入数据时,如果需要跨越页边界,就需要对两个相邻页的数据进行处理。 在使用STM32IICEEPROM进行跨页操作时,可以按照以下步骤进行: 1. 初始化IIC总线和相关的引脚设置,确保正确连接STM32EEPROM。 2. 设置EEPROM地址,确定要入的页和具体的地址。 3. 将待入的数据按照页的边界进行分割,分为需要入的第一个页和第二个页的数据。 4. 先发送入第一个页数据的命令,同时发送第一个页数据的地址和数据。 5. 等待IIC总线的传输完成,并确认入第一个页数据是否成功。 6. 如果入第一个页数据成功,再发送入第二个页数据的命令,同时发送第二个页数据的地址和数据。 7. 等待IIC总线的传输完成,并确认入第二个页数据是否成功。 8. 在每一步操作完成后,需要根据EEPROM的ACK信号来判断是否入成功。 在进行EEPROM的跨页操作时,需要注意以下几点: - 需要保证跨页入的数据长度不超出一个页的容量。 - 入数据时需要正确设置地址,并按照页的边界进行分割。 - 在入数据后,需要通过确认ACK信号来验证入是否成功。 - 需要正确处理IIC总线的通信协议,包括起始位、地址位、数据位和停止位等。 总结来说,使用STM32IIC总线EEPROM时,可以通过合适的设置和使用跨页操作来实现对EEPROM数据的

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hard-coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值