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()
|
|
* в ports/stm32-bxcan/protocan.c, поэтому обмен совместим с существующими
|
|
* устройствами.
|
|
*
|
|
* Запрос на чтение кодируется кадром GAS с DLC = 0: в исходном коде
|
|
* SETCAN такой кодировки нет, это расширение - см. docs/legacy/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 */
|