#include "plsr_modbus_control.h" #include "modbus_data_store.h" #include "plc_device.h" #include "plsr_address_map.h" #include "plsr_core.h" #include "plsr_hal_f407.h" #include "plsr_job.h" #include "plsr_modbus_data.h" #include "plsr_persistence.h" #include #include #ifndef PLSR_HOST_TEST #include "stm32f4xx.h" #include "usbd_cdc_if.h" #endif #define PLSR_MODBUS_MAGIC_LOW (0x504CU) #define PLSR_MODBUS_MAGIC_HIGH (0x5352U) #define PLSR_MODBUS_CAPABILITIES (0x0007U) #define PLSR_MODBUS_CALL_REQUEST_WORDS (16UL) #define PLSR_MODBUS_CALL_RESPONSE_WORDS (12UL) #define PLSR_MODBUS_COMMAND_REQUEST_WORDS (8UL) #define PLSR_MODBUS_COMMAND_RESPONSE_WORDS (8UL) #define PLSR_MODBUS_S0_HEADER_WORDS (10UL) #define PLSR_MODBUS_S0_SEGMENT_WORDS (10UL) #define PLSR_MODBUS_S1_WORDS (4UL) #define PLSR_MODBUS_HASH_OFFSET (2166136261UL) #define PLSR_MODBUS_HASH_PRIME (16777619UL) #define PLSR_MODBUS_PERSIST_MAGIC_A (0xDA7AU) #define PLSR_MODBUS_PERSIST_MAGIC_B (0x51F0U) #define PLSR_MODBUS_PERSIST_ARM (0xA55AU) #define PLSR_MODBUS_PERSIST_INVALIDATE_HSD (1U) #define PLSR_MODBUS_PERSIST_INVALIDATE_SFD (2U) typedef struct { PLSR_CALL call; uint32_t fingerprint; uint8_t valid; } PLSR_MODBUS_COMMITTED_CALL; static uint16_t PlsrModbusBaseAddress; static uint8_t PlsrModbusEnabled; static uint32_t PlsrModbusLastCallRequestSequence; static uint32_t PlsrModbusLastCommandRequestSequence; static uint32_t PlsrModbusStatusGeneration[PLSR_AXIS_COUNT]; static uint32_t PlsrModbusPersistenceGeneration; static uint32_t PlsrModbusUsbDiagnosticsGeneration; static uint32_t PlsrModbusLastPersistenceRequestSequence; static PLSR_MODBUS_COMMITTED_CALL PlsrModbusCommitted[PLSR_AXIS_COUNT]; static uint16_t PlsrModbusStatusWords[PLSR_AXIS_COUNT] [PLSR_MODBUS_AXIS_STATUS_WORDS]; static const uint16_t PlsrModbusZeroWindow[PLSR_MODBUS_WINDOW_WORDS] = {0U}; static void PlsrModbusPutU32(uint16_t *words, uint32_t offset, uint32_t value) { words[offset] = (uint16_t)(value & 0xFFFFUL); words[offset + 1UL] = (uint16_t)(value >> 16U); } static void PlsrModbusPutU64(uint16_t *words, uint32_t offset, uint64_t value) { words[offset] = (uint16_t)(value & 0xFFFFULL); words[offset + 1UL] = (uint16_t)((value >> 16U) & 0xFFFFULL); words[offset + 2UL] = (uint16_t)((value >> 32U) & 0xFFFFULL); words[offset + 3UL] = (uint16_t)(value >> 48U); } static uint32_t PlsrModbusGetU32(const uint16_t *words, uint32_t offset) { return ((uint32_t)words[offset + 1UL] << 16U) | words[offset]; } static uint64_t PlsrModbusGetU64(const uint16_t *words, uint32_t offset) { return ((uint64_t)words[offset + 3UL] << 48U) | ((uint64_t)words[offset + 2UL] << 32U) | ((uint64_t)words[offset + 1UL] << 16U) | words[offset]; } static uint8_t PlsrModbusReadWords(uint32_t offset, uint16_t *words, uint32_t wordCount) { uint32_t index; if (words == NULL) { return 0U; } for (index = 0UL; index < wordCount; index++) { if (ModbusDataReadWord(MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + offset + index, &words[index]) == 0U) { return 0U; } } return 1U; } static uint8_t PlsrModbusRangesOverlap(uint32_t firstA, uint32_t countA, uint32_t firstB, uint32_t countB) { return ((firstA < (firstB + countB)) && (firstB < (firstA + countA))) ? 1U : 0U; } static uint32_t PlsrModbusHashWord(uint32_t hash, uint16_t value) { hash ^= (uint8_t)(value & 0x00FFU); hash *= PLSR_MODBUS_HASH_PRIME; hash ^= (uint8_t)(value >> 8U); hash *= PLSR_MODBUS_HASH_PRIME; return hash; } static uint32_t PlsrModbusHashU32(uint32_t hash, uint32_t value) { hash = PlsrModbusHashWord(hash, (uint16_t)(value & 0xFFFFUL)); return PlsrModbusHashWord(hash, (uint16_t)(value >> 16U)); } static PLSR_RESULT PlsrModbusFingerprintCall(const PLSR_CALL *call, uint32_t *fingerprint) { uint16_t word; int32_t segmentCount; uint32_t s0Words; uint32_t index; uint32_t hash = PLSR_MODBUS_HASH_OFFSET; if ((call == NULL) || (fingerprint == NULL) || (call->source.readDword == NULL) || (call->source.readWord == NULL) || (call->source.validateWords == NULL)) { return PLSR_RESULT_INVALID_ARGUMENT; } if (call->source.readDword(call->source.context, call->s0.device, call->s0.address, &segmentCount) == 0U) { return PLSR_RESULT_DATA_ACCESS; } if ((segmentCount < 1) || (segmentCount > (int32_t)PLSR_MAX_SEGMENTS)) { return PLSR_RESULT_SEGMENT_OVERFLOW; } s0Words = PLSR_MODBUS_S0_HEADER_WORDS + (uint32_t)segmentCount * PLSR_MODBUS_S0_SEGMENT_WORDS; if ((call->source.validateWords(call->source.context, call->s0.device, call->s0.address, s0Words) == 0U) || (call->source.validateWords(call->source.context, call->s1.device, call->s1.address, PLSR_MODBUS_S1_WORDS) == 0U)) { return PLSR_RESULT_DATA_ACCESS; } if (((call->s0.device == PLSR_DEVICE_D) && (PlsrModbusRangesOverlap(call->s0.address, s0Words, PlsrModbusBaseAddress, PLSR_MODBUS_WINDOW_WORDS) != 0U)) || ((call->s1.device == PLSR_DEVICE_D) && (PlsrModbusRangesOverlap(call->s1.address, PLSR_MODBUS_S1_WORDS, PlsrModbusBaseAddress, PLSR_MODBUS_WINDOW_WORDS) != 0U))) { return PLSR_RESULT_BLOCK_OVERLAP; } hash = PlsrModbusHashWord(hash, (uint16_t)call->s0.device); hash = PlsrModbusHashU32(hash, call->s0.address); for (index = 0UL; index < s0Words; index++) { if (call->source.readWord(call->source.context, call->s0.device, call->s0.address + index, &word) == 0U) { return PLSR_RESULT_DATA_ACCESS; } hash = PlsrModbusHashWord(hash, word); } hash = PlsrModbusHashWord(hash, (uint16_t)call->s1.device); hash = PlsrModbusHashU32(hash, call->s1.address); for (index = 0UL; index < PLSR_MODBUS_S1_WORDS; index++) { if (call->source.readWord(call->source.context, call->s1.device, call->s1.address + index, &word) == 0U) { return PLSR_RESULT_DATA_ACCESS; } hash = PlsrModbusHashWord(hash, word); } hash = PlsrModbusHashWord(hash, (uint16_t)call->s2.type); hash = PlsrModbusHashWord(hash, (uint16_t)call->s2.data.device); hash = PlsrModbusHashU32(hash, call->s2.data.address); hash = PlsrModbusHashU32(hash, (uint32_t)call->s2.constant); if (call->s2.type == PLSR_OPERAND_DATA) { for (index = 0UL; index < 2UL; index++) { if (call->source.readWord(call->source.context, call->s2.data.device, call->s2.data.address + index, &word) == 0U) { return PLSR_RESULT_DATA_ACCESS; } hash = PlsrModbusHashWord(hash, word); } } hash = PlsrModbusHashWord(hash, call->dAxis); hash = PlsrModbusHashWord(hash, call->outputModeOverride); *fingerprint = hash; return PLSR_RESULT_OK; } static PLSR_RESULT PlsrModbusBuildCall(const uint16_t *request, PLSR_CALL *call) { uint16_t s2Type; uint16_t outputMode; if ((request == NULL) || (call == NULL)) { return PLSR_RESULT_INVALID_ARGUMENT; } s2Type = request[8UL]; outputMode = request[13UL]; if ((request[2UL] > (uint16_t)PLSR_DEVICE_FD) || (request[5UL] > (uint16_t)PLSR_DEVICE_FD) || (s2Type > (uint16_t)PLSR_OPERAND_DATA) || ((s2Type == (uint16_t)PLSR_OPERAND_DATA) && (request[9UL] > (uint16_t)PLSR_DEVICE_FD)) || (request[12UL] >= PLSR_AXIS_COUNT) || ((outputMode > (uint16_t)PLSR_OUTPUT_CW_CCW) && (outputMode != PLSR_OUTPUT_MODE_FROM_SFD))) { return PLSR_RESULT_INVALID_ARGUMENT; } (void)memset(call, 0, sizeof(*call)); call->sequence = PlsrModbusGetU32(request, 0UL); call->s0.device = (PLSR_DEVICE_TYPE)request[2UL]; call->s0.address = PlsrModbusGetU32(request, 3UL); call->s1.device = (PLSR_DEVICE_TYPE)request[5UL]; call->s1.address = PlsrModbusGetU32(request, 6UL); call->s2.type = (PLSR_OPERAND_TYPE)s2Type; call->s2.data.device = (PLSR_DEVICE_TYPE)request[9UL]; call->s2.data.address = PlsrModbusGetU32(request, 10UL); call->s2.constant = (int32_t)PlsrModbusGetU32(request, 10UL); call->dAxis = (uint8_t)request[12UL]; call->outputModeOverride = (uint8_t)outputMode; PlsrModbusDataSourceInit(&call->source); return PLSR_RESULT_OK; } static void PlsrModbusPublishCallResponse(uint32_t sequence, uint16_t operation, PLSR_RESULT result, const PLSR_PARSE_DETAIL *detail, uint8_t committed) { uint16_t response[PLSR_MODBUS_CALL_RESPONSE_WORDS] = {0U}; PlsrModbusPutU32(response, 0UL, sequence); response[2UL] = operation; response[3UL] = (uint16_t)result; if (detail != NULL) { response[4UL] = (uint16_t)detail->result; response[5UL] = (uint16_t)detail->block; PlsrModbusPutU32(response, 6UL, detail->address); PlsrModbusPutU32(response, 8UL, (uint32_t)detail->value); response[10UL] = detail->segment; } response[11UL] = committed; (void)ModbusDataWriteWords(MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_CALL_RESPONSE_OFFSET, response, PLSR_MODBUS_CALL_RESPONSE_WORDS); } static void PlsrModbusHandleCallRequest(void) { uint16_t request[PLSR_MODBUS_CALL_REQUEST_WORDS]; PLSR_CALL call; PLSR_PARSE_DETAIL detail; PLSR_RESULT result; uint32_t sequence; uint32_t fingerprint; uint16_t operation; uint8_t axis = 0U; if (PlsrModbusReadWords(PLSR_MODBUS_CALL_REQUEST_OFFSET, request, PLSR_MODBUS_CALL_REQUEST_WORDS) == 0U) { return; } sequence = PlsrModbusGetU32(request, 0UL); if ((sequence == 0UL) || (sequence == PlsrModbusLastCallRequestSequence)) { return; } PlsrModbusLastCallRequestSequence = sequence; operation = request[14UL]; (void)memset(&detail, 0, sizeof(detail)); result = PlsrModbusBuildCall(request, &call); if (result == PLSR_RESULT_OK) { axis = call.dAxis; } if ((result == PLSR_RESULT_OK) && (operation == PLSR_MODBUS_CALL_COMMIT)) { result = PlsrValidateCall(&call, &detail); if (result == PLSR_RESULT_OK) { result = PlsrModbusFingerprintCall(&call, &fingerprint); } if (result == PLSR_RESULT_OK) { PlsrModbusCommitted[axis].call = call; PlsrModbusCommitted[axis].fingerprint = fingerprint; PlsrModbusCommitted[axis].valid = 1U; } else { PlsrModbusCommitted[axis].valid = 0U; } } else if ((result == PLSR_RESULT_OK) && (operation == PLSR_MODBUS_CALL_START)) { if (PlsrModbusCommitted[axis].valid == 0U) { result = PLSR_RESULT_INVALID_STATE; } else { result = PlsrModbusFingerprintCall( &PlsrModbusCommitted[axis].call, &fingerprint); if ((result == PLSR_RESULT_OK) && (fingerprint != PlsrModbusCommitted[axis].fingerprint)) { result = PLSR_RESULT_BUSY; } if (result == PLSR_RESULT_OK) { result = PlsrValidateCall(&PlsrModbusCommitted[axis].call, &detail); } if (result == PLSR_RESULT_OK) { PlsrModbusCommitted[axis].call.sequence = sequence; result = PlsrPostCall(&PlsrModbusCommitted[axis].call); } } } else if (result == PLSR_RESULT_OK) { result = PLSR_RESULT_INVALID_ARGUMENT; } PlsrModbusPublishCallResponse( sequence, operation, result, &detail, (axis < PLSR_AXIS_COUNT) ? PlsrModbusCommitted[axis].valid : 0U); } static void PlsrModbusPublishCommandResponse(uint32_t sequence, uint16_t opcode, uint16_t axis, PLSR_RESULT result) { uint16_t response[PLSR_MODBUS_COMMAND_RESPONSE_WORDS] = {0U}; PlsrModbusPutU32(response, 0UL, sequence); response[2UL] = opcode; response[3UL] = axis; response[4UL] = (uint16_t)result; (void)ModbusDataWriteWords(MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_COMMAND_RESPONSE_OFFSET, response, PLSR_MODBUS_COMMAND_RESPONSE_WORDS); } static void PlsrModbusHandleCommandRequest(void) { uint16_t request[PLSR_MODBUS_COMMAND_REQUEST_WORDS]; PLSR_COMMAND command; PLSR_RESULT result; uint32_t sequence; if (PlsrModbusReadWords(PLSR_MODBUS_COMMAND_REQUEST_OFFSET, request, PLSR_MODBUS_COMMAND_REQUEST_WORDS) == 0U) { return; } sequence = PlsrModbusGetU32(request, 0UL); if ((sequence == 0UL) || (sequence == PlsrModbusLastCommandRequestSequence)) { return; } PlsrModbusLastCommandRequestSequence = sequence; (void)memset(&command, 0, sizeof(command)); if ((request[2UL] > (uint16_t)PLSR_CMD_SELF_TEST) || (request[2UL] == (uint16_t)PLSR_CMD_START) || (request[3UL] >= PLSR_AXIS_COUNT)) { result = PLSR_RESULT_INVALID_ARGUMENT; } else { command.sequence = sequence; command.opcode = (PLSR_COMMAND_OPCODE)request[2UL]; command.axis = (uint8_t)request[3UL]; command.argument = (int64_t)PlsrModbusGetU64(request, 4UL); result = PlsrPostCommand(&command); } PlsrModbusPublishCommandResponse(sequence, request[2UL], request[3UL], result); } static uint8_t PlsrModbusPersistenceAllAxesIdle(void) { PLSR_STATUS status; uint8_t axis; for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { if ((PlsrGetStatus(axis, &status) != PLSR_RESULT_OK) || (status.busy != 0U) || (status.pulseActive != 0U)) { return 0U; } } return 1U; } static uint32_t PlsrModbusEnterPersistenceDiagnosticCritical(void) { #ifdef PLSR_HOST_TEST return 0UL; #else uint32_t interruptState = __get_PRIMASK(); __disable_irq(); __DMB(); return interruptState; #endif } static void PlsrModbusExitPersistenceDiagnosticCritical( uint32_t interruptState) { #ifdef PLSR_HOST_TEST (void)interruptState; #else __DMB(); if (interruptState == 0UL) { __enable_irq(); } #endif } static PLSR_RESULT PlsrModbusMapPersistenceResult( PLSR_PERSISTENCE_RESULT persistenceResult) { if (persistenceResult == PLSR_PERSISTENCE_OK) { return PLSR_RESULT_OK; } if (persistenceResult == PLSR_PERSISTENCE_NOT_IMPLEMENTED) { return PLSR_RESULT_NOT_SUPPORTED; } if (persistenceResult == PLSR_PERSISTENCE_INVALID_ARGUMENT) { return PLSR_RESULT_INVALID_ARGUMENT; } return PLSR_RESULT_PERSISTENCE_ERROR; } static void PlsrModbusPublishPersistenceResponse(uint32_t sequence, uint16_t operation, PLSR_RESULT result) { PLSR_PERSISTENCE_DIAGNOSTICS diagnostics; uint16_t response[PLSR_MODBUS_PERSISTENCE_RESPONSE_WORDS] = {0U}; PlsrPersistenceGetDiagnostics(&diagnostics); PlsrModbusPutU32(response, 0UL, sequence); response[2UL] = operation; response[3UL] = (uint16_t)result; response[4UL] = (uint16_t)diagnostics.hsdValidMask | ((uint16_t)diagnostics.sfdValidMask << 8U); response[5UL] = (uint16_t)diagnostics.hsdNewestMask | ((uint16_t)diagnostics.sfdNewestMask << 8U); response[6UL] = diagnostics.destructiveDiagnosticEnabled; (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_PERSISTENCE_RESPONSE_OFFSET, response, PLSR_MODBUS_PERSISTENCE_RESPONSE_WORDS); } static void PlsrModbusHandlePersistenceRequest(void) { uint16_t request[PLSR_MODBUS_PERSISTENCE_REQUEST_WORDS]; const uint16_t clearRequest[PLSR_MODBUS_PERSISTENCE_REQUEST_WORDS] = {0U}; PLSR_PERSISTENCE_RESULT persistenceResult; PLSR_PERSISTENCE_DIAG_TARGET target; PLSR_RESULT result; uint32_t sequence; uint32_t inverseSequence; uint32_t interruptState; uint16_t operation; if (PlsrModbusReadWords(PLSR_MODBUS_PERSISTENCE_REQUEST_OFFSET, request, PLSR_MODBUS_PERSISTENCE_REQUEST_WORDS) == 0U) { return; } if ((request[0UL] != PLSR_MODBUS_PERSIST_MAGIC_A) || (request[1UL] != PLSR_MODBUS_PERSIST_MAGIC_B)) { return; } sequence = PlsrModbusGetU32(request, 2UL); inverseSequence = PlsrModbusGetU32(request, 4UL); operation = request[6UL]; if ((sequence != 0UL) && (sequence == PlsrModbusLastPersistenceRequestSequence)) { return; } if (sequence != 0UL) { PlsrModbusLastPersistenceRequestSequence = sequence; } result = PLSR_RESULT_INVALID_ARGUMENT; if ((sequence != 0UL) && (inverseSequence == ~sequence) && (request[7UL] == PLSR_MODBUS_PERSIST_ARM) && ((operation == PLSR_MODBUS_PERSIST_INVALIDATE_HSD) || (operation == PLSR_MODBUS_PERSIST_INVALIDATE_SFD))) { /* Keep the idle check and the optional one-word invalidation in one * scheduling exclusion window. This diagnostic is disabled in * normal builds; when enabled, no START can race the Flash write. */ interruptState = PlsrModbusEnterPersistenceDiagnosticCritical(); if (PlsrModbusPersistenceAllAxesIdle() == 0U) { result = PLSR_RESULT_BUSY; } else { target = (operation == PLSR_MODBUS_PERSIST_INVALIDATE_HSD) ? PLSR_PERSISTENCE_DIAG_TARGET_HSD : PLSR_PERSISTENCE_DIAG_TARGET_SFD; persistenceResult = PlsrPersistenceDiagnosticInvalidateNewest(target); result = PlsrModbusMapPersistenceResult(persistenceResult); } PlsrModbusExitPersistenceDiagnosticCritical(interruptState); } PlsrModbusPublishPersistenceResponse(sequence, operation, result); (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_PERSISTENCE_REQUEST_OFFSET, clearRequest, PLSR_MODBUS_PERSISTENCE_REQUEST_WORDS); } static void PlsrModbusPublishAxisStatus(uint8_t axis) { PLSR_STATUS status; uint16_t *words = PlsrModbusStatusWords[axis]; uint32_t flags = 0UL; uint32_t generation; if (PlsrGetStatus(axis, &status) != PLSR_RESULT_OK) { return; } generation = PlsrModbusStatusGeneration[axis] + 2UL; if (generation == 0UL) { generation = 2UL; } PlsrModbusStatusGeneration[axis] = generation; (void)memset(words, 0, sizeof(PlsrModbusStatusWords[axis])); if (status.busy != 0U) flags |= (1UL << 0U); if (status.pulseActive != 0U) flags |= (1UL << 1U); if (status.done != 0U) flags |= (1UL << 2U); if (status.wait != 0U) flags |= (1UL << 3U); if (status.directionPositive != 0U) flags |= (1UL << 4U); if (status.positionValid != 0U) flags |= (1UL << 5U); if (status.jobValid != 0U) flags |= (1UL << 6U); if (status.speedClamped != 0U) flags |= (1UL << 7U); if (status.positionOverflow != 0U) flags |= (1UL << 8U); if (status.positiveLimitActive != 0U) flags |= (1UL << 9U); if (status.negativeLimitActive != 0U) flags |= (1UL << 10U); if (status.emergencyLatched != 0U) flags |= (1UL << 11U); if (status.backlashActive != 0U) flags |= (1UL << 12U); PlsrModbusPutU32(words, 0UL, generation); words[2UL] = (uint16_t)status.state; PlsrModbusPutU32(words, 3UL, flags); words[5UL] = (uint16_t)status.outputMode; words[6UL] = (uint16_t)status.error; words[7UL] = (uint16_t)status.stopReason; words[8UL] = (uint16_t)status.lastCommandResult; words[9UL] = status.s2Set; PlsrModbusPutU32(words, 10UL, status.lastCommandSequence); PlsrModbusPutU32(words, 12UL, status.illegalTransitionCount); PlsrModbusPutU32(words, 14UL, status.pendingEvents); PlsrModbusPutU64(words, 16UL, (uint64_t)status.logicalPosition); PlsrModbusPutU64(words, 20UL, (uint64_t)status.taskPulses); PlsrModbusPutU64(words, 24UL, (uint64_t)status.totalPulses); PlsrModbusPutU64(words, 28UL, status.physicalPulses); words[32UL] = status.segmentCount; words[33UL] = status.startSegment; words[34UL] = status.currentSegment; words[35UL] = status.directionPoint; words[36UL] = status.highResourceMask; words[37UL] = status.hardwareCounter; PlsrModbusPutU32(words, 38UL, status.currentFrequencyHz); PlsrModbusPutU32(words, 40UL, status.targetFrequencyHz); PlsrModbusPutU32(words, 42UL, status.liveFrequencyRejectCount); words[44UL] = (uint16_t)status.lastLiveFrequencyResult; PlsrModbusPutU32(words, 46UL, generation); (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_AXIS_STATUS_OFFSET + (uint32_t)axis * PLSR_MODBUS_AXIS_STATUS_WORDS, words, PLSR_MODBUS_AXIS_STATUS_WORDS); } static void PlsrModbusPublishPerformance(void) { uint16_t words[PLSR_MODBUS_PERFORMANCE_WORDS]; uint16_t stageWords[PLSR_MODBUS_STAGE_PERFORMANCE_WORDS]; uint16_t abGateWords[PLSR_MODBUS_AB_GATE_PERFORMANCE_WORDS]; uint32_t outputCycles = PlsrHwGetMaxOutputIsrCycles(); uint32_t counterCycles = PlsrHwGetMaxCounterIsrCycles(); uint8_t stage; PlsrModbusPutU32(words, 0UL, PlsrGetMaxProcessCycles()); PlsrModbusPutU32(words, 2UL, PlsrGetMaxProcessResponseCycles()); PlsrModbusPutU32(words, 4UL, PlsrHwGetMaxControlIsrCycles()); words[6UL] = (uint16_t)((outputCycles > UINT16_MAX) ? UINT16_MAX : outputCycles); words[7UL] = (uint16_t)((counterCycles > UINT16_MAX) ? UINT16_MAX : counterCycles); (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_PERFORMANCE_OFFSET, words, PLSR_MODBUS_PERFORMANCE_WORDS); for (stage = 0U; stage < PLSR_PROCESS_STAGE_COUNT; stage++) { PlsrModbusPutU32(stageWords, (uint32_t)stage * 2UL, PlsrGetMaxProcessStageCycles(stage)); } (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_STAGE_PERFORMANCE_OFFSET, stageWords, PLSR_MODBUS_STAGE_PERFORMANCE_WORDS); PlsrModbusPutU32(abGateWords, 0UL, PlsrHwGetMaxAbGateCycles()); (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_AB_GATE_PERFORMANCE_OFFSET, abGateWords, PLSR_MODBUS_AB_GATE_PERFORMANCE_WORDS); } static void PlsrModbusPublishPersistence(void) { PLSR_PERSISTENCE_DIAGNOSTICS diagnostics; uint16_t words[PLSR_MODBUS_PERSISTENCE_WORDS] = {0U}; uint32_t flags = 0UL; uint32_t generation = PlsrModbusPersistenceGeneration + 2UL; if (generation == 0UL) { generation = 2UL; } PlsrModbusPersistenceGeneration = generation; PlsrPersistenceGetDiagnostics(&diagnostics); if (PlcDeviceIsHsdDirty() != 0U) flags |= (1UL << 0U); if (PlcDeviceIsSfdDirty() != 0U) flags |= (1UL << 1U); if (PlcDeviceGetRestoredHsdPositionValid() != 0U) { flags |= (1UL << 2U); } if (PlcDeviceGetRestoredHsdLastBusy() != 0U) { flags |= (1UL << 3U); } if (diagnostics.destructiveDiagnosticEnabled != 0U) { flags |= (1UL << 4U); } PlsrModbusPutU32(words, 0UL, generation); words[2UL] = PLSR_MODBUS_PERSISTENCE_VERSION; words[3UL] = (uint16_t)diagnostics.hsdValidMask | ((uint16_t)diagnostics.sfdValidMask << 8U); words[4UL] = (uint16_t)diagnostics.hsdNewestMask | ((uint16_t)diagnostics.sfdNewestMask << 8U); words[5UL] = (uint16_t)flags; words[6UL] = (uint16_t)diagnostics.lastHsdLoadResult; words[7UL] = (uint16_t)diagnostics.lastSfdLoadResult; words[8UL] = (uint16_t)diagnostics.lastHsdSaveResult; words[9UL] = (uint16_t)diagnostics.lastSfdSaveResult; words[10UL] = (uint16_t)diagnostics.lastSfdEraseResult; PlsrModbusPutU32(words, 12UL, diagnostics.hsdGeneration[0]); PlsrModbusPutU32(words, 14UL, diagnostics.hsdGeneration[1]); PlsrModbusPutU32(words, 16UL, diagnostics.sfdGeneration[0]); PlsrModbusPutU32(words, 18UL, diagnostics.sfdGeneration[1]); PlsrModbusPutU32(words, 20UL, diagnostics.hsdSaveCount); PlsrModbusPutU32(words, 22UL, diagnostics.sfdSaveCount); PlsrModbusPutU32(words, 24UL, diagnostics.selectedHsdCrc32); PlsrModbusPutU32(words, 26UL, diagnostics.selectedSfdCrc32); PlsrModbusPutU32(words, 28UL, generation); (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_PERSISTENCE_OFFSET, words, PLSR_MODBUS_PERSISTENCE_WORDS); } static void PlsrModbusPublishUsbDiagnostics(void) { uint16_t words[PLSR_MODBUS_USB_DIAGNOSTICS_WORDS] = {0U}; uint32_t generation = PlsrModbusUsbDiagnosticsGeneration + 2UL; #ifndef PLSR_HOST_TEST USB_CDC_RUNTIME_DIAGNOSTICS diagnostics; (void)memset(&diagnostics, 0, sizeof(diagnostics)); (void)CDC_GetRuntimeDiagnostics(&diagnostics); #endif if (generation == 0UL) { generation = 2UL; } PlsrModbusUsbDiagnosticsGeneration = generation; PlsrModbusPutU32(words, 0UL, generation); words[2UL] = PLSR_MODBUS_USB_DIAGNOSTICS_VERSION; #ifndef PLSR_HOST_TEST words[3UL] = diagnostics.initialized; PlsrModbusPutU32(words, 4UL, diagnostics.rxPacketCount); PlsrModbusPutU32(words, 6UL, diagnostics.rxByteCount); PlsrModbusPutU32(words, 8UL, diagnostics.rxRearmFailureCount); PlsrModbusPutU32(words, 10UL, diagnostics.txRequestCount); PlsrModbusPutU32(words, 12UL, diagnostics.txByteCount); PlsrModbusPutU32(words, 14UL, diagnostics.txBusyCount); PlsrModbusPutU32(words, 16UL, diagnostics.txFailureCount); PlsrModbusPutU32(words, 18UL, diagnostics.txCompleteCount); #endif PlsrModbusPutU32(words, 20UL, generation); (void)ModbusDataWriteWords( MODBUS_DATA_DEVICE_D, (uint32_t)PlsrModbusBaseAddress + PLSR_MODBUS_USB_DIAGNOSTICS_OFFSET, words, PLSR_MODBUS_USB_DIAGNOSTICS_WORDS); } PLSR_RESULT PlsrModbusControlInit(uint16_t baseAddress) { uint16_t header[8] = {0U}; if (ModbusDataValidateWords(MODBUS_DATA_DEVICE_D, baseAddress, PLSR_MODBUS_WINDOW_WORDS) == 0U) { return PLSR_RESULT_DATA_ACCESS; } PlsrModbusBaseAddress = baseAddress; PlsrModbusEnabled = 0U; PlsrModbusLastCallRequestSequence = 0UL; PlsrModbusLastCommandRequestSequence = 0UL; PlsrModbusLastPersistenceRequestSequence = 0UL; PlsrModbusPersistenceGeneration = 0UL; PlsrModbusUsbDiagnosticsGeneration = 0UL; (void)memset(PlsrModbusCommitted, 0, sizeof(PlsrModbusCommitted)); (void)memset(PlsrModbusStatusGeneration, 0, sizeof(PlsrModbusStatusGeneration)); if (ModbusDataWriteWords(MODBUS_DATA_DEVICE_D, baseAddress, PlsrModbusZeroWindow, PLSR_MODBUS_WINDOW_WORDS) == 0U) { return PLSR_RESULT_DATA_ACCESS; } header[0UL] = PLSR_MODBUS_MAGIC_LOW; header[1UL] = PLSR_MODBUS_MAGIC_HIGH; header[2UL] = PLSR_MODBUS_PROTOCOL_VERSION; header[3UL] = (uint16_t)PLSR_MODBUS_WINDOW_WORDS; header[4UL] = PLSR_MODBUS_CAPABILITIES; PlsrModbusPutU32(header, 5UL, PlsrHwGetTimerClockHz(0U)); header[7UL] = PLSR_MODBUS_PERFORMANCE_VERSION; if (ModbusDataWriteWords(MODBUS_DATA_DEVICE_D, baseAddress, header, 8UL) == 0U) { return PLSR_RESULT_DATA_ACCESS; } PlsrModbusEnabled = 1U; PlsrModbusControlPoll(); return PLSR_RESULT_OK; } void PlsrModbusControlPoll(void) { uint8_t axis; if (PlsrModbusEnabled == 0U) { return; } PlsrModbusHandleCallRequest(); PlsrModbusHandleCommandRequest(); PlsrModbusHandlePersistenceRequest(); for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { PlsrModbusPublishAxisStatus(axis); } PlsrModbusPublishPerformance(); PlsrModbusPublishPersistence(); PlsrModbusPublishUsbDiagnostics(); } uint8_t PlsrModbusControlIsEnabled(void) { return PlsrModbusEnabled; } uint16_t PlsrModbusControlGetBaseAddress(void) { return PlsrModbusBaseAddress; }