Files
templates/c/protocan-transport
Andrey Kruchinkin 3dc636e012 feat(protocan-transport): транспортный уровень ProtoCAN и каталог GUI
Перенесён из репозитория protocan-transport, который подключался
сабмодулем в CAN_to_RS485.

Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232,
USB CDC), разбор 29-битного идентификатора, общее адресное пространство
регистров и каталог с подпиской на поток значений для SETGUI. Состояние
живёт в структурах вызывающего, поэтому в одной прошивке поднимается
сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте.

Хостовые тесты test_transport и test_gui проходят.
2026-08-23 01:15:35 +03:00
..

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) и не зависит от порядка байт хоста.