Перенесён из репозитория protocan-transport, который подключался сабмодулем в CAN_to_RS485. Кадрирование AA 55 с CRC16 поверх любого байтового потока (RS485, RS232, USB CDC), разбор 29-битного идентификатора, общее адресное пространство регистров и каталог с подпиской на поток значений для SETGUI. Состояние живёт в структурах вызывающего, поэтому в одной прошивке поднимается сколько угодно независимых каналов. Порт STM32F4 (USART + DMA) в комплекте. Хостовые тесты test_transport и test_gui проходят.
130 lines
5.8 KiB
C
130 lines
5.8 KiB
C
/**
|
||
* @file pcan_gas.h
|
||
* @brief Общее адресное пространство (General Address Space).
|
||
*
|
||
* Плоское пространство 16-битных регистров с адресом 0x0000..0xFFFF,
|
||
* собранное из регионов. Регион либо ссылается на массив в памяти,
|
||
* либо обслуживается колбэками - так в карту попадают и обычные
|
||
* переменные, и вычисляемые значения, и регистры периферии.
|
||
*
|
||
* Транспорт ProtoCAN (MsgType = 0b0011) кладёт в MsgBody адрес первого
|
||
* регистра, а в данные - до 4 регистров подряд, младшим байтом вперёд.
|
||
* Это ровно то, что делает PROTOCAN_SEND_GENERAL_ADDRESS_SPACE()
|
||
* в SETCAN/Src/protocan.c, поэтому обмен совместим с существующими
|
||
* устройствами.
|
||
*
|
||
* Запрос на чтение кодируется кадром GAS с DLC = 0: в исходном коде
|
||
* SETCAN такой кодировки нет, это расширение - см. docs/GAS.md.
|
||
*/
|
||
#ifndef PCAN_GAS_H
|
||
#define PCAN_GAS_H
|
||
|
||
#include <stdbool.h>
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
|
||
#include "pcan_config.h"
|
||
#include "pcan_frame.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/** Сколько регистров помещается в один кадр (8 байт / 2). */
|
||
#define PCAN_GAS_REGS_PER_FRAME 4U
|
||
|
||
typedef enum {
|
||
PCAN_GAS_OK = 0,
|
||
PCAN_GAS_NO_REG, /**< адрес не покрыт ни одним регионом */
|
||
PCAN_GAS_READ_ONLY, /**< запись в регион только для чтения */
|
||
PCAN_GAS_WRITE_ONLY,
|
||
PCAN_GAS_REJECTED /**< колбэк отверг значение */
|
||
} pcan_gas_status_t;
|
||
|
||
/** Регион только для чтения. */
|
||
#define PCAN_GAS_RDONLY 0x01U
|
||
/** Регион только для записи. */
|
||
#define PCAN_GAS_WRONLY 0x02U
|
||
|
||
struct pcan_gas_region;
|
||
|
||
typedef pcan_gas_status_t (*pcan_gas_read_fn)(const struct pcan_gas_region *region,
|
||
uint16_t offset, uint16_t *value);
|
||
typedef pcan_gas_status_t (*pcan_gas_write_fn)(const struct pcan_gas_region *region,
|
||
uint16_t offset, uint16_t value);
|
||
|
||
/**
|
||
* @brief Непрерывный участок адресного пространства.
|
||
*
|
||
* Если storage != NULL, чтение и запись идут прямо в массив.
|
||
* Иначе вызываются read/write.
|
||
*/
|
||
typedef struct pcan_gas_region {
|
||
uint16_t base; /**< адрес первого регистра */
|
||
uint16_t count; /**< число регистров */
|
||
uint16_t *storage; /**< массив либо NULL */
|
||
pcan_gas_read_fn read;
|
||
pcan_gas_write_fn write;
|
||
uint8_t flags;
|
||
void *user;
|
||
const char *name; /**< для отладки, может быть NULL */
|
||
} pcan_gas_region_t;
|
||
|
||
/** Карта: набор регионов. Регионы не должны перекрываться. */
|
||
typedef struct {
|
||
const pcan_gas_region_t *regions;
|
||
uint16_t count;
|
||
} pcan_gas_map_t;
|
||
|
||
/** @return true, если регионы отсортированы и не перекрываются. */
|
||
bool pcan_gas_map_validate(const pcan_gas_map_t *map);
|
||
|
||
const pcan_gas_region_t *pcan_gas_find(const pcan_gas_map_t *map, uint16_t addr);
|
||
|
||
pcan_gas_status_t pcan_gas_read(const pcan_gas_map_t *map, uint16_t addr,
|
||
uint16_t *value);
|
||
pcan_gas_status_t pcan_gas_write(const pcan_gas_map_t *map, uint16_t addr,
|
||
uint16_t value);
|
||
|
||
/**
|
||
* @brief Читает подряд идущие регистры.
|
||
* @return сколько регистров удалось прочитать, начиная с addr.
|
||
*
|
||
* Чтение прекращается на первом адресе, которого нет в карте,
|
||
* поэтому вызывающий всегда получает непрерывный блок.
|
||
*/
|
||
uint16_t pcan_gas_read_block(const pcan_gas_map_t *map, uint16_t addr,
|
||
uint16_t *out, uint16_t max);
|
||
|
||
uint16_t pcan_gas_write_block(const pcan_gas_map_t *map, uint16_t addr,
|
||
const uint16_t *in, uint16_t count);
|
||
|
||
/* --- Мост между картой и кадрами ProtoCAN ---------------------------------- */
|
||
|
||
/** Извлекает регистры из данных кадра GAS (младший байт первым). */
|
||
uint16_t pcan_gas_frame_to_regs(const pcan_frame_t *frame, uint16_t *out,
|
||
uint16_t max);
|
||
|
||
/** Заполняет кадр GAS: адрес в MsgBody, до 4 регистров в данных. */
|
||
void pcan_gas_regs_to_frame(pcan_frame_t *frame, uint32_t base_id,
|
||
uint16_t addr, const uint16_t *regs, uint16_t count);
|
||
|
||
/**
|
||
* @brief Обрабатывает входящий кадр GAS.
|
||
*
|
||
* DLC = 0 - запрос на чтение: в rsp кладётся до 4 регистров с адреса
|
||
* MsgBody, Route переключается на FROM_DEVICE.
|
||
* DLC > 0 - запись: регистры пишутся в карту, ответ не формируется.
|
||
*
|
||
* @param[out] rsp кадр ответа; заполняется, только если функция вернула true.
|
||
* @return true, если ответ нужно передать.
|
||
*/
|
||
bool pcan_gas_handle(const pcan_gas_map_t *map, const pcan_frame_t *req,
|
||
pcan_frame_t *rsp);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* PCAN_GAS_H */
|