feat(eeprom-ft24c256): драйвер EEPROM 24Cxx по I2C

Перенесён из KONOR_ds18b20/lib/eeprom; в OpticalTester лежала такая же копия.

Запись режется по границам страниц внутри библиотеки: микросхема
принимает до 64 байт за транзакцию, но при переходе через границу
страницы счётчик адреса заворачивается и затирает уже принятые байты.
Вызывающему коду об этом думать не нужно.
This commit is contained in:
2026-08-23 01:15:12 +03:00
parent 1248a6551a
commit 11bf8b6c11
3 changed files with 720 additions and 0 deletions

View File

@@ -0,0 +1,277 @@
/**
* @file ft24c256.h
* @brief Портируемый драйвер последовательной EEPROM FT24C256 и её семейства.
*
* Библиотека не привязана к микроконтроллеру: она не включает заголовки
* периферии, не обращается к регистрам и не пользуется прерываниями. Обмен
* идёт через таблицу обратных вызовов FT24C256_Io, которую заполняет порт
* платы (для этой сборки — @c src/eeprom.c поверх I2C1 STM32F103C8T6).
*
* FT24C256 — 32768 байт (256 кбит) с двухбайтовым адресом слова и страницей
* записи 64 байта. Запись выполняется постранично: микросхема принимает до
* 64 байт в одной транзакции, но только внутри страницы — при переходе через
* её границу счётчик адреса заворачивается на начало той же страницы и
* затирает уже принятые байты. Поэтому FT24C256_Write() сама режет запрос по
* границам страниц:
*
* @code
* запись 100 байт с адреса 0x0030
*
* 0x0000 0x0040 0x0080 0x00C0
* | страница 0 | страница 1 | страница 2 |
* +---------------+---------------+---------------+
* [ 16 ][ 64 ][ 20 ]
* 1-я 2-я 3-я транзакция
* @endcode
*
* После каждой транзакции кристалл уходит во внутренний цикл записи (до 5 мс)
* и не отвечает на шину. Готовность определяется опросом подтверждения
* (ACK polling): библиотека шлёт адресный байт без данных, пока микросхема не
* ответит. Порт, который не умеет передавать транзакцию нулевой длины,
* выставляет @c poll_retries в ноль — тогда используется выдержка
* @c write_ms.
*
* Кристалл выбирается тремя адресными выводами A0...A2, поэтому на одной шине
* живут до восьми микросхем с адресами 0x50...0x57. Задавая @c size и
* @c page_size, тем же кодом обслуживаются младшие члены семейства с
* двухбайтовым адресом: FT24C32 (4 КБ, страница 32), FT24C64 (8 КБ, 32),
* FT24C128 (16 КБ, 64), FT24C512 (64 КБ, 128). Микросхемы до FT24C16
* включительно адресуют слово одним байтом и этой библиотекой не
* поддерживаются.
*/
#ifndef FT24C256_H
#define FT24C256_H
#include <stdint.h>
/** Объём FT24C256 в байтах (256 кбит). */
#define FT24C256_SIZE 32768UL
/** Размер страницы записи FT24C256 в байтах. */
#define FT24C256_PAGE_SIZE 64U
/** Предельная страница семейства (FT24C512); задаёт размер буфера транзакции. */
#define FT24C256_PAGE_MAX 128U
/** Базовый адрес на шине при A0 = A1 = A2 = 0. */
#define FT24C256_BASE_ADDRESS 0x50U
/** Число микросхем семейства на одной шине. */
#define FT24C256_MAX_DEVICES 8U
/** Длительность внутреннего цикла записи по даташиту, мс. */
#define FT24C256_WRITE_MS 5U
/** Число опросов подтверждения после записи по умолчанию. */
#define FT24C256_POLL_RETRIES 20U
/** Длина адреса слова в байтах: FT24C32 и старше адресуют двумя байтами. */
#define FT24C256_ADDRESS_SIZE 2U
/**
* @brief Доступ библиотеки к шине I2C.
*
* Обязательны @c write и @c write_read. Адрес в вызовах — семибитный, без
* бита направления: сдвиг и добавление бита выполняет порт.
*/
typedef struct {
/**
* Передаёт транзакцию «старт, адрес, данные, стоп».
* Вызов с @c size, равным нулю, передаёт один адресный байт и применяется
* для опроса готовности; 1 — микросхема ответила подтверждением.
*/
uint8_t (*write)(void *context, uint8_t address, const uint8_t *data,
uint16_t size);
/**
* Передаёт @c tx_size байт, затем без освобождения шины (повторный старт)
* читает @c rx_size байт; 1 — обмен завершён подтверждениями.
*/
uint8_t (*write_read)(void *context, uint8_t address, const uint8_t *tx,
uint16_t tx_size, uint8_t *rx, uint16_t rx_size);
/** Выдержка в миллисекундах; обязателен, если @c poll_retries равен нулю. */
void (*delay_ms)(void *context, uint32_t milliseconds);
/** Управление выводом WP: 1 — запись запрещена. Допускает значение 0. */
void (*set_write_protect)(void *context, uint8_t enabled);
void *context; /**< Контекст порта, передаётся вызовам без изменений. */
} FT24C256_Io;
/**
* @brief Параметры кристалла.
*
* Нулевые поля заменяются значениями по умолчанию вызовом
* FT24C256_ConfigDefault() или самой FT24C256_Init().
*/
typedef struct {
uint32_t size; /**< Объём в байтах; 0 — @ref FT24C256_SIZE. */
uint16_t page_size; /**< Страница записи; 0 — @ref FT24C256_PAGE_SIZE. */
uint8_t address; /**< Семибитный адрес; 0 — @ref FT24C256_BASE_ADDRESS. */
uint8_t write_ms; /**< Выдержка цикла записи; 0 — @ref FT24C256_WRITE_MS. */
uint8_t poll_retries; /**< Опросов готовности; 0 — только выдержка. */
} FT24C256_Config;
/**
* @brief Состояние кристалла: настройки, обратные вызовы и счётчики обмена.
*/
typedef struct {
FT24C256_Io io; /**< Обратные вызовы порта. */
FT24C256_Config config; /**< Параметры кристалла. */
uint8_t ready; /**< 1, если FT24C256_Init() прошла успешно. */
uint32_t written_bytes; /**< Байтов, отправленных на запись. */
uint32_t read_bytes; /**< Байтов, полученных чтением. */
uint32_t write_errors; /**< Транзакций записи, оставшихся без ответа. */
uint32_t read_errors; /**< Транзакций чтения, оставшихся без ответа. */
} FT24C256;
/**
* @brief Заполняет параметры значениями FT24C256 с адресными выводами на GND.
*
* @param config Параметры, принадлежащие вызывающему коду.
*/
void FT24C256_ConfigDefault(FT24C256_Config *config);
/**
* @brief Готовит кристалл к работе.
*
* Обмена с шиной не выполняет: наличие микросхемы проверяется отдельно
* вызовом FT24C256_IsPresent().
*
* @param device Состояние, принадлежащее вызывающему коду.
* @param io Обратные вызовы порта; копируются внутрь состояния.
* @param config Параметры либо 0 для значений по умолчанию.
* @return 1 при успешной настройке, 0 при неполных аргументах.
*/
uint8_t FT24C256_Init(FT24C256 *device, const FT24C256_Io *io,
const FT24C256_Config *config);
/**
* @brief Проверяет отклик микросхемы на её адрес.
*
* @param device Состояние кристалла.
* @return 1, если получено подтверждение, иначе 0.
*/
uint8_t FT24C256_IsPresent(FT24C256 *device);
/**
* @brief Ожидает завершения внутреннего цикла записи.
*
* При ненулевом @c poll_retries шлёт адресные байты, пока микросхема не
* ответит; иначе выдерживает паузу @c write_ms.
*
* @param device Состояние кристалла.
* @return 1, если микросхема готова, иначе 0.
*/
uint8_t FT24C256_WaitReady(FT24C256 *device);
/**
* @brief Читает произвольное число байтов подряд.
*
* Чтение границами страниц не ограничено: счётчик адреса проходит весь
* массив, поэтому запрос выполняется одной транзакцией.
*
* @param device Состояние кристалла.
* @param address Адрес первого байта.
* @param buffer Приёмник данных.
* @param size Число байтов.
* @return 1 при успехе; 0 при выходе за границы массива или отказе шины.
*/
uint8_t FT24C256_Read(FT24C256 *device, uint32_t address, void *buffer,
uint16_t size);
/**
* @brief Записывает произвольное число байтов, разрезая запрос по страницам.
*
* После каждой страницы выполняется ожидание готовности, поэтому вызов
* блокирующий: запись всего массива занимает около 2.6 с.
*
* @param device Состояние кристалла.
* @param address Адрес первого байта.
* @param data Записываемые данные.
* @param size Число байтов.
* @return 1 при успехе; 0 при выходе за границы массива или отказе шины.
*/
uint8_t FT24C256_Write(FT24C256 *device, uint32_t address, const void *data,
uint16_t size);
/**
* @brief Записывает только те страницы, содержимое которых отличается.
*
* Ресурс кристалла — миллион циклов записи на страницу, поэтому сохранение
* настроек, которые меняются редко, выгоднее вести этой функцией: неизменные
* страницы не переписываются.
*
* @param device Состояние кристалла.
* @param address Адрес первого байта.
* @param data Записываемые данные.
* @param size Число байтов.
* @return 1 при успехе, иначе 0.
*/
uint8_t FT24C256_Update(FT24C256 *device, uint32_t address, const void *data,
uint16_t size);
/**
* @brief Читает один байт.
*
* @param device Состояние кристалла.
* @param address Адрес байта.
* @param value Приёмник значения.
* @return 1 при успехе, иначе 0.
*/
uint8_t FT24C256_ReadByte(FT24C256 *device, uint32_t address, uint8_t *value);
/**
* @brief Записывает один байт.
*
* @param device Состояние кристалла.
* @param address Адрес байта.
* @param value Записываемое значение.
* @return 1 при успехе, иначе 0.
*/
uint8_t FT24C256_WriteByte(FT24C256 *device, uint32_t address, uint8_t value);
/**
* @brief Заполняет область одинаковым значением.
*
* @param device Состояние кристалла.
* @param address Адрес первого байта.
* @param value Записываемое значение.
* @param size Число байтов.
* @return 1 при успехе, иначе 0.
*/
uint8_t FT24C256_Fill(FT24C256 *device, uint32_t address, uint8_t value,
uint32_t size);
/**
* @brief Заполняет весь массив значением 0xFF.
*
* @param device Состояние кристалла.
* @return 1 при успехе, иначе 0.
*/
uint8_t FT24C256_Erase(FT24C256 *device);
/**
* @brief Включает или снимает аппаратную защиту записи выводом WP.
*
* @param device Состояние кристалла.
* @param enabled 1 — запись запрещена, 0 — разрешена.
* @return 1, если порт управляет выводом WP, иначе 0.
*/
uint8_t FT24C256_Protect(FT24C256 *device, uint8_t enabled);
/**
* @brief Возвращает объём кристалла.
*
* @param device Состояние кристалла.
* @return Число байтов; 0 для ненастроенного состояния.
*/
uint32_t FT24C256_Size(const FT24C256 *device);
/**
* @brief Возвращает адрес кристалла на шине для указанных выводов A0...A2.
*
* @param index Значение адресных выводов, 0...7.
* @return Семибитный адрес 0x50...0x57.
*/
uint8_t FT24C256_AddressOf(uint8_t index);
#endif /* FT24C256_H */