diff --git a/Core/Src/main.c b/Core/Src/main.c index 5fb8f1f..a3b15d7 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -24,6 +24,7 @@ /* USER CODE BEGIN Includes */ #include "ucos_ii.h" #include "modbus_rtu_slave.h" +#include "plc_device.h" #include "stdio.h" /* USER CODE END Includes */ @@ -156,7 +157,14 @@ int main(void) MX_DMA_Init(); MX_USB_DEVICE_Init(); MX_USART1_UART_Init(); - (void)BackupSramInit(); + if (BackupSramInit() != HAL_OK) + { + Error_Handler(); + } + if (PlcDeviceInit() != PLC_DEVICE_OK) + { + Error_Handler(); + } /* USER CODE BEGIN 2 */ INT8U osError; diff --git a/EWARM/Modbus.ewp b/EWARM/Modbus.ewp index 5c4ccb6..33fbc95 100644 --- a/EWARM/Modbus.ewp +++ b/EWARM/Modbus.ewp @@ -360,6 +360,7 @@ $PROJ_DIR$/../Drivers/CMSIS/Device/ST/STM32F4xx/Include $PROJ_DIR$/../Drivers/CMSIS/Include $PROJ_DIR$\..\Modbus\Inc + $PROJ_DIR$\..\PLSR\Inc $PROJ_DIR$\..\Middlewares\Third_Party\Micrium\Config $PROJ_DIR$\..\Middlewares\Third_Party\Micrium\uCOS-II\Source $PROJ_DIR$\..\Middlewares\Third_Party\Micrium\uCOS-II\Ports\ARM-Cortex-M4\IAR @@ -1260,6 +1261,24 @@ + + PLSR + + $PROJ_DIR$\..\PLSR\Inc\plsr_address_map.h + + + $PROJ_DIR$\..\PLSR\Inc\plsr_persistence.h + + + $PROJ_DIR$\..\PLSR\Inc\plc_device.h + + + $PROJ_DIR$\..\PLSR\Src\plsr_persistence.c + + + $PROJ_DIR$\..\PLSR\Src\plc_device.c + + Modbus diff --git a/Modbus/Src/modbus_rtu_slave.c b/Modbus/Src/modbus_rtu_slave.c index bf00866..b4756f1 100644 --- a/Modbus/Src/modbus_rtu_slave.c +++ b/Modbus/Src/modbus_rtu_slave.c @@ -56,7 +56,7 @@ static volatile MODBUS_BACKUP_DATA *ModbusBackupData = * 10000 个保持寄存器占用 20000 字节;10000 个线圈按位存储, * 占用 1250 字节 */ -static uint16_t ModbusHoldingRegisters[40000]; +static uint16_t ModbusHoldingRegisters[20000]; #pragma location = ".ccmram" #pragma data_alignment = 4 diff --git a/PLSR/Inc/plc_device.h b/PLSR/Inc/plc_device.h new file mode 100644 index 0000000..b3561c8 --- /dev/null +++ b/PLSR/Inc/plc_device.h @@ -0,0 +1,62 @@ +#ifndef PLC_DEVICE_H +#define PLC_DEVICE_H + +#include "plsr_address_map.h" +#include "plsr_persistence.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + PLC_DEVICE_OK = 0, + PLC_DEVICE_INVALID_ADDRESS, + PLC_DEVICE_INVALID_ARGUMENT, + PLC_DEVICE_NULL_POINTER, + PLC_DEVICE_READ_ONLY, + PLC_DEVICE_PERSISTENCE_ERROR, + PLC_DEVICE_NOT_IMPLEMENTED +} PLC_DEVICE_RESULT; + +PLC_DEVICE_RESULT PlcDeviceInit(void); + +PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value); +PLC_DEVICE_RESULT PlcDeviceWriteHsdConfig(uint16_t address, int32_t value); +PLC_DEVICE_RESULT PlcDevicePublishHsdRuntime(uint16_t address, int32_t value); +PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value); +PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value); +PLC_DEVICE_RESULT PlcDeviceCheckpointHsd(void); + +PLC_DEVICE_RESULT PlcDeviceReadSfd(uint16_t address, int32_t *value); +PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, int32_t value); +PLC_DEVICE_RESULT PlcDeviceLoadSfd(void); +PLC_DEVICE_RESULT PlcDeviceSaveSfd(void); + +PLC_DEVICE_RESULT PlcDeviceReadSm(uint16_t address, uint8_t *state); +PLC_DEVICE_RESULT PlcDeviceWriteSm(uint16_t address, uint8_t state); +PLC_DEVICE_RESULT PlcDevicePublishSm(uint8_t axis, + uint8_t pulseActive, + uint8_t direction); + +PLC_DEVICE_RESULT PlcDeviceReadSd(uint16_t address, int32_t *value); +PLC_DEVICE_RESULT PlcDeviceWriteSd(uint16_t address, int32_t value); +PLC_DEVICE_RESULT PlcDevicePublishSd(uint8_t axis, + uint8_t item, + int32_t value); + +PLC_DEVICE_RESULT PlcDeviceGetEventAddress(uint8_t axis, + uint16_t segmentNumber, + uint16_t *eventAddress); + +uint8_t PlcDeviceIsHsdDirty(void); +uint8_t PlcDeviceIsSfdDirty(void); +PLSR_PERSISTENCE_RESULT PlcDeviceGetLastHsdLoadResult(void); +PLSR_PERSISTENCE_RESULT PlcDeviceGetLastSfdLoadResult(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PLC_DEVICE_H */ diff --git a/PLSR/Inc/plsr_address_map.h b/PLSR/Inc/plsr_address_map.h new file mode 100644 index 0000000..5580be3 --- /dev/null +++ b/PLSR/Inc/plsr_address_map.h @@ -0,0 +1,40 @@ +#ifndef PLSR_ADDRESS_MAP_H +#define PLSR_ADDRESS_MAP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PLSR_AXIS_COUNT (4U) + +#define PLSR_HSD_RUNTIME_START (0U) +#define PLSR_HSD_RUNTIME_COUNT (16U) +#define PLSR_HSD_RUNTIME_AXIS_COUNT (4U) + +#define PLSR_HSD_CONFIG_START (460U) +#define PLSR_HSD_CONFIG_COUNT (80U) + +#define PLSR_SFD_CONFIG_START (900U) +#define PLSR_SFD_CONFIG_COUNT (520U) + +#define PLSR_SD_AXIS_ITEM_COUNT (12U) +#define PLSR_EVENT_AXIS_ITEM_COUNT (100U) + +typedef struct +{ + uint16_t hsdRuntimeBase; + uint16_t smPulseActiveAddress; + uint16_t smDirectionAddress; + uint16_t sdRuntimeBase; + uint16_t eventBase; +} PLSR_AXIS_ADDRESS_MAP; + +extern const PLSR_AXIS_ADDRESS_MAP PlsrAxisAddressMap[PLSR_AXIS_COUNT]; + +#ifdef __cplusplus +} +#endif + +#endif /* PLSR_ADDRESS_MAP_H */ diff --git a/PLSR/Inc/plsr_persistence.h b/PLSR/Inc/plsr_persistence.h new file mode 100644 index 0000000..e2f253a --- /dev/null +++ b/PLSR/Inc/plsr_persistence.h @@ -0,0 +1,47 @@ +#ifndef PLSR_PERSISTENCE_H +#define PLSR_PERSISTENCE_H + +#include "plsr_address_map.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + int32_t runtime[PLSR_HSD_RUNTIME_COUNT]; + int32_t config[PLSR_HSD_CONFIG_COUNT]; +} PLSR_HSD_DATA; + +typedef struct +{ + int32_t config[PLSR_SFD_CONFIG_COUNT]; +} PLSR_SFD_DATA; + +typedef enum +{ + PLSR_PERSISTENCE_OK = 0, + PLSR_PERSISTENCE_DEFAULTED, + PLSR_PERSISTENCE_INVALID_ARGUMENT, + PLSR_PERSISTENCE_VERIFY_FAILED, + PLSR_PERSISTENCE_NOT_IMPLEMENTED +} PLSR_PERSISTENCE_RESULT; + +PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadHsd(PLSR_HSD_DATA *data); +PLSR_PERSISTENCE_RESULT PlsrPersistenceSaveHsd(const PLSR_HSD_DATA *data); +void PlsrPersistenceResetHsd(void); + +PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadSfd(PLSR_SFD_DATA *data); +PLSR_PERSISTENCE_RESULT PlsrPersistenceSaveSfd(const PLSR_SFD_DATA *data); + +#ifdef PLSR_HOST_TEST +void PlsrPersistenceTestResetStorage(void); +void PlsrPersistenceTestCorruptNewestHsd(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PLSR_PERSISTENCE_H */ diff --git a/PLSR/Src/plc_device.c b/PLSR/Src/plc_device.c new file mode 100644 index 0000000..4a756ca --- /dev/null +++ b/PLSR/Src/plc_device.c @@ -0,0 +1,478 @@ +#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 uint8_t PlcHsdDirty; +static uint8_t PlcSfdDirty; +static uint32_t PlcHsdChangeCounter; +static PLSR_PERSISTENCE_RESULT PlcLastHsdLoadResult; +static PLSR_PERSISTENCE_RESULT PlcLastSfdLoadResult; + +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 int32_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; +} + +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)); + PlcHsdChangeCounter = 0UL; + + PlcLastHsdLoadResult = PlsrPersistenceLoadHsd(&PlcHsdData); + PlcHsdDirty = (PlcLastHsdLoadResult == PLSR_PERSISTENCE_DEFAULTED) + ? 1U + : 0U; + if ((PlcLastHsdLoadResult != PLSR_PERSISTENCE_OK) + && (PlcLastHsdLoadResult != PLSR_PERSISTENCE_DEFAULTED)) + { + return PLC_DEVICE_PERSISTENCE_ERROR; + } + + PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); + PlcSfdDirty = (PlcLastSfdLoadResult == PLSR_PERSISTENCE_OK) ? 0U : 1U; + if ((PlcLastSfdLoadResult != PLSR_PERSISTENCE_OK) + && (PlcLastSfdLoadResult != PLSR_PERSISTENCE_NOT_IMPLEMENTED)) + { + return PLC_DEVICE_PERSISTENCE_ERROR; + } + + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDeviceReadHsd(uint16_t address, int32_t *value) +{ + int32_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, int32_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, int32_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; +} + +PLC_DEVICE_RESULT PlcDeviceReadHsdPair(uint16_t lowAddress, int64_t *value) +{ + uint64_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 = (uint64_t)(uint32_t)PlcHsdData.runtime[offset]; + combined |= ((uint64_t)(uint32_t)PlcHsdData.runtime[offset + 1U]) << 32U; + PlcDeviceExitCritical(interruptState); + *value = (int64_t)combined; + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDevicePublishHsdPair(uint16_t lowAddress, int64_t value) +{ + uint64_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 = (uint64_t)value; + offset = (uint16_t)(lowAddress - PLSR_HSD_RUNTIME_START); + interruptState = PlcDeviceEnterCritical(); + PlcHsdData.runtime[offset] = (int32_t)(uint32_t)rawValue; + PlcHsdData.runtime[offset + 1U] = (int32_t)(uint32_t)(rawValue >> 32U); + PlcHsdChangeCounter++; + PlcHsdDirty = 1U; + PlcDeviceExitCritical(interruptState); + return PLC_DEVICE_OK; +} + +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, int32_t *value) +{ + 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; + } + + *value = PlcSfdData.config[address - PLSR_SFD_CONFIG_START]; + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDeviceWriteSfd(uint16_t address, int32_t value) +{ + if ((address < PLSR_SFD_CONFIG_START) + || (address >= PLSR_SFD_CONFIG_START + PLSR_SFD_CONFIG_COUNT)) + { + return PLC_DEVICE_INVALID_ADDRESS; + } + + PlcSfdData.config[address - PLSR_SFD_CONFIG_START] = value; + PlcSfdDirty = 1U; + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDeviceLoadSfd(void) +{ + PlcLastSfdLoadResult = PlsrPersistenceLoadSfd(&PlcSfdData); + if (PlcLastSfdLoadResult == PLSR_PERSISTENCE_NOT_IMPLEMENTED) + { + return PLC_DEVICE_NOT_IMPLEMENTED; + } + if (PlcLastSfdLoadResult != PLSR_PERSISTENCE_OK) + { + return PLC_DEVICE_PERSISTENCE_ERROR; + } + + PlcSfdDirty = 0U; + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDeviceSaveSfd(void) +{ + PLSR_PERSISTENCE_RESULT result = PlsrPersistenceSaveSfd(&PlcSfdData); + + if (result == PLSR_PERSISTENCE_NOT_IMPLEMENTED) + { + return PLC_DEVICE_NOT_IMPLEMENTED; + } + if (result != PLSR_PERSISTENCE_OK) + { + return PLC_DEVICE_PERSISTENCE_ERROR; + } + + PlcSfdDirty = 0U; + 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; + + 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; + } + + PlcSmFlags[axis] = flags; + return PLC_DEVICE_OK; +} + +PLC_DEVICE_RESULT PlcDeviceReadSd(uint16_t address, int32_t *value) +{ + int32_t *source; + + if (value == NULL) + { + return PLC_DEVICE_NULL_POINTER; + } + source = PlcDeviceResolveSd(address, NULL, NULL); + if (source == NULL) + { + return PLC_DEVICE_INVALID_ADDRESS; + } + + *value = *source; + 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) +{ + if ((axis >= PLSR_AXIS_COUNT) || (item >= PLSR_SD_AXIS_ITEM_COUNT)) + { + return PLC_DEVICE_INVALID_ARGUMENT; + } + + PlcSdRuntime[axis][item] = value; + 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; +} + +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; +} diff --git a/PLSR/Src/plsr_persistence.c b/PLSR/Src/plsr_persistence.c new file mode 100644 index 0000000..9375cb5 --- /dev/null +++ b/PLSR/Src/plsr_persistence.c @@ -0,0 +1,316 @@ +#include "plsr_persistence.h" +#include +#include + +#define PLSR_HSD_BACKUP_MAGIC (0x504C4853UL) +#define PLSR_HSD_BACKUP_VERSION (1U) +#define PLSR_BACKUP_SLOT_A_OFFSET (0x0100UL) +#define PLSR_BACKUP_SLOT_STRIDE (0x0200UL) + +typedef struct +{ + uint32_t magic; + uint16_t version; + uint16_t payloadLength; + uint32_t generation; + PLSR_HSD_DATA data; + uint32_t crc32; +} PLSR_HSD_BACKUP_RECORD; + +#ifdef PLSR_HOST_TEST +static PLSR_HSD_BACKUP_RECORD PlsrHostBackupSlots[2]; +#else +#include "stm32f4xx.h" +#endif + +static uint32_t PlsrPersistenceCrc32(const volatile uint8_t *data, + uint32_t length) +{ + uint32_t crc = 0xFFFFFFFFUL; + uint32_t index; + uint8_t bit; + + for (index = 0U; index < length; index++) + { + crc ^= data[index]; + for (bit = 0U; bit < 8U; bit++) + { + if ((crc & 1UL) != 0UL) + { + crc = (crc >> 1U) ^ 0xEDB88320UL; + } + else + { + crc >>= 1U; + } + } + } + + return ~crc; +} + +static volatile PLSR_HSD_BACKUP_RECORD *PlsrPersistenceGetHsdSlot( + uint8_t slot) +{ +#ifdef PLSR_HOST_TEST + return &PlsrHostBackupSlots[slot]; +#else + uint32_t offset = PLSR_BACKUP_SLOT_A_OFFSET + + (uint32_t)slot * PLSR_BACKUP_SLOT_STRIDE; + return (volatile PLSR_HSD_BACKUP_RECORD *)(BKPSRAM_BASE + offset); +#endif +} + +static uint8_t PlsrPersistenceHsdRecordIsValid( + const volatile PLSR_HSD_BACKUP_RECORD *record) +{ + uint32_t expectedCrc; + + if ((record->magic != PLSR_HSD_BACKUP_MAGIC) + || (record->version != PLSR_HSD_BACKUP_VERSION) + || (record->payloadLength != sizeof(PLSR_HSD_DATA))) + { + return 0U; + } + + expectedCrc = PlsrPersistenceCrc32( + (const volatile uint8_t *)record, + (uint32_t)offsetof(PLSR_HSD_BACKUP_RECORD, crc32)); + + return (expectedCrc == record->crc32) ? 1U : 0U; +} + +static uint8_t PlsrPersistenceGenerationIsNewer(uint32_t left, + uint32_t right) +{ + return (((int32_t)(left - right)) > 0) ? 1U : 0U; +} + +static void PlsrPersistenceCopyHsdFromVolatile( + PLSR_HSD_DATA *destination, + const volatile PLSR_HSD_DATA *source) +{ + uint16_t index; + + for (index = 0U; index < PLSR_HSD_RUNTIME_COUNT; index++) + { + destination->runtime[index] = source->runtime[index]; + } + for (index = 0U; index < PLSR_HSD_CONFIG_COUNT; index++) + { + destination->config[index] = source->config[index]; + } +} + +static void PlsrPersistenceWriteRecord( + volatile PLSR_HSD_BACKUP_RECORD *destination, + const PLSR_HSD_BACKUP_RECORD *source) +{ + const uint32_t *sourceWords = (const uint32_t *)source; + volatile uint32_t *destinationWords = (volatile uint32_t *)destination; + uint32_t wordCount = (uint32_t)(sizeof(PLSR_HSD_BACKUP_RECORD) + / sizeof(uint32_t)); + uint32_t index; + + destination->magic = 0UL; +#ifndef PLSR_HOST_TEST + __DMB(); +#endif + + for (index = 1U; index < wordCount; index++) + { + destinationWords[index] = sourceWords[index]; + } + +#ifndef PLSR_HOST_TEST + __DMB(); +#endif + destination->magic = PLSR_HSD_BACKUP_MAGIC; +#ifndef PLSR_HOST_TEST + __DMB(); +#endif +} + +PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadHsd(PLSR_HSD_DATA *data) +{ + volatile PLSR_HSD_BACKUP_RECORD *slotA; + volatile PLSR_HSD_BACKUP_RECORD *slotB; + const volatile PLSR_HSD_BACKUP_RECORD *selected; + uint32_t generationA; + uint32_t generationB; + uint8_t validA; + uint8_t validB; + + if (data == NULL) + { + return PLSR_PERSISTENCE_INVALID_ARGUMENT; + } + + slotA = PlsrPersistenceGetHsdSlot(0U); + slotB = PlsrPersistenceGetHsdSlot(1U); + validA = PlsrPersistenceHsdRecordIsValid(slotA); + validB = PlsrPersistenceHsdRecordIsValid(slotB); + + if ((validA == 0U) && (validB == 0U)) + { + (void)memset(data, 0, sizeof(*data)); + return PLSR_PERSISTENCE_DEFAULTED; + } + + if ((validA != 0U) && (validB != 0U)) + { + generationA = slotA->generation; + generationB = slotB->generation; + selected = (PlsrPersistenceGenerationIsNewer(generationB, + generationA) + != 0U) + ? slotB + : slotA; + } + else + { + selected = (validA != 0U) ? slotA : slotB; + } + + PlsrPersistenceCopyHsdFromVolatile(data, &selected->data); + return PLSR_PERSISTENCE_OK; +} + +PLSR_PERSISTENCE_RESULT PlsrPersistenceSaveHsd(const PLSR_HSD_DATA *data) +{ + volatile PLSR_HSD_BACKUP_RECORD *slotA; + volatile PLSR_HSD_BACKUP_RECORD *slotB; + volatile PLSR_HSD_BACKUP_RECORD *target; + PLSR_HSD_BACKUP_RECORD record; + uint32_t newestGeneration = 0UL; + uint32_t generationA; + uint32_t generationB; + uint8_t validA; + uint8_t validB; + + if (data == NULL) + { + return PLSR_PERSISTENCE_INVALID_ARGUMENT; + } + if (sizeof(PLSR_HSD_BACKUP_RECORD) > PLSR_BACKUP_SLOT_STRIDE) + { + return PLSR_PERSISTENCE_VERIFY_FAILED; + } + + slotA = PlsrPersistenceGetHsdSlot(0U); + slotB = PlsrPersistenceGetHsdSlot(1U); + validA = PlsrPersistenceHsdRecordIsValid(slotA); + validB = PlsrPersistenceHsdRecordIsValid(slotB); + + if ((validA != 0U) && (validB != 0U)) + { + generationA = slotA->generation; + generationB = slotB->generation; + if (PlsrPersistenceGenerationIsNewer(generationB, + generationA) + != 0U) + { + newestGeneration = generationB; + target = slotA; + } + else + { + newestGeneration = generationA; + target = slotB; + } + } + else if (validA != 0U) + { + newestGeneration = slotA->generation; + target = slotB; + } + else if (validB != 0U) + { + newestGeneration = slotB->generation; + target = slotA; + } + else + { + target = slotA; + } + + (void)memset(&record, 0, sizeof(record)); + record.magic = PLSR_HSD_BACKUP_MAGIC; + record.version = PLSR_HSD_BACKUP_VERSION; + record.payloadLength = (uint16_t)sizeof(PLSR_HSD_DATA); + record.generation = newestGeneration + 1UL; + record.data = *data; + record.crc32 = PlsrPersistenceCrc32( + (const volatile uint8_t *)&record, + (uint32_t)offsetof(PLSR_HSD_BACKUP_RECORD, crc32)); + + PlsrPersistenceWriteRecord(target, &record); + return (PlsrPersistenceHsdRecordIsValid(target) != 0U) + ? PLSR_PERSISTENCE_OK + : PLSR_PERSISTENCE_VERIFY_FAILED; +} + +void PlsrPersistenceResetHsd(void) +{ + PlsrPersistenceGetHsdSlot(0U)->magic = 0UL; + PlsrPersistenceGetHsdSlot(1U)->magic = 0UL; +#ifndef PLSR_HOST_TEST + __DMB(); +#endif +} + +PLSR_PERSISTENCE_RESULT PlsrPersistenceLoadSfd(PLSR_SFD_DATA *data) +{ + if (data == NULL) + { + return PLSR_PERSISTENCE_INVALID_ARGUMENT; + } + + (void)memset(data, 0, sizeof(*data)); + return PLSR_PERSISTENCE_NOT_IMPLEMENTED; +} + +PLSR_PERSISTENCE_RESULT PlsrPersistenceSaveSfd(const PLSR_SFD_DATA *data) +{ + if (data == NULL) + { + return PLSR_PERSISTENCE_INVALID_ARGUMENT; + } + + return PLSR_PERSISTENCE_NOT_IMPLEMENTED; +} + +#ifdef PLSR_HOST_TEST +void PlsrPersistenceTestResetStorage(void) +{ + (void)memset(PlsrHostBackupSlots, 0, sizeof(PlsrHostBackupSlots)); +} + +void PlsrPersistenceTestCorruptNewestHsd(void) +{ + volatile PLSR_HSD_BACKUP_RECORD *slotA = PlsrPersistenceGetHsdSlot(0U); + volatile PLSR_HSD_BACKUP_RECORD *slotB = PlsrPersistenceGetHsdSlot(1U); + uint8_t validA = PlsrPersistenceHsdRecordIsValid(slotA); + uint8_t validB = PlsrPersistenceHsdRecordIsValid(slotB); + volatile PLSR_HSD_BACKUP_RECORD *newest; + + if ((validA == 0U) && (validB == 0U)) + { + return; + } + if ((validA != 0U) && (validB != 0U)) + { + newest = (PlsrPersistenceGenerationIsNewer(slotB->generation, + slotA->generation) + != 0U) + ? slotB + : slotA; + } + else + { + newest = (validA != 0U) ? slotA : slotB; + } + + newest->crc32 ^= 1UL; +} +#endif diff --git a/PLSR/Test/run_host_tests.ps1 b/PLSR/Test/run_host_tests.ps1 new file mode 100644 index 0000000..9a11e9e --- /dev/null +++ b/PLSR/Test/run_host_tests.ps1 @@ -0,0 +1,51 @@ +$ErrorActionPreference = 'Stop' + +$workspacePath = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path +$compiler = Get-Command gcc.exe -ErrorAction SilentlyContinue +if ($null -eq $compiler) +{ + throw 'gcc.exe was not found; PLSR host tests cannot run.' +} + +$outputPath = Join-Path $workspacePath 'tmp\test_plc_device.exe' +$outputDirectory = Split-Path -Parent $outputPath +if (-not (Test-Path -LiteralPath $outputDirectory)) +{ + New-Item -ItemType Directory -Path $outputDirectory | Out-Null +} + +$arguments = @( + '-std=c11', + '-Wall', + '-Wextra', + '-Werror', + '-DPLSR_HOST_TEST', + "-I$workspacePath\PLSR\Inc", + "$workspacePath\PLSR\Src\plc_device.c", + "$workspacePath\PLSR\Src\plsr_persistence.c", + "$workspacePath\PLSR\Test\test_plc_device.c", + '-o', + $outputPath +) + +try +{ + & $compiler.Source @arguments + if ($LASTEXITCODE -ne 0) + { + throw "PLSR host-test compilation failed with exit code $LASTEXITCODE." + } + + & $outputPath + if ($LASTEXITCODE -ne 0) + { + throw "PLSR host tests failed with exit code $LASTEXITCODE." + } +} +finally +{ + if (Test-Path -LiteralPath $outputPath) + { + Remove-Item -LiteralPath $outputPath -Force + } +} diff --git a/PLSR/Test/test_plc_device.c b/PLSR/Test/test_plc_device.c new file mode 100644 index 0000000..17ad754 --- /dev/null +++ b/PLSR/Test/test_plc_device.c @@ -0,0 +1,135 @@ +#include "plc_device.h" +#include "plsr_persistence.h" +#include +#include +#include + +static unsigned int TestCount; + +#define TEST_CHECK(condition) \ + do \ + { \ + TestCount++; \ + if (!(condition)) \ + { \ + (void)fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #condition); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +static void TestAddressMap(void) +{ + uint16_t eventAddress; + + TEST_CHECK(PlsrAxisAddressMap[0].hsdRuntimeBase == 0U); + TEST_CHECK(PlsrAxisAddressMap[3].hsdRuntimeBase == 12U); + TEST_CHECK(PlsrAxisAddressMap[0].smPulseActiveAddress == 1000U); + TEST_CHECK(PlsrAxisAddressMap[3].smDirectionAddress == 1061U); + TEST_CHECK(PlsrAxisAddressMap[2].sdRuntimeBase == 1040U); + TEST_CHECK(PlsrAxisAddressMap[3].eventBase == 6300U); + + TEST_CHECK(PlcDeviceGetEventAddress(0U, 1U, &eventAddress) == PLC_DEVICE_OK); + TEST_CHECK(eventAddress == 6000U); + TEST_CHECK(PlcDeviceGetEventAddress(3U, 100U, &eventAddress) == PLC_DEVICE_OK); + TEST_CHECK(eventAddress == 6399U); + TEST_CHECK(PlcDeviceGetEventAddress(4U, 1U, &eventAddress) + == PLC_DEVICE_INVALID_ARGUMENT); + TEST_CHECK(PlcDeviceGetEventAddress(0U, 0U, &eventAddress) + == PLC_DEVICE_INVALID_ARGUMENT); +} + +static void TestHsdAndPersistence(void) +{ + int32_t value32; + int64_t value64; + const int64_t firstPair = INT64_C(0x1234567887654321); + const int64_t secondPair = -INT64_C(0x102030405060708); + + PlsrPersistenceTestResetStorage(); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_DEFAULTED); + TEST_CHECK(PlcDeviceIsHsdDirty() != 0U); + + TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 11) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceWriteHsdConfig(539U, -22) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceWriteHsdConfig(0U, 1) == PLC_DEVICE_READ_ONLY); + TEST_CHECK(PlcDeviceWriteHsdConfig(459U, 1) == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDeviceReadHsd(460U, &value32) == PLC_DEVICE_OK); + TEST_CHECK(value32 == 11); + TEST_CHECK(PlcDeviceReadHsd(539U, &value32) == PLC_DEVICE_OK); + TEST_CHECK(value32 == -22); + TEST_CHECK(PlcDeviceReadHsd(540U, &value32) == PLC_DEVICE_INVALID_ADDRESS); + + TEST_CHECK(PlcDevicePublishHsdPair(0U, firstPair) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceReadHsdPair(0U, &value64) == PLC_DEVICE_OK); + TEST_CHECK(value64 == firstPair); + TEST_CHECK(PlcDeviceReadHsdPair(1U, &value64) == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceIsHsdDirty() == 0U); + + TEST_CHECK(PlcDeviceWriteHsdConfig(460U, 22) == PLC_DEVICE_OK); + TEST_CHECK(PlcDevicePublishHsdPair(0U, secondPair) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceCheckpointHsd() == PLC_DEVICE_OK); + + PlsrPersistenceTestCorruptNewestHsd(); + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceGetLastHsdLoadResult() == PLSR_PERSISTENCE_OK); + TEST_CHECK(PlcDeviceReadHsd(460U, &value32) == PLC_DEVICE_OK); + TEST_CHECK(value32 == 11); + TEST_CHECK(PlcDeviceReadHsdPair(0U, &value64) == PLC_DEVICE_OK); + TEST_CHECK(value64 == firstPair); +} + +static void TestSfd(void) +{ + int32_t value; + + TEST_CHECK(PlcDeviceWriteSfd(900U, 123) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceWriteSfd(1419U, -456) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceReadSfd(900U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 123); + TEST_CHECK(PlcDeviceReadSfd(1419U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == -456); + TEST_CHECK(PlcDeviceReadSfd(899U, &value) == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDeviceReadSfd(1420U, &value) == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDeviceIsSfdDirty() != 0U); + TEST_CHECK(PlcDeviceSaveSfd() == PLC_DEVICE_NOT_IMPLEMENTED); +} + +static void TestSmAndSd(void) +{ + uint8_t state; + int32_t value; + + TEST_CHECK(PlcDevicePublishSm(2U, 1U, 0U) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceReadSm(1040U, &state) == PLC_DEVICE_OK); + TEST_CHECK(state == 1U); + TEST_CHECK(PlcDeviceReadSm(1041U, &state) == PLC_DEVICE_OK); + TEST_CHECK(state == 0U); + TEST_CHECK(PlcDeviceWriteSm(1040U, 0U) == PLC_DEVICE_READ_ONLY); + TEST_CHECK(PlcDeviceReadSm(1042U, &state) == PLC_DEVICE_INVALID_ADDRESS); + + TEST_CHECK(PlcDevicePublishSd(3U, 11U, -12345) == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceReadSd(1071U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == -12345); + TEST_CHECK(PlcDeviceWriteSd(1071U, 0) == PLC_DEVICE_READ_ONLY); + TEST_CHECK(PlcDeviceReadSd(1012U, &value) == PLC_DEVICE_INVALID_ADDRESS); + TEST_CHECK(PlcDevicePublishSd(4U, 0U, 0) == PLC_DEVICE_INVALID_ARGUMENT); + + TEST_CHECK(PlcDeviceInit() == PLC_DEVICE_OK); + TEST_CHECK(PlcDeviceReadSm(1040U, &state) == PLC_DEVICE_OK); + TEST_CHECK(state == 0U); + TEST_CHECK(PlcDeviceReadSd(1071U, &value) == PLC_DEVICE_OK); + TEST_CHECK(value == 0); +} + +int main(void) +{ + TestAddressMap(); + TestHsdAndPersistence(); + TestSfd(); + TestSmAndSd(); + + (void)printf("PASS: %u checks\n", TestCount); + return EXIT_SUCCESS; +}