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
- I2C Initialization: Ensure that the I2C peripheral clock is enabled and configured with the correct clock frequency (recommended 400kHz fast mode).
- Address Range: The 24C256 address is 16 bits and must be sent in high and low bytes.
- Page Write Limitation: A single page write cannot cross pages; control the write length to not exceed the remaining space in the current page.
- Write Wait: EEPROM writing takes time (approximately 5ms), wait for completion through polling.
- 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.