Перенесён из репозитория protocan-transport, который подключался сабмодулем в CAN_to_RS485. Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232, USB CDC), разбор 29-битного идентификатора, общее адресное пространство регистров и каталог с подпиской на поток значений для SETGUI. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте. Хостовые тесты test_transport и test_gui проходят.
127 lines
6.0 KiB
Markdown
127 lines
6.0 KiB
Markdown
# 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) и не зависит от порядка
|
||
байт хоста.
|