Files
F103C8T6/Core/Src/setcan.c

1490 lines
57 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.
#include "setcan.h"
SetCanDevice_t CurrentDevice;
SetCanDevice_t Device_on_the_Network[8][16];
struct controlflags ControlFlags;
CAN_HandleTypeDef *_HCAN = 0;
RTC_HandleTypeDef *_HRTC = 0;
TIM_HandleTypeDef *_HTIM = 0;
uint8_t CurrentStep = 1;
uint8_t LastStep = 0;
struct RXMsg rxMsg[SETCAN_RX_BUFFER_SIZE];
/**
* @brief Проверяет, является ли год високосным.
* В системе год хранится как смещение от 2000. Например, значение 24 соответствует 2024.
* Эта функция добавляет 2000 к значению и проверяет, високосный ли получившийся год.
* @param year Смещение от 2000, тип uint8_t (от 0 до 255).
* @return _Bool true, если год високосный; false — иначе.
*/
_Bool IsLeapYear(uint8_t year)
{
year += 2000;
return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
}
/**
* @brief Возвращает количество доступных сообщений в буфере приёма CAN.
* Используется кольцевой буфер.
* Возвращаемое значение — количество элементов между указателями LastStep и CurrentStep.
* @return uint16_t Количество доступных сообщений, ожидающих обработки, в буфере.
*/
uint16_t AvailableCanRxMsg(void)
{
return ((uint16_t)(SETCAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1))) % SETCAN_RX_BUFFER_SIZE;
}
void SETCAN_DEINIT(uint8_t stage)
{
switch(stage)
{
case 3:
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
HAL_TIM_UnRegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID);
#endif
case 2:
case 1:
#if (USE_HAL_CAN_REGISTER_CALLBACKS == 1)
HAL_CAN_UnRegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID);
#endif
}
return;
}
/**
* @brief Инициализация переферии
* @details Инициализация указателей на HCAN, HRTC, установка фильтров CAN.
* @note Фильтры CAN описаны в разделе SETCAN_CAN_FILTERS().
*/
SETCAN_INIT_StatusTypeDef SETCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_HandleTypeDef *tmp_hrtc, TIM_HandleTypeDef *tmp_tim)
{
unsigned initStage = 0;
if(tmp_hcan)
{
_HCAN = tmp_hcan;
#if (USE_HAL_CAN_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef CAN_RC_RESULT = HAL_CAN_RegisterCallback(_HCAN, HAL_CAN_RX_FIFO0_MSG_PENDING_CB_ID, SetCanRxFifo0MsgPendingCallback);
if(CAN_RC_RESULT != HAL_OK)
{
SETCAN_DEINIT(initStage);
return SETCAN_INIT_HRTC_ERROR;
}
#endif
} else {
SETCAN_DEINIT(initStage);
return SETCAN_INIT_HCAN_ERROR;
}
initStage++;
if(tmp_hrtc)
{
_HRTC = tmp_hrtc;
}
else
{
SETCAN_DEINIT(initStage);
return SETCAN_INIT_HRTC_ERROR;
}
initStage++;
if(tmp_tim)
{
_HTIM = tmp_tim;
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
HAL_StatusTypeDef TIM_RC_RESULT = HAL_TIM_RegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID, SetCanPulseCallback);
if(TIM_RC_RESULT != HAL_OK)
{
SETCAN_DEINIT(initStage);
return SETCAN_INIT_HRTC_ERROR;
}
#endif
}
else
{
SETCAN_DEINIT(initStage);
return SETCAN_INIT_TIM_ERROR;
}
initStage++;
SETCAN_FILTERS();
ControlFlags.IsPulse = 1;
return SETCAN_INIT_OK;
}
/**
* @brief Бесконечный цикл обработки входящих CAN-сообщений.
*
* Эта функция постоянно проверяет наличие доступных сообщений в буфере CAN и
* вызывает соответствующие обработчики в зависимости от типа полученного сообщения.
* Обрабатываемые типы сообщений:
* - Аналоговые данные
* - Широковещательные сообщения
* - Дискретные данные
* - Общее адресное пространство
* - Модбас сообщения
* - Ошибки
*
* Обработка сообщений осуществляется внутри бесконечного цикла с использованием
* функции AvailableCanRxMsg() для определения наличия новых сообщений.
*
* В конце каждого обработанного сообщения индекс текущего элемента буфера инкрементируется
* с учетом размера буфера через операцию по модулю.
*/
void SETCAN_LoopProcessRxMsgs(void)
{
while(1)
{
if(AvailableCanRxMsg())
{
if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ANALOG)
{
SETCAN_AnalogProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_BROADCAST)
{
SETCAN_BroadcastProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_DISCRETE)
{
SETCAN_DiscreticProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
{
SetCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_COIL ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_DISCRETE ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_HOLDING ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_INPUT)
{
SETCAN_ModbusProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ERROR)
{
SETCAN_RequestError(rxMsg[CurrentStep]);
}
// Обновление индекса текущего сообщения с циклическим масштабированием
CurrentStep = (uint16_t)(CurrentStep + 1) % SETCAN_RX_BUFFER_SIZE;
}
}
}
/**
* @brief Обрабатывает все доступные входящие CAN-сообщения.
*
* Эта функция последовательно обрабатывает все сообщения, доступные в буфере,
* вызывая соответствующие функции обработчики в зависимости от типа сообщения.
* После обработки каждого сообщения индекс текущего элемента буфера обновляется
* циклически.
*
* @return SETCAN_OK, если все сообщения были обработаны успешно.
* В противном случае возвращается код ошибки, полученный при обработке сообщения.
*
* Обработка происходит до тех пор, пока есть доступные сообщения, полученные функцией
* AvailableCanRxMsg().
*/
SETCAN_StatusTypeDef SETCAN_ProcessAllRxMsgs(void)
{
SETCAN_StatusTypeDef result = SETCAN_TIMEOUT;
while(AvailableCanRxMsg())
{
if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ANALOG)
{
result = SETCAN_AnalogProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_BROADCAST)
{
result = SETCAN_BroadcastProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_DISCRETE)
{
result = SETCAN_DiscreticProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
{
result = SetCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_COIL ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_DISCRETE ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_HOLDING ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_INPUT)
{
result = SETCAN_ModbusProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ERROR)
{
result = SETCAN_RequestError(rxMsg[CurrentStep]);
}
CurrentStep = (uint16_t)(CurrentStep + 1) % SETCAN_RX_BUFFER_SIZE;
if(result != SETCAN_OK)
{
return result;
}
}
return result;
}
/**
* @brief Обрабатывает одно доступное CAN-сообщение.
*
* Эта функция проверяет наличие доступных сообщений в буфере и, если таковое есть,
* выполняет обработку текущего сообщения в зависимости от его типа. После обработки
* индекс текущего сообщения обновляется циклически.
*
* @return Прошедший статус обработки:
* - SETCAN_OK, если сообщение успешно обработано,
* - SETCAN_TIMEOUT, если сообщений не было,
* - или код ошибки, возвращенный функцией обработки.
*/
SETCAN_StatusTypeDef SETCAN_ProcessSingleRxMsg(void)
{
SETCAN_StatusTypeDef result = SETCAN_TIMEOUT;
if(AvailableCanRxMsg())
{
if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ANALOG)
{
result = SETCAN_AnalogProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_BROADCAST)
{
result = SETCAN_BroadcastProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_DISCRETE)
{
result = SETCAN_DiscreticProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
{
result = SetCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_COIL ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_DISCRETE ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_HOLDING ||
rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_MODBUS_INPUT)
{
result = SETCAN_ModbusProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == SETCAN_MSGTYPE_ERROR)
{
result = SETCAN_RequestError(rxMsg[CurrentStep]);
}
CurrentStep = (uint16_t)(CurrentStep + 1) % SETCAN_RX_BUFFER_SIZE;
}
return result;
}
/**
* @brief Функция обработки широковещательных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, выполняющая команды, переданные в широковещательном формате с головного (master) устройства. Типы команд: Запрос статуса, запрос на включение или выключение, рестарт устройств, установка времени.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_BroadcastProcessing(struct RXMsg _rxMsg)
{
msgBodyBroadcastType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch(msg.Fields.Type)
{
case SETCAN_BROADCAST_STATUS:
{
//Обработка запроса статуса устройства
return SetCanMsgToBroadcastStatus(_rxMsg);
}
case SETCAN_BROADCAST_ONOFF:
{
//Обработка запроса на вкл/выкл
SetCanMsgToBroadcastOnOff(_rxMsg);
break;
}
case SETCAN_BROADCAST_RESTARTDEVICE:
{
SetCanMsgToBroadcastRestart(_rxMsg);
break;
}
case SETCAN_BROADCAST_RTCSETUP:
{
//Обработка запроса на синхронизацию времени
//С головным устройством
SetCanMsgToBroadcastRtcSetup(_rxMsg);
break;;
}
default:
//RESERVE SENSOR TYPE.
return SETCAN_ERROR;
break;
}
return SETCAN_OK;
}
/**
* @brief Формирует и отправляет широковещательное CAN-сообщение для ответа на запрос статуса.
* Устанавливает идентификатор, собирает текущие параметры времени и даты, и отправляет сообщение.
* @param _rxMsg Структура полученного по CAN запроса.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успехе.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastStatus(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.DLC = 7;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
tmp_eID.Fields.DeviceType = CURRENT_TYPE_DEVICE;
tmp_eID.Fields.DeviceID = CURRENT_ID_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
RTC_TimeTypeDef sTime = {0};
HAL_RTC_GetTime(_HRTC, &sTime, RTC_FORMAT_BIN);
data[0] = sTime.Hours;
data[1] = sTime.Minutes;
data[2] = sTime.Seconds;
RTC_DateTypeDef DateToUpdate = {0};
HAL_RTC_GetDate(_HRTC, &DateToUpdate, RTC_FORMAT_BIN);
data[3] = DateToUpdate.Year;
data[4] = DateToUpdate.Month;
data[5] = DateToUpdate.Date;
data[6] = DateToUpdate.WeekDay;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Виртуальная слабая функция для переключения режима пульсации ON/OFF.
* Меняет состояние флага IsPulse в структуре ControlFlags.
* В данном случае — просто инвертирует его логическое значение.
* @param _rxMsg Структура полученного CAN-сообщения (Пока не используется в функции).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastOnOff(struct RXMsg _rxMsg)
{
ControlFlags.IsPulse = !ControlFlags.IsPulse;
return SETCAN_OK;
}
/**
* @brief __weak функция для обработки широковещательного сообщения перезапуска устройства.
* Проверяет длину сообщения, извлекает ID из данных и, если она совпадает с текущим устройством,
* вызывает системный перезапуск.
* @param _rxMsg Структура полученного CAN-сообщения с командой перезапуска.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastRestart(struct RXMsg _rxMsg)
{
if(_rxMsg.DLC == 0)
{
return SETCAN_ERROR;
}
msgBodyBroadcastType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
if(msg.Fields.Body == (CURRENT_ID_DEVICE / (_rxMsg.DLC*8)))
{
uint64_t page = 0;
for(int i = 0; i < _rxMsg.DLC; i++)
{
page += (_rxMsg.Data[i] << (i * 8));
}
if((page >> CURRENT_ID_DEVICE) & 0b1)
{
NVIC_SystemReset();
}
}
return SETCAN_OK;
}
/**
* @brief __weak функция для обработки запроса на синхронизацию RTC через широковещательное сообщение.
* Проверяет длину сообщения и корректность данных (время и дата). Если все проверки пройдены,
* вызывает функцию синхронизации RTC.
* @param _rxMsg Структура полученного CAN-сообщения с данными для установки времени и даты.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg)
{
if(_rxMsg.DLC > 7)
{
return SETCAN_ERROR;
}
else
{
int DaysCount_Normal[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
if( _rxMsg.Data[0] > 23 ||
_rxMsg.Data[1] > 59 ||
_rxMsg.Data[2] > 59 ||
_rxMsg.Data[3] > 99 ||
_rxMsg.Data[4] > 12 ||
_rxMsg.Data[5] > DaysCount_Normal[IsLeapYear(_rxMsg.Data[3])][_rxMsg.Data[4]] ||
_rxMsg.Data[6] > 6)
{
return SETCAN_ERROR;
}
else
{
SETCAN_RTC_SYNC(_rxMsg.Data);
}
}
return SETCAN_OK;
}
/**
* @brief Функция обработки дискретных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, формирующая и отправляющая ответ на запросы. Типы запросов: Аварии, Предупреждения, Управляющие сигналы, Флаги, Рестарт устройства, Изменение режима работы устройства, Запрос на устройство.
* @note Запрос на устройство. Головное (master) устройство запрашивает некоторое колличество параметров. В Data - 64 битовых адресса параметров, тип которых задаётся в Sensor ID. Имеется возможность запрашивать непоследовательные параметры.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_DiscreticProcessing(struct RXMsg _rxMsg)
{
msgBodyDiscreteType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch(msg.Fields.Type)
{
case SETCAN_DISCRETE_ACCIDENT:
{
SetCanMsgToDiscreteAccident(_rxMsg);
break;
}
case SETCAN_DISCRETE_WARNING:
{
SetCanMsgToDiscreteWarning(_rxMsg);
break;
}
case SETCAN_DISCRETE_CONTROL_SIGNALS:
{
SetCanMsgToDiscreteControlSignals(_rxMsg);
break;
}
case SETCAN_DISCRETE_FLAGS:
{
SetCanMsgToDiscreteFlags(_rxMsg);
break;
}
case SETCAN_DISCRETE_RESET:
{
SetCanMsgToDiscreteReset(_rxMsg);
break;
}
case SETCAN_DISCRETE_CHANGE_MODE:
{
SetCanMsgToDiscreteChangeMode(_rxMsg);
break;
}
case SETCAN_DISCRETE_REQUEST_LIST_OF_PARAMETERS:
{
SetCanMsgToDiscreteRequestListOfParameters(_rxMsg);
break;
}
default:
//RESERVE SENSOR TYPE.
return SETCAN_ERROR;
break;
}
return SETCAN_OK;
}
/**
* @brief Обработка запроса аварийных ситуаций.
* В текущей реализации — заглушка, ничего не выполняет.
* Можно реализовать отправку или обработку аварийных данных.
* @param _rxMsg Входящее сообщение с данными запроса.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteAccident(struct RXMsg _rxMsg)
{
return SETCAN_OK;
}
/**
* @brief Обработка запроса предупреждений.
* В текущей реализации — заглушка.
* Можно реализовать обработку или ответ на предупреждения.
* @param _rxMsg Входящее сообщение.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteWarning(struct RXMsg _rxMsg)
{
return SETCAN_OK;
}
/**
* @brief Обработка запроса управляющих сигналов.
* В текущей реализации — заглушка.
* Можно реализовать управление сигналами.
* @param _rxMsg Входящее сообщение.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteControlSignals(struct RXMsg _rxMsg)
{
return SETCAN_OK;
}
/**
* @brief Обработка запроса флагов.
* В текущей реализации — заглушка.
* Можно реализовать работу с флагами.
* @param _rxMsg Входящее сообщение.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteFlags(struct RXMsg _rxMsg)
{
return SETCAN_OK;
}
/**
* @brief Обработка рестарта системы по CAN-запросу.
* Выполняет программный рестарт системы.
* @param _rxMsg Входящее сообщение.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteReset(struct RXMsg _rxMsg)
{
NVIC_SystemReset();
return SETCAN_ERROR;
}
/**
* @brief Обработка запроса на изменение режима.
* В текущей реализации — заглушка.
* Можно реализовать изменение режима работы.
* @param _rxMsg Входящее сообщение.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteChangeMode(struct RXMsg _rxMsg)
{
return SETCAN_OK;
}
/**
* @brief Обработка запроса на получение списка параметров.
* В текущей реализации — заглушка.
* Можно реализовать отправку списка параметров.
* @param _rxMsg Входящее сообщение.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToDiscreteRequestListOfParameters(struct RXMsg _rxMsg)
{
return SETCAN_OK;
}
/**
* @brief Функция обработки аналоговых запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, сортирующая запросы по типам и вызывающая соответствующие функции. Типы запросов: Универсальный, Уставки, Напряжение, Ток, Температура.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_AnalogProcessing(struct RXMsg _rxMsg)
{
msgBodyAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch (msg.Fields.Type)
{
case SETCAN_ANALOG_UNIVERSAL:
{
return SetCanMsgToAnalogUniversal(_rxMsg);
break;
}
case SETCAN_ANALOG_SETTINGS:
{
return SetCanMsgToAnalogSettings(_rxMsg);
break;
}
case SETCAN_ANALOG_U:
{
return SetCanMsgToAnalogUSens(_rxMsg);
break;
}
case SETCAN_ANALOG_I:
{
return SetCanMsgToAnalogISens(_rxMsg);
break;
}
case SETCAN_ANALOG_T:
{
return SetCanMsgToAnalogTSens(_rxMsg);
break;
}
default:
//RESERVE SENSOR TYPE
return SETCAN_ERROR;
break;
}
return SETCAN_ERROR;
}
/**
* @brief Функция ответа на запрос данных со всех датчиков.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogUniversal(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
data[0] = 'U';
data[1] = 'N';
data[2] = 'I';
data[3] = 'V';
data[4] = 'E';
data[5] = 'R';
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос уставок по CAN.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется только для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogSettings(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 7;
data[0] = 'U';
data[1] = 'S';
data[2] = 'T';
data[3] = 'A';
data[4] = 'V';
data[5] = 'K';
data[6] = 'I';
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос данных с датчиков напряжения.
* Создает сообщение с указанными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используемая для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK при успешной отправке.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogUSens(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
msgBodyAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'U';
data[1] = 'S';
data[2] = '0' + msg.Fields.SensorID / 1000;
data[3] = '0' + msg.Fields.SensorID / 100 % 10;
data[4] = '0' + msg.Fields.SensorID / 10 % 10;
data[5] = '0' + msg.Fields.SensorID % 10;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос данных с датчиков тока.
* Создает сообщение с определенными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успешной отправке.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogISens(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
msgBodyAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'I';
data[1] = 'S';
data[2] = '0' + msg.Fields.SensorID / 1000;
data[3] = '0' + msg.Fields.SensorID / 100 % 10;
data[4] = '0' + msg.Fields.SensorID / 10 % 10;
data[5] = '0' + msg.Fields.SensorID % 10;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос данных с датчиков температуры.
* Создает сообщение с определенным заголовком, формирует данные по ID сенсора и отправляет его через CAN.
* @param _rxMsg Структура полученного сообщения, которая используется для формирования идентификатора.
* @return HAL_StatusTypeDef Статус выполнения функции HAL, например HAL_OK при успехе.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToAnalogTSens(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
msgBodyAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'T';
data[1] = 'S';
data[2] = '0' + msg.Fields.SensorID / 1000;
data[3] = '0' + msg.Fields.SensorID / 100 % 10;
data[4] = '0' + msg.Fields.SensorID / 10 % 10;
data[5] = '0' + msg.Fields.SensorID % 10;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Отправляет сообщение по CAN-шине в соответствии с типом протокола.
* Эта функция формирует и отправляет сообщение через CAN-шину, основываясь на типе
* переданного ID и данных. Поддерживаются типы сообщений: широковещательные, дискретные
* и аналоговые.
* @param id Идентификатор сообщения, содержащий тип сообщения и другую информацию.
* @param coreData Структура данных, включающая тип, тело и данные для передачи.
* @return SETCAN_StatusTypeDef Статус операции.
* @details
* В зависимости от типа сообщения (панель broadcast, discrete или analog),
* формируется соответствующий пакет данных и отправляется на шину CAN.
* Для сообщений, превышающих длину 8 байт, передача выполняется пакетами.
* В случае ошибок при добавлении сообщения в очередь передачи, возвращается ошибка.
*/
SETCAN_StatusTypeDef SETCAN_SEND_CORE(const SetCanId_t id, const struct SetCanCoreData coreData)
{
SetCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
CAN_TxHeaderTypeDef TxHeader;
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
uint32_t TxMailBox = 0;
uint8_t canData[8];
switch(id.Fields.MsgType)
{
case SETCAN_MSGTYPE_BROADCAST:
{
msgBodyBroadcastType body;
body.Fields.Type = coreData.Type;
body.Fields.Body = coreData.Body;
localId.Fields.MsgBody = body.Body;
TxHeader.ExtId = localId.BitAll;
TxHeader.DLC = 0;
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return SETCAN_ERROR;
}
}
case SETCAN_MSGTYPE_DISCRETE:
{
msgBodyDiscreteType body;
body.Fields.Type = coreData.Type;
uint8_t regsRemaining = coreData.DataCount;
uint8_t currentIndex = 0;
while(regsRemaining > 0)
{
uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining;
body.Fields.Body = coreData.Body + currentIndex;
localId.Fields.MsgBody = body.Body;
TxHeader.ExtId = localId.BitAll;
TxHeader.DLC = regsInPacket;
for(int i = 0; i < regsInPacket; i++)
{
canData[i] = coreData.Data[coreData.DataCount - regsRemaining];
}
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
if(status != HAL_OK)
{
return (SETCAN_StatusTypeDef)status;
}
regsRemaining -= regsInPacket;
currentIndex += regsInPacket;
}
break;
}
case SETCAN_MSGTYPE_ANALOG:
{
msgBodyAnalogType body;
body.Fields.Type = coreData.Type;
uint8_t regsRemaining = coreData.DataCount;
uint8_t currentIndex = 0;
while(regsRemaining > 0)
{
uint8_t regsInPacket = (regsRemaining > 8) ? 8 : regsRemaining;
body.Fields.SensorID = coreData.Body + currentIndex;
localId.Fields.MsgBody = body.Body;
TxHeader.ExtId = localId.BitAll;
TxHeader.DLC = regsInPacket;
for(int i = 0; i < regsInPacket; i++)
{
canData[i] = coreData.Data[coreData.DataCount - regsRemaining];
}
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
if(status != HAL_OK)
{
return (SETCAN_StatusTypeDef)status;
}
regsRemaining -= regsInPacket;
currentIndex += regsInPacket;
}
break;
}
default:
{
return SETCAN_ERROR;
}
}
return SETCAN_OK;
}
/**
* @brief Обработка и ответ на запрос общего адресного пространства.
* Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg.
* @param _rxMsg Структура входящего сообщения, содержит идентификатор и тело сообщения.
*/
__weak SETCAN_StatusTypeDef SetCanMsgToGeneralAddressSpace(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
data[0] = 'G';
data[1] = 'A';
data[2] = 'S';
data[3] = '-';
for(int i = 0; i < 4; i++)
{
unsigned sym = (_rxMsg.eID.Fields.MsgBody >> (12 - (i * 4))) & 0xF;
if(sym >= 10)
{
data[4 + i] = sym % 10 + 'A';
}
else
{
data[4 + i] = sym + '0';
}
}
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Отправляет произвольный диапазон регистров в пакетах по, максимум, 4 регистра за раз через CAN.
* Функция разбивает передаваемый массив регистров на пакеты по 4 или меньше и отправляет их по отдельности.
* Для каждого пакета формируется уникальный MsgBody, равный адресу первого регистра в пакете.
* @param priority Приоритет сообщения CAN. SETCAN_Priority_TypeDef
* @param regStartAdr Адрес первого регистра из всего массива.
* @param data Указатель на массив регистров для отправки.
* @param regCount Общее количество регистров для отправки.
* @return Возвращает статус отправки: HAL_OK при успехе, или код ошибки HAL_StatusTypeDef при сбое.
*/
SETCAN_StatusTypeDef SETCAN_SEND_GENERAL_ADDRESS_SPACE(SetCanId_t id, uint16_t regStartAdr, uint16_t *data, uint8_t regCount)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t canData[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = id.BitAll;
uint8_t regsRemaining = regCount;
uint8_t currentIndex = 0;
while(regsRemaining > 0)
{
uint8_t regsInPacket = (regsRemaining > 4) ? 4 : regsRemaining;
tmp_eID.Fields.MsgBody = regStartAdr + currentIndex;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = regsInPacket*2;
for(int i = 0; i < regsInPacket; i++)
{
canData[(i * 2)] = LowByteOfWord(data[currentIndex + i]);
canData[(i * 2) + 1] = HighByteOfWord(data[currentIndex + i]);
}
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox);
if(status != HAL_OK)
{
return (SETCAN_StatusTypeDef)status;
}
regsRemaining -= regsInPacket;
currentIndex += regsInPacket;
}
return (SETCAN_StatusTypeDef)HAL_OK;
}
/**
* @brief Обработка запроса на основе типа Modbus.
* В зависимости от типа сообщения, вызывает соответствующую функцию
* для отправки Modbus-запроса по CAN-шине.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
SETCAN_StatusTypeDef SETCAN_ModbusProcessing(struct RXMsg _rxMsg)
{
switch(_rxMsg.eID.Fields.MsgType)
{
case SETCAN_MSGTYPE_MODBUS_COIL:
{
return SetCanMsgToModbusCoil(_rxMsg);
break;
}
case SETCAN_MSGTYPE_MODBUS_DISCRETE:
{
return SetCanMsgToModbusDiscrete(_rxMsg);
break;
}
case SETCAN_MSGTYPE_MODBUS_HOLDING:
{
return SetCanMsgToModbusHolding(_rxMsg);
break;
}
case SETCAN_MSGTYPE_MODBUS_INPUT:
{
return SetCanMsgToModbusInput(_rxMsg);
break;
}
default:
//ERROR
return SETCAN_ERROR;
break;
}
return SETCAN_ERROR;
}
/**
* @brief __weak Функция отправки ответа на запрос Coil Modbus по CAN.
* Формирует CAN ответ на запрос Coil Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusCoil(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgBodyModbusType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'M';
data[1] = 'C';
data[2] = ' ';
data[3] = 'S';
data[4] = msg.Fields.StrAdr;
data[5] = ' ';
data[6] = 'C';
data[7] = msg.Fields.RegCount;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки ответа на запрос Discrete Modbus по CAN.
* Формирует CAN ответ на запрос Discrete Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusDiscrete(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgBodyModbusType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'M';
data[1] = 'D';
data[2] = ' ';
data[3] = 'S';
data[4] = msg.Fields.StrAdr;
data[5] = ' ';
data[6] = 'C';
data[7] = msg.Fields.RegCount;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки ответа на запрос Holding Modbus по CAN.
* Формирует CAN ответ на запрос Holding Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusHolding(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgBodyModbusType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'M';
data[1] = 'H';
data[2] = ' ';
data[3] = 'S';
data[4] = msg.Fields.StrAdr;
data[5] = ' ';
data[6] = 'C';
data[7] = msg.Fields.RegCount;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки ответа на запрос Input Modbus по CAN.
* Формирует CAN ответ на запрос Input Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak SETCAN_StatusTypeDef SetCanMsgToModbusInput(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgBodyModbusType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'M';
data[1] = 'I';
data[2] = ' ';
data[3] = 'S';
data[4] = msg.Fields.StrAdr;
data[5] = ' ';
data[6] = 'C';
data[7] = msg.Fields.RegCount;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Отправляет данные Modbus через CAN-шину в соответствии с типом сообщения.
* Эта функция формирует и отправляет Modbus-данные по CAN-шине, поддерживая
* различные типы сообщений: Coil, Discrete, Holding Register и Input Register.
* Для сообщений, превышающих 4 регистров, данные отправляются пакетами.
* @param id Идентификатор сообщения, содержащий тип сообщения и другую информацию.
* @param modbusData Структура с данными Modbus: адрес, количество регистров и сами данные.
* @return SETCAN_StatusTypeDef Статус операции.
* @details
* В зависимости от типа Modbus (coil/discrete или holding/input), формируется
* соответствующий CAN-пакет. Для coil/discrete отправляется один пакет, для holding/input —
* несколько, по 4 регистра в пакете. В случае ошибок при добавлении сообщения API HAL возвращается ошибка.
*/
SETCAN_StatusTypeDef SETCAN_SEND_MODBUS(const SetCanId_t id, const struct SetCanModbusData modbusData)
{
SetCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
// Структура для отправки
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t canData[8];
if(msgType == SETCAN_MSGTYPE_MODBUS_COIL || msgType == SETCAN_MSGTYPE_MODBUS_DISCRETE)
{
// Формируем body
msgBodyModbusType body;
body.Fields.StrAdr = modbusData.StrAdr;
body.Fields.RegCount = modbusData.RegCount;
// Устанавливаем коллизию ExtId с body.Body
localId.Fields.MsgBody = (body.Body & 0xFFFF);
// Обновляем ExtId
TxHeader.ExtId = localId.BitAll;
// Остальные настройки
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = modbusData.RegCount % 8 + 1;
// Формируем CAN данные
canData[0] = LowByteOfWord(modbusData.Data[0]);
canData[1] = HighByteOfWord(modbusData.Data[0]);
if (HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return SETCAN_ERROR;
}
}
else if(msgType == SETCAN_MSGTYPE_MODBUS_HOLDING || msgType == SETCAN_MSGTYPE_MODBUS_INPUT)
{
uint16_t* dataPtr = modbusData.Data;
unsigned totalRegs = modbusData.RegCount;
unsigned regsProcessed = 0;
unsigned startAddress = modbusData.StrAdr;
while (regsProcessed < totalRegs)
{
uint8_t regsInPacket = (totalRegs - regsProcessed) > 4 ? 4 : (totalRegs - regsProcessed);
// Формируем body
msgBodyModbusType body;
body.Fields.StrAdr = startAddress;
body.Fields.RegCount = regsInPacket;
// Обновляем ExtId
localId.Fields.MsgBody = (body.Body & 0xFFFF);
TxHeader.ExtId = localId.BitAll;
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = regsInPacket * 2;
// Добавляем регистры
for (int i = 0; i < regsInPacket; i++)
{
canData[i * 2] = LowByteOfWord(dataPtr[regsProcessed + i]);
canData[i * 2 + 1] = HighByteOfWord(dataPtr[regsProcessed + i]);
}
// Отправляем сообщение
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return SETCAN_ERROR;
}
regsProcessed += regsInPacket;
startAddress += regsInPacket;
}
}
else
{
return SETCAN_ERROR; // Неверный тип сообщения
}
return SETCAN_OK;
}
/**
* @brief __weak Функция отправки сообщения об ошибке.
* Посылает CAN сообщение с кодом ошибки. Используется, когда необходимо оповестить о неуспешной операции.
* @param _rxMsg Структура входящего сообщения RXMsg, данными которого формируется ответ-ошибка.
* @return HAL_StatusTypeDef Статус выполнения команды CAN.
*/
__weak SETCAN_StatusTypeDef SETCAN_RequestError(struct RXMsg _rxMsg)
{
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
SetCanId_t tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
tmp_eID.Fields.MsgType = SETCAN_MSGTYPE_ERROR;
msgBodyErrorType msg;
msg.Fields.ErrorCode = 0xFF;
msg.Fields.Info = 0;
tmp_eID.Fields.MsgBody = msg.Body;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 0;
return (SETCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Отправляет сообщение об ошибке по CAN-шине.
* Эта функция формирует и отправляет сообщение об ошибке с кодом и информацией,
* объединёнными в поле MsgBody идентификатора сообщения, и передаёт его через CAN.
* @param id Идентификатор сообщения, содержащий базовую информацию.
* @param errorData Структура с данными ошибки: код и дополнительная информация.
* @return SETCAN_StatusTypeDef Статус операции.
* @details
* В качестве данных передаётся 2 байта: старшие биты — информация об ошибке, младшие — код ошибки.
* Размер данных в CANFrames устанавливается нулевым, так как все данные инкапсулированы в MsgBody.
*/
SETCAN_StatusTypeDef SETCAN_SEND_ERROR(const SetCanId_t id, const struct SetCanErrorData errorData)
{
SetCanId_t localId = id;
uint8_t msgType = localId.Fields.MsgType;
// Структура для отправки
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t canData[8];
localId.Fields.MsgBody = ((errorData.Info << 8) | errorData.Code);
// Обновляем ExtId
TxHeader.ExtId = localId.BitAll;
// Остальные настройки
TxHeader.IDE = CAN_ID_EXT;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = 0;
if(HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return SETCAN_ERROR;
}
return SETCAN_OK;
}
/**
* @brief Записывает полученное сообщение CAN в буфер rxMsg.
* Копирует параметры расширенного ID, типы, длину данных и сам данные в указанный элемент буфера.
* @param tmp_eID Расширенный ID сообщения.
* @param tmp_IDE Тип идентификатора (например, CAN_ID_EXT).
* @param tmp_RTR Тип запроса (например, CAN_RTR_DATA).
* @param tmp_DLC Длина данных в сообщении.
* @param tmp_DATA Указатель на массив данных, полученных из CAN.
* @param tmp_LastStep Индекс позиции в буфере, куда записывать сообщение.
*/
void TakeRxMsgToBuffer(SetCanId_t tmp_eID, uint32_t tmp_IDE, uint32_t tmp_RTR, uint32_t tmp_DLC, uint8_t *tmp_DATA, uint16_t tmp_LastStep)
{
rxMsg[tmp_LastStep].eID.BitAll = tmp_eID.BitAll;
rxMsg[tmp_LastStep].info.EXT = tmp_IDE;
rxMsg[tmp_LastStep].info.RTR = tmp_RTR;
rxMsg[tmp_LastStep].DLC = tmp_DLC;
for(int i = 0; i < tmp_DLC; i++)
{
rxMsg[tmp_LastStep].Data[i] = tmp_DATA[i];
}
LastStep = tmp_LastStep;
}
/**
* @brief Обработка сообщений, ожидающих в FIFO, по прерыванию HAL_CAN_RxFifo0MsgPendingCallback.
* Читает все сообщения из FIFO, проверяет их тип, обновляет статус устройств сети или сохраняет сообщение в буфер.
* @param hcan Указатель на структуру управления CAN-Hardware.
*/
void SetCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
//Обработка всех сообщений в FIFO
CAN_RxHeaderTypeDef RxHeader;
uint8_t RCAN_Data[8];
while(HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RCAN_Data) == HAL_OK)
{
//Расширенный ID
if(RxHeader.IDE == CAN_ID_EXT)
{
if(!((SETCAN_RX_BUFFER_SIZE + LastStep - (CurrentStep - 1)) & SETCAN_RX_BUFFER_SIZE))
{
//Буффер переполнен
return;
}
uint16_t tmp_LastStep = (uint16_t)(LastStep + 1) % SETCAN_RX_BUFFER_SIZE;
SetCanId_t ExtID_Of_RX_MSG;
ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId;
//Полученное сообщение - пульс устройств в сети
if(ExtID_Of_RX_MSG.Fields.MsgType == SETCAN_MSGTYPE_PULSE)
{
Device_on_the_Network[ExtID_Of_RX_MSG.Fields.DeviceType][ExtID_Of_RX_MSG.Fields.DeviceID].Status = ONLINE;
Device_on_the_Network[ExtID_Of_RX_MSG.Fields.DeviceType][ExtID_Of_RX_MSG.Fields.DeviceID].TimeFromLastPulse = 0;
return;
}
TakeRxMsgToBuffer(ExtID_Of_RX_MSG, RxHeader.IDE, RxHeader.RTR, RxHeader.DLC, RCAN_Data, tmp_LastStep);
}
}
}
/**
* @brief Функция отправки пульса устройства.
* @details Пульс устройства. Есть возможность отключить пульс по запросу.
*/
void SetCanPulseCallback(TIM_HandleTypeDef *htim)
{
if(ControlFlags.IsPulse)
{
static unsigned PulseStage = 0;
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
SetCanId_t currentID;
currentID.BitAll = 0;
currentID.Fields.Priority = SETCAN_PRIORITY_STANDARD;
currentID.Fields.Route = SETCAN_ROUTE_FROM_DEVICE;
currentID.Fields.DeviceType = CURRENT_TYPE_DEVICE;
currentID.Fields.DeviceID = CURRENT_ID_DEVICE;
currentID.Fields.MsgType = SETCAN_MSGTYPE_PULSE;
currentID.Fields.MsgBody = 0;
TxHeader.ExtId = currentID.BitAll;
uint8_t data[8];
TxHeader.IDE = CAN_ID_EXT;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.DLC = 1;
PulseStage++;
unsigned int debugID;
debugID = currentID.BitAll;
if(PulseStage > 0xFF)
{
PulseStage = 0;
}
data[0] = PulseStage;
HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
}
/**
* @brief Функция установки в RTC полученной даты/время из запроса.
* @param uint8_t *data - Байтовый массив, 7 элементов. [0] - Часы. [1] - Минуты. [2] - Секунды. [3] - Год. [4] - Месяц. [5] - Дата. [6] - День недели.
*/
void SETCAN_RTC_SYNC(uint8_t *data)
{
__HAL_RTC_WRITEPROTECTION_DISABLE(_HRTC);
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
sTime.Hours = data[0];
sTime.Minutes = data[1];
sTime.Seconds = data[2];
if(HAL_RTC_SetTime(_HRTC, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
DateToUpdate.Year = data[3];
DateToUpdate.Month = data[4];
DateToUpdate.Date = data[5];
DateToUpdate.WeekDay = data[6];
if(HAL_RTC_SetDate(_HRTC, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
__HAL_RTC_WRITEPROTECTION_ENABLE(_HRTC);
}
/**
* @brief Настройка CAN-Фильтра.
* На основе переданных ID и маски настраивает фильтр CAN для приема только определённых сообщений.
* @param filterBank Номер фильтра (обычно 0..27).
* @param idFilter 32-битный идентификатор, используемый для фильтрации.
* @param idMask 32-битная маска, определяющая биты, проверяемые фильтром.
*/
void SETCAN_CONFIG_FILTER(uint8_t filterBank, uint32_t idFilter, uint32_t idMask)
{
CAN_FilterTypeDef canFilterConfig;
canFilterConfig.FilterBank = filterBank;
canFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
canFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
canFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;
canFilterConfig.FilterActivation = ENABLE;
canFilterConfig.SlaveStartFilterBank = 14;
#define CAN_IDE_32 0b00000100 // Для 32-х битного масштаба
// Разбиваем 32-битный ID и маску для фильтрации на High и Low 16 бит
canFilterConfig.FilterIdHigh = (uint16_t)(((idFilter) >> 13)); // верхние 16 бит
canFilterConfig.FilterIdLow = (uint16_t)((((idFilter) << 3)) | CAN_IDE_32); // низкие 16 бит,
canFilterConfig.FilterMaskIdHigh = (uint16_t)(((idMask) >> 13));
canFilterConfig.FilterMaskIdLow = (uint16_t)((((idMask) << 3)) | CAN_IDE_32);
if(HAL_CAN_ConfigFilter(_HCAN, &canFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief Конфигурирует три фильтра CAN для различных типов сообщений.
*/
void SETCAN_FILTERS()
{
#define BIT27 (1UL << 27)
#define BITS_20_23_MASK (0xFUL << 20) // 4 бита (20..23)
#define BITS_24_26_MASK (0x7UL << 24) // 3 бита (24..26)
#if ISPM == 0
uint32_t filter1_id = 0 | (CURRENT_TYPE_DEVICE << 24) | (CURRENT_ID_DEVICE << 20);
uint32_t filter1_mask = BIT27 | BITS_24_26_MASK | BITS_20_23_MASK; // все проверяемые биты
#else
uint32_t filter1_id = BIT27;
uint32_t filter1_mask = BIT27; // все проверяемые биты
#endif
#define BITS_16_19_MASK (0xFUL << 16)
uint32_t filter2_id = SETCAN_MSGTYPE_BROADCAST << 16;
uint32_t filter2_mask = BITS_16_19_MASK;
uint32_t filter3_id = SETCAN_MSGTYPE_PULSE << 16;
uint32_t filter3_mask = BITS_16_19_MASK;
// Первый фильтр: проверяем 20-27 биты, где 27-й всегда 1
SETCAN_CONFIG_FILTER(0, filter1_id, filter1_mask);
// Второй фильтр: проверяем 16-19 биты, равны SETCAN_MSGTYPE_BROADCASTE
SETCAN_CONFIG_FILTER(1, filter2_id, filter2_mask);
// Третий фильтр: проверяем 16-19 биты, равны SETCAN_MSGTYPE_PULSE
SETCAN_CONFIG_FILTER(2, filter3_id, filter3_mask);
}
/**
* @brief Отправляет CAN-сообщение в зависимости от типа сообщения.
* Эта функция анализирует тип сообщения в идентификаторе и вызывает
* соответствующую функцию отправки. В текущей реализации поддерживается только
* тип SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE.
* @param id Идентификатор CAN-сообщения, содержащий информацию о типе.
* @param data Структура с данными, которые необходимо отправить. Зависит от типа сообщения.
* @return Статус выполнения операции.
*/
SETCAN_StatusTypeDef SETCAN_SEND(SetCanId_t id, SetCanData_t data)
{
switch(id.Fields.MsgType)
{
case SETCAN_MSGTYPE_BROADCAST:
case SETCAN_MSGTYPE_DISCRETE:
case SETCAN_MSGTYPE_ANALOG:
{
return SETCAN_SEND_CORE(id, data.CoreData);
break;
}
case SETCAN_MSGTYPE_GENERAL_ADDRESS_SPACE:
{
/* Если есть регистры для отправки, указатель data должен быть валиден. */
if((data.GeneralAddressSpaceData.RegCount > 0U) && data.GeneralAddressSpaceData.Data == 0)
{
return SETCAN_ERROR;
}
return SETCAN_SEND_GENERAL_ADDRESS_SPACE(id, data.GeneralAddressSpaceData.RegStartAdr,
data.GeneralAddressSpaceData.Data,
data.GeneralAddressSpaceData.RegCount);
}
case SETCAN_MSGTYPE_MODBUS_COIL:
case SETCAN_MSGTYPE_MODBUS_DISCRETE:
case SETCAN_MSGTYPE_MODBUS_HOLDING:
case SETCAN_MSGTYPE_MODBUS_INPUT:
{
return SETCAN_SEND_MODBUS(id, data.ModbusData);
break;
}
case SETCAN_MSGTYPE_ERROR:
{
return SETCAN_SEND_ERROR(id, data.ErrorData);
break;
}
default:
return SETCAN_ERROR;
}
return SETCAN_ERROR;
}