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