/** * @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 #include #include #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 */