# protocan-transport Переносимая библиотека транспортного уровня для протокола **ProtoCAN**: кадрирование поверх любого байтового потока (RS485, RS232, USB CDC), разбор идентификатора и общее адресное пространство регистров. Написана на C99, без динамической памяти, без ОС, без зависимостей от HAL и от конкретного микроконтроллера. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. ``` ваш код protocan-transport платформа ┌────────┐ ┌────────────────────┐ ┌──────────────┐ │ кадры │─────►│ pcan_link_send() │─────►│ io.write() │──► UART/DMA │ │◄─────│ on_frame() │◄─────│ pcan_link_feed() └────────┘ └────────────────────┘ └──────────────┘ pcan_gas_* pcan_id_* ``` ## Состав | Модуль | Назначение | |---|---| | `pcan_frame` | кадр `AA 55 … CRC16` и потоковый разборщик с ресинхронизацией | | `pcan_crc` | CRC-16/CCITT-FALSE, побитовый или табличный | | `pcan_link` | экземпляр канала: приём, передача, SEQ, счётчики | | `pcan_ring` | кольцевой буфер, отдаёт непрерывный участок для DMA | | `pcan_id` | упаковка и разбор 29-битного идентификатора ProtoCAN | | `pcan_gas` | общее адресное пространство: карта регионов, чтение/запись, мост к кадрам | | `ports/stm32f4` | готовый порт USART + DMA (пакетная передача, кольцевой приём) | ## Кадр ``` AA 55 | LEN | SEQ | FLAGS | ID0 ID1 ID2 ID3 | DATA[0..8] | CRC_L CRC_H ``` `LEN = 6 + DLC` (6..14), CRC-16/CCITT-FALSE по байтам `LEN..DATA`, little-endian. Подробности — [docs/FRAME.md](docs/FRAME.md). ## Общее адресное пространство Плоское пространство 16-битных регистров `0x0000..0xFFFF`, собранное из регионов. Регион ссылается либо на массив в памяти, либо на пару колбэков — так в карту попадают и переменные, и вычисляемые значения, и регистры периферии. Подробности — [docs/GAS.md](docs/GAS.md). ```c static uint16_t holding[8]; static const pcan_gas_region_t regions[] = { { 0x0000, 8, holding, NULL, NULL, 0, NULL, "holding" }, { 0xFF00, 4, NULL, diag_read, NULL, PCAN_GAS_RDONLY, NULL, "diag" }, }; static const pcan_gas_map_t map = { regions, 2 }; ``` ## Использование ```c #include "protocan_transport.h" static void on_frame(const pcan_frame_t *f, void *user) { /* ... */ } pcan_io_t io; pcan_uart_io(&uart, &io); /* или свой io */ pcan_link_t link; pcan_link_init(&link, &io, on_frame, NULL); pcan_frame_t f = { .flags = PCAN_FLAG_IDE, .id = 0x1234567, .dlc = 2 }; f.data[0] = 0xAA; f.data[1] = 0xBB; pcan_link_send(&link, &f); /* SEQ проставится сам */ ``` Для своей платформы достаточно реализовать две функции: ```c size_t my_write(void *ctx, const uint8_t *data, size_t len); /* всё-или-ничего */ size_t my_space(void *ctx); ``` ## Сборка Библиотека — шесть файлов в `src/` и заголовки в `include/`. Добавьте их в проект и укажите `include/` в путях поиска. CMake для тестов и хостовых сборок: ```bash cmake -B build && cmake --build build && ctest --test-dir build ``` Либо напрямую: ```bash clang -std=c99 -Wall -Wextra -Iinclude tests/test_transport.c src/pcan_*.c -o test && ./test ``` Порт `ports/stm32f4` в тесты не входит: ему нужен CMSIS-заголовок `stm32f4xx.h`, подключайте его в проект прошивки отдельно. ## Настройки Переопределяются через `-D` либо через свой `pcan_config_user.h` (с `-DPCAN_USE_USER_CONFIG`): | Макрос | По умолчанию | Смысл | |---|---|---| | `PCAN_DATA_MAX` | 8 | длина поля данных CAN | | `PCAN_CRC_TABLE` | 0 | 1 — таблица на 512 байт вместо побитового расчёта | | `PCAN_GAS_MAX_REGIONS` | 16 | предел числа регионов в карте | | `PCAN_BARRIER()` | барьер компилятора | для очередей, разделяемых с прерыванием | ## Ограничения - Очереди рассчитаны на схему «один писатель + один читатель». Если писателей несколько, оборачивайте вызовы своей блокировкой. - Размер кольцевого буфера обязан быть степенью двойки; `pcan_ring_init()` вернёт `false`, а не станет молча портить индексы. - На STM32F407 DMA не видит CCM RAM (`0x10000000`) — буферы держите в основном SRAM. - Порядок байт на линии фиксирован (little-endian) и не зависит от порядка байт хоста.