120 lines
4.4 KiB
C
120 lines
4.4 KiB
C
/*
|
|
* Преобразование CAN-кадра в поток AA 55 и обратный побайтный разбор. LEN описывает участок
|
|
* SEQ..DATA, CRC16 покрывает LEN..DATA; частичный пакет остаётся в состоянии parser до
|
|
* следующего фрагмента.
|
|
*/
|
|
|
|
/**
|
|
* @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 */
|