Отправка Modbus. Промежуточный коммит

This commit is contained in:
2026-06-08 11:42:22 +03:00
parent 4da9640c3f
commit a01376255e
2 changed files with 198 additions and 92 deletions

View File

@@ -218,11 +218,26 @@ typedef union{
* Используется для хранения данных о регистре и его содержимом.
*/
typedef struct{
struct ProtoCanCoreData{
uint16_t Type;
uint16_t Body;
uint8_t *Data;
unsigned DataCount;
} CoreData;
struct ProtoCanGeneralAddressSpaceData{
uint16_t RegStartAdr; /**< Начальный адрес регистров. */
uint16_t *Data; /**< Указатель на массив данных. */
unsigned RegCount; /**< Количество регистров. */
} GeneralAddressSpaceData;
struct ProtoCanModbusData{
uint16_t StrAdr;
uint16_t *Data;
unsigned RegCount:4;
} ModbusData;
struct ProtoCanErrorData{
uint16_t Info;
uint16_t Code;
} ErrorData;
} ProtoCanData_t;
/**

View File

@@ -21,8 +21,8 @@ struct RXMsg rxMsg[PROTOCAN_RX_BUFFER_SIZE];
*/
_Bool IsLeapYear(uint8_t year)
{
year+=2000;
return (year%400==0)||((year%4==0)&&(year%100!=0));
year += 2000;
return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
}
/**
@@ -33,12 +33,13 @@ _Bool IsLeapYear(uint8_t year)
*/
uint16_t AvailableCanRxMsg(void)
{
return ((uint16_t)(PROTOCAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1)))%PROTOCAN_RX_BUFFER_SIZE;
return ((uint16_t)(PROTOCAN_RX_BUFFER_SIZE + (LastStep - CurrentStep + 1))) % PROTOCAN_RX_BUFFER_SIZE;
}
void PROTOCAN_DEINIT(uint8_t stage)
{
switch(stage) {
switch(stage)
{
case 3:
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
HAL_TIM_UnRegisterCallback(_HTIM, HAL_TIM_PERIOD_ELAPSED_CB_ID);
@@ -60,11 +61,13 @@ void PROTOCAN_DEINIT(uint8_t stage)
PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_HandleTypeDef *tmp_hrtc, TIM_HandleTypeDef *tmp_tim)
{
unsigned initStage = 0;
if(tmp_hcan) {
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, ProtoCanRxFifo0MsgPendingCallback);
if(CAN_RC_RESULT != HAL_OK) {
if(CAN_RC_RESULT != HAL_OK)
{
PROTOCAN_DEINIT(initStage);
return PROTOCAN_INIT_HRTC_ERROR;
}
@@ -74,23 +77,30 @@ PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_Handl
return PROTOCAN_INIT_HCAN_ERROR;
}
initStage++;
if(tmp_hrtc) {
if(tmp_hrtc)
{
_HRTC = tmp_hrtc;
} else {
}
else
{
PROTOCAN_DEINIT(initStage);
return PROTOCAN_INIT_HRTC_ERROR;
}
initStage++;
if(tmp_tim) {
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, ProtoCanPulseCallback);
if(TIM_RC_RESULT != HAL_OK) {
if(TIM_RC_RESULT != HAL_OK)
{
PROTOCAN_DEINIT(initStage);
return PROTOCAN_INIT_HRTC_ERROR;
}
#endif
} else {
}
else
{
PROTOCAN_DEINIT(initStage);
return PROTOCAN_INIT_TIM_ERROR;
}
@@ -111,53 +121,39 @@ PROTOCAN_INIT_StatusTypeDef PROTOCAN_INIT(CAN_HandleTypeDef *tmp_hcan, RTC_Handl
*/
void PROTOCAN_LOOP(void)
{
ProtoCanId_t testId;
testId.BitAll = 0;
testId.Fields.Priority = PROTOCAN_PRIORITY_STANDARD;
testId.Fields.Route = PROTOCAN_ROUTE_FROM_DEVICE;
testId.Fields.DeviceType = CURRENT_TYPE_DEVICE;
testId.Fields.DeviceID = CURRENT_ID_DEVICE;
testId.Fields.MsgType = PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE;
ProtoCanData_t testData;
uint16_t massiv[] = {0xABCD, 0x1234, 0xAFBF, 0x5678, 0x9AF1};
testData.GeneralAddressSpaceData.Data = massiv;
testData.GeneralAddressSpaceData.RegCount = 5;
testData.GeneralAddressSpaceData.RegStartAdr = 0xFA;
while(1)
{
if(AvailableCanRxMsg())
{
if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_ANALOG)
{
PROTOCAN_AnalogProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_BROADCAST)
{
PROTOCAN_BroadcastProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_DISCRETE)
{
PROTOCAN_DiscreticProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
{
ProtoCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_COIL ||
rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_DISCRETE ||
rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING ||
rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_INPUT)
{
PROTOCAN_ModbusProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_ERROR)
{
CanRequestError(rxMsg[CurrentStep]);
}
if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_ANALOG)
{
PROTOCAN_AnalogProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_BROADCAST)
{
PROTOCAN_BroadcastProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_DISCRETE)
{
PROTOCAN_DiscreticProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE)
{
ProtoCanMsgToGeneralAddressSpace(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_COIL ||
rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_DISCRETE ||
rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING ||
rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_MODBUS_INPUT)
{
PROTOCAN_ModbusProcessing(rxMsg[CurrentStep]);
}
else if(rxMsg[CurrentStep].eID.Fields.MsgType == PROTOCAN_MSGTYPE_ERROR)
{
CanRequestError(rxMsg[CurrentStep]);
}
CurrentStep = (uint16_t)(CurrentStep + 1) % PROTOCAN_RX_BUFFER_SIZE;
}
PROTOCAN_SEND(testId, testData);
}
}
@@ -225,20 +221,17 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastStatus(struct RXMsg _rxMsg)
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 (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
@@ -273,9 +266,9 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRestart(struct RXMsg _rxMsg)
uint64_t page = 0;
for(int i = 0; i < _rxMsg.DLC; i++)
{
page+=(_rxMsg.Data[i]<<(i*8));
page += (_rxMsg.Data[i] << (i * 8));
}
if((page>>CURRENT_ID_DEVICE)&0b1)
if((page >> CURRENT_ID_DEVICE) & 0b1)
{
NVIC_SystemReset();
}
@@ -291,25 +284,30 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRestart(struct RXMsg _rxMsg)
*/
__weak PROTOCAN_StatusTypeDef ProtoCanMsgToBroadcastRtcSetup(struct RXMsg _rxMsg)
{
if(_rxMsg.DLC > 7) {
if(_rxMsg.DLC > 7)
{
return PROTOCAN_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 PROTOCAN_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 PROTOCAN_ERROR;
//ERROR
} else {
PROTOCAN_RTC_SYNC(_rxMsg.Data);
}
}
return PROTOCAN_OK;
else
{
PROTOCAN_RTC_SYNC(_rxMsg.Data);
}
}
return PROTOCAN_OK;
}
/**
@@ -323,7 +321,8 @@ PROTOCAN_StatusTypeDef PROTOCAN_DiscreticProcessing(struct RXMsg _rxMsg)
{
msgBodyDiscreteType msg;
msg.Body = _rxMsg.eID.Fields.MsgBody;
switch(msg.Fields.Type){
switch(msg.Fields.Type)
{
case PROTOCAN_DISCRETE_ACCIDENT:
{
ProtoCanMsgToDiscreteAccident(_rxMsg);
@@ -660,14 +659,14 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToGeneralAddressSpace(struct RXMsg _rxM
data[3] = '-';
for(int i = 0; i < 4; i++)
{
unsigned sym = (_rxMsg.eID.Fields.MsgBody>>(12-(i*4)))&0xF;
unsigned sym = (_rxMsg.eID.Fields.MsgBody >> (12 - (i * 4))) & 0xF;
if(sym >= 10)
{
data[4+i] = sym%10+'A';
data[4 + i] = sym % 10 + 'A';
}
else
{
data[4+i] = sym+'0';
data[4 + i] = sym + '0';
}
}
return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
@@ -712,8 +711,8 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND_GENERAL_ADDRESS_SPACE(ProtoCanId_t id, uint
for(int i = 0; i < regsInPacket; i++)
{
canData[(i*2)+1] = LowByteOfWord(data[currentIndex+i]);
canData[(i*2)] = HighByteOfWord(data[currentIndex+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);
@@ -896,6 +895,85 @@ __weak PROTOCAN_StatusTypeDef ProtoCanMsgToModbusInput(struct RXMsg _rxMsg)
return (PROTOCAN_StatusTypeDef)HAL_CAN_AddTxMessage(_HCAN, &TxHeader, data, &TxMailBox);
}
/**
*/
int PROTOCAN_SEND_MODBUS(const ProtoCanId_t* id, const struct ProtoCanModbusData* modbusData)
{
if (!id || !modbusData)
{
return PROTOCAN_ERROR;
}
ProtoCanId_t localId = *id;
uint8_t msgType = localId.Fields.MsgType;
// Структура для отправки
CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailBox = 0;
uint8_t canData[8];
if(msgType == PROTOCAN_MSGTYPE_MODBUS_COIL || msgType == PROTOCAN_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; // Предположим, что используете расширенные ID
TxHeader.RTR = CAN_RTR_DATA;
TxHeader.TransmitGlobalTime = DISABLE;
TxHeader.DLC = modbusData->RegCount % 8 + 1;
// Формируем CAN данные
canData[0] = (*modbusData->Data) & 0xFF;
canData[1] = (*modbusData->Data >> 8) & 0xFF;
if (HAL_CAN_AddTxMessage(_HCAN, &TxHeader, canData, &TxMailBox) != HAL_OK)
{
return PROTOCAN_ERROR;
}
}
else if(msgType == PROTOCAN_MSGTYPE_MODBUS_HOLDING || msgType == PROTOCAN_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 PROTOCAN_ERROR;
}
regsProcessed += regsInPacket;
startAddress += regsInPacket;
}
}
else
{
return PROTOCAN_ERROR; // Неверный тип сообщения
}
return PROTOCAN_OK;
}
/**
* @brief __weak Функция отправки сообщения об ошибке.
* Посылает CAN сообщение с кодом ошибки. Используется, когда необходимо оповестить о неуспешной операции.
@@ -961,17 +1039,14 @@ void ProtoCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
//Расширенный ID
if(RxHeader.IDE == CAN_ID_EXT)
{
if(!((PROTOCAN_RX_BUFFER_SIZE + LastStep - (CurrentStep-1))&PROTOCAN_RX_BUFFER_SIZE))
if(!((PROTOCAN_RX_BUFFER_SIZE + LastStep - (CurrentStep - 1)) & PROTOCAN_RX_BUFFER_SIZE))
{
//Буффер переполнен
return;
}
uint16_t tmp_LastStep = (uint16_t)(LastStep + 1) % PROTOCAN_RX_BUFFER_SIZE;
ProtoCanId_t ExtID_Of_RX_MSG;
ExtID_Of_RX_MSG.BitAll = RxHeader.ExtId;
//Полученное сообщение - пульс устройств в сети
if(ExtID_Of_RX_MSG.Fields.MsgType == PROTOCAN_MSGTYPE_PULSE)
{
@@ -979,7 +1054,6 @@ void ProtoCanRxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
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);
}
}
@@ -1031,16 +1105,13 @@ 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];
@@ -1068,18 +1139,15 @@ void PROTOCAN_CONFIG_FILTER(uint8_t filterBank, uint32_t idFilter, uint32_t idMa
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();
Error_Handler();
}
}
@@ -1131,6 +1199,18 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data)
{
switch (id.Fields.MsgType)
{
case PROTOCAN_MSGTYPE_BROADCAST:
{
break;
}
case PROTOCAN_MSGTYPE_DISCRETE:
{
break;
}
case PROTOCAN_MSGTYPE_ANALOG:
{
break;
}
case PROTOCAN_MSGTYPE_GENERAL_ADDRESS_SPACE:
{
/* Если есть регистры для отправки, указатель data должен быть валиден. */
@@ -1142,6 +1222,17 @@ PROTOCAN_StatusTypeDef PROTOCAN_SEND(ProtoCanId_t id, ProtoCanData_t data)
data.GeneralAddressSpaceData.Data,
data.GeneralAddressSpaceData.RegCount);
}
case PROTOCAN_MSGTYPE_MODBUS_COIL:
case PROTOCAN_MSGTYPE_MODBUS_DISCRETE:
case PROTOCAN_MSGTYPE_MODBUS_HOLDING:
case PROTOCAN_MSGTYPE_MODBUS_INPUT:
{
break;
}
case PROTOCAN_MSGTYPE_ERROR:
{
break;
}
default:
return PROTOCAN_ERROR;
}