From a01376255e1da47502225e4c73637e0a8a4c718c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D1=8F=D1=87=D0=B5=D1=81=D0=BB=D0=B0=D0=B2=20=D0=A8?= =?UTF-8?q?=D1=82=D0=B5=D0=B9=D0=B1=D0=B5=D0=B7=D0=B0=D0=BD=D0=B4=D1=82?= Date: Mon, 8 Jun 2026 11:42:22 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20Modbus.=20=D0=9F=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/Inc/protocan.h | 15 +++ Core/Src/protocan.c | 275 +++++++++++++++++++++++++++++--------------- 2 files changed, 198 insertions(+), 92 deletions(-) diff --git a/Core/Inc/protocan.h b/Core/Inc/protocan.h index 114ad90..d6e8506 100644 --- a/Core/Inc/protocan.h +++ b/Core/Inc/protocan.h @@ -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; /** diff --git a/Core/Src/protocan.c b/Core/Src/protocan.c index de1bac8..e7778a1 100644 --- a/Core/Src/protocan.c +++ b/Core/Src/protocan.c @@ -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; }