|
- #ifndef MODBUS_RTU_SLAVE_H
- #define MODBUS_RTU_SLAVE_H
-
- #include "stm32f4xx_hal.h"
- #include <stdint.h>
- #ifdef __cplusplus
- extern "C"
- {
- #endif
-
- /**
- * @brief Modbus 数据模型的最大地址
- *
- * 任务要求规定线圈和保持寄存器地址范围均为 0~0x270F,
- * 因此每种数据共有 10000 项
- */
- #define MODBUS_MAP_LAST_ADDRESS (0x270FU) // 保持寄存器或线圈地址
- #define MODBUS_MAP_ITEM_COUNT (0x2710U) // 保持寄存器或线圈数量
-
- #define MODBUS_SLAVE_DEFAULT_ADDRESS (1U)
- #define MODBUS_CONNECTION_TIMEOUT_MS (1000U)
-
- /* Read-only vendor diagnostics. The request uses the same start/quantity
- * layout as function 0x03; diagnostic words are described below. */
- #define MODBUS_RUNTIME_DIAGNOSTICS_FUNCTION (0x47U)
- #define MODBUS_RUNTIME_DIAGNOSTICS_SIGNATURE (0x4D42U)
- #define MODBUS_RUNTIME_DIAGNOSTICS_VERSION (1U)
- #define MODBUS_RUNTIME_DIAGNOSTICS_WORDS (40U)
-
- #define MODBUS_RUNTIME_FLAG_INITIALIZED (1U << 0U)
- #define MODBUS_RUNTIME_FLAG_VALID_FRAME_SEEN (1U << 1U)
- #define MODBUS_RUNTIME_FLAG_CONNECTED (1U << 2U)
- #define MODBUS_RUNTIME_FLAG_TX_BUSY (1U << 3U)
- #define MODBUS_RUNTIME_FLAG_RX_FRAME_READY (1U << 4U)
- #define MODBUS_RUNTIME_FLAG_RX_ASSEMBLY_INVALID (1U << 5U)
- #define MODBUS_RUNTIME_FLAG_RX_RESTART_OK (1U << 6U)
-
- /**
- * @brief 与 TouchWin 触摸屏联调使用的演示地址
- *
- * 可以直接访问以下地址
- */
- #define HMI_REG_DEVICE_ID (0U)
- #define HMI_REG_UPTIME_SECONDS (1U)
- #define HMI_REG_RX_FRAME_COUNT (2U)
- #define D3 (3U)
- #define D4 (4U)
- #define D5 (5U)
- #define D6 (6U)
- #define D7 (7U)
- #define HMI_REG_WRITE_TEST (10U)
- #define HMI_COIL_WRITE_TEST (0U)
-
- #define MODBUS_RETAINED_D_START (100U)
- #define MODBUS_RETAINED_D_END (120U)
- #define MODBUS_RETAINED_D_COUNT (21U)
-
- #define MODBUS_BACKUP_MAGIC (0x44313030UL)
-
- /**
- * @brief 掉电保持寄存器在Backup SRAM中的存储格式
- */
- typedef struct
- {
- uint32_t magic; ///< 掉电保持数据有效标志
- uint16_t retainedD[MODBUS_RETAINED_D_COUNT]; ///< D100~D120保持值
- } MODBUS_BACKUP_DATA;
- /**
- * @brief Modbus 从站通信统计信息
- */
- typedef struct
- {
- uint32_t rxEventCount; ///< 串口接收事件总数
- uint32_t validFrameCount; ///< CRC 和从站地址均有效的帧数
- uint32_t txFrameCount; ///< 成功启动 DMA 发送的帧数
- uint32_t crcErrorCount; ///< CRC 校验错误帧数
- uint32_t ignoredAddressCount; ///< 因从站地址不匹配而忽略的帧数
- uint32_t illegalFunctionCount; ///< 非法功能码计数
- uint32_t illegalAddressCount; ///< 非法数据地址计数
- uint32_t illegalValueCount; ///< 非法数据值计数
- uint32_t droppedFrameCount; ///< 接收槽占用或长度错误导致的丢帧数
- uint32_t uartErrorCount; ///< HAL 串口错误计数
- } MODBUS_SLAVE_STATS;
-
- /**
- * @brief Read-only snapshot of the Modbus RTU runtime state.
- *
- * Every aligned 32-bit-or-smaller field is read without tearing on Cortex-M4.
- * The getter does not mask motion or UART interrupts, so the complete structure
- * may intentionally span adjacent UART events and is not a transaction-wide
- * atomic snapshot.
- */
- typedef struct
- {
- MODBUS_SLAVE_STATS statistics;
- uint32_t currentTick;
- uint32_t lastValidFrameTick;
- uint32_t lastInterFrameGapCycles;
- uint32_t receiveRestartAttemptCount;
- uint32_t receiveRestartFailureCount;
- uint32_t lastUartErrorCode;
- uint16_t rxAssemblyLength;
- uint16_t rxFrameLength;
- uint8_t lastReceiveStartStatus;
- uint8_t initialized;
- uint8_t hasReceivedValidFrame;
- uint8_t connected;
- uint8_t txBusy;
- uint8_t rxFrameReady;
- uint8_t rxAssemblyInvalid;
- } MODBUS_SLAVE_RUNTIME_DIAGNOSTICS;
- /** @brief Modbus 从站通信统计数据 */
- extern volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics;
- /**
- * @brief 初始化 Modbus 从站并启动 USART DMA 空闲接收
- * @param[in] huart 已由 HAL 初始化完成的串口句柄
- * @param[in] slaveAddress 从站地址,有效范围为 1~247
- * @retval HAL_OK 初始化成功且 DMA 接收已启动
- * @retval HAL_ERROR 输入参数无效
- * @retval HAL_BUSY 串口或 DMA 当前忙
- */
- HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart,
- uint8_t slaveAddress);
- /**
- * @brief 校验并处理一帧待处理请求
- *
- * 本函数应在 uC/OS-II 任务中周期调用中断只负责接收数据,
- * CRC 校验、协议解析和响应发送均在任务上下文中执行
- */
- void ModbusSlavePoll(void);
- /**
- * @brief 处理 HAL 串口 DMA 接收事件
- * @param[in] huart 产生接收事件的串口句柄
- * @param[in] size 本次接收到的字节数
- */
- void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size);
- /**
- * @brief 处理 HAL 串口 DMA 发送完成事件
- * @param[in] huart 完成发送的串口句柄
- */
- void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart);
- /**
- * @brief 处理 HAL 串口错误事件并恢复接收
- * @param[in] huart 发生错误的串口句柄
- */
- void ModbusSlaveOnUartError(UART_HandleTypeDef *huart);
- /**
- * @brief 写入一个保持寄存器
- * @param[in] address 保持寄存器地址
- * @param[in] value 待写入的 16 位数据
- * @retval 1 写入成功
- * @retval 0 保持寄存器地址无效
- */
- uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value);
- /**
- * @brief 读取一个保持寄存器
- * @param[in] address 保持寄存器地址
- * @param[out] value 用于保存读取结果的指针
- * @retval 1 读取成功
- * @retval 0 保持寄存器地址无效或输出指针为空
- */
- uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value);
- /**
- * @brief 设置一个线圈的状态
- * @param[in] address 线圈地址
- * @param[in] state 0 表示复位,非 0 表示置位
- * @retval 1 设置成功
- * @retval 0 线圈地址无效
- */
- uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state);
- /**
- * @brief 读取一个线圈的状态
- * @param[in] address 线圈地址
- * @param[out] state 用于保存线圈状态的指针,结果为 0 或 1
- * @retval 1 读取成功
- * @retval 0 线圈地址无效或输出指针为空
- */
- uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state);
- /**
- * @brief 判断指定时间内是否收到过有效请求
- * @param[in] timeoutMs 连接超时时间,单位为毫秒
- * @retval 1 从站在指定时间内收到过有效请求
- * @retval 0 尚未收到有效请求或连接已经超时
- */
- uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs);
-
- /**
- * @brief Copy all RTU diagnostics with tear-free individual scalar fields.
- * @param[in] timeoutMs Age used to calculate the connected flag.
- * @param[out] diagnostics Destination for the complete snapshot.
- * @retval 1 Snapshot returned.
- * @retval 0 diagnostics is NULL.
- */
- uint8_t ModbusSlaveGetRuntimeDiagnostics(
- uint32_t timeoutMs,
- MODBUS_SLAVE_RUNTIME_DIAGNOSTICS *diagnostics);
- void ModbusRetainedRegistersLoad(void);
- void ModbusRetainedRegistersPoll(void);
-
- #ifdef __cplusplus
- }
- #endif
-
- #endif /* MODBUS_RTU_SLAVE_H */
|