摘要:由于需要实现掉电存储功能,在无线传输模块上增加了一块EEPROM芯片,因为工程使用CubeMx配置并生成,为了方便不再使用IO口模拟I2C,而是使用f1自带的硬件I2C来实现。配置和调试过程和遇到一些问题,在此记录如下。
MCU | 储存芯片 | CubeMx版本 |
STM32F103C8T6 | AT24C512 | 5.0.1 |
关键词:硬件I2C,HAL库,EEPROM存取
开启I2C1,其余参数默认
在旧版本的CubeMx中,HAL库实现的I2C初始化带有一些BUG,比如需要在I2C引脚复用GPIO配置前加上I2C的时钟使能,不过这些BUG在新版本的CubeMx中似乎得到了解决,直接使用其生成的初始化程序和HAL库的I2C接口就可以实现成功的读写操作
AT24Cxx系列芯片内存大小各异,页数和每页字节数也不同,要根据数据手册来确定该型号芯片的内存结构(Memory Organization)
由DS可知AT24C512的内存一共有 512 Pages x 128 Bytes = 65536 Bytes = 512 kbits,同时地址是16位数据字,范围是0x0000~0xFFFF
和同系列其他芯片一样,器件地址为8位,前5位固定,最低位为读/写标志,第二、三位为总线地址。电路中A0、A1引脚接地,故读、写的器件地址分别为0xA1,0xA0
HAL库将I2C操作封装后提供了直接操作EEPROM的接口:
//EEPORM写函数
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
//EEPORM读函数
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
参数表
参数 | 类型 | 含义 |
hi2c | I2C_HandleTypeDef * | HAL库I2C结构体指针 |
DevAddress | uint16_t | 器件地址 |
MemAddress | uint16_t | 内存地址 |
MemAddSize | uint16_t | 内存地址大小(8或16) |
pData | uint8_t * | 缓冲区头指针 |
Size | uint16_t | 缓冲区大小 |
Timeout | uint32_t | 超时时间 |
返回值
HAL_OK | 成功 |
HAL_TIMEOUT | 等待超时 |
HAL_BUSY | 总线繁忙 |
HAL_ERROR | 其他错误 |
读写的实现
#define ADDR_AT24C02_Write 0xA0
#define ADDR_AT24C02_Read 0xA1
int16_t AT24C512_Write_nBytes(uint8_t *data, uint16_t addr, uint16_t length)
{
if( HAL_I2C_Mem_Write( &hi2c1, ADDR_AT24C02_Write, addr, I2C_MEMADD_SIZE_16BIT, data, length, 1000 ) == HAL_OK )
return 0;
else
return -1;
}
int16_t AT24C512_Read_nBytes(uint8_t *data, uint16_t addr, uint16_t length)
{
if( HAL_I2C_Mem_Read( &hi2c1, ADDR_AT24C02_Write, addr, I2C_MEMADD_SIZE_16BIT, data, length, 1000 ) == HAL_OK )
return 0;
else
return -1;
}
连续读写测试
void E2PROMTestTask(void const * argument)
{
uint8_t write_buff[18] = "read & write test";
uint8_t read_buff[18] = {0};
AT24C512_Write_nBytes( &write_buff[0], 0, 10 );
HAL_Delay( 10 ); //这里的延时是必要的
AT24C512_Write_nBytes( &write_buff[10], 10, 8 );
HAL_Delay( 10 );
AT24C512_Read_nBytes( read_buff, 0, 18 );
printf( "%s\r\n", read_buff );
uint8_t long_write_buff[256];
uint8_t long_read_buff[256] = {0};
for( size_t i = 0; i < 256; ++i )
{
long_write_buff[i] = i;
}
//写入有页限制
AT24C512_Write_nBytes( &long_write_buff[0], 0, 128 );
HAL_Delay( 10 );
AT24C512_Write_nBytes( &long_write_buff[128], 128, 128 );
HAL_Delay( 10 );
//读出没有页限制
AT24C512_Read_nBytes( long_read_buff, 0, 256 );
for( size_t i = 0; i < 256; ++i )
{
printf("0x%02X ",long_read_buff[i]);
}
}
1. 写操作结束后,MCU会向EEPROM发送一个停止位,在发出下一个起始位之前,EEPORM芯片会进入写周期(internally-timed write cycle),这段时间内不能进行任何输入,所以在两个写操作或写操作与读操作之间需要有一个保证wirte cycle的延时,一般5~10ms即可
2. 调用HAL库自带的HAL_I2C_Mem_Write函数时,需要注意一次写入的字节数不能大于EEPROM芯片的页字节数,否则会回滚覆盖低位地址的数据