Files
templates/c/can-sensor/can_sensor.h
Andrey Kruchinkin 13f2c8fe64 feat(can-sensor): передача ROM датчиков парой CAN-кадров
Перенесён из KONOR_ds18b20/lib/can.

64-битный идентификатор датчика и преамбула не помещаются в восемь байт
поля данных, поэтому сообщение идёт двумя кадрами. Сборку на приёме
библиотека делает сама: кадр ROM без преамбулы и кадр неверной длины
отбрасываются и учитываются в dropped_frames.
2026-08-23 01:15:12 +03:00

218 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file can_sensor.h
* @brief Портируемая передача идентификаторов датчиков по шине CAN.
*
* Библиотека не привязана к микроконтроллеру: она не включает заголовки
* периферии, не обращается к регистрам и не пользуется прерываниями. Обмен
* идёт через таблицу обратных вызовов CanSensor_Io, которую заполняет порт
* платы (для этой сборки — @c src/can.c поверх bxCAN STM32F103C8T6).
*
* Сообщение состоит из двух кадров классического CAN, потому что 64-битный
* идентификатор датчика и преамбула вместе не помещаются в восемь байтов
* поля данных:
*
* @code
* кадр 1 — преамбула, DLC = 2 +---------+----------+
* | команда | позиция |
* +---------+----------+
* кадр 2 — идентификатор, DLC=8 +----------------------------+
* | ROM датчика, байты 0..7 |
* +----------------------------+
* @endcode
*
* Преамбула несёт команду записи датчика в позицию: первый байт — код
* команды (@ref CAN_SENSOR_CMD_WRITE_POSITION), второй — номер позиции в
* таблице узла-приёмника. Кадр идентификатора передаётся сразу за преамбулой
* и без неё считается недействительным, поэтому приёмник не запишет ROM в
* позицию, которая ему не была назначена.
*/
#ifndef CAN_SENSOR_H
#define CAN_SENSOR_H
#include <stdint.h>
/** Длина идентификатора датчика (ROM 1-Wire) в байтах. */
#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
/** Код команды «очистить позицию»; кадр идентификатора передаётся нулевым. */
#define CAN_SENSOR_CMD_CLEAR_POSITION 0xA2U
/** Смещение идентификатора кадра данных относительно кадра преамбулы. */
#define CAN_SENSOR_DATA_ID_OFFSET 1U
/** Число попыток передачи одного кадра по умолчанию. */
#define CAN_SENSOR_DEFAULT_RETRIES 3U
/** Идентификатор кадра преамбулы по умолчанию (стандартный, 11 бит). */
#define CAN_SENSOR_DEFAULT_TX_ID 0x200U
/**
* @brief Кадр шины CAN в форме, не зависящей от контроллера.
*/
typedef struct {
uint32_t id; /**< Идентификатор кадра, 11 или 29 бит. */
uint8_t extended; /**< 1 — расширенный идентификатор. */
uint8_t length; /**< Число значащих байтов поля данных. */
uint8_t data[CAN_SENSOR_MAX_DATA]; /**< Поле данных кадра. */
} CanSensor_Frame;
/**
* @brief Принятое или собранное сообщение: преамбула и идентификатор датчика.
*/
typedef struct {
uint8_t command; /**< Код команды из преамбулы. */
uint16_t position; /**< Позиция записи из преамбулы. */
uint8_t id[CAN_SENSOR_ID_SIZE]; /**< Идентификатор датчика. */
} CanSensor_Message;
/**
* @brief Доступ библиотеки к контроллеру CAN.
*
* Обязателен только @c send; при нулевом @c receive функция CanSensor_Poll()
* ничего не делает и узел работает только на передачу.
*/
typedef struct {
/** Ставит кадр в очередь передачи; 1 — кадр принят контроллером. */
uint8_t (*send)(void *context, const CanSensor_Frame *frame);
/** Забирает принятый кадр; 1 — кадр получен, 0 — очередь пуста. */
uint8_t (*receive)(void *context, CanSensor_Frame *frame);
void *context; /**< Контекст порта, передаётся вызовам без изменений. */
} CanSensor_Io;
/**
* @brief Идентификаторы кадров и режим передачи.
*
* Нулевые поля заменяются значениями по умолчанию вызовом
* CanSensor_ConfigDefault() или самой CanSensor_Init().
*/
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 — значение по умолчанию. */
} 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;
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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 */