Files
F103C8T6/Core/Src/protocan.c

992 lines
36 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 "protocan.h"
struct device CurrentDevice;
struct device Device_on_the_Network[8][16];
struct controlflags ControlFlags;
CAN_HandleTypeDef *_HCAN = 0;
RTC_HandleTypeDef *_HRTC = 0;
uint8_t CurrentStep = 1;
uint8_t LastStep = 0;
struct RXMsg rxMsg[CAN_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)(CAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1)))%CAN_RX_BUFFER_SIZE;
}
/**
* @brief Инициализация переферии
* @details Инициализация указателей на HCAN, HRTC, установка фильтров CAN.
* @note Фильтры CAN описаны в разделе PROTOCAN_CAN_FILTERS().
*/
PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_HandleTypeDef *tmp_hrtc)
{
//HAL_CAN_Start(_HCAN);
//HAL_CAN_ActivateNotification(_HCAN, CAN_IT_RX_FIFO0_MSG_PENDING | CAN_IT_ERROR | CAN_IT_BUSOFF | CAN_IT_LAST_ERROR_CODE);
//MX_TIM4_Init();
//MX_RTC_Init();
if(tmp_hcan)
_HCAN = tmp_hcan;
else
return PROTOCAN_HCAN_ERROR;
if(tmp_hrtc)
_HRTC = tmp_hrtc;
else
return PROTOCAN_HRTC_ERROR;
PROTOCAN_FILTERS();
ControlFlags.IsPulse = 1;
return PROTOCAN_OK;
}
/**
* @brief Функция с обработкой полученных запросов
* @details В бесконечном цикле функция ожидает приёма сообщения. После этого сообщение распределяется в зависимости от DataType.
* Обработка запроса аналоговых значений - PROTOCAN_AnalogProcessing().
* Обработка широковещательных запросов - PROTOCAN_BroadcastProcessing().
* Обработка запроса дискретных значений - PROTOCAN_DiscreticProcessing().
* Обработка Modbus - PROTOCAN_ModbusProcessing().
*/
void PROTOCAN_LOOP(void)
{
//HAL_TIM_Base_Start_IT(&htim4);
while(1)
{
if(AvailableCanRxMsg())
{
if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_ANALOG)
{
PROTOCAN_AnalogProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_BROADCAST)
{
PROTOCAN_BroadcastProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_DISCRETE)
{
PROTOCAN_DiscreticProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_GENERAL_ADDRESS_SPACE)
{
PROTOCAN_GeneralAddressSpace_Answer(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_COIL ||
rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_DISCRETE ||
rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_HOLDING ||
rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_MODBUS_INPUT)
{
PROTOCAN_ModbusProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == DATA_TYPE_ERROR)
{
CanRequestError(rxMsg[CurrentStep]);
}
CurrentStep = (uint16_t)(CurrentStep + 1) % CAN_RX_BUFFER_SIZE;
}
}
}
/**
* @brief Функция обработки аналоговых запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, сортирующая запросы по типам и вызывающая соответствующие функции. Типы запросов: Универсальный, Уставки, Напряжение, Ток, Температура.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_AnalogProcessing(struct RXMsg _rxMsg)
{
msgAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch (msg.Fields.SensorType)
{
case SENSOR_TYPE_ANALOG_UNIVERSAL:
{
return ProtoCanMsgToAnalogUniversal(_rxMsg);
break;
}
case SENSOR_TYPE_ANALOG_USTAVKI:
{
return ProtoCanMsgToAnalogUSTAVKI(_rxMsg);
break;
}
case SENSOR_TYPE_ANALOG_U:
{
return ProtoCanMsgToAnalogUSens(_rxMsg);
break;
}
case SENSOR_TYPE_ANALOG_I:
{
return ProtoCanMsgToAnalogISens(_rxMsg);
break;
}
case SENSOR_TYPE_ANALOG_T:
{
return ProtoCanMsgToAnalogTSens(_rxMsg);
break;
}
default:
//RESERVE SENSOR TYPE
return HAL_ERROR;
break;
}
return HAL_ERROR;
}
/**
* @brief Функция ответа на запрос данных со всех датчиков.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogUniversal(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос уставок по CAN.
* Создает сообщение с определенными данными и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется только для формирования ID.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogUSTAVKI(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос данных с датчиков напряжения.
* Создает сообщение с указанными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используемая для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус выполнения операции HAL, например HAL_OK при успешной отправке.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogUSens(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
msgAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'U';
data[1] = 'S';
data[2] = 47 + msg.Fields.SensorID / 1000;
data[3] = 47 + msg.Fields.SensorID / 100 % 10;
data[4] = 47 + msg.Fields.SensorID / 10 % 10;
data[5] = 47 + msg.Fields.SensorID % 10;
return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос данных с датчиков тока.
* Создает сообщение с определенными параметрами и отправляет его через CAN-шину.
* @param _rxMsg Структура входящего сообщения, используется для формирования идентификатора и данных.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успешной отправке.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogISens(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
msgAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'I';
data[1] = 'S';
data[2] = 47 + msg.Fields.SensorID / 1000;
data[3] = 47 + msg.Fields.SensorID / 100 % 10;
data[4] = 47 + msg.Fields.SensorID / 10 % 10;
data[5] = 47 + msg.Fields.SensorID % 10;
return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция ответа на запрос данных с датчиков температуры.
* Создает сообщение с определенным заголовком, формирует данные по ID сенсора и отправляет его через CAN.
* @param _rxMsg Структура полученного сообщения, которая используется для формирования идентификатора.
* @return HAL_StatusTypeDef Статус выполнения функции HAL, например HAL_OK при успехе.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToAnalogTSens(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 6;
msgAnalogType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
data[0] = 'T';
data[1] = 'S';
data[2] = 47 + msg.Fields.SensorID / 1000;
data[3] = 47 + msg.Fields.SensorID / 100 % 10;
data[4] = 47 + msg.Fields.SensorID / 10 % 10;
data[5] = 47 + msg.Fields.SensorID % 10;
return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Функция обработки широковещательных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, выполняющая команды, переданные в широковещательном формате с головного (master) устройства. Типы команд: Запрос статуса, запрос на включение или выключение, рестарт устройств, установка времени.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_BroadcastProcessing(struct RXMsg _rxMsg)
{
msgBroadcastType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch(msg.Fields.BroadcastType)
{
case SENSOR_TYPE_BROADCAST_STATUS:
{
//Обработка запроса статуса устройства
return ProtoCanMsgToBroadcastStatus(_rxMsg);
}
case SENSOR_TYPE_BROADCAST_ONOFF:
{
//Обработка запроса на вкл/выкл
ProtoCanMsgToBroadcastOnOff(_rxMsg);
break;
}
case SENSOR_TYPE_BROADCAST_RESTARTDEVICE:
{
ProtoCanMsgToBroadcastRestart(_rxMsg);
break;
}
case SENSOR_TYPE_BROADCAST_RTCSETUP:
{
//Обработка запроса на синхронизацию времени
//С головным устройством
ProtoCanMsgToBroadcastRtcSetup(_rxMsg);
break;;
}
default:
//RESERVE SENSOR TYPE.
return HAL_ERROR;
break;
}
return HAL_OK;
}
/**
* @brief Формирует и отправляет широковещательное CAN-сообщение для ответа на запрос статуса.
* Устанавливает идентификатор, собирает текущие параметры времени и даты, и отправляет сообщение.
* @param _rxMsg Структура полученного по CAN запроса.
* @return HAL_StatusTypeDef Статус операции HAL, например HAL_OK при успехе.
*/
__weak HAL_StatusTypeDef ProtoCanMsgToBroadcastStatus(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Виртуальная слабая функция для переключения режима пульсации ON/OFF.
* Меняет состояние флага IsPulse в структуре ControlFlags.
* В данном случае — просто инвертирует его логическое значение.
* @param _rxMsg Структура полученного CAN-сообщения (Пока не используется в функции).
*/
__weak void ProtoCanMsgToBroadcastOnOff(struct RXMsg _rxMsg)
{
ControlFlags.IsPulse = !ControlFlags.IsPulse;
}
/**
* @brief __weak функция для обработки широковещательного сообщения перезапуска устройства.
* Проверяет длину сообщения, извлекает ID из данных и, если она совпадает с текущим устройством,
* вызывает системный перезапуск.
* @param _rxMsg Структура полученного CAN-сообщения с командой перезапуска.
*/
__weak void ProtoCanMsgToBroadcastRestart(struct RXMsg _rxMsg)
{
if(_rxMsg.DLC == 0)
{
return;
}
msgBroadcastType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
if(msg.Fields.Page == (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;
}
/**
* @brief __weak функция для обработки запроса на синхронизацию RTC через широковещательное сообщение.
* Проверяет длину сообщения и корректность данных (время и дата). Если все проверки пройдены,
* вызывает функцию синхронизации RTC.
* @param _rxMsg Структура полученного CAN-сообщения с данными для установки времени и даты.
*/
__weak void ProtoCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg)
{
if(_rxMsg.DLC > 7) {
//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) {
//ERROR
} else {
PROTOCAN_RTC_SYNC(_rxMsg.Data);
}
}
}
/**
* @brief Функция обработки дискретных запросов.
* @param struct RXMsg _rxMsg - структура для полученного сообщения.
* @details Функция, формирующая и отправляющая ответ на запросы. Типы запросов: Аварии, Предупреждения, Управляющие сигналы, Флаги, Рестарт устройства, Изменение режима работы устройства, Запрос на устройство.
* @note Запрос на устройство. Головное (master) устройство запрашивает некоторое колличество параметров. В Data - 64 битовых адресса параметров, тип которых задаётся в Sensor ID. Имеется возможность запрашивать непоследовательные параметры.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_DiscreticProcessing(struct RXMsg _rxMsg)
{
msgDiscreteType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch(msg.Fields.Type){
case SENSOR_TYPE_DISCRETE_ACCIDENT:
{
ProtoCanMsgToDiscreteAccident(_rxMsg);
break;
}
case SENSOR_TYPE_DISCRETE_WARNING:
{
ProtoCanMsgToDiscreteWarning(_rxMsg);
break;
}
case SENSOR_TYPE_DISCRETE_CONTROL_SIGNALS:
{
ProtoCanMsgToDiscreteControlSignals(_rxMsg);
break;
}
case SENSOR_TYPE_DISCRETE_FLAGS:
{
ProtoCanMsgToDiscreteFlags(_rxMsg);
break;
}
case SENSOR_TYPE_DISCRETE_RESET:
{
ProtoCanMsgToDiscreteReset(_rxMsg);
break;
}
case SENSOR_TYPE_DISCRETE_CHANGE_MODE:
{
ProtoCanMsgToDiscreteChangeMode(_rxMsg);
break;
}
case SENSOR_TYPE_DISCRETE_REQUEST_LIST_OF_PARAMETERS:
{
ProtoCanMsgToDiscreteRequestListOfParameters(_rxMsg);
break;
}
default:
//RESERVE SENSOR TYPE.
return HAL_ERROR;
break;
}
return HAL_OK;
}
/**
* @brief Обработка запроса аварийных ситуаций.
* В текущей реализации — заглушка, ничего не выполняет.
* Можно реализовать отправку или обработку аварийных данных.
* @param _rxMsg Входящее сообщение с данными запроса.
*/
__weak void ProtoCanMsgToDiscreteAccident(struct RXMsg _rxMsg)
{
return;
}
/**
* @brief Обработка запроса предупреждений.
* В текущей реализации — заглушка.
* Можно реализовать обработку или ответ на предупреждения.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteWarning(struct RXMsg _rxMsg)
{
return;
}
/**
* @brief Обработка запроса управляющих сигналов.
* В текущей реализации — заглушка.
* Можно реализовать управление сигналами.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteControlSignals(struct RXMsg _rxMsg)
{
return;
}
/**
* @brief Обработка запроса флагов.
* В текущей реализации — заглушка.
* Можно реализовать работу с флагами.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteFlags(struct RXMsg _rxMsg)
{
return;
}
/**
* @brief Обработка рестарта системы по CAN-запросу.
* Выполняет программный рестарт системы.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteReset(struct RXMsg _rxMsg)
{
NVIC_SystemReset();
}
/**
* @brief Обработка запроса на изменение режима.
* В текущей реализации — заглушка.
* Можно реализовать изменение режима работы.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteChangeMode(struct RXMsg _rxMsg)
{
return;
}
/**
* @brief Обработка запроса на получение списка параметров.
* В текущей реализации — заглушка.
* Можно реализовать отправку списка параметров.
* @param _rxMsg Входящее сообщение.
*/
__weak void ProtoCanMsgToDiscreteRequestListOfParameters(struct RXMsg _rxMsg)
{
return;
}
/**
* @brief Обработка и ответ на запрос общего адресного пространства.
* Формирует и передает сообщение с данными о статусе "GAS-XXXX", где XXXX — значение из сообщения _rxMsg.
* @param _rxMsg Структура входящего сообщения, содержит идентификатор и тело сообщения.
*/
void PROTOCAN_GeneralAddressSpace_Answer(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
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';
}
HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief Обработка запроса на основе типа Modbus.
* В зависимости от типа сообщения, вызывает соответствующую функцию
* для отправки Modbus-запроса по CAN-шине.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
HAL_StatusTypeDef PROTOCAN_ModbusProcessing(struct RXMsg _rxMsg)
{
switch(_rxMsg.eID.Fields.MsgType)
{
case DATA_TYPE_MODBUS_COIL:
{
return ProtoCanMsgToModbusCoil(_rxMsg);
break;
}
case DATA_TYPE_MODBUS_DISCRETE:
{
return ProtoCanMsgToModbusDiscrete(_rxMsg);
break;
}
case DATA_TYPE_MODBUS_HOLDING:
{
return ProtoCanMsgToModbusHolding(_rxMsg);
break;
}
case DATA_TYPE_MODBUS_INPUT:
{
return ProtoCanMsgToModbusInput(_rxMsg);
break;
}
default:
//ERROR
return HAL_ERROR;
break;
}
return HAL_ERROR;
}
/**
* @brief __weak Функция отправки ответа на запрос Coil Modbus по CAN.
* Формирует CAN ответ на запрос Coil Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusCoil(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgModbusType 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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки ответа на запрос Discrete Modbus по CAN.
* Формирует CAN ответ на запрос Discrete Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusDiscrete(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgModbusType 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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки ответа на запрос Holding Modbus по CAN.
* Формирует CAN ответ на запрос Holding Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusHolding(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgModbusType 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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки ответа на запрос Input Modbus по CAN.
* Формирует CAN ответ на запрос Input Modbus и отправляет его.
* @param _rxMsg Структура входящего сообщения RXMsg.
* @return HAL_StatusTypeDef Статус выполнения команды CAN (успех или ошибка).
*/
__weak HAL_StatusTypeDef ProtoCanMsgToModbusInput(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 8;
msgModbusType 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 HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @brief __weak Функция отправки сообщения об ошибке.
* Посылает CAN сообщение с кодом ошибки. Используется, когда необходимо оповестить о неуспешной операции.
* @param _rxMsg Структура входящего сообщения RXMsg, данными которого формируется ответ-ошибка.
* @return HAL_StatusTypeDef Статус выполнения команды CAN.
*/
__weak HAL_StatusTypeDef CanRequestError(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;
extID tmp_eID;
tmp_eID.BitAll = _rxMsg.eID.BitAll;
tmp_eID.Fields.Route = ROUTE_SLAVE;
tmp_eID.Fields.MsgType = DATA_TYPE_ERROR;
msgErrorType msg;
msg.Fields.ErrorCode = 0xFF;
msg.Fields.Info = 0;
tmp_eID.Fields.MsgBody = msg.Body;
TxHeader.ExtId = tmp_eID.BitAll;
TxHeader.DLC = 0;
return HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
* @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(extID 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 HAL_CAN_RxFifo0MsgPendingCallback(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(!((CAN_RX_BUFFER_SIZE + LastStep - (CurrentStep-1))&CAN_RX_BUFFER_SIZE))
{
//Буффер переполнен
return;
}
uint16_t tmp_LastStep = (uint16_t)(LastStep + 1) % CAN_RX_BUFFER_SIZE;
extID ExtID_Of_RX_MSG;
ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId;
//Полученное сообщение - пульс устройств в сети
if(ExtID_Of_RX_MSG.Fields.MsgType == DATA_TYPE_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 PROTOCAN_Pulse_TIM_Handler()
{
if(ControlFlags.IsPulse)
{
static unsigned PulseStage = 0;
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
extID currentID;
currentID.BitAll = 0;
currentID.Fields.MsgBody = 0;
currentID.Fields.DeviceID = CURRENT_ID_DEVICE;
currentID.Fields.DeviceType = CURRENT_TYPE_DEVICE;
currentID.Fields.MsgType = DATA_TYPE_PULSE;
currentID.Fields.Route = ROUTE_SLAVE;
currentID.Fields.Priority = PRIORITY_STANDARD;
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 PROTOCAN_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 PROTOCAN_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;
// Разбиваем 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();
}
}
#define BIT27 (1UL << 27)
#define BITS_20_23_MASK (0xFUL << 20) // 4 бита (20..23)
#define BITS_24_26_MASK (0x7UL << 24) // 3 бита (24..26)
uint32_t filter1_id = BIT27 | (CURRENT_TYPE_DEVICE << 24) | (CURRENT_ID_DEVICE << 20);
uint32_t filter1_mask = BIT27 | BITS_24_26_MASK | BITS_20_23_MASK; // все проверяемые биты
#define BITS_16_19_MASK (0xFUL << 16)
uint32_t filter2_id = DATA_TYPE_BROADCAST << 16;
uint32_t filter2_mask = BITS_16_19_MASK;
uint32_t filter3_id = DATA_TYPE_PULSE << 16;
uint32_t filter3_mask = BITS_16_19_MASK;
/**
* @brief Конфигурирует три фильтра CAN для различных типов сообщений.
*/
void PROTOCAN_FILTERS()
{
// Первый фильтр: проверяем 20-27 биты, где 27-й всегда 1
PROTOCAN_CONFIG_FILTER(0, filter1_id, filter1_mask);
// Второй фильтр: проверяем 16-19 биты, равны DATA_TYPE_BROADCASTE
PROTOCAN_CONFIG_FILTER(1, filter2_id, filter2_mask);
// Третий фильтр: проверяем 16-19 биты, равны DATA_TYPE_PULSE
PROTOCAN_CONFIG_FILTER(2, filter3_id, filter3_mask);
}