104 lines
4.1 KiB
C
104 lines
4.1 KiB
C
/*
|
|
* Канал ProtoCAN поверх произвольного потока байтов. Контекст хранит parser и параметры
|
|
* отправки; физический ввод-вывод подключается обратными вызовами, а готовые CAN-кадры
|
|
* передаются обработчику приложения.
|
|
*/
|
|
|
|
/**
|
|
* @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 */
|