/** * @file ft24c256.c * @brief Постраничная запись, сплошное чтение и ожидание готовности FT24C256. * * Реализация не хранит буфера всего массива: единственная крупная переменная — * кадр одной транзакции записи на стеке, длиной адрес слова плюс страница * (@ref FT24C256_ADDRESS_SIZE + @ref FT24C256_PAGE_MAX байт). Времени * библиотека не измеряет: выдержку цикла записи даёт порт, а готовность * определяется откликом самой микросхемы. */ #include "ft24c256.h" /** Кадр записи: адрес слова и страница данных. */ #define FT24C256_FRAME_MAX (FT24C256_ADDRESS_SIZE + FT24C256_PAGE_MAX) /** Длина куска, которым FT24C256_Update() сверяет содержимое. */ #define FT24C256_COMPARE_MAX 32U /** * @brief Проверяет пригодность состояния к обмену. * * @param device Состояние кристалла. * @return 1, если состояние настроено и обратные вызовы на месте, иначе 0. */ static uint8_t ft24c256_valid(const FT24C256 *device) { if (device == 0) { return 0U; } if (device->ready == 0U) { return 0U; } return (uint8_t)((device->io.write != 0) && (device->io.write_read != 0)); } /** * @brief Проверяет, что область целиком помещается в массив. * * @param device Состояние кристалла. * @param address Адрес первого байта. * @param size Число байтов. * @return 1, если область допустима, иначе 0. */ static uint8_t ft24c256_in_range(const FT24C256 *device, uint32_t address, uint32_t size) { if (address >= device->config.size) { return 0U; } return (uint8_t)(size <= (device->config.size - address)); } /** * @brief Раскладывает адрес слова в два байта, старший первым. * * @param address Адрес байта в массиве. * @param frame Приёмник кадра длиной не менее @ref FT24C256_ADDRESS_SIZE. */ static void ft24c256_put_address(uint32_t address, uint8_t *frame) { frame[0] = (uint8_t)((address >> 8U) & 0xFFU); frame[1] = (uint8_t)(address & 0xFFU); } /** * @brief Выдерживает паузу, если порт умеет её отсчитывать. * * @param device Состояние кристалла. * @param milliseconds Длительность паузы. */ static void ft24c256_delay(FT24C256 *device, uint32_t milliseconds) { if (device->io.delay_ms != 0) { device->io.delay_ms(device->io.context, milliseconds); } } /** * @brief Записывает часть страницы одной транзакцией и ждёт готовности. * * @param device Состояние кристалла. * @param address Адрес первого байта; не пересекает границу страницы. * @param data Записываемые данные. * @param size Число байтов, не больше размера страницы. * @return 1 при успехе, иначе 0. */ static uint8_t ft24c256_write_page(FT24C256 *device, uint32_t address, const uint8_t *data, uint16_t size) { uint8_t frame[FT24C256_FRAME_MAX]; uint16_t index; if (size > FT24C256_PAGE_MAX) { return 0U; } ft24c256_put_address(address, frame); for (index = 0U; index < size; index++) { frame[FT24C256_ADDRESS_SIZE + index] = data[index]; } if (device->io.write(device->io.context, device->config.address, frame, (uint16_t)(FT24C256_ADDRESS_SIZE + size)) == 0U) { device->write_errors++; return 0U; } device->written_bytes += size; return FT24C256_WaitReady(device); } void FT24C256_ConfigDefault(FT24C256_Config *config) { if (config == 0) { return; } config->size = FT24C256_SIZE; config->page_size = FT24C256_PAGE_SIZE; config->address = FT24C256_BASE_ADDRESS; config->write_ms = FT24C256_WRITE_MS; config->poll_retries = FT24C256_POLL_RETRIES; } uint8_t FT24C256_Init(FT24C256 *device, const FT24C256_Io *io, const FT24C256_Config *config) { if ((device == 0) || (io == 0)) { return 0U; } if ((io->write == 0) || (io->write_read == 0)) { return 0U; } device->io = *io; if (config != 0) { device->config = *config; } else { FT24C256_ConfigDefault(&device->config); } if (device->config.size == 0U) { device->config.size = FT24C256_SIZE; } if (device->config.page_size == 0U) { device->config.page_size = FT24C256_PAGE_SIZE; } if (device->config.page_size > FT24C256_PAGE_MAX) { device->config.page_size = FT24C256_PAGE_MAX; } if (device->config.address == 0U) { device->config.address = FT24C256_BASE_ADDRESS; } if (device->config.write_ms == 0U) { device->config.write_ms = FT24C256_WRITE_MS; } /* Без опроса готовности единственная мера времени — выдержка порта. */ if ((device->config.poll_retries == 0U) && (device->io.delay_ms == 0)) { return 0U; } device->ready = 1U; device->written_bytes = 0U; device->read_bytes = 0U; device->write_errors = 0U; device->read_errors = 0U; return 1U; } uint8_t FT24C256_IsPresent(FT24C256 *device) { if (ft24c256_valid(device) == 0U) { return 0U; } return device->io.write(device->io.context, device->config.address, 0, 0U); } uint8_t FT24C256_WaitReady(FT24C256 *device) { uint8_t attempt; if (ft24c256_valid(device) == 0U) { return 0U; } if (device->config.poll_retries == 0U) { ft24c256_delay(device, device->config.write_ms); return 1U; } for (attempt = 0U; attempt < device->config.poll_retries; attempt++) { if (device->io.write(device->io.context, device->config.address, 0, 0U) != 0U) { return 1U; } ft24c256_delay(device, 1U); } device->write_errors++; return 0U; } uint8_t FT24C256_Read(FT24C256 *device, uint32_t address, void *buffer, uint16_t size) { uint8_t frame[FT24C256_ADDRESS_SIZE]; if ((ft24c256_valid(device) == 0U) || (buffer == 0)) { return 0U; } if (size == 0U) { return 1U; } if (ft24c256_in_range(device, address, size) == 0U) { return 0U; } ft24c256_put_address(address, frame); if (device->io.write_read(device->io.context, device->config.address, frame, FT24C256_ADDRESS_SIZE, (uint8_t *)buffer, size) == 0U) { device->read_errors++; return 0U; } device->read_bytes += size; return 1U; } uint8_t FT24C256_Write(FT24C256 *device, uint32_t address, const void *data, uint16_t size) { const uint8_t *source = (const uint8_t *)data; uint32_t position = address; uint16_t left = size; if ((ft24c256_valid(device) == 0U) || (data == 0)) { return 0U; } if (size == 0U) { return 1U; } if (ft24c256_in_range(device, address, size) == 0U) { return 0U; } while (left != 0U) { const uint32_t page = device->config.page_size; const uint32_t tail = page - (position % page); uint16_t chunk = (tail < left) ? (uint16_t)tail : left; if (ft24c256_write_page(device, position, source, chunk) == 0U) { return 0U; } position += chunk; source += chunk; left = (uint16_t)(left - chunk); } return 1U; } uint8_t FT24C256_Update(FT24C256 *device, uint32_t address, const void *data, uint16_t size) { const uint8_t *source = (const uint8_t *)data; uint32_t position = address; uint16_t left = size; if ((ft24c256_valid(device) == 0U) || (data == 0)) { return 0U; } if (size == 0U) { return 1U; } if (ft24c256_in_range(device, address, size) == 0U) { return 0U; } while (left != 0U) { uint8_t stored[FT24C256_COMPARE_MAX]; const uint16_t chunk = (left < FT24C256_COMPARE_MAX) ? left : (uint16_t)FT24C256_COMPARE_MAX; uint16_t index; uint8_t differs = 0U; if (FT24C256_Read(device, position, stored, chunk) == 0U) { return 0U; } for (index = 0U; index < chunk; index++) { if (stored[index] != source[index]) { differs = 1U; break; } } if ((differs != 0U) && (FT24C256_Write(device, position, source, chunk) == 0U)) { return 0U; } position += chunk; source += chunk; left = (uint16_t)(left - chunk); } return 1U; } uint8_t FT24C256_ReadByte(FT24C256 *device, uint32_t address, uint8_t *value) { if (value == 0) { return 0U; } return FT24C256_Read(device, address, value, 1U); } uint8_t FT24C256_WriteByte(FT24C256 *device, uint32_t address, uint8_t value) { return FT24C256_Write(device, address, &value, 1U); } uint8_t FT24C256_Fill(FT24C256 *device, uint32_t address, uint8_t value, uint32_t size) { uint8_t page[FT24C256_PAGE_MAX]; uint32_t position = address; uint32_t left = size; uint16_t index; if (ft24c256_valid(device) == 0U) { return 0U; } if (size == 0U) { return 1U; } if (ft24c256_in_range(device, address, size) == 0U) { return 0U; } for (index = 0U; index < device->config.page_size; index++) { page[index] = value; } while (left != 0U) { const uint32_t tail = device->config.page_size - (position % device->config.page_size); const uint32_t chunk = (tail < left) ? tail : left; if (ft24c256_write_page(device, position, page, (uint16_t)chunk) == 0U) { return 0U; } position += chunk; left -= chunk; } return 1U; } uint8_t FT24C256_Erase(FT24C256 *device) { if (ft24c256_valid(device) == 0U) { return 0U; } return FT24C256_Fill(device, 0U, 0xFFU, device->config.size); } uint8_t FT24C256_Protect(FT24C256 *device, uint8_t enabled) { if (device == 0) { return 0U; } if (device->io.set_write_protect == 0) { return 0U; } device->io.set_write_protect(device->io.context, (uint8_t)(enabled != 0U)); return 1U; } uint32_t FT24C256_Size(const FT24C256 *device) { if ((device == 0) || (device->ready == 0U)) { return 0U; } return device->config.size; } uint8_t FT24C256_AddressOf(uint8_t index) { return (uint8_t)(FT24C256_BASE_ADDRESS + (index & 0x7U)); }