#include "plc_device.h" #include #ifndef PLSR_HOST_TEST #include "stm32f4xx.h" #endif #define PLC_SM_PULSE_ACTIVE_MASK (0x01U) #define PLC_SM_DIRECTION_MASK (0x02U) const PLSR_AXIS_ADDRESS_MAP PlsrAxisAddressMap[PLSR_AXIS_COUNT] = { {0U, 1000U, 1001U, 1000U, 6000U}, {4U, 1020U, 1021U, 1020U, 6100U}, {8U, 1040U, 1041U, 1040U, 6200U}, {12U, 1060U, 1061U, 1060U, 6300U} }; static PLSR_HSD_DATA PlcHsdData; static PLSR_SFD_DATA PlcSfdData; static int32_t PlcSdRuntime[PLSR_AXIS_COUNT][PLSR_SD_AXIS_ITEM_COUNT]; static uint8_t PlcSmFlags[PLSR_AXIS_COUNT]; static PLC_DEVICE_EVENT_RECORD PlcEventRecords[PLSR_AXIS_COUNT][PLSR_EVENT_AXIS_ITEM_COUNT]; static uint8_t PlcHsdDirty; static uint8_t PlcSfdDirty; static uint8_t PlcSfdOperationActive; static uint32_t PlcHsdChangeCounter; static PLSR_PERSISTENCE_RESULT PlcLastHsdLoadResult; static PLSR_PERSISTENCE_RESULT PlcLastSfdLoadResult; static uint8_t PlcRestoredHsdPositionValid; static uint8_t PlcRestoredHsdLastBusy; static uint32_t PlcDeviceEnterCritical(void) { #ifdef PLSR_HOST_TEST return 0UL; #else uint32_t interruptState = __get_PRIMASK(); __disable_irq(); __DMB(); return interruptState; #endif } static void PlcDeviceExitCritical(uint32_t interruptState) { #ifdef PLSR_HOST_TEST (void)interruptState; #else __DMB(); if (interruptState == 0UL) { __enable_irq(); } #endif } static uint8_t PlcDeviceBeginSfdOperation(void) { uint8_t axis; uint8_t busy; uint32_t interruptState; #ifndef PLSR_HOST_TEST if (__get_IPSR() != 0UL) { return 0U; } #endif interruptState = PlcDeviceEnterCritical(); busy = PlcSfdOperationActive; for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { if ((PlcSmFlags[axis] & PLC_SM_PULSE_ACTIVE_MASK) != 0U) { busy = 1U; } } if (busy == 0U) { PlcSfdOperationActive = 1U; } PlcDeviceExitCritical(interruptState); return (busy == 0U) ? 1U : 0U; } static void PlcDeviceEndSfdOperation(void) { uint32_t interruptState = PlcDeviceEnterCritical(); PlcSfdOperationActive = 0U; PlcDeviceExitCritical(interruptState); } static uint16_t *PlcDeviceResolveHsd(uint16_t address) { if (address < PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT) { return &PlcHsdData.runtime[address - PLSR_HSD_RUNTIME_START]; } if ((address >= PLSR_HSD_CONFIG_START) && (address < PLSR_HSD_CONFIG_START + PLSR_HSD_CONFIG_COUNT)) { return &PlcHsdData.config[address - PLSR_HSD_CONFIG_START]; } return NULL; } static int32_t *PlcDeviceResolveSd(uint16_t address, uint8_t *axis, uint8_t *item) { uint8_t axisIndex; uint16_t base; for (axisIndex = 0U; axisIndex < PLSR_AXIS_COUNT; axisIndex++) { base = PlsrAxisAddressMap[axisIndex].sdRuntimeBase; if ((address >= base) && (address < base + PLSR_SD_AXIS_ITEM_COUNT)) { if (axis != NULL) { *axis = axisIndex; } if (item != NULL) { *item = (uint8_t)(address - base); } return &PlcSdRuntime[axisIndex][address - base]; } } return NULL; } static PLC_DEVICE_RESULT PlcDeviceResolveSm(uint16_t address, uint8_t *axis, uint8_t *mask) { uint8_t axisIndex; if ((axis == NULL) || (mask == NULL)) { return PLC_DEVICE_NULL_POINTER; } for (axisIndex = 0U; axisIndex < PLSR_AXIS_COUNT; axisIndex++) { if (address == PlsrAxisAddressMap[axisIndex].smPulseActiveAddress) { *axis = axisIndex; *mask = PLC_SM_PULSE_ACTIVE_MASK; return PLC_DEVICE_OK; } if (address == PlsrAxisAddressMap[axisIndex].smDirectionAddress) { *axis = axisIndex; *mask = PLC_SM_DIRECTION_MASK; return PLC_DEVICE_OK; } } return PLC_DEVICE_INVALID_ADDRESS; } static PLC_DEVICE_EVENT_RECORD *PlcDeviceResolveEvent(uint16_t address) { uint8_t axis; uint16_t base; for (axis = 0U; axis < PLSR_AXIS_COUNT; axis++) { base = PlsrAxisAddressMap[axis].eventBase; if ((address >= base) && (address < base + PLSR_EVENT_AXIS_ITEM_COUNT)) { return &PlcEventRecords[axis][address - base]; } } return NULL; } PLC_DEVICE_RESULT PlcDeviceInit(void) { (void)memset(&PlcHsdData, 0, sizeof(PlcHsdData)); (void)memset(&PlcSfdData, 0, sizeof(PlcSfdData)); (void)memset(PlcSdRuntime, 0, sizeof(PlcSdRuntime)); (void)memset(PlcSmFlags, 0, sizeof(PlcSmFlags)); (void)memset(PlcEventRecords, 0, sizeof(PlcEventRecords)); PlcSfdOperationActive = 0U; PlcHsdChangeCounter = 0UL; PlcRestoredHsdPositionValid = 0U; PlcRestoredHsdLastBusy = 0U; PlcLastHsdLoadResult = PlsrPersistenceLoadHsd(&PlcHsdData); PlcHsdDirty = (PlcLastHsdLoadResult == PLSR_PERSISTENCE_DEFAULTED) ? 1U : 0U; if ((PlcLastHsdLoadResult != PLSR_PERSISTENCE_OK) && (PlcLastHsdLoadResult != PLSR_PERSISTENCE_DEFAULTED)) { return PLC_DEVICE_PERSISTENCE_ERROR; } PlcRestoredHsdPositionValid = ((PlcHsdData.metadata & PLSR_HSD_META_POSITION_VALID) != 0UL) ? 1U : 0U; PlcRestoredHsdLastBusy = ((PlcHsdData.metadata & PLSR_HSD_META_LAST_BUSY) != 0UL) ? 1U : 0U; PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U; if ((PlcLastSfdLoadResult != PLSR_PERSISTENCE_OK) && (PlcLastSfdLoadResult != PLSR_PERSISTENCE_DEFAULTED)) { return PLC_DEVICE_PERSISTENCE_ERROR; } return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, uint16_t *value) { uint16_t *source; if (value == NULL) { return PLC_DEVICE_NULL_POINTER; } source = PlcDeviceResolveHsd(address); if (source == NULL) { return PLC_DEVICE_INVALID_ADDRESS; } *value = *source; return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, uint16_t value) { if ((address < PLSR_HSD_CONFIG_START) || (address >= PLSR_HSD_CONFIG_START + PLSR_HSD_CONFIG_COUNT)) { return (address < PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT) ? PLC_DEVICE_READ_ONLY : PLC_DEVICE_INVALID_ADDRESS; } PlcHsdData.config[address - PLSR_HSD_CONFIG_START] = value; PlcHsdChangeCounter++; PlcHsdDirty = 1U; return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, uint16_t value) { if (address >= PLSR_HSD_RUNTIME_START + PLSR_HSD_RUNTIME_COUNT) { return PLC_DEVICE_INVALID_ADDRESS; } PlcHsdData.runtime[address - PLSR_HSD_RUNTIME_START] = value; PlcHsdChangeCounter++; PlcHsdDirty = 1U; return PLC_DEVICE_OK; } /* 两个相邻 WORD 组成一个 32 位值:低地址=低16位,高地址=高16位(信捷兼容布局)。 */ PLC_DEVICE_RESULT PlcDeviceReadHsdDword(uint16_t lowAddress, int32_t *value) { uint32_t combined; uint32_t interruptState; uint16_t offset; if (value == NULL) { return PLC_DEVICE_NULL_POINTER; } if ((lowAddress >= PLSR_HSD_RUNTIME_COUNT) || ((lowAddress & 1U) != 0U) || ((uint16_t)(lowAddress + 1U) >= PLSR_HSD_RUNTIME_COUNT)) { return PLC_DEVICE_INVALID_ADDRESS; } offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); interruptState = PlcDeviceEnterCritical(); combined = (uint32_t)PlcHsdData.runtime[offset]; combined |= ((uint32_t)PlcHsdData.runtime[offset + 1U]) << 16U; PlcDeviceExitCritical(interruptState); *value = (int32_t)combined; return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDevicePublishHsdDword(uint16_t lowAddress, int32_t value) { uint32_t rawValue; uint32_t interruptState; uint16_t offset; if ((lowAddress >= PLSR_HSD_RUNTIME_COUNT) || ((lowAddress & 1U) != 0U) || ((uint16_t)(lowAddress + 1U) >= PLSR_HSD_RUNTIME_COUNT)) { return PLC_DEVICE_INVALID_ADDRESS; } rawValue = (uint32_t)value; offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); interruptState = PlcDeviceEnterCritical(); PlcHsdData.runtime[offset] = (uint16_t)(rawValue & 0xFFFFUL); PlcHsdData.runtime[offset + 1U] = (uint16_t)(rawValue >> 16U); PlcHsdChangeCounter++; PlcHsdDirty = 1U; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceSetHsdCheckpointMeta(uint8_t positionValid, uint8_t lastBusy) { uint32_t interruptState = PlcDeviceEnterCritical(); PlcHsdData.metadata &= ~(PLSR_HSD_META_POSITION_VALID | PLSR_HSD_META_LAST_BUSY); if (positionValid != 0U) { PlcHsdData.metadata |= PLSR_HSD_META_POSITION_VALID; } if (lastBusy != 0U) { PlcHsdData.metadata |= PLSR_HSD_META_LAST_BUSY; } PlcHsdChangeCounter++; PlcHsdDirty = 1U; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } uint8_t PlcDeviceGetRestoredHsdPositionValid(void) { return PlcRestoredHsdPositionValid; } uint8_t PlcDeviceGetRestoredHsdLastBusy(void) { return PlcRestoredHsdLastBusy; } PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void) { PLSR_HSD_DATA snapshot; PLSR_PERSISTENCE_RESULT result; uint32_t snapshotCounter; uint32_t interruptState; if (PlcHsdDirty == 0U) { return PLC_DEVICE_OK; } interruptState = PlcDeviceEnterCritical(); snapshot = PlcHsdData; snapshotCounter = PlcHsdChangeCounter; PlcDeviceExitCritical(interruptState); result = PlsrPersistenceSaveHsd(&snapshot); if (result != PLSR_PERSISTENCE_OK) { return PLC_DEVICE_PERSISTENCE_ERROR; } interruptState = PlcDeviceEnterCritical(); if (PlcHsdChangeCounter == snapshotCounter) { PlcHsdDirty = 0U; } PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, uint16_t *value) { uint32_t interruptState; if (value == NULL) { return PLC_DEVICE_NULL_POINTER; } if ((address < PLSR_SFD_CONFIG_START) || (address >= PLSR_SFD_CONFIG_START + PLSR_SFD_CONFIG_COUNT)) { return PLC_DEVICE_INVALID_ADDRESS; } interruptState = PlcDeviceEnterCritical(); if (PlcSfdOperationActive != 0U) { PlcDeviceExitCritical(interruptState); return PLC_DEVICE_BUSY; } *value = PlcSfdData.config[address - PLSR_SFD_CONFIG_START]; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, uint16_t value) { uint32_t interruptState; if ((address < PLSR_SFD_CONFIG_START) || (address >= PLSR_SFD_CONFIG_START + PLSR_SFD_CONFIG_COUNT)) { return PLC_DEVICE_INVALID_ADDRESS; } interruptState = PlcDeviceEnterCritical(); if (PlcSfdOperationActive != 0U) { PlcDeviceExitCritical(interruptState); return PLC_DEVICE_BUSY; } PlcSfdData.config[address - PLSR_SFD_CONFIG_START] = value; PlcSfdDirty = 1U; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceLoadSfd(void) { if (PlcDeviceBeginSfdOperation() == 0U) { return PLC_DEVICE_BUSY; } PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); if ((PlcLastSfdLoadResult != PLSR_PERSISTENCE_OK) && (PlcLastSfdLoadResult != PLSR_PERSISTENCE_DEFAULTED)) { PlcDeviceEndSfdOperation(); return PLC_DEVICE_PERSISTENCE_ERROR; } PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U; PlcDeviceEndSfdOperation(); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceSaveSfd(void) { PLSR_PERSISTENCE_RESULT result; if (PlcDeviceBeginSfdOperation() == 0U) { return PLC_DEVICE_BUSY; } if (PlcSfdDirty == 0U) { PlcDeviceEndSfdOperation(); return PLC_DEVICE_OK; } result = PlsrPersistenceSaveSfd(&PlcSfdData); if (result != PLSR_PERSISTENCE_OK) { PlcDeviceEndSfdOperation(); return PLC_DEVICE_PERSISTENCE_ERROR; } PlcSfdDirty = 0U; PlcDeviceEndSfdOperation(); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceResetSfdDefaults(void) { PLSR_PERSISTENCE_RESULT result; if (PlcDeviceBeginSfdOperation() == 0U) { return PLC_DEVICE_BUSY; } result = PlsrPersistenceEraseSfd(); if (result != PLSR_PERSISTENCE_OK) { PlcDeviceEndSfdOperation(); return PLC_DEVICE_PERSISTENCE_ERROR; } PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); if (PlcLastSfdLoadResult != PLSR_PERSISTENCE_DEFAULTED) { PlcDeviceEndSfdOperation(); return PLC_DEVICE_PERSISTENCE_ERROR; } PlcSfdDirty = 1U; PlcDeviceEndSfdOperation(); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceReadSm(uint16_t address, uint8_t *state) { uint8_t axis; uint8_t mask; PLC_DEVICE_RESULT result; if (state == NULL) { return PLC_DEVICE_NULL_POINTER; } result = PlcDeviceResolveSm(address, &axis, &mask); if (result != PLC_DEVICE_OK) { return result; } *state = ((PlcSmFlags[axis] & mask) != 0U) ? 1U : 0U; return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceWriteSm(uint16_t address, uint8_t state) { uint8_t axis; uint8_t mask; PLC_DEVICE_RESULT result; (void)state; result = PlcDeviceResolveSm(address, &axis, &mask); return (result == PLC_DEVICE_OK) ? PLC_DEVICE_READ_ONLY : result; } PLC_DEVICE_RESULT PlcDevicePublishSm(uint8_t axis, uint8_t pulseActive, uint8_t direction) { uint8_t flags = 0U; uint32_t interruptState; if (axis >= PLSR_AXIS_COUNT) { return PLC_DEVICE_INVALID_ARGUMENT; } if (pulseActive != 0U) { flags |= PLC_SM_PULSE_ACTIVE_MASK; } if (direction != 0U) { flags |= PLC_SM_DIRECTION_MASK; } interruptState = PlcDeviceEnterCritical(); if ((pulseActive != 0U) && (PlcSfdOperationActive != 0U)) { PlcDeviceExitCritical(interruptState); return PLC_DEVICE_BUSY; } PlcSmFlags[axis] = flags; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceReadSd(uint16_t address, int32_t *value) { int32_t *source; uint32_t interruptState; if (value == NULL) { return PLC_DEVICE_NULL_POINTER; } source = PlcDeviceResolveSd(address, NULL, NULL); if (source == NULL) { return PLC_DEVICE_INVALID_ADDRESS; } interruptState = PlcDeviceEnterCritical(); *value = *source; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceWriteSd(uint16_t address, int32_t value) { (void)value; return (PlcDeviceResolveSd(address, NULL, NULL) != NULL) ? PLC_DEVICE_READ_ONLY : PLC_DEVICE_INVALID_ADDRESS; } PLC_DEVICE_RESULT PlcDevicePublishSd(uint8_t axis, uint8_t item, int32_t value) { uint32_t interruptState; if ((axis >= PLSR_AXIS_COUNT) || (item >= PLSR_SD_AXIS_ITEM_COUNT)) { return PLC_DEVICE_INVALID_ARGUMENT; } interruptState = PlcDeviceEnterCritical(); PlcSdRuntime[axis][item] = value; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceReadSdDword(uint16_t lowAddress, int32_t *value) { int32_t *source; uint32_t rawValue; uint32_t interruptState; uint8_t axis; uint8_t item; if (value == NULL) { return PLC_DEVICE_NULL_POINTER; } source = PlcDeviceResolveSd(lowAddress, &axis, &item); if ((source == NULL) || ((item & 1U) != 0U) || (item > PLSR_SD_ITEM_SPEED)) { return PLC_DEVICE_INVALID_ADDRESS; } interruptState = PlcDeviceEnterCritical(); rawValue = (uint32_t)PlcSdRuntime[axis][item] & 0xFFFFUL; rawValue |= ((uint32_t)PlcSdRuntime[axis][item + 1U] & 0xFFFFUL) << 16U; PlcDeviceExitCritical(interruptState); *value = (int32_t)rawValue; return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDevicePublishSdDword(uint8_t axis, uint8_t lowItem, int32_t value) { uint32_t rawValue; uint32_t interruptState; if ((axis >= PLSR_AXIS_COUNT) || ((lowItem & 1U) != 0U) || (lowItem > PLSR_SD_ITEM_SPEED)) { return PLC_DEVICE_INVALID_ARGUMENT; } rawValue = (uint32_t)value; interruptState = PlcDeviceEnterCritical(); PlcSdRuntime[axis][lowItem] = (int32_t)(rawValue & 0xFFFFUL); PlcSdRuntime[axis][lowItem + 1U] = (int32_t)(rawValue >> 16U); PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceGetEventAddress(uint8_t axis, uint16_t segmentNumber, uint16_t *eventAddress) { if (eventAddress == NULL) { return PLC_DEVICE_NULL_POINTER; } if ((axis >= PLSR_AXIS_COUNT) || (segmentNumber == 0U) || (segmentNumber > PLSR_EVENT_AXIS_ITEM_COUNT)) { return PLC_DEVICE_INVALID_ARGUMENT; } *eventAddress = (uint16_t)(PlsrAxisAddressMap[axis].eventBase + segmentNumber - 1U); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDevicePublishEvent(uint8_t axis, uint16_t segmentNumber, uint16_t reason) { PLC_DEVICE_EVENT_RECORD *record; uint32_t interruptState; if ((axis >= PLSR_AXIS_COUNT) || (segmentNumber == 0U) || (segmentNumber > PLSR_EVENT_AXIS_ITEM_COUNT)) { return PLC_DEVICE_INVALID_ARGUMENT; } record = &PlcEventRecords[axis][segmentNumber - 1U]; interruptState = PlcDeviceEnterCritical(); if (record->count != UINT32_MAX) { record->count++; } record->lastReason = reason; record->pending = 1U; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceReadEvent(uint16_t eventAddress, PLC_DEVICE_EVENT_RECORD *record) { PLC_DEVICE_EVENT_RECORD *source; uint32_t interruptState; if (record == NULL) { return PLC_DEVICE_NULL_POINTER; } source = PlcDeviceResolveEvent(eventAddress); if (source == NULL) { return PLC_DEVICE_INVALID_ADDRESS; } interruptState = PlcDeviceEnterCritical(); *record = *source; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } PLC_DEVICE_RESULT PlcDeviceAcknowledgeEvent(uint16_t eventAddress) { PLC_DEVICE_EVENT_RECORD *record = PlcDeviceResolveEvent(eventAddress); uint32_t interruptState; if (record == NULL) { return PLC_DEVICE_INVALID_ADDRESS; } interruptState = PlcDeviceEnterCritical(); record->pending = 0U; PlcDeviceExitCritical(interruptState); return PLC_DEVICE_OK; } uint8_t PlcDeviceIsHsdDirty(void) { return PlcHsdDirty; } uint8_t PlcDeviceIsSfdDirty(void) { return PlcSfdDirty; } PLSR_PERSISTENCE_RESULT PlcDeviceGetLastHsdLoadResult(void) { return PlcLastHsdLoadResult; } PLSR_PERSISTENCE_RESULT PlcDeviceGetLastSfdLoadResult(void) { return PlcLastSfdLoadResult; }