Перенесён из репозитория protocan-transport, который подключался сабмодулем в CAN_to_RS485. Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232, USB CDC), разбор 29-битного идентификатора, общее адресное пространство регистров и каталог с подпиской на поток значений для SETGUI. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте. Хостовые тесты test_transport и test_gui проходят.
114 lines
4.0 KiB
C
114 lines
4.0 KiB
C
/**
|
||
* @file pcan_frame.h
|
||
* @brief Транспортный кадр и потоковый разборщик.
|
||
*
|
||
* AA 55 | LEN | SEQ | FLAGS | ID0 ID1 ID2 ID3 | DATA[0..8] | CRC_L CRC_H
|
||
*
|
||
* LEN = 6 + DLC (длина участка SEQ..DATA), диапазон 6..14
|
||
* CRC = CRC-16/CCITT-FALSE по байтам LEN..DATA, little-endian
|
||
*
|
||
* Кадр самосинхронизирующийся: при мусоре в линии разборщик сдвигается
|
||
* на байт и продолжает искать сигнатуру, теряя не больше одного кадра.
|
||
*
|
||
* Формат не зависит ни от платформы, ни от физического уровня: он одинаково
|
||
* ложится на RS485, RS232, USB CDC и любой байтовый поток.
|
||
*/
|
||
#ifndef PCAN_FRAME_H
|
||
#define PCAN_FRAME_H
|
||
|
||
#include <stdbool.h>
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
|
||
#include "pcan_config.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#define PCAN_SOF0 0xAAU
|
||
#define PCAN_SOF1 0x55U
|
||
|
||
/** Расширенный (29-битный) идентификатор. */
|
||
#define PCAN_FLAG_IDE 0x01U
|
||
/** Remote frame. */
|
||
#define PCAN_FLAG_RTR 0x02U
|
||
/** 0: кадр пришёл из CAN; 1: кадр надо передать в CAN. */
|
||
#define PCAN_FLAG_DIR 0x04U
|
||
/** Служебный кадр моста (диагностика), не относится к трафику CAN. */
|
||
#define PCAN_FLAG_ERR 0x08U
|
||
|
||
#define PCAN_LEN_MIN 6U
|
||
#define PCAN_LEN_MAX (PCAN_LEN_MIN + PCAN_DATA_MAX)
|
||
/** SOF(2) + LEN(1) + LEN_MAX + CRC(2). */
|
||
#define PCAN_FRAME_MAX (PCAN_LEN_MAX + 5U)
|
||
|
||
/** Разобранный кадр. */
|
||
typedef struct {
|
||
uint8_t seq;
|
||
uint8_t flags;
|
||
uint32_t id; /**< 29-битный идентификатор */
|
||
uint8_t dlc;
|
||
uint8_t data[PCAN_DATA_MAX];
|
||
} pcan_frame_t;
|
||
|
||
/**
|
||
* @brief Сериализует кадр.
|
||
* @param out буфер приёмника;
|
||
* @param out_size его размер, нужно не меньше PCAN_FRAME_MAX;
|
||
* @return число записанных байт либо 0, если буфер мал.
|
||
*/
|
||
size_t pcan_frame_encode(const pcan_frame_t *frame, uint8_t *out, size_t out_size);
|
||
|
||
/* --- Потоковый разборщик -------------------------------------------------- */
|
||
|
||
typedef enum {
|
||
PCAN_ST_SOF0 = 0,
|
||
PCAN_ST_SOF1,
|
||
PCAN_ST_LEN,
|
||
PCAN_ST_BODY,
|
||
PCAN_ST_CRC_L,
|
||
PCAN_ST_CRC_H
|
||
} pcan_parse_state_t;
|
||
|
||
/** Счётчики разборщика: полезны для диагностики линии. */
|
||
typedef struct {
|
||
uint32_t frames; /**< успешно разобрано кадров */
|
||
uint32_t crc_errors; /**< кадров отброшено по CRC */
|
||
uint32_t bad_len; /**< LEN вне допустимого диапазона */
|
||
uint32_t stray_bytes; /**< байт отброшено вне кадров */
|
||
} pcan_parse_stats_t;
|
||
|
||
typedef struct {
|
||
pcan_parse_state_t state;
|
||
uint8_t len;
|
||
uint8_t idx;
|
||
uint8_t body[PCAN_LEN_MAX + 1U]; /**< LEN + участок SEQ..DATA */
|
||
uint16_t crc_rx;
|
||
pcan_parse_stats_t stats;
|
||
} pcan_parser_t;
|
||
|
||
void pcan_parser_init(pcan_parser_t *p);
|
||
|
||
/**
|
||
* @brief Скармливает разборщику один байт.
|
||
* @return true, если кадр собран целиком и записан в out.
|
||
*/
|
||
bool pcan_parser_push(pcan_parser_t *p, uint8_t byte, pcan_frame_t *out);
|
||
|
||
/** Колбэк на готовый кадр. */
|
||
typedef void (*pcan_frame_cb_t)(const pcan_frame_t *frame, void *user);
|
||
|
||
/**
|
||
* @brief Скармливает разборщику блок байт, вызывая cb на каждый кадр.
|
||
* @return число разобранных кадров.
|
||
*/
|
||
size_t pcan_parser_feed(pcan_parser_t *p, const uint8_t *data, size_t len,
|
||
pcan_frame_cb_t cb, void *user);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* PCAN_FRAME_H */
|