Перенесён из репозитория protocan-transport, который подключался сабмодулем в CAN_to_RS485. Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232, USB CDC), разбор 29-битного идентификатора, общее адресное пространство регистров и каталог с подпиской на поток значений для SETGUI. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте. Хостовые тесты test_transport и test_gui проходят.
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.
Общее адресное пространство
Плоское пространство 16-битных регистров 0x0000..0xFFFF, собранное из
регионов. Регион ссылается либо на массив в памяти, либо на пару колбэков —
так в карту попадают и переменные, и вычисляемые значения, и регистры
периферии. Подробности — docs/GAS.md.
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 };
Использование
#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 проставится сам */
Для своей платформы достаточно реализовать две функции:
size_t my_write(void *ctx, const uint8_t *data, size_t len); /* всё-или-ничего */
size_t my_space(void *ctx);
Сборка
Библиотека — шесть файлов в src/ и заголовки в include/. Добавьте их
в проект и укажите include/ в путях поиска. CMake для тестов и хостовых
сборок:
cmake -B build && cmake --build build && ctest --test-dir build
Либо напрямую:
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) и не зависит от порядка байт хоста.