Files
templates/c/protocan-transport/include/pcan_link.h
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

98 lines
3.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file pcan_link.h
* @brief Экземпляр канала связи поверх произвольного байтового потока.
*
* Библиотека не знает ни про UART, ни про DMA, ни про ОС: платформа
* отдаёт две функции (записать байты, узнать свободное место) и сама
* решает, как их реализовать - опросом, по прерыванию или через DMA.
*
* Состояния в глобальных переменных нет, поэтому в одной прошивке
* поднимается сколько угодно каналов: полевой RS485, отдельный UART
* для GUI, USB CDC - каждый со своим pcan_link_t.
*
* Пример:
* @code
* static uint8_t txbuf[512];
* pcan_link_t link;
* pcan_link_init(&link, &io, on_frame, NULL);
* pcan_link_send(&link, &frame); // из фонового кода
* pcan_link_feed(&link, chunk, n); // из порта, когда пришли байты
* @endcode
*/
#ifndef PCAN_LINK_H
#define PCAN_LINK_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "pcan_config.h"
#include "pcan_frame.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Интерфейс, который реализует платформа.
*
* write() обязана быть неблокирующей и принимать блок целиком либо
* не принимать вовсе - частично отданный кадр сломал бы приёмник.
*/
typedef struct {
/** @return число принятых байт: len (принято) либо 0 (не влезло). */
size_t (*write)(void *ctx, const uint8_t *data, size_t len);
/** Свободное место в очереди передачи, байт. */
size_t (*tx_space)(void *ctx);
void *ctx;
} pcan_io_t;
typedef struct {
uint32_t tx_frames;
uint32_t tx_dropped; /**< кадров не влезло в очередь передачи */
} pcan_link_tx_stats_t;
typedef struct {
pcan_io_t io;
pcan_parser_t parser;
pcan_frame_cb_t on_frame;
void *user;
uint8_t tx_seq;
pcan_link_tx_stats_t tx;
} pcan_link_t;
/**
* @brief Инициализирует канал.
* @param io интерфейс платформы, копируется внутрь;
* @param on_frame вызывается на каждый принятый кадр, может быть NULL;
* @param user произвольный контекст для on_frame.
*/
bool pcan_link_init(pcan_link_t *link, const pcan_io_t *io,
pcan_frame_cb_t on_frame, void *user);
/**
* @brief Отправляет кадр, проставив в него очередной SEQ.
* @return false, если в очереди передачи не хватило места.
*/
bool pcan_link_send(pcan_link_t *link, pcan_frame_t *frame);
/** Как pcan_link_send(), но SEQ берётся из кадра. */
bool pcan_link_send_raw(pcan_link_t *link, const pcan_frame_t *frame);
/** Помещается ли ещё хотя бы один кадр максимального размера. */
bool pcan_link_can_send(const pcan_link_t *link);
/** Скармливает каналу принятые из линии байты. @return число кадров. */
size_t pcan_link_feed(pcan_link_t *link, const uint8_t *data, size_t len);
static inline const pcan_parse_stats_t *pcan_link_rx_stats(const pcan_link_t *link)
{
return &link->parser.stats;
}
#ifdef __cplusplus
}
#endif
#endif /* PCAN_LINK_H */