Обновить can-sensor для SETCAN SETTINGS

This commit is contained in:
2026-08-26 20:47:59 +03:00
parent 085eb3c8bd
commit 1345766efb
3 changed files with 175 additions and 308 deletions

View File

@@ -1,27 +1,27 @@
# can-sensor
Передача 64-битных идентификаторов датчиков (ROM 1-Wire) по шине CAN.
Однокадровый транспорт SETCAN SETTINGS для привязки 64-битного ROM 1-Wire к
локации «номер сборки + позиция» по шине CAN.
Ядро на C99: не включает заголовки периферии, не обращается к регистрам,
не пользуется прерываниями. Обмен идёт через таблицу `CanSensor_Io`,
которую заполняет порт платы.
Сообщение состоит из двух классических CAN-кадров — 64-битный идентификатор
и преамбула вместе не помещаются в восемь байтов поля данных:
Сообщение помещается в один Extended CAN-кадр: локация находится в Body
29-битного ID, а ROM — в восьми байтах payload:
```
кадр 1 — преамбула, DLC = 2 | команда | позиция |
кадр 2 — идентификатор, DLC = 8| ROM 8 байт |
запрос 0x17FEZZYY: DLC=0 — GET, DLC=8 — WRITE/REPLACE/CLEAR
ответ 0x1FFEZZYY: DLC=8 — текущий ROM, DLC=1 — код ошибки
```
## Состав
| Файл | Что делает | Зависимости |
|---|---|---|
| `can_sensor.h`, `can_sensor.c` | сборка и разбор пары кадров, повторы передачи, счётчики обмена | `stdint.h` |
| `can_sensor.h`, `can_sensor.c` | сборка и разбор SETTINGS, повторы передачи, счётчики обмена | `stdint.h` |
Приём собирает сообщение сам: кадр ROM без преамбулы и кадр с неверной
длиной отбрасываются и учитываются в `dropped_frames`.
`ZZ` — номер сборки, `YY` — позиция. Нулевой ROM с DLC=8 очищает локацию.
## Что нужно от платформы
@@ -38,10 +38,10 @@ uint8_t receive(void *ctx, CanSensor_Frame *frame); /* 1 — кадр п
CanSensor link;
CanSensor_Io io = { .send = bxcan_send, .receive = bxcan_receive, .context = &board };
CanSensor_Config config;
CanSensor_ConfigDefault(&config); /* tx_id 0x200, кадр ROM 0x201, 3 попытки */
CanSensor_ConfigDefault(&config); /* TX 0x1FFE0000, RX 0x17FE0000 */
CanSensor_Init(&link, &io, &config);
CanSensor_SendId(&link, position, rom);
CanSensor_SendId(&link, position, assembly_serial, rom);
CanSensor_Message message;
if (CanSensor_Poll(&link, &message)) { /* принят идентификатор */ }

View File

@@ -1,72 +1,35 @@
/**
* @file can_sensor.c
* @brief Сборка, передача и разбор сообщений «преамбула + идентификатор».
*
* Реализация не хранит очередей и не пользуется временем: состояние приёма
* ограничено признаком принятой преамбулы, поэтому библиотека одинаково
* работает и в главном цикле, и в обработчике прерывания порта.
* @brief Однокадровый транспорт SETCAN SETTINGS для локаций DS18B20.
*/
#include "can_sensor.h"
/**
* @brief Возвращает идентификатор кадра данных для заданных настроек.
*
* @param config Настройки узла.
* @return Идентификатор кадра ROM при передаче.
*/
static uint32_t can_sensor_tx_data_id(const CanSensor_Config *config)
/** Возвращает 1, если все восемь байтов ROM равны нулю. */
static uint8_t can_sensor_rom_is_zero(const uint8_t *rom)
{
if (config->tx_data_id != 0U) {
return config->tx_data_id;
uint8_t index;
for (index = 0U; index < CAN_SENSOR_ID_SIZE; index++) {
if (rom[index] != 0U) {
return 0U;
}
return config->tx_id + CAN_SENSOR_DATA_ID_OFFSET;
}
return 1U;
}
/**
* @brief Возвращает ожидаемый идентификатор преамбулы при приёме.
*
* @param config Настройки узла.
* @return Идентификатор кадра преамбулы.
*/
static uint32_t can_sensor_rx_id(const CanSensor_Config *config)
{
if (config->rx_id != 0U) {
return config->rx_id;
}
return config->tx_id;
}
/**
* @brief Возвращает ожидаемый идентификатор кадра ROM при приёме.
*
* @param config Настройки узла.
* @return Идентификатор кадра идентификатора датчика.
*/
static uint32_t can_sensor_rx_data_id(const CanSensor_Config *config)
{
if (config->rx_data_id != 0U) {
return config->rx_data_id;
}
return can_sensor_rx_id(config) + CAN_SENSOR_DATA_ID_OFFSET;
}
/**
* @brief Передаёт один кадр с повторами при отказе контроллера.
*
* @param link Состояние узла.
* @param frame Передаваемый кадр.
* @return 1, если кадр принят контроллером, иначе 0.
*/
/** Передаёт уже собранный кадр с ограниченным числом повторов. */
static uint8_t can_sensor_send_frame(CanSensor *link, const CanSensor_Frame *frame)
{
uint8_t attempt;
for (attempt = 0U; attempt < link->config.retries; attempt++) {
if (link->io.send(link->io.context, frame) != 0U) {
link->sent_messages++;
return 1U;
}
}
link->send_errors++;
return 0U;
}
@@ -76,22 +39,17 @@ void CanSensor_ConfigDefault(CanSensor_Config *config)
return;
}
config->tx_id = CAN_SENSOR_DEFAULT_TX_ID;
config->tx_data_id = CAN_SENSOR_DEFAULT_TX_ID + CAN_SENSOR_DATA_ID_OFFSET;
config->rx_id = CAN_SENSOR_DEFAULT_TX_ID;
config->rx_data_id = CAN_SENSOR_DEFAULT_TX_ID + CAN_SENSOR_DATA_ID_OFFSET;
config->extended = 0U;
config->rx_id = CAN_SENSOR_DEFAULT_RX_ID;
config->extended = 1U;
config->retries = CAN_SENSOR_DEFAULT_RETRIES;
}
uint8_t CanSensor_Init(CanSensor *link, const CanSensor_Io *io,
const CanSensor_Config *config)
{
uint8_t index;
if ((link == 0) || (io == 0) || (io->send == 0)) {
return 0U;
}
link->io = *io;
if (config != 0) {
link->config = *config;
@@ -101,16 +59,15 @@ uint8_t CanSensor_Init(CanSensor *link, const CanSensor_Io *io,
if (link->config.tx_id == 0U) {
link->config.tx_id = CAN_SENSOR_DEFAULT_TX_ID;
}
if (link->config.rx_id == 0U) {
link->config.rx_id = CAN_SENSOR_DEFAULT_RX_ID;
}
link->config.tx_id &= CAN_SENSOR_HEADER_MASK;
link->config.rx_id &= CAN_SENSOR_HEADER_MASK;
link->config.extended = 1U;
if (link->config.retries == 0U) {
link->config.retries = CAN_SENSOR_DEFAULT_RETRIES;
}
link->rx.command = 0U;
link->rx.position = 0U;
for (index = 0U; index < CAN_SENSOR_ID_SIZE; index++) {
link->rx.id[index] = 0U;
}
link->rx_preamble = 0U;
link->sent_messages = 0U;
link->send_errors = 0U;
link->received_messages = 0U;
@@ -118,114 +75,114 @@ uint8_t CanSensor_Init(CanSensor *link, const CanSensor_Io *io,
return 1U;
}
uint8_t CanSensor_BuildPreamble(const CanSensor_Config *config, CanSensor_Frame *frame,
uint8_t command, uint16_t position)
uint8_t CanSensor_BuildFrame(const CanSensor_Config *config, CanSensor_Frame *frame,
uint8_t response, uint8_t position,
uint8_t assembly_serial, const uint8_t *data,
uint8_t length)
{
uint8_t index;
uint32_t base;
if ((config == 0) || (frame == 0)) {
if ((config == 0) || (frame == 0) || (length > CAN_SENSOR_MAX_DATA)
|| ((length != 0U) && (data == 0))) {
return 0U;
}
frame->id = config->tx_id;
frame->extended = config->extended;
frame->length = CAN_SENSOR_PREAMBLE_SIZE;
frame->data[0] = command;
/* Позиция занимает два байта в порядке от старшего к младшему. */
frame->data[1] = (uint8_t)((position >> 8U) & 0xFFU);
frame->data[2] = (uint8_t)(position & 0xFFU);
for (index = CAN_SENSOR_PREAMBLE_SIZE; index < CAN_SENSOR_MAX_DATA; index++) {
frame->data[index] = 0U;
base = (response != 0U) ? config->tx_id : config->rx_id;
frame->id = (base & CAN_SENSOR_HEADER_MASK)
| ((uint32_t)assembly_serial << 8U) | (uint32_t)position;
frame->extended = 1U;
frame->length = length;
for (index = 0U; index < CAN_SENSOR_MAX_DATA; index++) {
frame->data[index] = (index < length) ? data[index] : 0U;
}
return 1U;
}
uint8_t CanSensor_BuildIdFrame(const CanSensor_Config *config, CanSensor_Frame *frame,
const uint8_t *id)
{
uint8_t index;
if ((config == 0) || (frame == 0)) {
return 0U;
}
frame->id = can_sensor_tx_data_id(config);
frame->extended = config->extended;
frame->length = CAN_SENSOR_ID_SIZE;
for (index = 0U; index < CAN_SENSOR_ID_SIZE; index++) {
frame->data[index] = (id != 0) ? id[index] : 0U;
}
return 1U;
}
uint8_t CanSensor_Send(CanSensor *link, uint8_t command, uint16_t position,
const uint8_t *id)
uint8_t CanSensor_SendId(CanSensor *link, uint8_t position,
uint8_t assembly_serial, const uint8_t *id)
{
CanSensor_Frame frame;
if ((link == 0) || (link->io.send == 0)) {
if ((link == 0) || (id == 0) || (link->io.send == 0)) {
return 0U;
}
(void)CanSensor_BuildPreamble(&link->config, &frame, command, position);
if (can_sensor_send_frame(link, &frame) == 0U) {
if (CanSensor_BuildFrame(&link->config, &frame, 1U, position,
assembly_serial, id, CAN_SENSOR_ID_SIZE) == 0U) {
link->send_errors++;
return 0U;
}
(void)CanSensor_BuildIdFrame(&link->config, &frame, id);
if (can_sensor_send_frame(link, &frame) == 0U) {
/* Одиночная преамбула приёмником отбрасывается, позиция не изменится. */
link->send_errors++;
return 0U;
}
link->sent_messages++;
return 1U;
return can_sensor_send_frame(link, &frame);
}
uint8_t CanSensor_SendId(CanSensor *link, uint16_t position, const uint8_t *id)
uint8_t CanSensor_SendError(CanSensor *link, uint8_t position,
uint8_t assembly_serial, uint8_t result)
{
if (id == 0) {
CanSensor_Frame frame;
if ((link == 0) || (link->io.send == 0) || (result == CAN_SENSOR_RESULT_OK)) {
return 0U;
}
return CanSensor_Send(link, CAN_SENSOR_CMD_WRITE_POSITION, position, id);
if (CanSensor_BuildFrame(&link->config, &frame, 1U, position,
assembly_serial, &result, 1U) == 0U) {
link->send_errors++;
return 0U;
}
return can_sensor_send_frame(link, &frame);
}
uint8_t CanSensor_HandleFrame(CanSensor *link, const CanSensor_Frame *frame,
CanSensor_Message *out)
{
CanSensor_Message message;
uint32_t header;
uint8_t index;
if ((link == 0) || (frame == 0)) {
return 0U;
}
if ((frame->id == can_sensor_rx_id(&link->config))
&& (frame->extended == link->config.extended)) {
if (frame->length < CAN_SENSOR_PREAMBLE_SIZE) {
link->dropped_frames++;
if (frame->extended == 0U) {
return 0U;
}
link->rx.command = frame->data[0];
link->rx.position = (uint16_t)(((uint16_t)frame->data[1] << 8U)
| frame->data[2]);
link->rx_preamble = 1U;
header = frame->id & CAN_SENSOR_HEADER_MASK;
if (header == (link->config.rx_id & CAN_SENSOR_HEADER_MASK)) {
message.request = 1U;
} else if (header == (link->config.tx_id & CAN_SENSOR_HEADER_MASK)) {
message.request = 0U;
} else {
return 0U;
}
if ((frame->id != can_sensor_rx_data_id(&link->config))
|| (frame->extended != link->config.extended)) {
return 0U;
}
if ((link->rx_preamble == 0U) || (frame->length < CAN_SENSOR_ID_SIZE)) {
/* Идентификатор без преамбулы не говорит, в какую позицию его писать. */
link->rx_preamble = 0U;
link->dropped_frames++;
return 0U;
}
message.assembly_serial = (uint8_t)((frame->id >> 8U) & 0xFFU);
message.position = (uint8_t)(frame->id & 0xFFU);
message.result = CAN_SENSOR_RESULT_OK;
for (index = 0U; index < CAN_SENSOR_ID_SIZE; index++) {
link->rx.id[index] = frame->data[index];
message.id[index] = (index < frame->length) ? frame->data[index] : 0U;
}
link->rx_preamble = 0U;
if (message.request != 0U) {
if (frame->length == 0U) {
message.operation = CAN_SENSOR_OPERATION_GET;
} else if (frame->length == CAN_SENSOR_ID_SIZE) {
message.operation = (can_sensor_rom_is_zero(message.id) != 0U)
? CAN_SENSOR_OPERATION_CLEAR
: CAN_SENSOR_OPERATION_WRITE;
} else {
message.operation = CAN_SENSOR_OPERATION_INVALID;
message.result = CAN_SENSOR_RESULT_INVALID_DLC;
}
} else if (frame->length == CAN_SENSOR_ID_SIZE) {
message.operation = CAN_SENSOR_OPERATION_RESPONSE;
} else if (frame->length == 1U) {
message.operation = CAN_SENSOR_OPERATION_ERROR;
message.result = frame->data[0];
} else {
link->dropped_frames++;
return 0U;
}
link->received_messages++;
if (out != 0) {
*out = link->rx;
*out = message;
}
return 1U;
}

View File

@@ -1,30 +1,19 @@
/**
* @file can_sensor.h
* @brief Портируемая передача идентификаторов датчиков по шине CAN.
* @brief Портируемый транспорт SETTINGS для привязки DS18B20 к локации.
*
* Библиотека не привязана к микроконтроллеру: она не включает заголовки
* периферии, не обращается к регистрам и не пользуется прерываниями. Обмен
* идёт через таблицу обратных вызовов CanSensor_Io, которую заполняет порт
* платы (для этой сборки — @c src/can.c поверх bxCAN STM32F103C8T6).
*
* Сообщение состоит из двух кадров классического CAN, потому что 64-битный
* идентификатор датчика и преамбула вместе не помещаются в восемь байтов
* поля данных:
* Один Extended CAN-кадр содержит полную операцию. Поля локации находятся в
* Body идентификатора SETCAN, а восьмибайтовый ROM — в payload:
*
* @code
* кадр 1 — преамбула, DLC = 2 +---------+----------+
* | команда | позиция |
* +---------+----------+
* кадр 2 — идентификатор, DLC=8 +----------------------------+
* | ROM датчика, байты 0..7 |
* +----------------------------+
* Priority=1, DeviceType=0x7, DeviceID=0xF, MsgType=0xE (SETTINGS)
* Body[15:8] = номер сборки, Body[7:0] = позиция
* @endcode
*
* Преамбула несёт команду записи датчика в позицию: первый байт — код
* команды (@ref CAN_SENSOR_CMD_WRITE_POSITION), второй — номер позиции в
* таблице узла-приёмника. Кадр идентификатора передаётся сразу за преамбулой
* и без неё считается недействительным, поэтому приёмник не запишет ROM в
* позицию, которая ему не была назначена.
* Запрос ПМ использует Route=0 (0x17FEZZYY), ответ прибора — Route=1
* (0x1FFEZZYY). DLC=0 означает GET, DLC=8 с ненулевым ROM — SET/REPLACE,
* DLC=8 с нулевым ROM — CLEAR. Успешный ответ возвращает текущий ROM с DLC=8,
* ошибка — однобайтовый код с DLC=1.
*/
#ifndef CAN_SENSOR_H
@@ -32,186 +21,107 @@
#include <stdint.h>
/** Длина идентификатора датчика (ROM 1-Wire) в байтах. */
/** Длина ROM 1-Wire и максимального payload classic CAN. */
#define CAN_SENSOR_ID_SIZE 8U
/** Длина преамбулы: код команды (1 байт) и номер позиции (2 байта, u16 BE). */
#define CAN_SENSOR_PREAMBLE_SIZE 3U
/** Предельная длина поля данных классического кадра CAN. */
#define CAN_SENSOR_MAX_DATA 8U
/** Код команды «записать идентификатор датчика в позицию». */
#define CAN_SENSOR_CMD_WRITE_POSITION 0xA1U
/** Назначенный проекту тип сообщения SETCAN SETTINGS. */
#define CAN_SENSOR_MSGTYPE_SETTINGS 0xEU
/** Код команды «очистить позицию»; кадр идентификатора передаётся нулевым. */
#define CAN_SENSOR_CMD_CLEAR_POSITION 0xA2U
/** Маска полей SETCAN от Priority до MsgType; Body под маской не находится. */
#define CAN_SENSOR_HEADER_MASK 0x1FFF0000UL
#define CAN_SENSOR_BODY_MASK 0x0000FFFFUL
/** Смещение идентификатора кадра данных относительно кадра преамбулы. */
#define CAN_SENSOR_DATA_ID_OFFSET 1U
/** Базовые ID SETTINGS для выбранных DeviceType=0x7 и DeviceID=0xF. */
#define CAN_SENSOR_DEFAULT_TX_ID 0x1FFE0000UL
#define CAN_SENSOR_DEFAULT_RX_ID 0x17FE0000UL
/** Число попыток передачи одного кадра по умолчанию. */
/** Число попыток помещения кадра в почтовый ящик. */
#define CAN_SENSOR_DEFAULT_RETRIES 3U
/** Идентификатор кадра преамбулы по умолчанию (стандартный, 11 бит). */
#define CAN_SENSOR_DEFAULT_TX_ID 0x200U
/** Операции, полученные из направления, DLC и payload кадра. */
#define CAN_SENSOR_OPERATION_GET 0U
#define CAN_SENSOR_OPERATION_WRITE 1U
#define CAN_SENSOR_OPERATION_CLEAR 2U
#define CAN_SENSOR_OPERATION_RESPONSE 3U
#define CAN_SENSOR_OPERATION_ERROR 4U
#define CAN_SENSOR_OPERATION_INVALID 5U
/**
* @brief Кадр шины CAN в форме, не зависящей от контроллера.
*/
/** Коды результата SETTINGS, передаваемые ответом с DLC=1. */
#define CAN_SENSOR_RESULT_OK 0x00U
#define CAN_SENSOR_RESULT_INVALID_DLC 0x01U
#define CAN_SENSOR_RESULT_INVALID_ROM_CRC 0x02U
#define CAN_SENSOR_RESULT_NOT_FOUND 0x03U
#define CAN_SENSOR_RESULT_ALREADY_ASSIGNED 0x04U
#define CAN_SENSOR_RESULT_EEPROM_ERROR 0x05U
#define CAN_SENSOR_RESULT_INVALID_LOCATION 0x06U
#define CAN_SENSOR_RESULT_BUSY 0x07U
/** Кадр CAN в форме, не зависящей от периферийного контроллера. */
typedef struct {
uint32_t id; /**< Идентификатор кадра, 11 или 29 бит. */
uint8_t extended; /**< 1 — расширенный идентификатор. */
uint8_t length; /**< Число значащих байтов поля данных. */
uint8_t data[CAN_SENSOR_MAX_DATA]; /**< Поле данных кадра. */
uint32_t id; /**< Расширенный идентификатор, 29 бит. */
uint8_t extended; /**< Для SETTINGS всегда 1. */
uint8_t length; /**< DLC, 0..8. */
uint8_t data[CAN_SENSOR_MAX_DATA]; /**< Payload. */
} CanSensor_Frame;
/**
* @brief Принятое или собранное сообщение: преамбула и идентификатор датчика.
*/
/** Разобранная команда или ответ SETTINGS. */
typedef struct {
uint8_t command; /**< Код команды из преамбулы. */
uint16_t position; /**< Позиция записи из преамбулы. */
uint8_t id[CAN_SENSOR_ID_SIZE]; /**< Идентификатор датчика. */
uint8_t operation; /**< CAN_SENSOR_OPERATION_*. */
uint8_t request; /**< 1 — запрос Route=0, 0 — ответ Route=1. */
uint8_t assembly_serial; /**< Body[15:8], номер сборки Z. */
uint8_t position; /**< Body[7:0], позиция Y. */
uint8_t result; /**< Код ошибки для ERROR/INVALID. */
uint8_t id[CAN_SENSOR_ID_SIZE]; /**< ROM для WRITE/RESPONSE, нули для CLEAR. */
} CanSensor_Message;
/**
* @brief Доступ библиотеки к контроллеру CAN.
*
* Обязателен только @c send; при нулевом @c receive функция CanSensor_Poll()
* ничего не делает и узел работает только на передачу.
*/
/** Доступ переносимой части к CAN-контроллеру. */
typedef struct {
/** Ставит кадр в очередь передачи; 1 — кадр принят контроллером. */
uint8_t (*send)(void *context, const CanSensor_Frame *frame);
/** Забирает принятый кадр; 1 — кадр получен, 0 — очередь пуста. */
uint8_t (*receive)(void *context, CanSensor_Frame *frame);
void *context; /**< Контекст порта, передаётся вызовам без изменений. */
void *context;
} CanSensor_Io;
/**
* @brief Идентификаторы кадров и режим передачи.
*
* Нулевые поля заменяются значениями по умолчанию вызовом
* CanSensor_ConfigDefault() или самой CanSensor_Init().
*/
/** Базовые идентификаторы SETTINGS и режим транспорта. */
typedef struct {
uint32_t tx_id; /**< Идентификатор кадра преамбулы при передаче. */
uint32_t tx_data_id; /**< Идентификатор кадра ROM; 0 — @c tx_id + 1. */
uint32_t rx_id; /**< Ожидаемая преамбула при приёме; 0 — @c tx_id. */
uint32_t rx_data_id; /**< Ожидаемый кадр ROM; 0 — @c rx_id + 1. */
uint8_t extended; /**< 1 — расширенные идентификаторы 29 бит. */
uint8_t retries; /**< Попыток передачи кадра; 0 — значение по умолчанию. */
uint32_t tx_id; /**< База ответа 0x1FFE0000, Body должен быть нулевым. */
uint32_t rx_id; /**< База запроса 0x17FE0000, Body должен быть нулевым. */
uint8_t extended; /**< 1 — Extended CAN ID. */
uint8_t retries; /**< Число попыток передачи. */
} CanSensor_Config;
/**
* @brief Состояние узла: настройки, счётчики и сборка принимаемого сообщения.
*/
/** Состояние транспорта и диагностические счётчики. */
typedef struct {
CanSensor_Io io; /**< Обратные вызовы порта. */
CanSensor_Config config; /**< Идентификаторы кадров и режим передачи. */
CanSensor_Message rx; /**< Собираемое сообщение приёма. */
uint8_t rx_preamble; /**< 1 — преамбула принята, ожидается кадр ROM. */
uint32_t sent_messages; /**< Полностью переданные сообщения. */
uint32_t send_errors; /**< Сообщения, не ушедшие в шину. */
uint32_t received_messages; /**< Полностью принятые сообщения. */
uint32_t dropped_frames; /**< Кадры без преамбулы или с неверной длиной. */
CanSensor_Io io;
CanSensor_Config config;
uint32_t sent_messages;
uint32_t send_errors;
uint32_t received_messages;
uint32_t dropped_frames;
} CanSensor;
/**
* @brief Заполняет настройки значениями по умолчанию.
*
* Идентификатор преамбулы — @ref CAN_SENSOR_DEFAULT_TX_ID, кадр ROM идёт
* следующим идентификатором, приём настроен на те же значения.
*
* @param config Настройки, принадлежащие вызывающему коду.
*/
void CanSensor_ConfigDefault(CanSensor_Config *config);
/**
* @brief Готовит узел к работе.
*
* @param link Состояние узла, принадлежащее вызывающему коду.
* @param io Обратные вызовы порта; копируются внутрь состояния.
* @param config Настройки либо 0 для значений по умолчанию.
* @return 1 при успешной настройке, 0 при неполных аргументах.
*/
uint8_t CanSensor_Init(CanSensor *link, const CanSensor_Io *io,
const CanSensor_Config *config);
/**
* @brief Собирает кадр преамбулы.
*
* @param config Настройки узла.
* @param frame Кадр приёмника.
* @param command Код команды, например @ref CAN_SENSOR_CMD_WRITE_POSITION.
* @param position Номер позиции записи датчика.
* @return 1 при успешной сборке, иначе 0.
*/
uint8_t CanSensor_BuildPreamble(const CanSensor_Config *config, CanSensor_Frame *frame,
uint8_t command, uint16_t position);
/** Собирает SETTINGS-кадр на базе tx_id или rx_id. */
uint8_t CanSensor_BuildFrame(const CanSensor_Config *config, CanSensor_Frame *frame,
uint8_t response, uint8_t position,
uint8_t assembly_serial, const uint8_t *data,
uint8_t length);
/**
* @brief Собирает кадр идентификатора датчика.
*
* @param config Настройки узла.
* @param frame Кадр приёмника.
* @param id Идентификатор датчика длиной @ref CAN_SENSOR_ID_SIZE либо 0 для
* нулевого кадра команды очистки.
* @return 1 при успешной сборке, иначе 0.
*/
uint8_t CanSensor_BuildIdFrame(const CanSensor_Config *config, CanSensor_Frame *frame,
const uint8_t *id);
/** Передаёт успешный ответ с текущим ROM (DLC=8). */
uint8_t CanSensor_SendId(CanSensor *link, uint8_t position,
uint8_t assembly_serial, const uint8_t *id);
/**
* @brief Передаёт преамбулу и идентификатор датчика.
*
* Кадры уходят подряд; при отказе контроллера передача повторяется
* @c retries раз. Если преамбула ушла, а кадр ROM — нет, сообщение считается
* несостоявшимся: приёмник отбросит одиночную преамбулу.
*
* @param link Состояние узла.
* @param command Код команды преамбулы.
* @param position Номер позиции записи датчика.
* @param id Идентификатор датчика либо 0 для команды очистки позиции.
* @return 1, если оба кадра приняты контроллером, иначе 0.
*/
uint8_t CanSensor_Send(CanSensor *link, uint8_t command, uint16_t position,
const uint8_t *id);
/** Передаёт ошибку SETTINGS (DLC=1). */
uint8_t CanSensor_SendError(CanSensor *link, uint8_t position,
uint8_t assembly_serial, uint8_t result);
/**
* @brief Передаёт команду записи датчика в позицию.
*
* Краткая форма CanSensor_Send() с кодом @ref CAN_SENSOR_CMD_WRITE_POSITION.
*
* @param link Состояние узла.
* @param position Номер позиции записи датчика.
* @param id Идентификатор датчика длиной @ref CAN_SENSOR_ID_SIZE.
* @return 1, если оба кадра приняты контроллером, иначе 0.
*/
uint8_t CanSensor_SendId(CanSensor *link, uint16_t position, const uint8_t *id);
/**
* @brief Разбирает принятый кадр и собирает из пары кадров сообщение.
*
* Кадр идентификатора без предшествующей преамбулы отбрасывается, а новая
* преамбула заменяет незавершённую: сборка не требует таймера.
*
* @param link Состояние узла.
* @param frame Принятый кадр.
* @param out Приёмник готового сообщения либо 0.
* @return 1, если сообщение собрано полностью, иначе 0.
*/
uint8_t CanSensor_HandleFrame(CanSensor *link, const CanSensor_Frame *frame,
CanSensor_Message *out);
/**
* @brief Забирает кадры у порта и возвращает первое собранное сообщение.
*
* @param link Состояние узла.
* @param out Приёмник сообщения либо 0.
* @return 1, если сообщение собрано, иначе 0.
*/
uint8_t CanSensor_Poll(CanSensor *link, CanSensor_Message *out);
#endif /* CAN_SENSOR_H */