Перенесён из KONOR_ds18b20/lib/can. 64-битный идентификатор датчика и преамбула не помещаются в восемь байт поля данных, поэтому сообщение идёт двумя кадрами. Сборку на приёме библиотека делает сама: кадр ROM без преамбулы и кадр неверной длины отбрасываются и учитываются в dropped_frames.
247 lines
7.6 KiB
C
247 lines
7.6 KiB
C
/**
|
||
* @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;
|
||
}
|