前言

  本文介绍如何在STM32芯片上,使用HAL库对AT24C02 EEPROM进行单字节与页读写操作。文章侧重关键实现细节,适合已有一定STM32和I2C基础的读者。如果是第一次接触EEPROM或HAL库,建议先了解相关基础知识后再阅读

相关介绍

AT24C02

  该芯片为1颗2K的串行EEPROM。在寻址上,当前芯片只需要8位寻址,是可以只通过I2C的第1个寻址字节来找到目标地址的,当存储变大时,8位寻址位不再满足后,就要引入引脚A0~A2作为高位,来扩大寻址能力,所以A0~A2引脚的功能为设备地址/内存地址。回绕问题,读取和写入都存在该现象,区别在于读取的回绕是超过芯片的存储大小后会回到芯片的起始位置,也就是0x00处,而写入的回绕是发生在页内,在当前页尾回到页头,覆盖掉旧数据部分,所以同样的回绕,读取和写入的风险级别不同

AT24C01A,1K串行EEPROM:内部组织为16页,每页8字节,1K需要7位数据字地址进行随机字寻址
AT24C02,2K串行EEPROM:内部组织为32页,每页8字节,2K需要8位数据字地址进行随机字寻址
AT24C04,4K串行EEPROM:内部组织为32页,每页16字节,4K需要9位数据字地址进行随机字寻址
AT24C08A,8K串行EEPROM:内部组织为64页,每页16字节,8K需要10位数据字地址进行随机字寻址
AT24C16A,16K串行EEPROM:内部组织为128页,每页16字节,16K需要11位数据字地址进行随机字寻址

在这里插入图片描述

STM32的HAL配置

这里仅展示I2C的配置,勾选使能即可。在代码测试案例中,还用到了串口输出,这个大家可以自行配置,在重定向下,不再赘述。

在这里插入图片描述

驱动代码

AT24C02.h

#pragma once
#include <stdint.h>

/* 硬件参数宏定义 */
#define AT24C02_PAGE_SIZE 8
#define AT24C02_8BIT_ADDRESS 0xA0
#define AT24C02_TIMEOUT_MS 100
#define AT24C02_PAGE_BYTE   8

/* 操作状态枚举 */
typedef enum {
    AT24C02_OK       = 0x00U,
    AT24C02_ERROR    = 0x01U,
    AT24C02_BUSY     = 0x02U,
    AT24C02_TIMEOUT  = 0x03U
} AT24C02_Status_e;
#define AT24C02_Status_e HAL_StatusTypeDef

AT24C02_Status_e AT24C02_IsDeviceReady(void);

/**
 * @brief 写入单个字节
 *
 * @param addr EEPROM 内部地址,范围 0 ~ 255
 * @param data 待写入数据
 * @return AT24C02_Status_e 操作结果
 */
AT24C02_Status_e AT24C02_WriteByte(uint8_t addr, uint8_t data);

/**
 * @brief 读取单个字节
 *
 * @param addr EEPROM 内部地址,范围 0 ~ 255
 * @param data 数据缓冲区指针
 * @return AT24C02_Status_e 操作结果
 */
AT24C02_Status_e AT24C02_ReadByte(uint8_t addr, uint8_t *data);

/**
 * @brief 按页写入,最多 8 字节,不可跨页边界
 *
 * @param addr EEPROM 内部起始地址,范围 0 ~ 255
 * @param data 待写入数据缓冲区指针
 * @param len  写入字节数,不超过页大小且不可跨页
 * @return AT24C02_Status_e 操作结果(跨页返回 AT24C02_ERROR)
 */
AT24C02_Status_e AT24C02_WritePage(uint8_t addr, uint8_t *data, uint8_t len);

/**
 * @brief 连续读取多个字节
 *
 * @param addr EEPROM 内部起始地址,范围 0 ~ 255
 * @param data 读取数据缓冲区指针
 * @param len  读取字节数
 * @return AT24C02_Status_e 操作结果
 */
AT24C02_Status_e AT24C02_ReadBytes(uint8_t addr, uint8_t *data, uint8_t len);

AT24C02.c

#include "main.h"
#include "i2c.h"
#include "AT24C02.h"

/**
 * @brief I2C 主机发送
 */
AT24C02_Status_e _AT24C02_I2C_Master_Transmit(uint8_t *pData, uint16_t Size)
{
    return HAL_I2C_Master_Transmit(&hi2c1, AT24C02_8BIT_ADDRESS, pData, Size, AT24C02_TIMEOUT_MS);
}

/**
 * @brief I2C 主机接收
 */
AT24C02_Status_e _AT24C02_I2C_Master_Receive(uint8_t *pData, uint16_t Size)
{
    return HAL_I2C_Master_Receive(&hi2c1, AT24C02_8BIT_ADDRESS, pData, Size, AT24C02_TIMEOUT_MS);
}

/**
 * @brief I2C 内存读取
 */
AT24C02_Status_e _AT24C02_I2C_Mem_Read(uint8_t addr, uint8_t *data, uint8_t len)
{
    return HAL_I2C_Mem_Read(&hi2c1, AT24C02_8BIT_ADDRESS, addr, I2C_MEMADD_SIZE_8BIT, data, len, AT24C02_TIMEOUT_MS);
}

/**
 * @brief I2C 内存写入
 */
AT24C02_Status_e _AT24C02_I2C_Mem_Write(uint8_t addr, uint8_t *data, uint8_t len)
{
    return HAL_I2C_Mem_Write(&hi2c1, AT24C02_8BIT_ADDRESS, addr, I2C_MEMADD_SIZE_8BIT, data, len, AT24C02_TIMEOUT_MS);
}


/**
 * @brief I2C 设备是否准备好
 */
AT24C02_Status_e _AT24C02_I2C_IsDeviceReady(uint8_t addr)
{
    return HAL_I2C_IsDeviceReady(&hi2c1, addr, 3, AT24C02_TIMEOUT_MS);
}


/**
 * @brief I2C 设备是否准备好
 */
AT24C02_Status_e AT24C02_IsDeviceReady()
{
    return _AT24C02_I2C_IsDeviceReady(AT24C02_8BIT_ADDRESS);
}

/**
 * @brief 写入单个字节
 */
AT24C02_Status_e AT24C02_WriteByte(uint8_t addr, uint8_t data)
{
    AT24C02_Status_e result = AT24C02_ERROR;

    uint8_t input_data[] = {addr, data};
    result = _AT24C02_I2C_Master_Transmit(input_data, sizeof(input_data));
    return result;
}

/**
 * @brief 读取单个字节
 */
AT24C02_Status_e AT24C02_ReadByte(uint8_t addr, uint8_t *data)
{
    AT24C02_Status_e result = AT24C02_ERROR;

    result =_AT24C02_I2C_Mem_Read(addr, data, 1);
    return result;
}

/**
 * @brief 按页写入
 */
AT24C02_Status_e AT24C02_WritePage(uint8_t addr, uint8_t *data, uint8_t len)
{
    AT24C02_Status_e result = AT24C02_ERROR;

    // 防止回卷覆盖:检查写入不会跨页边界
    if ((addr % AT24C02_PAGE_BYTE) + len > AT24C02_PAGE_BYTE) {return AT24C02_ERROR;}

    result = _AT24C02_I2C_Mem_Write(addr, data, len);

    // 等待
    HAL_Delay(10);

    return result;
}

/**
 * @brief 连续读
 */
AT24C02_Status_e AT24C02_ReadBytes(uint8_t addr, uint8_t *data, uint8_t len)
{
    return _AT24C02_I2C_Mem_Read(addr, data, len);
}

在主函数中,调用的测试函数如下

/**
 * @brief AT24C02 测试
 */
void AT24C02_TEST()
{
    uint8_t data = 0;
    uint8_t i;
    uint8_t wbuf[8] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
    uint8_t rbuf[8] = {0};

    // 设备就绪
    while (AT24C02_IsDeviceReady() != AT24C02_OK)
    {
        printf("设备未就绪\r\n");
        HAL_Delay(1000);
    }
    printf("设备已就绪\r\n");

    // 单字节读写
    AT24C02_WriteByte(0x00, 0x5A);
    HAL_Delay(10);
    AT24C02_ReadByte(0x00, &data);
    printf("单字节: 写 0x5A, 读 0x%02X  %s\r\n", data, (data == 0x5A) ? "OK" : "FAIL");

    // 页写入与连续读
    AT24C02_WritePage(0x08, wbuf, 8);
    HAL_Delay(10);
    AT24C02_ReadBytes(0x08, rbuf, 8);
    printf("页写入: ");
    for (i = 0; i < 8; i++) printf("%02X ", rbuf[i]);
    printf("\r\n");
}

在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐