H753_CPP_I2C_DS3231_RTC_01 1.0
STM32H753 DS3231 real-time clock over I2C, with an AT24C32 EEPROM marker that stops the clock being set twice, built on the STM32_DS3231 and STM32_AT24C32 libraries
Loading...
Searching...
No Matches
AT24C32.c
1/*
2 * AT24C32.c
3 *
4 * STM32 HAL driver for the AT24C32 I2C EEPROM. See AT24C32.h for the overview,
5 * error handling, wiring and thread-safety notes.
6 *
7 * Created on: Oct 7, 2026
8 * Author: johng
9 */
10
11#include "AT24C32.h"
12#include <stdbool.h>
13#include <string.h>
14
16#define DEVICE_TYPE_BASE 0x50U
17
18/* I2C error codes that a second attempt can fix (HAL_I2C_GetError()). */
19#define I2C_TRANSIENT_ERRORS (HAL_I2C_ERROR_BERR | HAL_I2C_ERROR_ARLO | HAL_I2C_ERROR_AF | \
20 HAL_I2C_ERROR_OVR | HAL_I2C_ERROR_TIMEOUT)
21
30static bool isTransient(I2C_HandleTypeDef *hi2c, HAL_StatusTypeDef status)
31{
32 if (status == HAL_BUSY || status == HAL_TIMEOUT) {
33 return true;
34 }
35 if (status == HAL_ERROR) {
36 return (HAL_I2C_GetError(hi2c) & I2C_TRANSIENT_ERRORS) != 0U;
37 }
38 return false;
39}
40
56static AT24C32_Status_t transfer(AT24C32_t *dev, bool write, uint16_t address, uint8_t *buffer, uint16_t length)
57{
58 uint8_t attempt;
59
60 for (attempt = 1U; attempt <= dev->maxAttempts; attempt++) {
61 HAL_StatusTypeDef status;
62
63 if (write) {
64 status = HAL_I2C_Mem_Write(dev->hi2c, dev->deviceAddress, address, I2C_MEMADD_SIZE_16BIT,
65 buffer, length, AT24C32_I2C_TIMEOUT_MS);
66 } else {
67 status = HAL_I2C_Mem_Read(dev->hi2c, dev->deviceAddress, address, I2C_MEMADD_SIZE_16BIT,
68 buffer, length, AT24C32_I2C_TIMEOUT_MS);
69 }
70
71 if (status == HAL_OK) {
72 return AT24C32_OK;
73 }
74
75 dev->lastStatus = status;
76
77 if (!isTransient(dev->hi2c, status)) {
78 break;
79 }
80 HAL_Delay(1);
81 }
82
83 Error_Handler();
84 return AT24C32_ERR_BUS;
85}
86
98static AT24C32_Status_t waitWriteComplete(AT24C32_t *dev)
99{
100 uint32_t start = HAL_GetTick();
101 HAL_StatusTypeDef status;
102
103 do {
104 status = HAL_I2C_IsDeviceReady(dev->hi2c, dev->deviceAddress, 1U, AT24C32_I2C_TIMEOUT_MS);
105 if (status == HAL_OK) {
106 return AT24C32_OK;
107 }
108 } while ((HAL_GetTick() - start) <= AT24C32_WRITE_TIMEOUT_MS);
109
110 dev->lastStatus = status;
111 Error_Handler();
112 return AT24C32_ERR_BUS;
113}
114
131AT24C32_Status_t at24c32Init(AT24C32_t *dev, I2C_HandleTypeDef *hi2c, uint8_t addressPins, uint8_t maxAttempts)
132{
133 HAL_StatusTypeDef status;
134
135 if (dev == NULL || hi2c == NULL || addressPins > AT24C32_MAX_ADDRESS_PINS) {
136 return AT24C32_ERR_PARAM;
137 }
138
139 dev->hi2c = hi2c;
140 dev->deviceAddress = (uint16_t)((DEVICE_TYPE_BASE | addressPins) << 1);
141 dev->maxAttempts = (maxAttempts == 0U) ? (uint8_t)AT24C32_DEFAULT_MAX_ATTEMPTS : maxAttempts;
142 dev->lastStatus = HAL_OK;
143
144 status = HAL_I2C_IsDeviceReady(hi2c, dev->deviceAddress, dev->maxAttempts, AT24C32_I2C_TIMEOUT_MS);
145 if (status != HAL_OK) {
146 dev->lastStatus = status;
148 }
149 return AT24C32_OK;
150}
151
166AT24C32_Status_t at24c32Read(AT24C32_t *dev, uint16_t memAddress, uint8_t *data, uint16_t length)
167{
168 if (dev == NULL || data == NULL || length == 0U ||
169 memAddress >= AT24C32_SIZE_BYTES || length > (AT24C32_SIZE_BYTES - memAddress)) {
170 return AT24C32_ERR_PARAM;
171 }
172
173 return transfer(dev, false, memAddress, data, length);
174}
175
192AT24C32_Status_t at24c32Write(AT24C32_t *dev, uint16_t memAddress, const uint8_t *data, uint16_t length)
193{
194 if (dev == NULL || data == NULL || length == 0U ||
195 memAddress >= AT24C32_SIZE_BYTES || length > (AT24C32_SIZE_BYTES - memAddress)) {
196 return AT24C32_ERR_PARAM;
197 }
198
199 while (length > 0U) {
200 uint8_t page[AT24C32_PAGE_SIZE];
201 uint16_t room = (uint16_t)(AT24C32_PAGE_SIZE - (memAddress % AT24C32_PAGE_SIZE));
202 uint16_t chunk = (length < room) ? length : room;
203
204 memcpy(page, data, chunk); /* HAL takes a non-const pointer; a copy avoids casting const away */
205
206 if (transfer(dev, true, memAddress, page, chunk) != AT24C32_OK ||
207 waitWriteComplete(dev) != AT24C32_OK) {
208 return AT24C32_ERR_BUS;
209 }
210
211 memAddress = (uint16_t)(memAddress + chunk);
212 data += chunk;
213 length = (uint16_t)(length - chunk);
214 }
215 return AT24C32_OK;
216}
Types, constants and function prototypes of the STM32_AT24C32 driver.
#define AT24C32_MAX_ADDRESS_PINS
Definition AT24C32.h:85
#define AT24C32_SIZE_BYTES
Definition AT24C32.h:79
#define AT24C32_DEFAULT_MAX_ATTEMPTS
Definition AT24C32.h:88
AT24C32_Status_t at24c32Init(AT24C32_t *dev, I2C_HandleTypeDef *hi2c, uint8_t addressPins, uint8_t maxAttempts)
Set up a driver handle and check that an AT24C32 answers on the bus.
Definition AT24C32.c:131
AT24C32_Status_t at24c32Read(AT24C32_t *dev, uint16_t memAddress, uint8_t *data, uint16_t length)
Read bytes from the memory.
Definition AT24C32.c:166
#define AT24C32_WRITE_TIMEOUT_MS
Definition AT24C32.h:97
#define AT24C32_I2C_TIMEOUT_MS
Definition AT24C32.h:91
#define AT24C32_PAGE_SIZE
Definition AT24C32.h:82
AT24C32_Status_t
Definition AT24C32.h:100
@ AT24C32_ERR_NOT_FOUND
Definition AT24C32.h:103
@ AT24C32_ERR_PARAM
Definition AT24C32.h:102
@ AT24C32_OK
Definition AT24C32.h:101
@ AT24C32_ERR_BUS
Definition AT24C32.h:104
AT24C32_Status_t at24c32Write(AT24C32_t *dev, uint16_t memAddress, const uint8_t *data, uint16_t length)
Write bytes to the memory.
Definition AT24C32.c:192
HAL_StatusTypeDef lastStatus
Definition AT24C32.h:112
I2C_HandleTypeDef * hi2c
Definition AT24C32.h:109
uint16_t deviceAddress
Definition AT24C32.h:110
uint8_t maxAttempts
Definition AT24C32.h:111