STM32页读页写AT24CXX(HAL库 模拟IIC)

本文详细介绍了AT24CXX系列EEPROM的容量、寻址方式、时序图以及基于STM32HAL库的IIC通信代码示例,涵盖了字节写、页写、读取等操作。
摘要由CSDN通过智能技术生成

参考文章:

这里附上一篇看到写得很好的大佬的文章

通用24CXX读写程序

 AT24C32/64官方手册:AT24C32/64

 

一、AT24CXX容量表

二、AT24CXX寻址方式

三、AT24CXX时序图

1.字节写

2.页写

3.当前地址读

4.顺序读

 5.随机读

四、代码


 

一、AT24CXX容量表

型号容量(bit)容量(byte)页数每页字节数(byte)
AT24C011K128168
AT24C022K256328
AT24C044K5123216
AT24C088K10246416
AT24C1616K204812816
AT24C3232K409612832
AT24C6464K819225632
AT24C128128K1638425664
AT24C256256K3276851264
AT24C512512K65536512128

二、AT24CXX寻址方式

型号WORD ADDRESS(bit)型号WORD ADDRESS(bit)
AT24C017AT24C3212
AT24C028AT24C6413
AT24C049AT24C12814
AT24C0810AT24C25615
AT24C1611AT24C51216

三、AT24CXX时序图

1.字节写

8732d55d166b4cdb9ac7cb5abaeb4ed3.png

2.页写

fa8748762cb545a78aa91abec373bcf0.png

         AT24CXX内部是有分页的,根据型号不同,页数不同,每页字节数不同。连续写入数据的时候,内部指针会+1,当内部指针移动到当前页末的时候,就会自动移动到当前页头部,再往里写数据的时候就会覆盖掉之前的数据。

        如果想要连续写多页数据,那就需要去判断是否需要翻页,如果地址是在另一页,就需要重新发送字节写的时序。

3.当前地址读

f6e563a171794e55b5d0216ac5019bbd.png

4.顺序读

0c5da7e3ca4345e8927eeef7c9e3126a.png

 5.随机读

2828d5fe472948118274e986650d1685.png

        顺序读是从当前地址开始读,那么随机读搭配顺序读即可以读取任意地址。随机读就是先发送写命令,让EEPROM将指针移动到要读取的位置,然后主机发送起始条件,发送从机地址(读写位为读),即开始顺序读。

四、代码

这里附上的代码是基于STM32 HAL库,模拟IIC读写EEPROM,对AT24CXX系列通用。

bsp_at24cxx.c

/**	BSP_AT24CXX.C	EEPROM AT24CXX/FM24CXX驱动
 * 
 * @author	Dai Zu<zhangruilin@163.com>
 * @date	2024/4/10
 * 
*/

/* BSP头文件 */
#include "BSP_AT24CXX.h"

/* 宏定义 */
#define AT24CXX_ADDR	0xA0		// 从机地址
// #define AT24C01		{128, 8, AT24CXX_ADDR}
// #define AT24C02		{256, 8, AT24CXX_ADDR}
// #define AT24C04		{512, 16, AT24CXX_ADDR}
// #define AT24C08		{1024, 16, AT24CXX_ADDR}
// #define AT24C16		{2048, 16, AT24CXX_ADDR}
// #define AT24C32		{4096, 32, AT24CXX_ADDR}
#define AT24C64	    {8192, 32, AT24CXX_ADDR}
// #define AT24C128	{16384, 64, AT24CXX_ADDR}
// #define AT24C256	{32768, 64, AT24CXX_ADDR}
// #define AT24C512	{65536, 128, AT24CXX_ADDR}

/* EEPROM结构体 */
struct AT24CXX_TYPE
{
	uint32_t size;		// 容量,单位(字节)
	uint8_t	pageSize;	// 每页字节数
	uint8_t addr;		// 从机地址
};

struct AT24CXX_TYPE EEPROM_TYPE= AT24C64;





#define IIC_Soft	1	// 软件IIC
#if IIC_Soft
/* 使用STM32Hal库,以下是IIC接口,移植IIC时只需要实现以下接口即可 */
#define	IIC_SCL_PORT	EEP_SCL_GPIO_Port
#define IIC_SDA_PORT	EEP_SDA_GPIO_Port
#define IIC_SCL_PIN		EEP_SCL_Pin
#define IIC_SDA_PIN		EEP_SDA_Pin

#define IIC_SCL_SET		HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET)
#define IIC_SCL_RESET	HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET)
#define IIC_SDA_SET		HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET)
#define IIC_SDA_RESET	HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET)

#define READ_SDA		HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN)

void IIC_SDA_Dir(uint8_t dir);

/* IO方向 */
enum IIC_SDA_DIR
{
	IIC_SDA_OUTPUT = 0,
	IIC_SDA_INPUT
};

enum IIC_ACK
{
	ACK = 0,
	NACK = 1
};

/**
 * @brief	初始化IIC相关的外设
*/
void IIC_MSP_Init(void)
{
	
}

/**
 * @brief	初始化IIC
*/
void IIC_Init(void)
{					     
 	IIC_MSP_Init();
}

/**
 * @brief		设置SDA的方向
 * @param	dir	IIC_SDA_DIR
 * IIC_SDA_OUTPUT or IIC_SDA_INPUT
*/
void IIC_SDA_Dir(uint8_t dir)
{
	if (dir == IIC_SDA_INPUT)
	{
		IIC_SDA_SET;
	}
}

void Delay_us(uint32_t us)
{
	__IO uint32_t Delay = us * 48 / 8;//(SystemCoreClock / 8U / 1000000U)
    //见stm32f1xx_hal_rcc.c -- static void RCC_Delay(uint32_t mdelay)
  	do
  	{
  	  __NOP();
  	}
  	while (Delay --);
}

/**
 * @brief	产生IIC起始信号
 * SCL高电平期间,SDA产生下降沿
*/
void IIC_Start(void)
{
	IIC_SDA_Dir(IIC_SDA_OUTPUT);
	IIC_SDA_SET;	  	  
	IIC_SCL_SET;
	Delay_us(4);
 	IIC_SDA_RESET;
	Delay_us(4);
	IIC_SCL_RESET; 
}

/**
 * @brief	产生IIC停止信号
 * SCL高电平期间,SDA产生上升沿
*/
void IIC_Stop(void)
{
	IIC_SDA_Dir(IIC_SDA_OUTPUT);
	IIC_SCL_RESET;
	IIC_SDA_RESET;
 	Delay_us(4); 
	IIC_SCL_SET;
 	Delay_us(4); 
	IIC_SDA_SET;						   	
}

/**
 * @brief	等待应答信号
 * @return	ACK or NACK
*/
uint8_t IIC_Wait_Ack(void)
{
	uint8_t ucErrTime=0;
	IIC_SDA_Dir(IIC_SDA_INPUT);
	IIC_SDA_SET;Delay_us(1);	   
	IIC_SCL_SET;Delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return NACK;
		}
	}
	IIC_SCL_RESET;	   
	return ACK;  
} 

/**
 * @brief	产生ACK应答
*/
void IIC_Ack(void)
{
	IIC_SCL_RESET;
	IIC_SDA_Dir(IIC_SDA_OUTPUT);
	IIC_SDA_RESET;
	Delay_us(2);
	IIC_SCL_SET;
	Delay_us(2);
	IIC_SCL_RESET;
}

/**
 * @brief	不应答 
*/	    
void IIC_NAck(void)
{
	IIC_SCL_RESET;
	IIC_SDA_Dir(IIC_SDA_OUTPUT);
	IIC_SDA_SET;
	Delay_us(2);
	IIC_SCL_SET;
	Delay_us(2);
	IIC_SCL_RESET;
}

/**
 * @brief	IIC发送一个字节
*/	  
void IIC_Send_Byte(uint8_t txd)
{                        
    uint8_t t;   
	IIC_SDA_Dir(IIC_SDA_OUTPUT);
    IIC_SCL_RESET;
    for(t=0;t<8;t++)
    {
		if ((txd&0x80)>>7)
		{
			IIC_SDA_SET;
		}
		else
		{
			IIC_SDA_RESET;
		}
        txd<<=1; 	  
		Delay_us(2);
		IIC_SCL_SET;
		Delay_us(2); 
		IIC_SCL_RESET;	
		Delay_us(2);
    }	 
}

/**
 * @brief	读一个字节
 * @param	ack	是否发送应答
 * 0-发送应答,1-不发送应答
*/  
uint8_t IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	IIC_SDA_Dir(IIC_SDA_INPUT); 
    for(i=0;i<8;i++ )
	{
        IIC_SCL_RESET; 
        Delay_us(2);
		IIC_SCL_SET;
        receive<<=1;
        if(READ_SDA)receive++;   
		Delay_us(1); 
    }
    if (ack)
	{
        IIC_NAck();
	}
    else
	{
        IIC_Ack();
	}
    return receive;
}

/**
 * @brief	初始化
*/
void AT24CXX_Init(void)
{
	IIC_Init();
}

/**
 * @brief				从指定地址读出一个数据
 * @param	ReadAddr	数据地址
 * @retval				读取到的数据
*/
uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr)
{				  
	uint8_t temp=0;		  	    																 
    IIC_Start();  
	if(EEPROM_TYPE.size>2048)
	{
		IIC_Send_Byte(EEPROM_TYPE.addr);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(ReadAddr>>8);//发送高地址	    
	}else 
	{
		IIC_Send_Byte(EEPROM_TYPE.addr+((ReadAddr/256)<<1));   //发送器件地址,写数据
	}   
	IIC_Wait_Ack(); 
    IIC_Send_Byte(ReadAddr%256);   				//发送低地址
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(EEPROM_TYPE.addr+1);		   
	IIC_Wait_Ack();	 
    temp=IIC_Read_Byte(1);		   
    IIC_Stop();    
	return temp;
}

/**
 * @brief				向指定地址写入一个字节
 * @param	WriteAddr	数据地址
 * @param	DataToWrite	要写入的数据
*/
void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
{				   	  	    																 
    IIC_Start();
	if(EEPROM_TYPE.size>2048)
	{
		IIC_Send_Byte(EEPROM_TYPE.addr);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(WriteAddr>>8);//发送高地址
	}else
	{
		IIC_Send_Byte(EEPROM_TYPE.addr+((WriteAddr/256)<<1));   //发送器件地址,写数据
	}
	IIC_Wait_Ack();
    IIC_Send_Byte(WriteAddr%256);				//发送低地址
	IIC_Wait_Ack();
	IIC_Send_Byte(DataToWrite);
	IIC_Wait_Ack();
    IIC_Stop();
	HAL_Delay(10);
}

/**
 * @brief				向指定地址写入16位或32位数据
 * @param	WriteAddr	数据地址
 * @param	DataToWrite	要写入的数据
 * @param	Len			2字节或4字节
*/
void AT24CXX_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len)
{  	
	uint8_t t;
	for(t=0;t<Len;t++)
	{
		AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
	}												    
} 

/**
 * @brief				从指定地址读取16位或32位的数据
 * @param	WriteAddr	数据地址
 * @param	Len			2字节或4字节
 * @retval				读出的数据
*/
uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len)
{  	
	uint8_t t;
	uint32_t temp=0;
	for(t=0;t<Len;t++)
	{
		temp<<=8;
		temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1); 	 				   
	}
	return temp;												    
}

/**
 * @brief	检查EEPROM是否正常
 * @retval	1-检测失败	0-成功
*/
uint8_t AT24CXX_Check(void)
{
	uint8_t temp;
	temp=AT24CXX_ReadOneByte(255);
	if(temp==0X55)return 0;		   
	else//排除第一次初始化的情况
	{
		AT24CXX_WriteOneByte(255,0X55);
	    temp=AT24CXX_ReadOneByte(255);	  
		if(temp==0X55)return 0;
	}
	return 1;											  
}

/**
 * @brief			连续读
 * @param	addr	数据地址
 * @param	data	存储位置
 * @param	length	读取长度
 * 
*/
void AT24CXX_SequentialRead(uint16_t addr,uint8_t *data,uint16_t length)
{
	if (length == 0)
	{
		return;
	}
	
    IIC_Start();
	if(EEPROM_TYPE.size>2048)
	{
		IIC_Send_Byte(EEPROM_TYPE.addr);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(addr>>8);//发送高地址
	}else
	{
		IIC_Send_Byte(EEPROM_TYPE.addr+((addr/256)<<1));   //发送器件地址,写数据
	}
	IIC_Wait_Ack();
    IIC_Send_Byte(addr%256);   				//发送低地址
	IIC_Wait_Ack();
	IIC_Start();
	IIC_Send_Byte(EEPROM_TYPE.addr+1);
	IIC_Wait_Ack();

    *data++=IIC_Read_Byte(0);
	while(--length)
	{
		*data++=IIC_Read_Byte(0);
	}
	IIC_Stop();
}

/**
 * @brief			页写
 * @param	addr	数据地址
 * @param	data 	数据指针
 * @param	length 	要写入数据的个数
*/
void AT24CXX_PageWrite(uint16_t addr,uint8_t *data,uint16_t length)
{
	if (length==0 || addr>=EEPROM_TYPE.size)
	{
		return;
	}

	IIC_Start();
	if(EEPROM_TYPE.size>2048)
	{
		IIC_Send_Byte(EEPROM_TYPE.addr);		// 发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(addr>>8);					// 发送高地址
	}else
	{
		IIC_Send_Byte(EEPROM_TYPE.addr+((addr/256)<<1));   //发送器件地址,写数据
	}
	IIC_Wait_Ack();
    IIC_Send_Byte(addr%256);					// 发送低地址
	IIC_Wait_Ack();

	for (uint16_t i = 0; i < length; i++)
	{
		IIC_Send_Byte(data[i]);
		IIC_Wait_Ack();
		addr++;

		if (addr >= EEPROM_TYPE.size)			// 内存已满
		{
			break;
		}
		
		if ((addr)%EEPROM_TYPE.pageSize == 0)	// 满页
		{
			IIC_Stop();
			HAL_Delay(10);
			IIC_Start();
			if(EEPROM_TYPE.size>2048)
			{
				IIC_Send_Byte(EEPROM_TYPE.addr);		// 发送写命令
				IIC_Wait_Ack();
				IIC_Send_Byte(addr>>8);					// 发送高地址
			}else
			{
				IIC_Send_Byte(EEPROM_TYPE.addr+((addr/256)<<1));   //发送器件地址,写数据
			}
			IIC_Wait_Ack();
    		IIC_Send_Byte(addr%256);					// 发送低地址
			IIC_Wait_Ack();
		}
		
	}

	IIC_Stop();
	HAL_Delay(10);
}
#elif

#endif

BSP_AT24CXX.h

/**	BSP_AT24CXX.h	EEPROM AT24CXX/FM24CXX驱动
 * 
 * @author	Dai Zu<zhangruilin@163.com>
 * @date	2024/4/10
 * 
*/

#ifndef __BSP_AT24Cxx_H
#define __BSP_AT24Cxx_H

#include "main.h"

uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr);
void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite);
void AT24CXX_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len);
uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len);
void AT24CXX_SequentialRead(uint16_t addr,uint8_t *data,uint16_t length);
void AT24CXX_PageWrite(uint16_t addr,uint8_t *data,uint16_t length);

uint8_t AT24CXX_Check(void);
void AT24CXX_Init(void);

#endif


 

 

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用HAL库进行STM32IICAT24C02时,你可以参考以下步骤: 1. 首先,你需要在你的工程中包含AT24CXX.c文件,并在代码中引用该文件。该文件中定义了一些常量和函数,用于初始化AT24CXX芯片、入数据和读取数据等操作。 2. 在AT24CXX.c文件中,你可以看到定义了一些常量,如AT24C02的地址为255。这些常量可以根据你所使用的芯片型号进行修改。 3. 在AT24CXX.c文件中,还定义了一些函数,如AT24CXX_Init()用于初始化AT24CXX芯片,AT24CXX_Write()用于入数据,AT24CXX_Read()用于读取数据,AT24CXX_Check()用于检查AT24CXX芯片是否正常工作。你可以根据需要调用这些函数来实现对AT24C02的读操作。 4. 在配置STM32的引脚时,你需要将IIC的引脚与AT24C02芯片的引脚相连接。具体的引脚配置可以参考AT24CXX.c文件中的注释。 5. 在配置串口时,你可以选择使用串口进行数据查看,以便调试和验证读操作的结果。 6. 最后,根据你的需求选择适当的时钟频率,生成Keil工程代码。 综上所述,你可以使用HAL库的函数和AT24CXX.c文件中定义的函数来实现对AT24C02芯片的读操作。 #### 引用[.reference_title] - *1* *3* [STM32 (基于HAL库) 硬件IIC任意AT24CXX芯片](https://blog.csdn.net/weixin_56565733/article/details/124401443)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [STM32系列(HAL库)——F103C8T6通过IIC/I2C方式读AT24C02—(EEPROM 存储模块)](https://blog.csdn.net/lwb450921/article/details/124394615)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天师电通电容爆破工程师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值