STMCUBEMX_IIC_LL库_AT24C64分页读取和写入
前言:
一个项目中构建的软件系统需要存储非常多的用户参数,大约有几千字节,所以牵扯到自己设计跨页写入算法,注意读出也是需要设计跨页读出算法的(手册没强调,但是实际测试结果不分页直接连续读,最多一次性只能读出280个字节,不同厂家的芯片可能有不同)
1、分页写入算法
//eeprom设备发送多个字节
unsigned char I2CX_DeviceWriteArray_addr16(I2C_TypeDef *I2Cx,unsigned char dev_addr,unsigned short addr,unsigned char *data,unsigned int len,unsigned int overtime)
{
unsigned int time = timestamp;
LL_I2C_GenerateStartCondition(I2Cx);//起始信号
while (!LL_I2C_IsActiveFlag_SB(I2Cx))//起始信号发送完毕
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_TransmitData8(I2Cx, dev_addr);//发送器件地址
//LL_I2C_TransmitData8(I2Cx, dev_addr);//发送器件地址
while (!LL_I2C_IsActiveFlag_ADDR(I2Cx))//等待从机回应ACK
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_ClearFlag_ADDR(I2Cx);
LL_I2C_TransmitData8(I2Cx, addr >> 8);//发送数据
LL_I2C_TransmitData8(I2Cx, addr);//发送数据
while (!LL_I2C_IsActiveFlag_TXE(I2Cx))
{
if(timestamp > time + overtime) return 1;
}
while(len--)
{
LL_I2C_TransmitData8(I2Cx, *data);//发送数据
data++;
while (!LL_I2C_IsActiveFlag_TXE(I2Cx))
{
if(timestamp > time + overtime) return 1;
}
}
LL_I2C_GenerateStopCondition(I2Cx);
return 0;
}
//unsigned short addr:写器件的寄存器地址
//unsigned char *data:写数据
//unsigned short len:写数据长度
void eeprom_24c64_write(unsigned short addr,unsigned char *data,unsigned short len)
{
if(I2CX_DeviceWriteArray_addr16(I2C1,AT24C64_ADDR,addr,data,len,100))
{
EEPROM_error("eeprom_24c64_write fail!!!\r\n");
}
}
//unsigned short address:EEPROM的写起始地址
//unsigned char * data:EEPROM的写数据
//unsigned char * data:EEPROM的写数据的长度
void eepromwriteData(unsigned short address, unsigned char * data, unsigned short size)
{
unsigned short pageIndex = address / EEP_MAX_PAGE_SIZE; // 获取起始页地址
unsigned short offset = address % EEP_MAX_PAGE_SIZE; // 获取起始地址在页内的偏移量
int remainingSize = size;
unsigned short dataSize;
while (remainingSize > 0)
{
// 当前页的起始地址
unsigned short currentPageAddress = pageIndex * EEP_MAX_PAGE_SIZE;
//Debug_debug("currentPageAddress:%d\r\n",currentPageAddress);
// 计算当前页的剩余空间大小
unsigned short remainingSpace = EEP_MAX_PAGE_SIZE - offset;
//Debug_debug("remainingSpace:%d\r\n",remainingSpace);
// 计算当前要写入的数据块大小
dataSize = (remainingSize > remainingSpace) ? remainingSpace : remainingSize;
//Debug_debug("dataSize:%d\r\n",dataSize);
// 将数据块写入当前页
unsigned char write_data[EEP_MAX_PAGE_SIZE] = {0};
memcpy(write_data,&data[size - remainingSize],dataSize);
//Debug_debug("write_data:%x,%x,%x,%x,%x,%x,%x,%x\r\n",write_data[0],
//write_data[1],write_data[2],write_data[3],write_data[4],write_data[5],write_data[6],write_data[7]);
eeprom_24c64_write(currentPageAddress + offset,write_data,dataSize);
remainingSize -= dataSize; //计算剩余数据
offset = 0; //偏移量重置为0,从下一页的起始位置开始写入
pageIndex++; //准备写下一页
LL_mDelay(5); //eeprom器件每次写入时间要有一个安全间隔
}
}
2、分页读算法
//eeprom设备接收多个字节
unsigned char I2CX_DeviceReadArray_addr16(I2C_TypeDef *I2Cx,unsigned char dev_addr,unsigned short addr,unsigned char *data,unsigned int len,unsigned int overtime)
{
volatile unsigned int time = timestamp;
while (LL_I2C_IsActiveFlag_BUSY(I2Cx))
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_GenerateStartCondition(I2Cx);//起始信号
while (!LL_I2C_IsActiveFlag_SB(I2Cx))//起始信号发送完毕
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_TransmitData8(I2Cx, dev_addr);//发送器件地址,写模式
// LL_I2C_TransmitData8(I2Cx, dev_addr | 0x01);//发送器件地址,写模式
while (!LL_I2C_IsActiveFlag_ADDR(I2Cx))//等待从机回应ACK
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_ClearFlag_ADDR(I2Cx);
LL_I2C_TransmitData8(I2Cx, addr >> 8);//发送寄存器地址
while (!LL_I2C_IsActiveFlag_TXE(I2Cx))
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_TransmitData8(I2Cx, addr);//发送寄存器地址
while (!LL_I2C_IsActiveFlag_TXE(I2Cx))
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_GenerateStartCondition(I2Cx);//起始信号
while (!LL_I2C_IsActiveFlag_SB(I2Cx))//起始信号发送完毕
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_TransmitData8(I2Cx, dev_addr | 0x01);//发送器件地址,读模式
while (!LL_I2C_IsActiveFlag_ADDR(I2Cx))//等待从机回应ACK
{
if(timestamp > time + overtime) return 1;
}
LL_I2C_ClearFlag_ADDR(I2Cx);
while(len)
{
if(len == 1)
{
LL_I2C_AcknowledgeNextData(I2Cx, LL_I2C_NACK);
LL_I2C_GenerateStopCondition(I2Cx);
}
while (!LL_I2C_IsActiveFlag_RXNE(I2Cx))
{
if(timestamp > time + overtime) return 1;
}
*data = LL_I2C_ReceiveData8(I2Cx);//接收数据
data++;
len--;
}
LL_I2C_AcknowledgeNextData(I2Cx, LL_I2C_ACK);
return 0;
}
//unsigned short addr:读器件的寄存器地址
//unsigned char *data:读数据
//unsigned short len:读数据长度
void eeprom_24c64_read(unsigned short addr,unsigned char *data,unsigned short len)
{
if(I2CX_DeviceReadArray_addr16(I2C1,AT24C64_ADDR,addr,data, len,100))
{
EEPROM_error("eeprom_24c64_read fail!!!\r\n");
}
}
//unsigned short address:EEPROM的读起始地址
//unsigned char * data:EEPROM的读数据
//unsigned char * data:EEPROM的读数据的长度
void eepromreadData(unsigned short address, unsigned char * data, unsigned short size)
{
unsigned short pageIndex = address / EEP_MAX_PAGE_SIZE; // 获取起始页地址
unsigned short offset = address % EEP_MAX_PAGE_SIZE; // 获取起始地址在页内的偏移量
int remainingSize = size;
unsigned short dataSize;
while (remainingSize > 0)
{
// 当前页的起始地址
unsigned short currentPageAddress = pageIndex * EEP_MAX_PAGE_SIZE;
//Debug_debug("currentPageAddress:%d\r\n",currentPageAddress);
// 计算当前页的剩余空间大小
unsigned short remainingSpace = EEP_MAX_PAGE_SIZE - offset;
//Debug_debug("remainingSpace:%d\r\n",remainingSpace);
// 计算当前要写入的数据块大小
dataSize = (remainingSize > remainingSpace) ? remainingSpace : remainingSize;
//Debug_debug("dataSize:%d\r\n",dataSize);
// 将数据块写入当前页
unsigned char write_data[EEP_MAX_PAGE_SIZE] = {0};
//Debug_debug("write_data:%x,%x,%x,%x,%x,%x,%x,%x\r\n",write_data[0],
//write_data[1],write_data[2],write_data[3],write_data[4],write_data[5],write_data[6],write_data[7]);
eeprom_24c64_read(currentPageAddress + offset,write_data,dataSize);
memcpy(&data[size - remainingSize],write_data,dataSize);
remainingSize -= dataSize; //计算剩余数据
offset = 0; //偏移量重置为0,从下一页的起始位置开始写入
pageIndex++; //准备写下一页
LL_mDelay(5); //eeprom器件每次读出时间要有一个安全间隔
}
}
3、完整代码
eeprom.c
#include "eeprom.h"
eepromMessageData_t eepromMessageData = {0};
eepromMessageData_t eepromMessageData_old = {0};
void eepromInit(void)
{
eepromreadData(0x00,(unsigned char *)&eepromMessageData,sizeof(eepromMessageData));
/********************避免新生产的机器中EEPROM中读出的都是0xff*********************/
if(eepromMessageData.eeprom_flow_target == 0xffff) eepromMessageData.eeprom_flow_target = 20;
if(eepromMessageData.eeprom_pressure_target == 0xff) eepromMessageData.eeprom_pressure_target = 10;
if(eepromMessageData.eeprom_pressure_mode == 0xff) eepromMessageData.eeprom_pressure_mode = 0;
if(eepromMessageData.eeprom_buzzer_sound == 0xff) eepromMessageData.eeprom_buzzer_sound = 0;
if(eepromMessageData.eeprom_flow_all == 0xffff) eepromMessageData.eeprom_flow_all = 0;
memcpy(&eepromMessageData_old,&eepromMessageData,sizeof(eepromMessageData));
}
void eepromEndInit(void)
{
}
void eepromTask(void)
{
if(memcmp(&eepromMessageData,&eepromMessageData_old,sizeof(eepromMessageData)) != 0)
{
eepromwriteData(0x00, (unsigned char *)&eepromMessageData, sizeof(eepromMessageData));
memcpy(&eepromMessageData_old,&eepromMessageData,sizeof(eepromMessageData));
}
}
void eeprom_24c64_write(unsigned short addr,unsigned char *data,unsigned short len)
{
if(I2CX_DeviceWriteArray_addr16(I2C1,AT24C64_ADDR,addr,data,len,100))
{
EEPROM_error("eeprom_24c64_write fail!!!\r\n");
}
}
void eeprom_24c64_read(unsigned short addr,unsigned char *data,unsigned short len)
{
if(I2CX_DeviceReadArray_addr16(I2C1,AT24C64_ADDR,addr,data, len,100))
{
EEPROM_error("eeprom_24c64_read fail!!!\r\n");
}
}
void eepromwriteData(unsigned short address, unsigned char * data, unsigned short size)
{
unsigned short pageIndex = address / EEP_MAX_PAGE_SIZE; // 获取起始页地址
unsigned short offset = address % EEP_MAX_PAGE_SIZE; // 获取起始地址在页内的偏移量
int remainingSize = size;
unsigned short dataSize;
while (remainingSize > 0)
{
// 当前页的起始地址
unsigned short currentPageAddress = pageIndex * EEP_MAX_PAGE_SIZE;
//Debug_debug("currentPageAddress:%d\r\n",currentPageAddress);
// 计算当前页的剩余空间大小
unsigned short remainingSpace = EEP_MAX_PAGE_SIZE - offset;
//Debug_debug("remainingSpace:%d\r\n",remainingSpace);
// 计算当前要写入的数据块大小
dataSize = (remainingSize > remainingSpace) ? remainingSpace : remainingSize;
//Debug_debug("dataSize:%d\r\n",dataSize);
// 将数据块写入当前页
unsigned char write_data[EEP_MAX_PAGE_SIZE] = {0};
memcpy(write_data,&data[size - remainingSize],dataSize);
//Debug_debug("write_data:%x,%x,%x,%x,%x,%x,%x,%x\r\n",write_data[0],
//write_data[1],write_data[2],write_data[3],write_data[4],write_data[5],write_data[6],write_data[7]);
eeprom_24c64_write(currentPageAddress + offset,write_data,dataSize);
remainingSize -= dataSize; //计算剩余数据
offset = 0; //偏移量重置为0,从下一页的起始位置开始写入
pageIndex++; //准备写下一页
LL_mDelay(5); //eeprom器件每次写入时间要有一个安全间隔
}
}
void eepromreadData(unsigned short address, unsigned char * data, unsigned short size)
{
unsigned short pageIndex = address / EEP_MAX_PAGE_SIZE; // 获取起始页地址
unsigned short offset = address % EEP_MAX_PAGE_SIZE; // 获取起始地址在页内的偏移量
int remainingSize = size;
unsigned short dataSize;
while (remainingSize > 0)
{
// 当前页的起始地址
unsigned short currentPageAddress = pageIndex * EEP_MAX_PAGE_SIZE;
//Debug_debug("currentPageAddress:%d\r\n",currentPageAddress);
// 计算当前页的剩余空间大小
unsigned short remainingSpace = EEP_MAX_PAGE_SIZE - offset;
//Debug_debug("remainingSpace:%d\r\n",remainingSpace);
// 计算当前要写入的数据块大小
dataSize = (remainingSize > remainingSpace) ? remainingSpace : remainingSize;
//Debug_debug("dataSize:%d\r\n",dataSize);
// 将数据块写入当前页
unsigned char write_data[EEP_MAX_PAGE_SIZE] = {0};
//Debug_debug("write_data:%x,%x,%x,%x,%x,%x,%x,%x\r\n",write_data[0],
//write_data[1],write_data[2],write_data[3],write_data[4],write_data[5],write_data[6],write_data[7]);
eeprom_24c64_read(currentPageAddress + offset,write_data,dataSize);
memcpy(&data[size - remainingSize],write_data,dataSize);
remainingSize -= dataSize; //计算剩余数据
offset = 0; //偏移量重置为0,从下一页的起始位置开始写入
pageIndex++; //准备写下一页
LL_mDelay(5); //eeprom器件每次读出时间要有一个安全间隔
}
}
eeprom.h
#ifndef __EEPROM_H
#define __EEPROM_H
#include "main.h"
#include "log.h"
#include "my_i2c.h"
#include <string.h>
#define EEPROM_LOG_EN 1
#if EEPROM_LOG_EN
#define EEPROM_printf(format, ...) printf(RTT_CTRL_TEXT_WHITE format , ##__VA_ARGS__)//"\r\n"
#define EEPROM_info(format, ...) printf(RTT_CTRL_TEXT_GREEN"[eeprom]info:" format , ##__VA_ARGS__)
#define EEPROM_debug(format, ...) printf(RTT_CTRL_TEXT_WHITE"[eeprom]debug:" format , ##__VA_ARGS__)
#define EEPROM_warning(format, ...) printf(RTT_CTRL_TEXT_YELLOW"[eeprom]warning:" format , ##__VA_ARGS__)
#define EEPROM_error(format, ...) printf(RTT_CTRL_TEXT_RED"[eeprom]error:" format ,##__VA_ARGS__)
#else
#define EEPROM_printf(format, ...)
#define EEPROM_info(format, ...)
#define EEPROM_debug(format, ...)
#define EEPROM_warning(format, ...)
#define EEPROM_error(format, ...)
#endif
#define AT24C64_ADDR 0xA0
#define AT24C64_ADDR_WRITE 0xA0
#define AT24C64_ADDR_READ 0xA1
#define EEP_MAX_PAGE_SIZE 32 // 最大页写字节数
#define EEP_MAX_ROM_SIZE 8192 // EEROM容量字节数
typedef struct
{
int eeprom_pressure_target; //预设压力值
int eeprom_pressure_mode; //压力模式
int eeprom_buzzer_sound; //蜂鸣器声音大小
int eeprom_flow_target; //预设流量值
int eeprom_flow_all[1500]; //统计流量
}eepromMessageData_t;
extern eepromMessageData_t eepromMessageData;
void eepromInit(void);
void eepromEndInit(void);
void eepromTask(void);
void eeprom_24c64_write(unsigned short addr,unsigned char *data,unsigned short len);
void eeprom_24c64_read(unsigned short addr,unsigned char *data,unsigned short len);
void eepromwriteData(unsigned short address, unsigned char * data, unsigned short size);
void eepromreadData(unsigned short address, unsigned char * data, unsigned short size);
#endif