Sfoglia il codice sorgente

固化四轴地址映射,包括HSD、SFD、SM、SD和I事件入口。[plsr_address_map.h (line 10)](F:\\Xinje_Modbus_IAR\\TrainCamp_yuwenhao_modbus\\PLSR\\Inc\\plsr_address_map.h:10)

实现紧凑软元件存储: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
ywh 1 mese fa
parent
commit
1fa91b403e
10 ha cambiato i file con 1158 aggiunte e 2 eliminazioni
  1. +9
    -1
      Core/Src/main.c
  2. +19
    -0
      EWARM/Modbus.ewp
  3. +1
    -1
      Modbus/Src/modbus_rtu_slave.c
  4. +62
    -0
      PLSR/Inc/plc_device.h
  5. +40
    -0
      PLSR/Inc/plsr_address_map.h
  6. +47
    -0
      PLSR/Inc/plsr_persistence.h
  7. +478
    -0
      PLSR/Src/plc_device.c
  8. +316
    -0
      PLSR/Src/plsr_persistence.c
  9. +51
    -0
      PLSR/Test/run_host_tests.ps1
  10. +135
    -0
      PLSR/Test/test_plc_device.c

+ 9
- 1
Core/Src/main.c Vedi File

@@ -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;


+ 19
- 0
EWARM/Modbus.ewp Vedi File

@@ -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>


+ 1
- 1
Modbus/Src/modbus_rtu_slave.c Vedi 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


+ 62
- 0
PLSR/Inc/plc_device.h Vedi File

@@ -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 */

+ 40
- 0
PLSR/Inc/plsr_address_map.h Vedi File

@@ -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 */

+ 47
- 0
PLSR/Inc/plsr_persistence.h Vedi File

@@ -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 */

+ 478
- 0
PLSR/Src/plc_device.c Vedi File

@@ -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;
}

+ 316
- 0
PLSR/Src/plsr_persistence.c Vedi File

@@ -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

+ 51
- 0
PLSR/Test/run_host_tests.ps1 Vedi File

@@ -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
}
}

+ 135
- 0
PLSR/Test/test_plc_device.c Vedi File

@@ -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;
}

Caricamento…
Annulla
Salva