STM32HAL库学习笔记--硬件I2C读写AT24C512

摘要:由于需要实现掉电存储功能,在无线传输模块上增加了一块EEPROM芯片,因为工程使用CubeMx配置并生成,为了方便不再使用IO口模拟I2C,而是使用f1自带的硬件I2C来实现。配置和调试过程和遇到一些问题,在此记录如下。

MCU 储存芯片 CubeMx版本
STM32F103C8T6 AT24C512 5.0.1

关键词:硬件I2C,HAL库,EEPROM存取

一、CubeMx配置

STM32HAL库学习笔记--硬件I2C读写AT24C512_第1张图片

开启I2C1,其余参数默认

在旧版本的CubeMx中,HAL库实现的I2C初始化带有一些BUG,比如需要在I2C引脚复用GPIO配置前加上I2C的时钟使能,不过这些BUG在新版本的CubeMx中似乎得到了解决,直接使用其生成的初始化程序和HAL库的I2C接口就可以实现成功的读写操作

二、读AT24C512DataSheet

AT24Cxx系列芯片内存大小各异,页数和每页字节数也不同,要根据数据手册来确定该型号芯片的内存结构(Memory Organization)

STM32HAL库学习笔记--硬件I2C读写AT24C512_第2张图片

由DS可知AT24C512的内存一共有 512 Pages x 128 Bytes = 65536 Bytes = 512 kbits,同时地址是16位数据字,范围是0x0000~0xFFFF

和同系列其他芯片一样,器件地址为8位,前5位固定,最低位为读/写标志,第二、三位为总线地址。电路中A0、A1引脚接地,故读、写的器件地址分别为0xA1,0xA0

三、调用HAL库接口实现读写

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即可

STM32HAL库学习笔记--硬件I2C读写AT24C512_第3张图片

STM32HAL库学习笔记--硬件I2C读写AT24C512_第4张图片

2. 调用HAL库自带的HAL_I2C_Mem_Write函数时,需要注意一次写入的字节数不能大于EEPROM芯片的页字节数,否则会回滚覆盖低位地址的数据

STM32HAL库学习笔记--硬件I2C读写AT24C512_第5张图片

你可能感兴趣的:(STM32)