实现紧凑软元件存储:HSD:384字节 SFD RAM缓存:2080字节 SD:192字节 SM:4字节 实现地址范围和空洞检查,例如SD1012~1019会判定为非法地址。 实现访问权限:HSD0~15、SM、SD禁止外部直接写入。 HSD460~539和SFD900~1419允许配置写入。 实现HSD相邻两个32位软元件的原子64位读写。 实现HSD脏标志和安全检查点。[plc_device.c (line 254)](F:\\Xinje_Modbus_IAR\\TrainCamp_yuwenhao_modbus\\PLSR\\Src\\plc_device.c:254) 实现I6000~6399事件地址计算,段号使用1~100。[plc_device.c (line 441)](F:\\Xinje_Modbus_IAR\\TrainCamp_yuwenhao_modbus\\PLSR\\Src\\plc_device.c:441) 在系统启动并开启Backup SRAM后初始化软元件层。[main.c (line 164)](F:\\Xinje_Modbus_IAR\\TrainCamp_yuwenhao_modbus\\Core\\Src\\main.c:164) 将新模块加入IAR工程和头文件搜索路径。deepseek
| @@ -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; | |||
| @@ -360,6 +360,7 @@ | |||
| <state>$PROJ_DIR$/../Drivers/CMSIS/Device/ST/STM32F4xx/Include</state> | |||
| <state>$PROJ_DIR$/../Drivers/CMSIS/Include</state> | |||
| <state>$PROJ_DIR$\..\Modbus\Inc</state> | |||
| <state>$PROJ_DIR$\..\PLSR\Inc</state> | |||
| <state>$PROJ_DIR$\..\Middlewares\Third_Party\Micrium\Config</state> | |||
| <state>$PROJ_DIR$\..\Middlewares\Third_Party\Micrium\uCOS-II\Source</state> | |||
| <state>$PROJ_DIR$\..\Middlewares\Third_Party\Micrium\uCOS-II\Ports\ARM-Cortex-M4\IAR</state> | |||
| @@ -1260,6 +1261,24 @@ | |||
| </file> | |||
| </group> | |||
| </group> | |||
| <group> | |||
| <name>PLSR</name> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_address_map.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plsr_persistence.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Inc\plc_device.h</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Src\plsr_persistence.c</name> | |||
| </file> | |||
| <file> | |||
| <name>$PROJ_DIR$\..\PLSR\Src\plc_device.c</name> | |||
| </file> | |||
| </group> | |||
| <group> | |||
| <name>Modbus</name> | |||
| <file> | |||
| @@ -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 | |||
| @@ -0,0 +1,62 @@ | |||
| #ifndef PLC_DEVICE_H | |||
| #define PLC_DEVICE_H | |||
| #include "plsr_address_map.h" | |||
| #include "plsr_persistence.h" | |||
| #include <stdint.h> | |||
| #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 */ | |||
| @@ -0,0 +1,40 @@ | |||
| #ifndef PLSR_ADDRESS_MAP_H | |||
| #define PLSR_ADDRESS_MAP_H | |||
| #include <stdint.h> | |||
| #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 */ | |||
| @@ -0,0 +1,47 @@ | |||
| #ifndef PLSR_PERSISTENCE_H | |||
| #define PLSR_PERSISTENCE_H | |||
| #include "plsr_address_map.h" | |||
| #include <stdint.h> | |||
| #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 */ | |||
| @@ -0,0 +1,478 @@ | |||
| #include "plc_device.h" | |||
| #include <string.h> | |||
| #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; | |||
| } | |||
| @@ -0,0 +1,316 @@ | |||
| #include "plsr_persistence.h" | |||
| #include <stddef.h> | |||
| #include <string.h> | |||
| #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 | |||
| @@ -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 | |||
| } | |||
| } | |||
| @@ -0,0 +1,135 @@ | |||
| #include "plc_device.h" | |||
| #include "plsr_persistence.h" | |||
| #include <inttypes.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| 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; | |||
| } | |||