STM32 Driver for AT24C256 EEPROM via I2C

STM32 Driver for 24C256 EEPROM Read/Write Implementation

The 24C256 is a 256Kbit (32KB) I2C interface EEPROM. The following implementation is based on the STM32 HAL library, which includes initialization, single-byte read/write, page write, and multi-byte read functionalities.

1. Hardware Connection

STM32 Pin 24C256 Pin Description
SCL SCL I2C Clock Line
SDA SDA I2C Data Line
VCC VCC Power Supply (2.5-5.5V)
GND GND Ground
A0/A1/A2 GND Device Address Pins (When connected to GND, the address is 0xA0)

2. Software Implementation

1. Header File Definition (<span>24c256.h</span>)

#ifndef __24C256_H
#define __24C256_H
#include "stm32f1xx_hal.h"
/* Device address definition (A0/A1/A2 connected to GND) */
#define EEPROM_ADDR        0xA0
/* Page size (24C256 has 64 bytes per page) */
#define EEPROM_PAGE_SIZE   64
/* Total capacity (32768 bytes) */
#define EEPROM_TOTAL_SIZE  32768
/* I2C handle (must match hardware) */
extern I2C_HandleTypeDef hi2c1;
/* Function declarations */
HAL_StatusTypeDef EEPROM_WriteByte(uint16_t addr, uint8_t data);
HAL_StatusTypeDef EEPROM_ReadByte(uint16_t addr, uint8_t *data);
HAL_StatusTypeDef EEPROM_WritePage(uint16_t addr, uint8_t *pData, uint16_t len);
HAL_StatusTypeDef EEPROM_ReadBytes(uint16_t addr, uint8_t *pData, uint16_t len);
#endif

2. Driver Implementation (<span>24c256.c</span>)

#include "24c256.h"
/**
 * @brief  Wait for EEPROM write to complete
 */
static void EEPROM_WaitIdle(void) {
    uint8_t dummy = 0;
    while (HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, &dummy, 1, 100) != HAL_OK);
}
/**
 * @brief  Write a single byte
 * @param  addr: Storage address (0~32767)
 * @param  data: Data to be written
 * @retval HAL status
 */
HAL_StatusTypeDef EEPROM_WriteByte(uint16_t addr, uint8_t data) {
    HAL_StatusTypeDef status;
    uint8_t tx_buf[3];
    if (addr >= EEPROM_TOTAL_SIZE) return HAL_ERROR;
    /* High 8 bits address + Low 8 bits address + Data */
    tx_buf[0] = (addr >> 8) & 0xFF;
    tx_buf[1] = addr & 0xFF;
    tx_buf[2] = data;
    /* Send data */
    status = HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, tx_buf, 3, 100);
    if (status == HAL_OK) {
        EEPROM_WaitIdle(); // Wait for write to complete
    }
    return status;
}
/**
 * @brief  Read a single byte
 * @param  addr: Storage address
 * @param  data: Buffer for read data
 * @retval HAL status
 */
HAL_StatusTypeDef EEPROM_ReadByte(uint16_t addr, uint8_t *data) {
    HAL_StatusTypeDef status;
    uint8_t addr_buf[2];
    if (addr >= EEPROM_TOTAL_SIZE || data == NULL) return HAL_ERROR;
    /* Send read address */
    addr_buf[0] = (addr >> 8) & 0xFF;
    addr_buf[1] = addr & 0xFF;
    status = HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, addr_buf, 2, 100);
    if (status == HAL_OK) {
        /* Receive data */
        status = HAL_I2C_Master_Receive(&hi2c1, EEPROM_ADDR, data, 1, 100);
    }
    return status;
}
/**
 * @brief  Page write (up to 64 bytes)
 * @param  addr: Starting address
 * @param  pData: Data buffer
 * @param  len: Length to write
 * @retval HAL status
 */
HAL_StatusTypeDef EEPROM_WritePage(uint16_t addr, uint8_t *pData, uint16_t len) {
    HAL_StatusTypeDef status;
    uint16_t bytes_to_write;
    if (addr >= EEPROM_TOTAL_SIZE || pData == NULL || len == 0) return HAL_ERROR;
    /* Calculate remaining space in current page */
    bytes_to_write = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE);
    if (len > bytes_to_write) len = bytes_to_write;
    /* Send address + data */
    uint8_t tx_buf[EEPROM_PAGE_SIZE + 2];
    tx_buf[0] = (addr >> 8) & 0xFF;
    tx_buf[1] = addr & 0xFF;
    memcpy(&tx_buf[2], pData, len);
    status = HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, tx_buf, len + 2, 100);
    if (status == HAL_OK) {
        EEPROM_WaitIdle();
    }
    return status;
}
/**
 * @brief  Multi-byte read
 * @param  addr: Starting address
 * @param  pData: Data buffer
 * @param  len: Length to read
 * @retval HAL status
 */
HAL_StatusTypeDef EEPROM_ReadBytes(uint16_t addr, uint8_t *pData, uint16_t len) {
    HAL_StatusTypeDef status;
    uint8_t addr_buf[2];
    if (addr >= EEPROM_TOTAL_SIZE || pData == NULL || len == 0) return HAL_ERROR;
    /* Send read address */
    addr_buf[0] = (addr >> 8) & 0xFF;
    addr_buf[1] = addr & 0xFF;
    status = HAL_I2C_Master_Transmit(&hi2c1, EEPROM_ADDR, addr_buf, 2, 100);
    if (status == HAL_OK) {
        /* Receive multi-byte data */
        status = HAL_I2C_Master_Receive(&hi2c1, EEPROM_ADDR, pData, len, 100);
    }
    return status;
}

3. Usage Example (<span>main.c</span>)

#include "stm32f1xx_hal.h"
#include "24c256.h"
I2C_HandleTypeDef hi2c1; // Must be initialized in CubeMX
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_I2C1_Init(); // Initialize I2C (recommended clock frequency 400kHz)
    uint8_t write_data[] = "STM32 + 24C256 Test";
    uint8_t read_data[32] = {0};
    /* Page write example */
    EEPROM_WritePage(0x0000, write_data, sizeof(write_data));
    /* Multi-byte read example */
    EEPROM_ReadBytes(0x0000, read_data, sizeof(write_data));
    /* Single byte read/write example */
    EEPROM_WriteByte(0x1000, 0xAB);
    uint8_t byte_data;
    EEPROM_ReadByte(0x1000, &byte_data);
    while (1) {
        // Loop execution
    }
}

3. Key Considerations

  1. I2C Initialization: Ensure that the I2C peripheral clock is enabled and configured with the correct clock frequency (recommended 400kHz fast mode).
  2. Address Range: The 24C256 address is 16 bits and must be sent in high and low bytes.
  3. Page Write Limitation: A single page write cannot cross pages; control the write length to not exceed the remaining space in the current page.
  4. Write Wait: EEPROM writing takes time (approximately 5ms), wait for completion through polling.
  5. Device Address: If A0/A1/A2 are connected to other levels, modify<span>EEPROM_ADDR</span> (for example, if A0 is connected to VCC, the address becomes 0xA2).

4. Debugging Tips

  • Use a logic analyzer to capture the I2C bus waveform and check if the address and data transmission are correct.
  • Ensure that the SDA/SCL pins are configured as open-drain outputs and connected with pull-up resistors (4.7kΩ).
  • Check if the HAL library I2C initialization is correct to avoid configuration errors when using DMA mode.

I am using STM32F103RCT6, but this driver can also be directly adapted to STM32F1/F4/H7 series, only requiring adjustments to the I2C handle and hardware initialization part.

Leave a Comment