赞
踩
这里附上一篇看到写得很好的大佬的文章:
STM32F407单片机通用24CXXX读写程序(KEIL),兼容24C系列存储器(24C01到24C512),支持存储器任意地址跨页连续读写多个页
AT24C32/64官方手册:AT24C32/64
型号 | 容量(bit) | 容量(byte) | 页数 | 每页字节数(byte) |
AT24C01 | 1K | 128 | 16 | 8 |
AT24C02 | 2K | 256 | 32 | 8 |
AT24C04 | 4K | 512 | 32 | 16 |
AT24C08 | 8K | 1024 | 64 | 16 |
AT24C16 | 16K | 2048 | 128 | 16 |
AT24C32 | 32K | 4096 | 128 | 32 |
AT24C64 | 64K | 8192 | 256 | 32 |
AT24C128 | 128K | 16384 | 256 | 64 |
AT24C256 | 256K | 32768 | 512 | 64 |
AT24C512 | 512K | 65536 | 512 | 128 |
型号 | WORD ADDRESS(bit) | 型号 | WORD ADDRESS(bit) |
AT24C01 | 7 | AT24C32 | 12 |
AT24C02 | 8 | AT24C64 | 13 |
AT24C04 | 9 | AT24C128 | 14 |
AT24C08 | 10 | AT24C256 | 15 |
AT24C16 | 11 | AT24C512 | 16 |
AT24CXX内部是有分页的,根据型号不同,页数不同,每页字节数不同。连续写入数据的时候,内部指针会+1,当内部指针移动到当前页末的时候,就会自动移动到当前页头部,再往里写数据的时候就会覆盖掉之前的数据。
如果想要连续写多页数据,那就需要去判断是否需要翻页,如果地址是在另一页,就需要重新发送字节写的时序。
顺序读是从当前地址开始读,那么随机读搭配顺序读即可以读取任意地址。随机读就是先发送写命令,让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
-
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。