Добавить блочное чтение EEPROM в прошивку 166

Перенести контракт FC03 из 167: до 120 слов за запрос, проверка границ окна и точная длина ответа с CRC. Использовать существующий буфер UART и разрешённые прерывания SPI. Проверить полный дамп из 137 блоков и сохранение прежних маршрутов Modbus и CAN.
This commit is contained in:
2026-10-16 22:50:25 +03:00
parent 7713ff7573
commit 345c81166e
3 changed files with 51 additions and 2 deletions
+27
View File
@@ -24,6 +24,14 @@ int ump_log_write(unsigned int address, unsigned int value) { (void)address; (vo
static void UMP_commands_write(unsigned int value) { command = value; } /* Сохраняет наблюдение штатной команды регистра 127. */
static void ump_log_rs(void) { ++command_events; } /* Проверяет сохранение прежней регистрации команды RS. */
#include "ump_can_protocol.h" /* Тестирует реальный адаптер CAN 166, сохранивший двенадцатисловный логгер v4. */
#include "include/balsam167_eeprom_block.h" /* Использует общий с 167 контракт FC03: окно 0x8000..0xBFFF, до 120 слов AT25256B за запрос. */
static unsigned int spi_reads, spi_address, spi_bytes, spi_blocked; /* Наблюдает число SPI-транзакций, словный адрес, длину в октетах и маску IRQ во время чтения. */
static void Seeprom_read(unsigned int address, unsigned int *data, unsigned int length) /* Имитирует только SPI-периферию с независимым узнаваемым содержимым всех 32 КиБ. */
{ /* Производственный обработчик, упаковка и CRC остаются настоящими. */
unsigned int index; /* Перебирает аппаратные 16-битные слова запрошенного блока. */
++spi_reads; spi_address = address; spi_bytes = length; spi_blocked |= irq_mask; /* Выявляет лишние SPI-чтения и запрет Timer2 IRQ внутри нового маршрута. */
for (index = 0U; index < length / 2U; ++index) data[index] = (address + index) ^ 0xA55AU; /* Эталон зависит от полного адреса, поэтому обнаруживает повтор блока и ошибку смещения. */
} /* Модель не записывает EEPROM и не подменяет поведение UART. */
/* PRODUCTION_MODBUS */
static void request(RS_DATA *rs, unsigned int address, unsigned int count) /* Формирует уже проверенный транспортом заголовок для настоящих обработчиков. */
{ /* Здесь не подменяется алгоритм чтения или упаковки ответа. */
@@ -35,6 +43,25 @@ int main(void) /* Проверяет перенос паспорта и сохр
{ /* Эталон задан независимо от макроса общего C-инициализатора. */
const unsigned int expected[12] = {1, 1, 0, 0, 2026, 0x0A10, 0x0834, 22, 0x3132, 0x3334, 0x6162, 0x6364}; /* Формат v1, версия 1.0.0, UTC 2026-10-16 08:52:22, Build ID 1234abcd. */
RS_DATA rs; unsigned int words[UMP_CAN_MAX_WORDS], error, i, before; /* Использует те же ёмкости ответа, что действующий служебный CAN. */
for (before = 0U; before < 16384U; before += 120U) { /* Полный дамп 32 КиБ должен занять 137 запросов вместо 16384 запросов PEEK. */
unsigned int count = 16384U - before < 120U ? 16384U - before : 120U; /* Последний блок содержит только оставшиеся 64 слова, не выходя за физическую границу. */
request(&rs, 0x8000U + before, count); ReceiveCommandModbus3(&rs); /* Вызывает настоящий новый маршрут FC03 по всему окну, включая последний блок. */
CHECK(sent == count * 2U + 5U && rs.buffer[1] == 3U && rs.buffer[2] == count * 2U); /* Ответ имеет точную длину и не содержит нулей после CRC. */
CHECK(spi_address == before && spi_bytes == count * 2U && !spi_blocked && !irq_calls); /* SPI получает правильные единицы адреса и длины при разрешённых прерываниях Timer2. */
CHECK(get_crc_16(0xFFFFU, rs.buffer, sent) == 0U); /* Проверяет настоящую CRC каждого полного кадра независимым проходом по всем октетам. */
for (i = 0U; i < count; ++i) CHECK(((rs.buffer[3U + i * 2U] << 8) | rs.buffer[4U + i * 2U]) == ((before + i) ^ 0xA55AU)); /* Сверяет каждое слово и порядок октетов после расширения UART-буфера на месте. */
} /* Все 16384 слова должны быть доступны без чтения соседней памяти. */
CHECK(spi_reads == 137U && reads == 0U && writes == 0U); /* Блочный маршрут не обращается к backend логгера и не выполняет записи. */
request(&rs, 0xBFFFU, 1U); ReceiveCommandModbus3(&rs); CHECK(sent == 7U && spi_address == 0x3FFFU && spi_bytes == 2U); /* Последнее слово EEPROM остаётся доступно отдельным запросом. */
before = spi_reads; /* Запоминает число разрешённых чтений до проверки всех ошибочных границ. */
request(&rs, 0xBFFFU, 2U); ReceiveCommandModbus3(&rs); CHECK(sent == 5U && rs.buffer[1] == 0x83U && rs.buffer[2] == 2U); /* Пересечение конца EEPROM возвращает исключение до обращения к SPI. */
request(&rs, 0x8000U, 0U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Нулевой блок не начинает зависающее аппаратное ожидание. */
request(&rs, 0x8000U, 121U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Максимум 120 слов соответствует контракту SETGUI и безопасной длине ответа 245 октетов. */
request(&rs, 0xC000U, 1U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Адрес сразу после окна не считается первым словом другой EEPROM. */
request(&rs, 0xFFFFU, 120U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Переполнение 16-битного адреса не обходит ограничение диапазона. */
request(&rs, 0x8000U, 0x8001U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Маркер M95M02 не принимается прошивкой с историческим 16-битным SPI-драйвером. */
request(&rs, 0x7FFFU, 1U); ReceiveCommandModbus3(&rs); CHECK(rs.buffer[2] == 2U); /* Адрес перед окном остаётся запрещённым в старой карте регистров. */
CHECK(spi_reads == before && get_crc_16(0xFFFFU, rs.buffer, sent) == 0U); /* Неверные запросы не читают SPI и получают корректное исключение с CRC. */
request(&rs, 0x1200U, 12U); ReceiveCommandModbus3(&rs); CHECK(sent == 29U && rs.buffer[1] == 3U && rs.buffer[2] == 24U); /* Полный паспорт отвечает стандартным FC03 без хвостовых нулей. */
for (i = 0; i < 12U; ++i) CHECK(((rs.buffer[3 + i * 2] << 8) | rs.buffer[4 + i * 2]) == expected[i]); /* Проверяет все поля, порядок символов и октетов независимо от производственной упаковки. */
CHECK(get_crc_16(0xFFFFU, rs.buffer, sent) == 0U && reads == 0U && irq_calls == 0U); /* Паспорт защищён настоящей CRC и не обращается к логгеру или маске IRQ. */