feat(can-sensor): передача ROM датчиков парой CAN-кадров

Перенесён из KONOR_ds18b20/lib/can.

64-битный идентификатор датчика и преамбула не помещаются в восемь байт
поля данных, поэтому сообщение идёт двумя кадрами. Сборку на приёме
библиотека делает сама: кадр ROM без преамбулы и кадр неверной длины
отбрасываются и учитываются в dropped_frames.
This commit is contained in:
2026-08-23 01:15:12 +03:00
parent 11bf8b6c11
commit 13f2c8fe64
3 changed files with 516 additions and 0 deletions

246
c/can-sensor/can_sensor.c Normal file
View File

@@ -0,0 +1,246 @@
/**
* @file can_sensor.c
* @brief Сборка, передача и разбор сообщений «преамбула + идентификатор».
*
* Реализация не хранит очередей и не пользуется временем: состояние приёма
* ограничено признаком принятой преамбулы, поэтому библиотека одинаково
* работает и в главном цикле, и в обработчике прерывания порта.
*/
#include "can_sensor.h"
/**
* @brief Возвращает идентификатор кадра данных для заданных настроек.
*
* @param config Настройки узла.
* @return Идентификатор кадра ROM при передаче.
*/
static uint32_t can_sensor_tx_data_id(const CanSensor_Config *config)
{
if (config->tx_data_id != 0U) {
return config->tx_data_id;
}
return config->tx_id + CAN_SENSOR_DATA_ID_OFFSET;
}
/**
* @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) {
return 1U;
}
}
return 0U;
}
void CanSensor_ConfigDefault(CanSensor_Config *config)
{
if (config == 0) {
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->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;
} else {
CanSensor_ConfigDefault(&link->config);
}
if (link->config.tx_id == 0U) {
link->config.tx_id = CAN_SENSOR_DEFAULT_TX_ID;
}
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;
link->dropped_frames = 0U;
return 1U;
}
uint8_t CanSensor_BuildPreamble(const CanSensor_Config *config, CanSensor_Frame *frame,
uint8_t command, uint16_t position)
{
uint8_t index;
if ((config == 0) || (frame == 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;
}
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)
{
CanSensor_Frame frame;
if ((link == 0) || (link->io.send == 0)) {
return 0U;
}
(void)CanSensor_BuildPreamble(&link->config, &frame, command, position);
if (can_sensor_send_frame(link, &frame) == 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;
}
uint8_t CanSensor_SendId(CanSensor *link, uint16_t position, const uint8_t *id)
{
if (id == 0) {
return 0U;
}
return CanSensor_Send(link, CAN_SENSOR_CMD_WRITE_POSITION, position, id);
}
uint8_t CanSensor_HandleFrame(CanSensor *link, const CanSensor_Frame *frame,
CanSensor_Message *out)
{
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++;
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;
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;
}
for (index = 0U; index < CAN_SENSOR_ID_SIZE; index++) {
link->rx.id[index] = frame->data[index];
}
link->rx_preamble = 0U;
link->received_messages++;
if (out != 0) {
*out = link->rx;
}
return 1U;
}
uint8_t CanSensor_Poll(CanSensor *link, CanSensor_Message *out)
{
CanSensor_Frame frame;
if ((link == 0) || (link->io.receive == 0)) {
return 0U;
}
while (link->io.receive(link->io.context, &frame) != 0U) {
if (CanSensor_HandleFrame(link, &frame, out) != 0U) {
return 1U;
}
}
return 0U;
}