Files
templates/c/can-sensor/can_sensor.c
T

262 lines
8.0 KiB
C

/*
* Обмен настройками датчиков DS18B20 через однокадровые сообщения SETCAN. ROM идентифицирует
* датчик независимо от его позиции; прикладное состояние и отправка CAN предоставляются через
* интерфейс библиотеки.
*/
/**
* @file can_sensor.c
* @brief Однокадровый транспорт SETCAN SETTINGS для локаций DS18B20.
*/
#include "can_sensor.h"
/** Возвращает 1, если все восемь байтов ROM равны нулю. */
static uint8_t can_sensor_rom_is_zero(const uint8_t *rom)
{
uint8_t index;
for (index = 0U; index < CAN_SENSOR_ID_SIZE; index++) {
if (rom[index] != 0U) {
return 0U;
}
}
return 1U;
}
/** Передаёт уже собранный кадр с ограниченным числом повторов. */
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;
}
void CanSensor_ConfigDefault(CanSensor_Config *config)
{
if (config == 0) {
return;
}
config->tx_id = CAN_SENSOR_DEFAULT_TX_ID;
config->rx_id = CAN_SENSOR_DEFAULT_RX_ID;
config->pulse_period_ms = 0U;
config->extended = 1U;
config->retries = CAN_SENSOR_DEFAULT_RETRIES;
}
uint8_t CanSensor_Init(CanSensor *link, const CanSensor_Io *io,
const CanSensor_Config *config)
{
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.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->sent_messages = 0U;
link->send_errors = 0U;
link->received_messages = 0U;
link->dropped_frames = 0U;
link->pulse_last_ms = 0U;
link->pulse_counter = 0U;
link->pulse_armed = 0U;
return 1U;
}
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) || (length > CAN_SENSOR_MAX_DATA)
|| ((length != 0U) && (data == 0))) {
return 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_SendId(CanSensor *link, uint8_t position,
uint8_t assembly_serial, const uint8_t *id)
{
CanSensor_Frame frame;
if ((link == 0) || (id == 0) || (link->io.send == 0)) {
return 0U;
}
if (CanSensor_BuildFrame(&link->config, &frame, 1U, position,
assembly_serial, id, CAN_SENSOR_ID_SIZE) == 0U) {
link->send_errors++;
return 0U;
}
return can_sensor_send_frame(link, &frame);
}
uint8_t CanSensor_SendError(CanSensor *link, uint8_t position,
uint8_t assembly_serial, uint8_t result)
{
CanSensor_Frame frame;
if ((link == 0) || (link->io.send == 0) || (result == CAN_SENSOR_RESULT_OK)) {
return 0U;
}
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->extended == 0U) {
return 0U;
}
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;
}
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++) {
message.id[index] = (index < frame->length) ? frame->data[index] : 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 = message;
}
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;
}
void CanSensor_SetPulsePeriod(CanSensor *link, uint32_t period_ms)
{
if (link == 0) {
return;
}
if (link->config.pulse_period_ms != period_ms) {
link->config.pulse_period_ms = period_ms;
/* После включения или смены периода отсчитываем полный новый период. */
link->pulse_armed = 0U;
}
}
uint32_t CanSensor_PulsePeriod(const CanSensor *link)
{
return (link != 0) ? link->config.pulse_period_ms : 0U;
}
void CanSensor_Task(CanSensor *link, uint32_t now_ms)
{
CanSensor_Frame frame;
const uint32_t period_ms = (link != 0) ? link->config.pulse_period_ms : 0U;
if ((link == 0) || (link->io.send == 0) || (period_ms == 0U)) {
return;
}
if (link->pulse_armed == 0U) {
link->pulse_last_ms = now_ms;
link->pulse_armed = 1U;
return;
}
if ((now_ms - link->pulse_last_ms) < period_ms) {
return;
}
frame.id = (link->config.tx_id & CAN_SENSOR_ADDRESS_MASK)
| ((uint32_t)CAN_SENSOR_MSGTYPE_PULSE << 16U);
frame.extended = 1U;
frame.length = 1U;
link->pulse_counter++;
frame.data[0] = link->pulse_counter;
if (can_sensor_send_frame(link, &frame) != 0U) {
link->pulse_last_ms = now_ms;
} else {
/* Счётчик меняется только для реально поставленных в очередь кадров. */
link->pulse_counter--;
}
}