|
- #include "modbus_rtu_slave.h"
- #include "modbus_data_store.h"
- #include <string.h>
-
- #define MODBUS_RTU_ADU_SIZE_MAX (256U) // Modbus RTU 最大 ADU 长度,单位为字节
-
- #define MODBUS_RTU_T15_US (750UL) // 高波特率下固定T1.5时间,单位us
- #define MODBUS_RTU_T35_US (1750UL) // 高波特率下固定T3.5时间,单位us
- #define MODBUS_RTU_BITS_PER_CHAR (11UL) // 8E1包含11个传输位
- #define MODBUS_RTU_HIGH_BAUD_LIMIT (19200UL) // 高低波特率计算方式的分界值
-
- #define MODBUS_BROADCAST_ADDRESS (0U) // 广播地址
-
- #define MODBUS_EX_ILLEGAL_FUNCTION (0x01U) // 非法功能码的异常码
- #define MODBUS_EX_ILLEGAL_ADDRESS (0x02U) // 非法地址的异常码
- #define MODBUS_EX_ILLEGAL_VALUE (0x03U) // 非法数据的异常码
-
- #define MODBUS_READ_COILS_MAX (2000U) // 一次ADU最大线圈读取数量
- #define MODBUS_READ_REGS_MAX (125U) // 一次ADU最大寄存器读取数量
- #define MODBUS_WRITE_COILS_MAX (1968U) // 一次ADU最大线圈写入数量
- #define MODBUS_WRITE_REGS_MAX (123U) // 一次ADU最大寄存器写入数量
-
- #define MODBUS_COIL_VALUE_ON (0xFF00U)
- #define MODBUS_COIL_VALUE_OFF (0x0000U)
-
- static UART_HandleTypeDef *ModbusUart;
- static uint8_t ModbusSlaveAddress;
- static HAL_StatusTypeDef ModbusStartReceive(void);
- /*最近一次字节尾到字节头的间隔周期数*/
- static volatile uint32_t ModbusLastInterFrameGapCycles;
- /**
- * DMA 缓冲区只由 DMA 写入;帧缓冲区在接收回调中完成一次快照,
- * 随后由任务解析,避免 DMA 重启后覆盖尚未处理的数据
- */
- static uint8_t ModbusRxDmaBuffer[MODBUS_RTU_ADU_SIZE_MAX];
- /* 保存被UART IDLE事件分开的DMA片段,达到T3.5后再提交解析 */
- static uint8_t ModbusRxAssemblyBuffer[MODBUS_RTU_ADU_SIZE_MAX];
- static uint8_t ModbusRxFrame[MODBUS_RTU_ADU_SIZE_MAX];
- static uint8_t ModbusTxFrame[MODBUS_RTU_ADU_SIZE_MAX];
- static uint16_t ModbusWriteRegisterScratch[MODBUS_WRITE_REGS_MAX];
- /* D100~D120 上一次已保存的值,用于检测数据是否变化 */
- static uint16_t ModbusRetainedSnapshot[MODBUS_RETAINED_D_COUNT];
- static volatile uint16_t ModbusRxFrameLength;
- static volatile uint8_t ModbusRxFrameReady;
- static volatile uint8_t ModbusTxBusy;
- static volatile uint16_t ModbusRxAssemblyLength; // 当前拼帧长度
- static volatile uint8_t ModbusRxAssemblyInvalid; // 帧内间隔超过T1.5时置1
- static volatile uint32_t ModbusRxLastByteCycle; // 上一片段末字节结束时刻
- static uint32_t ModbusRtuT15Cycles; // T1.5对应的CPU周期数
- static uint32_t ModbusRtuT35Cycles; // T3.5对应的CPU周期数
- static uint32_t ModbusRtuCharCycles; // 一个UART字符对应的CPU周期数
- static volatile uint32_t ModbusLastValidFrameTick;
- static volatile uint8_t ModbusHasReceivedValidFrame;
- static volatile uint32_t ModbusReceiveRestartAttemptCount;
- static volatile uint32_t ModbusReceiveRestartFailureCount;
- static volatile uint32_t ModbusNextReceiveRetryTick;
- static volatile uint32_t ModbusLastUartErrorCode;
- static volatile uint8_t ModbusLastReceiveStartStatus = (uint8_t)HAL_ERROR;
- static volatile MODBUS_BACKUP_DATA *ModbusBackupData =
- (volatile MODBUS_BACKUP_DATA *)BKPSRAM_BASE;
-
- /* Word and bit images are owned by modbus_data_store.c. Standard Modbus
- * coils are the protocol view of the PLC M device space. */
-
- volatile MODBUS_SLAVE_STATS ModbusSlaveStatistics;
-
- /**
- * @brief 计算 Modbus RTU CRC16 校验值
- * @param[in] data 待校验数据
- * @param[in] length 待校验数据长度
- * @return CRC16 校验值
- */
- static uint16_t ModbusCrc16(const uint8_t *data, uint16_t length)
- {
- uint16_t crc = 0xFFFFU;
- uint16_t index;
- uint8_t bit;
-
- for (index = 0U; index < length; index++)
- {
- crc ^= data[index];
-
- for (bit = 0U; bit < 8U; bit++)
- {
- if ((crc & 0x0001U) != 0U)
- {
- crc = (uint16_t)((crc >> 1U) ^ 0xA001U);
- }
- else
- {
- crc >>= 1U;
- }
- }
- }
-
- return crc;
- }
-
- /**
- * @brief 提取一个 16 位无符号整数
- * @param[in] data 两个字节的数据地址
- * @return 转换后的 16 位无符号整数
- */
- static uint16_t ModbusGetU16Be(const uint8_t *data)
- {
- return (uint16_t)(((uint16_t)data[0] << 8U) | data[1]);
- }
-
- /**
- * @brief 提取一个 24 位无符号整数
- * @param[in] data 3个字节的数据地址
- * @return 转换后的 24 位无符号整数
- */
- static uint32_t ModbusGetU24Be(const uint8_t *data)
- {
- return (uint32_t)(((uint32_t)data[0] << 16U) | ((uint32_t)data[1] << 8U)
- | (uint32_t)data[2]);
- }
-
- /**
- * @brief 检查连续数据地址范围是否合法
- * @param[in] start 起始地址
- * @param[in] quantity 数据项数量
- */
- static uint8_t ModbusAddressRangeIsValid(uint16_t start, uint16_t quantity)
- {
- if (start >= MODBUS_MAP_ITEM_COUNT)
- {
- return 0U;
- }
-
- // 先做减法再比较,避免溢出
- return (quantity <= (MODBUS_MAP_ITEM_COUNT - start)) ? 1U : 0U;
- }
-
- /**
- * @brief 读取已经确认地址合法的线圈
- * @param[in] address 线圈地址
- * @return 线圈状态,取值为 0 或 1
- */
- static uint8_t ModbusCoilGetUnchecked(uint16_t address)
- {
- uint8_t value = 0U;
-
- (void)ModbusDataReadBit(MODBUS_BIT_DEVICE_M, address, &value);
- return value;
- }
-
- /**
- * @brief 设置已经确认地址合法的线圈
- * @param[in] address 线圈地址
- * @param[in] state 0 表示复位,非 0 表示置位
- */
- static void ModbusCoilSetUnchecked(uint16_t address, uint8_t state)
- {
- (void)ModbusDataWriteBit(MODBUS_BIT_DEVICE_M, address, state);
- }
-
- /**
- * @brief 初始化用于RTU帧间隔测量的DWT周期计数器
- * @param[in] baudRate 当前串口波特率
- */
- static void ModbusRtuTimingInit(uint32_t baudRate)
- {
- uint64_t coreClock;
- /* 开启DWT周期计数器,CYCCNT每经过一个CPU时钟周期自动加1 */
- CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
- DWT->CYCCNT = 0U;
- DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
-
- coreClock = SystemCoreClock;
- /* 一个字符周期数=CPU频率*每字符位数/波特率 */
- ModbusRtuCharCycles =
- (uint32_t)((coreClock * MODBUS_RTU_BITS_PER_CHAR) / baudRate);
-
- if (baudRate > MODBUS_RTU_HIGH_BAUD_LIMIT)
- {
- /* 高波特率使用固定750us和1750us,999999用于向上取整 */
- ModbusRtuT15Cycles =
- (uint32_t)((coreClock * MODBUS_RTU_T15_US + 999999UL) / 1000000UL);
-
- ModbusRtuT35Cycles =
- (uint32_t)((coreClock * MODBUS_RTU_T35_US + 999999UL) / 1000000UL);
- }
- else
- {
- /* 低波特率按照1.5个字符时间和3.5个字符时间计算 */
- ModbusRtuT15Cycles =
- (uint32_t)(((uint64_t)ModbusRtuCharCycles * 3UL + 1UL) / 2UL);
- ModbusRtuT35Cycles =
- (uint32_t)(((uint64_t)ModbusRtuCharCycles * 7UL + 1UL) / 2UL);
- }
- }
-
- /**
- * @brief 结束当前RTU接收组帧
- * @note 调用本函数时必须保证不会与串口接收中断并发执行
- */
- static void ModbusRxAssemblyFinalize(void)
- {
- if (ModbusRxAssemblyLength == 0U)
- {
- return;
- }
- /*拼帧区是否有数据*/
- if ((ModbusRxAssemblyInvalid == 0U) && (ModbusRxFrameReady == 0U))
- {
- /* 当前帧未违反T1.5且解析缓冲区空闲时提交完整帧 */
- (void)memcpy(ModbusRxFrame, ModbusRxAssemblyBuffer,
- ModbusRxAssemblyLength);
- ModbusRxFrameLength = ModbusRxAssemblyLength;
- ModbusRxFrameReady = 1U;
- }
- else
- {
- /* T1.5无效帧或上一帧尚未处理完成时丢弃 */
- ModbusSlaveStatistics.droppedFrameCount++;
- }
-
- ModbusRxAssemblyLength = 0U;
- ModbusRxAssemblyInvalid = 0U;
- }
-
- /**
- * @brief 静默时间达到T3.5后,将接收数据交给协议解析任务
- */
- static void ModbusTryFinalizeReceive(void)
- {
- uint32_t now;
-
- if (ModbusRxAssemblyLength == 0U)
- {
- return;
- }
-
- /* DMA缓冲区出现新数据时,说明串口仍在接收当前片段 */
- if ((ModbusUart->hdmarx != NULL) && /*串口DMA使能*/
- ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) != 0U)
- && (__HAL_DMA_GET_COUNTER(ModbusUart->hdmarx)
- < MODBUS_RTU_ADU_SIZE_MAX))
- {
- return;
- }
-
- now = DWT->CYCCNT;
- /* 从末字节结束时刻开始计算静默时间,未达到T3.5时继续等待 */
- if ((uint32_t)(now - ModbusRxLastByteCycle) < ModbusRtuT35Cycles)
- {
- return;
- }
-
- /* 静默达到T3.5,当前RTU帧结束,发送响应前停止接收DMA */
- (void)HAL_UART_AbortReceive(ModbusUart);
-
- /* HAL_UART_AbortReceive has already stopped DMA/IDLE callbacks. The
- * finalize routine may copy a full RTU ADU, so it must not globally mask
- * the 100 kHz motion interrupts around that memcpy. */
- ModbusRxAssemblyFinalize();
- /* 无效帧被丢弃后,重新启动DMA接收。 */
- if (ModbusRxFrameReady == 0U)
- {
- (void)ModbusStartReceive();
- }
-
-
- }
-
- /**
- * @brief 启动 USART DMA 空闲接收
- * @retval HAL_OK DMA 接收启动成功
- * @retval HAL_BUSY 串口未配置或发送尚未结束
- * @return 其他 HAL 状态表示 DMA 接收启动失败
- */
- static HAL_StatusTypeDef ModbusStartReceive(void)
- {
- HAL_StatusTypeDef status;
-
- ModbusReceiveRestartAttemptCount++;
-
- if (ModbusUart == NULL)
- {
- status = HAL_ERROR;
- ModbusReceiveRestartFailureCount++;
- ModbusLastReceiveStartStatus = (uint8_t)status;
- return status;
- }
-
- if (ModbusTxBusy != 0U)
- {
- status = HAL_BUSY;
- ModbusReceiveRestartFailureCount++;
- ModbusLastReceiveStartStatus = (uint8_t)status;
- return status;
- }
-
- status = HAL_UARTEx_ReceiveToIdle_DMA(ModbusUart, ModbusRxDmaBuffer,
- sizeof(ModbusRxDmaBuffer));
-
- /* A duplicate start request can race with an already healthy DMA receiver.
- * Treat that specific HAL_BUSY case as operational, not as a recovery
- * failure; all other HAL_BUSY/HAL_ERROR results remain visible. */
- if ((status == HAL_BUSY)
- && (ModbusUart->RxState == HAL_UART_STATE_BUSY_RX)
- && ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) != 0U))
- {
- status = HAL_OK;
- }
-
- if ((status == HAL_OK) && (ModbusUart->hdmarx != NULL))
- {
- /*
- * 普通 Modbus 帧只应在 IDLE 或缓冲区满时交给应用
- * 关闭 DMA 半传输中断,避免长帧在一半位置被误认为完整帧
- */
- __HAL_DMA_DISABLE_IT(ModbusUart->hdmarx, DMA_IT_HT);
- }
-
- if (status != HAL_OK)
- {
- ModbusReceiveRestartFailureCount++;
- ModbusLastUartErrorCode = ModbusUart->ErrorCode;
- ModbusNextReceiveRetryTick = HAL_GetTick() + 10UL;
- }
- else
- {
- ModbusNextReceiveRetryTick = 0UL;
- }
- ModbusLastReceiveStartStatus = (uint8_t)status;
-
- return status;
- }
-
- /**
- * @brief 在 RTU 帧末尾追加 CRC 低字节和高字节
- * @param[in,out] frame 待追加 CRC 的帧缓冲区
- * @param[in] payloadLength 不包含 CRC 的有效载荷长度
- */
- static void ModbusAppendCrc(uint8_t *frame, uint16_t payloadLength)
- {
- uint16_t crc = ModbusCrc16(frame, payloadLength);
-
- /* Modbus RTU 在线路上传输 CRC 低字节在前、高字节在后 */
- frame[payloadLength] = (uint8_t)(crc & 0x00FFU);
- frame[payloadLength + 1U] = (uint8_t)(crc >> 8U);
- }
-
- /**
- * @brief 构造 Modbus 异常响应
- * @param[in] function 请求功能码
- * @param[in] exception 异常码
- * @return 异常响应 ADU 长度
- */
- static uint16_t ModbusBuildException(uint8_t function, uint8_t exception)
- {
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = (uint8_t)(function | 0x80U);
- ModbusTxFrame[2] = exception;
- ModbusAppendCrc(ModbusTxFrame, 3U);
-
- return 5U;
- }
-
- /**
- * @brief Build a bounded, read-only runtime diagnostic response (function 0x47).
- *
- * The request is: function, start word (BE), quantity (BE). Multi-word
- * 32-bit values use low word first, matching the PLSR diagnostic window.
- */
- static uint16_t ModbusProcessRuntimeDiagnostics(const uint8_t *request,
- uint16_t requestLength)
- {
- MODBUS_SLAVE_RUNTIME_DIAGNOSTICS diagnostics;
- uint16_t words[MODBUS_RUNTIME_DIAGNOSTICS_WORDS];
- uint32_t flags = 0U;
- uint32_t stats[10];
- uint16_t start;
- uint16_t quantity;
- uint16_t index;
- uint16_t responseLength;
-
- if (requestLength != 8U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- start = ModbusGetU16Be(&request[2]);
- quantity = ModbusGetU16Be(&request[4]);
- if ((quantity == 0U)
- || (start >= MODBUS_RUNTIME_DIAGNOSTICS_WORDS)
- || (quantity > (MODBUS_RUNTIME_DIAGNOSTICS_WORDS - start)))
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
- }
-
- (void)ModbusSlaveGetRuntimeDiagnostics(MODBUS_CONNECTION_TIMEOUT_MS,
- &diagnostics);
- if (diagnostics.initialized != 0U)
- {
- flags |= MODBUS_RUNTIME_FLAG_INITIALIZED;
- }
- if (diagnostics.hasReceivedValidFrame != 0U)
- {
- flags |= MODBUS_RUNTIME_FLAG_VALID_FRAME_SEEN;
- }
- if (diagnostics.connected != 0U)
- {
- flags |= MODBUS_RUNTIME_FLAG_CONNECTED;
- }
- if (diagnostics.txBusy != 0U)
- {
- flags |= MODBUS_RUNTIME_FLAG_TX_BUSY;
- }
- if (diagnostics.rxFrameReady != 0U)
- {
- flags |= MODBUS_RUNTIME_FLAG_RX_FRAME_READY;
- }
- if (diagnostics.rxAssemblyInvalid != 0U)
- {
- flags |= MODBUS_RUNTIME_FLAG_RX_ASSEMBLY_INVALID;
- }
- if (diagnostics.lastReceiveStartStatus == (uint8_t)HAL_OK)
- {
- flags |= MODBUS_RUNTIME_FLAG_RX_RESTART_OK;
- }
-
- words[0] = MODBUS_RUNTIME_DIAGNOSTICS_SIGNATURE;
- words[1] = MODBUS_RUNTIME_DIAGNOSTICS_VERSION;
- words[2] = MODBUS_RUNTIME_DIAGNOSTICS_WORDS;
- words[3] = (uint16_t)flags;
- words[4] = (uint16_t)diagnostics.currentTick;
- words[5] = (uint16_t)(diagnostics.currentTick >> 16U);
- words[6] = (uint16_t)diagnostics.lastValidFrameTick;
- words[7] = (uint16_t)(diagnostics.lastValidFrameTick >> 16U);
- words[8] = (uint16_t)diagnostics.lastInterFrameGapCycles;
- words[9] = (uint16_t)(diagnostics.lastInterFrameGapCycles >> 16U);
- words[10] = (uint16_t)diagnostics.receiveRestartAttemptCount;
- words[11] = (uint16_t)(diagnostics.receiveRestartAttemptCount >> 16U);
- words[12] = (uint16_t)diagnostics.receiveRestartFailureCount;
- words[13] = (uint16_t)(diagnostics.receiveRestartFailureCount >> 16U);
- words[14] = (uint16_t)diagnostics.lastUartErrorCode;
- words[15] = (uint16_t)(diagnostics.lastUartErrorCode >> 16U);
- words[16] = diagnostics.lastReceiveStartStatus;
- words[17] = diagnostics.rxAssemblyLength;
- words[18] = diagnostics.rxFrameLength;
- words[19] = MODBUS_CONNECTION_TIMEOUT_MS;
-
- stats[0] = diagnostics.statistics.rxEventCount;
- stats[1] = diagnostics.statistics.validFrameCount;
- stats[2] = diagnostics.statistics.txFrameCount;
- stats[3] = diagnostics.statistics.crcErrorCount;
- stats[4] = diagnostics.statistics.ignoredAddressCount;
- stats[5] = diagnostics.statistics.illegalFunctionCount;
- stats[6] = diagnostics.statistics.illegalAddressCount;
- stats[7] = diagnostics.statistics.illegalValueCount;
- stats[8] = diagnostics.statistics.droppedFrameCount;
- stats[9] = diagnostics.statistics.uartErrorCount;
- for (index = 0U; index < 10U; index++)
- {
- words[20U + (index * 2U)] = (uint16_t)stats[index];
- words[21U + (index * 2U)] = (uint16_t)(stats[index] >> 16U);
- }
-
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = MODBUS_RUNTIME_DIAGNOSTICS_FUNCTION;
- ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
- for (index = 0U; index < quantity; index++)
- {
- uint16_t value = words[start + index];
- ModbusTxFrame[3U + (index * 2U)] = (uint8_t)(value >> 8U);
- ModbusTxFrame[4U + (index * 2U)] = (uint8_t)value;
- }
- responseLength = (uint16_t)(3U + (quantity * 2U));
- ModbusAppendCrc(ModbusTxFrame, responseLength);
- return (uint16_t)(responseLength + 2U);
- }
-
- /**
- * @brief Process read coils (0x01) or discrete inputs (0x02).
- * @param[in] request RTU request frame.
- * @param[in] requestLength Request frame length.
- * @param[in] functionCode Function code echoed in the response.
- * @param[in] device Backing PLC bit-device image.
- * @return Response ADU length, including an exception response when invalid.
- */
- static uint16_t ModbusProcessReadBits(const uint8_t *request,
- uint16_t requestLength,
- uint8_t functionCode,
- MODBUS_BIT_DEVICE device)
- {
-
- uint16_t start;
- uint16_t quantity;
- uint16_t index;
- uint8_t byteCount;
-
- if (requestLength != 8U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- start = ModbusGetU16Be(&request[2]);
- quantity = ModbusGetU16Be(&request[4]);
-
- if ((quantity == 0U)
- || (quantity > MODBUS_READ_COILS_MAX)) // 数量0或者数量大于最大值
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- if (ModbusAddressRangeIsValid(start, quantity) == 0U) // 地址检查
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
- }
-
- byteCount = (uint8_t)((quantity + 7U) / 8U);
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = functionCode;
- ModbusTxFrame[2] = byteCount;
- (void)memset(&ModbusTxFrame[3], 0, byteCount);
-
- for (index = 0U; index < quantity; index++)
- {
- uint8_t value = 0U;
-
- if ((ModbusDataReadBit(device,
- (uint32_t)start + index,
- &value) != 0U)
- && (value != 0U))
- {
- ModbusTxFrame[3U + (index >> 3U)] |=
- (uint8_t)(1U << (index & 0x0007U));
- }
- }
-
- ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + byteCount));
- return (uint16_t)(5U + byteCount);
- }
-
- /**
- * @brief 处理读保持寄存器功能码 0x03
- * @param[in] request RTU 请求帧
- * @param[in] requestLength 请求帧长度
- * @return 待发送响应长度,异常请求返回异常响应长度
- */
- static uint16_t ModbusProcessReadHolding(const uint8_t *request,
- uint16_t requestLength)
-
- {
- uint16_t start;
- uint16_t quantity;
- uint16_t index;
- uint16_t value;
-
- if (requestLength != 8U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- start = ModbusGetU16Be(&request[2]);
- quantity = ModbusGetU16Be(&request[4]);
-
- if ((quantity == 0U)
- || (quantity > MODBUS_READ_REGS_MAX)) // 数量0或者数量大于最大值
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- if (ModbusAddressRangeIsValid(start, quantity) == 0U) // 地址检查
- {
- if ((start >= 20000U) && (start < 25000U) && (quantity > 0U)
- && (quantity <= (25000U - start)))
- goto tx;
- ModbusSlaveStatistics.illegalAddressCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
- }
- tx:
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = 0X03;
- ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
-
- for (index = 0U; index < quantity; index++)
- {
- uint16_t address = start + index;
- /* D20000~D24999映射到D1、D3、D5……D9999 */
- if (address >= 20000U)
- {
- address = (address - 20000U) * 2U + 1U;
- }
- if (ModbusDataReadWord(MODBUS_DATA_DEVICE_D, address, &value) == 0U)
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
- }
- ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
- ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
- }
-
- ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
- return (uint16_t)(5U + quantity * 2U);
- }
-
- /**
- * @brief 处理写单个保持寄存器功能码 0x06
- * @param[in] request RTU 请求帧
- * @param[in] requestLength 请求帧长度
- * @param[in] isBroadcast 非 0 表示当前请求为广播
- * @return 单播响应长度;广播或无法响应时返回 0
- */
- static uint16_t ModbusProcessWriteSingleRegister(const uint8_t *request,
- uint16_t requestLength,
- uint8_t isBroadcast)
- {
- uint16_t address;
- uint16_t value;
-
- if (requestLength != 8U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- address = ModbusGetU16Be(&request[2]);
- value = ModbusGetU16Be(&request[4]);
-
- if (address >= MODBUS_MAP_ITEM_COUNT)
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
-
-
- }
-
- (void)ModbusDataWriteWord(MODBUS_DATA_DEVICE_D, address, value);
-
- if (isBroadcast != 0U)
- {
- return 0U;
- }
-
- /* 0x06 的正常响应必须原样回显请求的前 6 个字节 */
- (void)memcpy(ModbusTxFrame, request, 6U);
- ModbusAppendCrc(ModbusTxFrame, 6U);
- return 8U;
- }
-
- /**
- * @brief 处理写单个线圈功能码 0x05
- * @param[in] request RTU 请求帧
- * @param[in] requestLength 请求帧长度
- * @param[in] isBroadcast 非 0 表示当前请求为广播
- * @return 单播响应长度;广播或无法响应时返回 0
- */
- static uint16_t ModbusProcessWriteSingleCoil(const uint8_t *request,
- uint16_t requestLength,
- uint8_t isBroadcast)
- {
- uint16_t address;
- uint16_t value;
-
- if (requestLength != 8U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U)? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- address = ModbusGetU16Be(&request[2]);
- value = ModbusGetU16Be(&request[4]);
-
- /*
- * 0x05 只接受 0xFF00(线圈置位)和 0x0000(线圈复位);
- * 其他数值属于非法数据值
- */
- if ((value != MODBUS_COIL_VALUE_ON) && (value != MODBUS_COIL_VALUE_OFF))
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U)? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- if (address >= MODBUS_MAP_ITEM_COUNT)
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
-
-
- }
-
- ModbusCoilSetUnchecked(address, (value == MODBUS_COIL_VALUE_ON) ? 1U : 0U);
-
- if (isBroadcast != 0U)
- {
- return 0U;
- }
-
- /* 0x05 正常应答原样回显请求的前 6 个字节,再追加 CRC */
- (void)memcpy(ModbusTxFrame, request, 6U);
- ModbusAppendCrc(ModbusTxFrame, 6U);
- return 8U;
- }
-
- /**
- * @brief 处理写多个线圈功能码 0x0F
- * @param[in] request RTU 请求帧
- * @param[in] requestLength 请求帧长度
- * @param[in] isBroadcast 非 0 表示当前请求为广播
- * @return 单播响应长度;广播或无法响应时返回 0
- */
- static uint16_t ModbusProcessWriteMultipleCoils(const uint8_t *request,
- uint16_t requestLength,
- uint8_t isBroadcast)
- {
- uint16_t start;
- uint16_t quantity;
- uint16_t index;
- uint8_t byteCount;
- uint8_t expectedByteCount;
-
- if (requestLength < 9U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- start = ModbusGetU16Be(&request[2]);
- quantity = ModbusGetU16Be(&request[4]);
- byteCount = request[6];
- expectedByteCount = (uint8_t)((quantity + 7U) / 8U);
-
- if ((quantity == 0U) || (quantity > MODBUS_WRITE_COILS_MAX)
- || (byteCount != expectedByteCount)
- || (requestLength != (uint16_t)(9U + byteCount)))
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U)? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- if (ModbusAddressRangeIsValid(start, quantity) == 0U)
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return (isBroadcast != 0U) ? 0U: ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
-
-
- }
-
- for (index = 0U; index < quantity; index++)
- {
- uint8_t state;
-
- state = (uint8_t)((request[7U + (index >> 3U)] >> (index & 0x0007U)) & 0x01U);
-
- ModbusCoilSetUnchecked((uint16_t)(start + index), state);
- }
-
- if (isBroadcast != 0U)
- {
- return 0U;
- }
-
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = 0X0F;
- (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
- ModbusAppendCrc(ModbusTxFrame, 6U);
- return 8U;
- }
-
- /**
- * @brief 处理写多个保持寄存器功能码 0x10
- * @param[in] request RTU 请求帧
- * @param[in] requestLength 请求帧长度
- * @param[in] isBroadcast 非 0 表示当前请求为广播
- * @return 单播响应长度;广播或无法响应时返回 0
- */
- static uint16_t ModbusProcessWriteMultipleRegisters(const uint8_t *request,
- uint16_t requestLength,
- uint8_t isBroadcast)
- {
- uint16_t start;
- uint16_t quantity;
- uint16_t index;
- uint8_t byteCount;
-
- if (requestLength < 9U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- start = ModbusGetU16Be(&request[2]);
- quantity = ModbusGetU16Be(&request[4]);
- byteCount = request[6];
-
- if ((quantity == 0U) || (quantity > MODBUS_WRITE_REGS_MAX)
- || (byteCount != (uint8_t)(quantity * 2U))
- || (requestLength != (uint16_t)(9U + byteCount)))
- {
- ModbusSlaveStatistics.illegalValueCount++;
- return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
-
-
- }
-
- if (ModbusAddressRangeIsValid(start, quantity) == 0U)
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return (isBroadcast != 0U) ? 0U : ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
-
-
- }
-
- for (index = 0U; index < quantity; index++)
- {
- ModbusWriteRegisterScratch[index] =
- ModbusGetU16Be(&request[7U + index * 2U]);
- }
- (void)ModbusDataWriteWords(MODBUS_DATA_DEVICE_D,
- start,
- ModbusWriteRegisterScratch,
- quantity);
-
- if (isBroadcast != 0U)
- {
- return 0U;
- }
-
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = 0X10;
- (void)memcpy(&ModbusTxFrame[2], &request[2], 4U);
- ModbusAppendCrc(ModbusTxFrame, 6U);
- return 8U;
- }
-
- /**
- * @brief 处理读取扩展地址保持寄存器功能码0x48
- * @param[in] request RTU请求帧
- * @param[in] requestLength 请求帧长度
- * @return 待发送响应长度,异常请求返回异常响应长度
- */
- static uint16_t ModbusProcessReadBigHolding(const uint8_t *request,
- uint16_t requestLength)
- {
- uint32_t start;
- uint32_t currentAddress;
- uint16_t quantity;
- uint16_t index;
- uint16_t value;
-
- /* 请求帧:站号1 + 功能码1 + 地址3 + 数量2 + CRC2 */
- if (requestLength != 9U)
- {
- ModbusSlaveStatistics.illegalValueCount++;
-
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- /* 提取24位起始地址和16位寄存器数量 */
- start = ModbusGetU24Be(&request[2]);
- quantity = ModbusGetU16Be(&request[5]);
-
- /* 一次最多读取125个寄存器 */
- if ((quantity == 0U) || (quantity > MODBUS_READ_REGS_MAX))
- {
- ModbusSlaveStatistics.illegalValueCount++;
-
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_VALUE);
- }
-
- /* Function 0x48 retains the existing sparse range: SRAM 0..19999 and
- * CCMRAM 40000..69998. The unallocated 20000..39999 gap is rejected. */
- if ((start >= 69999UL) || ((uint32_t)quantity > (69999UL - start))
- || ((start < 40000UL)
- && ((start >= 20000UL)
- || ((start + quantity) > 20000UL))))
-
- {
- ModbusSlaveStatistics.illegalAddressCount++;
-
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
- }
-
- /* 组成正常响应帧头 */
- ModbusTxFrame[0] = ModbusSlaveAddress;
- ModbusTxFrame[1] = 0x48U;
- ModbusTxFrame[2] = (uint8_t)(quantity * 2U);
-
- for (index = 0U; index < quantity; index++)
- {
- currentAddress = start + (uint32_t)index;
-
- /* The data-store layer validates the sparse physical range. */
- if (ModbusDataReadLinear(currentAddress, &value) == 0U)
- {
- ModbusSlaveStatistics.illegalAddressCount++;
- return ModbusBuildException(request[1], MODBUS_EX_ILLEGAL_ADDRESS);
- }
-
- /* 每个寄存器按照高字节、低字节装入响应帧 */
- ModbusTxFrame[3U + index * 2U] = (uint8_t)(value >> 8U);
- ModbusTxFrame[4U + index * 2U] = (uint8_t)(value & 0x00FFU);
- }
- /* 添加CRC */
- ModbusAppendCrc(ModbusTxFrame, (uint16_t)(3U + quantity * 2U));
-
- return (uint16_t)(5U + quantity * 2U);
- }
-
- /**
- * @brief 校验并分发一帧 Modbus RTU 请求
- * @param[in] request RTU 请求帧
- * @param[in] requestLength 请求帧长度
- * @return 待发送响应长度;无需响应时返回 0
- */
- static uint16_t ModbusProcessRequest(const uint8_t *request,
- uint16_t requestLength)
- {
- uint16_t calculatedCrc;
- uint16_t receivedCrc;
- uint8_t isBroadcast;
-
- if (requestLength < 4U)
- {
- return 0U;
- }
-
- calculatedCrc = ModbusCrc16(request, (uint16_t)(requestLength - 2U));
- receivedCrc = (uint16_t)(request[requestLength - 2U]
- | ((uint16_t)request[requestLength - 1U] << 8U));
-
- if (calculatedCrc != receivedCrc) // CRC校验
- {
- ModbusSlaveStatistics.crcErrorCount++;
- return 0U;
- }
-
- if ((request[0] != ModbusSlaveAddress)
- && (request[0] != MODBUS_BROADCAST_ADDRESS)) // 非法从站地址
- {
- ModbusSlaveStatistics.ignoredAddressCount++;
- return 0U;
- }
-
- isBroadcast =
- (request[0] == MODBUS_BROADCAST_ADDRESS) ? 1U : 0U; // 是否广播请求
- ModbusSlaveStatistics.validFrameCount++;
- ModbusLastValidFrameTick = HAL_GetTick();
- ModbusHasReceivedValidFrame = 1U;
-
- switch (request[1])
- {
- case 0X01U: // 读线圈
- // 广播请求不允许读取,从站不作响应
- return (isBroadcast != 0U)
- ? 0U
- : ModbusProcessReadBits(request,
- requestLength,
- 0X01U,
- MODBUS_BIT_DEVICE_M);
-
- case 0X02U: /* Read discrete inputs: protocol view of PLC X. */
- return (isBroadcast != 0U)
- ? 0U
- : ModbusProcessReadBits(request,
- requestLength,
- 0X02U,
- MODBUS_BIT_DEVICE_X);
-
- case 0X03U: // 读保持寄存器
- return (isBroadcast != 0U)
- ? 0U
- : ModbusProcessReadHolding(request, requestLength);
- case 0x05U: // 写单个线圈
- return ModbusProcessWriteSingleCoil(request, requestLength,
- isBroadcast);
-
- case 0x06U: // 写单个保持寄存器
- return ModbusProcessWriteSingleRegister(request, requestLength,
- isBroadcast);
-
- case 0x0FU: // 写多个线圈
- return ModbusProcessWriteMultipleCoils(request, requestLength,
- isBroadcast);
-
- case 0x10U: // 写多个保持寄存器
- return ModbusProcessWriteMultipleRegisters(request, requestLength,
- isBroadcast);
-
- case MODBUS_RUNTIME_DIAGNOSTICS_FUNCTION:
- return (isBroadcast != 0U)
- ? 0U
- : ModbusProcessRuntimeDiagnostics(request,
- requestLength);
-
- case 0x48U: // 读大地址保持寄存器
- return (isBroadcast != 0U)
- ? 0U
- : ModbusProcessReadBigHolding(request, requestLength);
-
- default: // 未知功能码
- ModbusSlaveStatistics.illegalFunctionCount++;
- return (isBroadcast != 0U)
- ? 0U
- : ModbusBuildException(request[1],
- MODBUS_EX_ILLEGAL_FUNCTION);
- }
- }
-
- HAL_StatusTypeDef ModbusSlaveInit(UART_HandleTypeDef *huart,
- uint8_t slaveAddress)
-
- {
- if ((huart == NULL) || (slaveAddress == 0U) || (slaveAddress > 247U))
- {
- return HAL_ERROR;
- }
-
- ModbusUart = huart;
- ModbusSlaveAddress = slaveAddress;
- /* 清空拼帧状态并根据当前波特率初始化T1.5和T3.5 */
- ModbusRxAssemblyLength = 0U;
- ModbusRxAssemblyInvalid = 0U;
- ModbusRxFrameReady = 0U;
- ModbusTxBusy = 0U;
- ModbusNextReceiveRetryTick = 0UL;
- ModbusRtuTimingInit(huart->Init.BaudRate);
-
- return ModbusStartReceive();
- }
-
- void ModbusSlavePoll(void)
- {
- uint16_t responseLength;
-
- /* Recover from a failed DMA rearm even when no later UART callback occurs.
- * Limit retries to 100 Hz so a persistent HAL fault cannot monopolize the
- * 1 ms application task. */
- if ((ModbusUart != NULL) && (ModbusTxBusy == 0U)
- && (ModbusRxFrameReady == 0U)
- && ((ModbusUart->RxState != HAL_UART_STATE_BUSY_RX)
- || ((ModbusUart->Instance->CR3 & USART_CR3_DMAR) == 0U))
- && ((ModbusNextReceiveRetryTick == 0UL)
- || ((int32_t)(HAL_GetTick() - ModbusNextReceiveRetryTick) >= 0)))
- {
- (void)ModbusStartReceive();
- }
-
- /* 检查末字节后的静默时间是否已经达到T3.5 */
- ModbusTryFinalizeReceive();
- if ((ModbusRxFrameReady == 0U) || (ModbusTxBusy != 0U))
- {
- return;
- }
-
- responseLength = ModbusProcessRequest(ModbusRxFrame, ModbusRxFrameLength);
- if (responseLength > 0U)
- {
- ModbusTxBusy = 1U;
-
- if (HAL_UART_Transmit_DMA(ModbusUart, ModbusTxFrame, responseLength)
- == HAL_OK)
- {
- ModbusSlaveStatistics.txFrameCount++;
- }
- else
- {
- ModbusTxBusy = 0U;
- ModbusSlaveStatistics.uartErrorCount++;
- ModbusLastUartErrorCode = ModbusUart->ErrorCode;
- (void)ModbusStartReceive(); // 重启DMA接收
- }
- }
- else
- {
- (void)ModbusStartReceive();
- }
-
- /*
- * 当前帧;处理完成后再释放帧槽
- */
- ModbusRxFrameReady = 0U;
- }
-
- void ModbusSlaveOnRxEvent(UART_HandleTypeDef *huart, uint16_t size)
- {
- HAL_UART_RxEventTypeTypeDef eventType;
- uint32_t now;
- uint32_t lastByteCycle;
- uint32_t firstByteCycle;
- uint32_t chunkCycles;
- uint32_t interFrameGap;
-
- ModbusSlaveStatistics.rxEventCount++; // 串口接收事件计数
-
- if ((huart != ModbusUart) || (ModbusUart == NULL))
- {
- return;
- }
-
- if ((size > 0U) && (size <= MODBUS_RTU_ADU_SIZE_MAX))
- {
- now = DWT->CYCCNT;
- eventType = HAL_UARTEx_GetRxEventType(huart);
-
- /* IDLE事件比末字节结束晚约一个字符时间,减去字符时间得到末字节时刻 */
- lastByteCycle = now;
- if (eventType == HAL_UART_RXEVENT_IDLE)
- {
- lastByteCycle -= ModbusRtuCharCycles;
- }
-
- /* 根据本次接收字节数反推DMA片段首字节的开始时刻 */
- chunkCycles = (uint32_t)((uint64_t)size * ModbusRtuCharCycles);
- firstByteCycle = lastByteCycle - chunkCycles;
-
- if (ModbusRxAssemblyLength > 0U)
- {
- interFrameGap = (uint32_t)(firstByteCycle - ModbusRxLastByteCycle);
- ModbusLastInterFrameGapCycles = interFrameGap;
- if (interFrameGap >= ModbusRtuT35Cycles)
- {
- /* 间隔达到T3.5,结束上一帧,本片段作为新帧开始 */
- ModbusRxAssemblyFinalize();
- }
- else if (interFrameGap > ModbusRtuT15Cycles)
- {
- /* 帧内静默超过T1.5但不足T3.5,标记整帧无效 */
- ModbusRxAssemblyInvalid = 1U;
- }
- else
- {
- /* 间隔不超过T1.5,当前片段继续拼入同一帧 */
- }
- }
-
- if (size
- <= (uint16_t)(MODBUS_RTU_ADU_SIZE_MAX - ModbusRxAssemblyLength))
- {
- (void)memcpy(&ModbusRxAssemblyBuffer[ModbusRxAssemblyLength],
- ModbusRxDmaBuffer, size);
- ModbusRxAssemblyLength += size;
- }
- else
- {
- ModbusRxAssemblyInvalid = 1U;
- }
-
- /* 保存末字节时刻并立即重启DMA,继续等待可能的后续片段 */
- ModbusRxLastByteCycle = lastByteCycle;
- (void)ModbusStartReceive();
- }
- else
- {
- // 长度异常帧
- ModbusSlaveStatistics.droppedFrameCount++;
- ModbusRxAssemblyLength = 0U;
- ModbusRxAssemblyInvalid = 0U;
- (void)ModbusStartReceive();
- }
- }
-
- void ModbusSlaveOnTxComplete(UART_HandleTypeDef *huart)
- {
- if ((huart != ModbusUart) || (ModbusUart == NULL))
- {
- return;
- }
-
- ModbusTxBusy = 0U;
- (void)ModbusStartReceive(); // 重启DMA接收
- }
-
- void ModbusSlaveOnUartError(UART_HandleTypeDef *huart)
- {
- if ((huart != ModbusUart) || (ModbusUart == NULL))
- {
- return;
- }
-
- ModbusSlaveStatistics.uartErrorCount++;
- ModbusLastUartErrorCode = huart->ErrorCode;
- ModbusTxBusy = 0U;
- ModbusRxAssemblyLength = 0U;
- ModbusRxAssemblyInvalid = 0U;
- (void)HAL_UART_Abort(huart); // 立即终止这个串口当前正在进行的发送和接收操作
- (void)ModbusStartReceive(); // 重启DMA接收
- }
-
- uint8_t ModbusSlaveSetHoldingRegister(uint16_t address, uint16_t value)
- {
- if (address >= MODBUS_MAP_ITEM_COUNT)
- {
- return 0U;
- }
-
- return ModbusDataWriteWord(MODBUS_DATA_DEVICE_D, address, value);
- }
-
- uint8_t ModbusSlaveGetHoldingRegister(uint16_t address, uint16_t *value)
- {
- if ((address >= MODBUS_MAP_ITEM_COUNT) || (value == NULL))
- {
- return 0U;
- }
-
- return ModbusDataReadWord(MODBUS_DATA_DEVICE_D, address, value);
- }
-
- uint8_t ModbusSlaveSetCoil(uint16_t address, uint8_t state)
- {
- if (address >= MODBUS_MAP_ITEM_COUNT)
- {
- return 0U;
- }
-
- ModbusCoilSetUnchecked(address, state);
- return 1U;
- }
-
- uint8_t ModbusSlaveGetCoil(uint16_t address, uint8_t *state)
- {
- if ((address >= MODBUS_MAP_ITEM_COUNT) || (state == NULL))
- {
- return 0U;
- }
-
- *state = ModbusCoilGetUnchecked(address);
- return 1U;
- }
-
- uint8_t ModbusSlaveIsConnected(uint32_t timeoutMs)
- {
- if (ModbusHasReceivedValidFrame == 0U)
- {
- return 0U;
- }
-
- return ((HAL_GetTick() - ModbusLastValidFrameTick) <= timeoutMs) ? 1U : 0U;
- }
-
- uint8_t ModbusSlaveGetRuntimeDiagnostics(
- uint32_t timeoutMs,
- MODBUS_SLAVE_RUNTIME_DIAGNOSTICS *diagnostics)
- {
- if (diagnostics == NULL)
- {
- return 0U;
- }
-
- /* Every source field is naturally aligned and 32-bit-or-smaller on the
- * Cortex-M4. A diagnostic snapshot may span adjacent frame events, but
- * must never mask the 100 kHz motion/AB fast-gate interrupts. */
- __DMB();
- diagnostics->statistics.rxEventCount =
- ModbusSlaveStatistics.rxEventCount;
- diagnostics->statistics.validFrameCount =
- ModbusSlaveStatistics.validFrameCount;
- diagnostics->statistics.txFrameCount =
- ModbusSlaveStatistics.txFrameCount;
- diagnostics->statistics.crcErrorCount =
- ModbusSlaveStatistics.crcErrorCount;
- diagnostics->statistics.ignoredAddressCount =
- ModbusSlaveStatistics.ignoredAddressCount;
- diagnostics->statistics.illegalFunctionCount =
- ModbusSlaveStatistics.illegalFunctionCount;
- diagnostics->statistics.illegalAddressCount =
- ModbusSlaveStatistics.illegalAddressCount;
- diagnostics->statistics.illegalValueCount =
- ModbusSlaveStatistics.illegalValueCount;
- diagnostics->statistics.droppedFrameCount =
- ModbusSlaveStatistics.droppedFrameCount;
- diagnostics->statistics.uartErrorCount =
- ModbusSlaveStatistics.uartErrorCount;
- diagnostics->currentTick = HAL_GetTick();
- diagnostics->lastValidFrameTick = ModbusLastValidFrameTick;
- diagnostics->lastInterFrameGapCycles = ModbusLastInterFrameGapCycles;
- diagnostics->receiveRestartAttemptCount =
- ModbusReceiveRestartAttemptCount;
- diagnostics->receiveRestartFailureCount =
- ModbusReceiveRestartFailureCount;
- diagnostics->lastUartErrorCode = ModbusLastUartErrorCode;
- diagnostics->rxAssemblyLength = ModbusRxAssemblyLength;
- diagnostics->rxFrameLength = ModbusRxFrameLength;
- diagnostics->lastReceiveStartStatus = ModbusLastReceiveStartStatus;
- diagnostics->initialized = (ModbusUart != NULL) ? 1U : 0U;
- diagnostics->hasReceivedValidFrame = ModbusHasReceivedValidFrame;
- diagnostics->connected =
- ((ModbusHasReceivedValidFrame != 0U)
- && ((diagnostics->currentTick - ModbusLastValidFrameTick)
- <= timeoutMs))
- ? 1U
- : 0U;
- diagnostics->txBusy = ModbusTxBusy;
- diagnostics->rxFrameReady = ModbusRxFrameReady;
- diagnostics->rxAssemblyInvalid = ModbusRxAssemblyInvalid;
- __DMB();
- return 1U;
- }
-
- // 上电恢复函数
- void ModbusRetainedRegistersLoad(void)
- {
- uint16_t index;
-
- if (ModbusBackupData->magic == MODBUS_BACKUP_MAGIC)
- {
- for (index = 0U; index < MODBUS_RETAINED_D_COUNT; index++)
- {
- (void)ModbusDataWriteWord(
- MODBUS_DATA_DEVICE_D,
- MODBUS_RETAINED_D_START + index,
- ModbusBackupData->retainedD[index]);
- }
- }
- else
- {
- for (index = 0U; index < MODBUS_RETAINED_D_COUNT; index++)
- {
- (void)ModbusDataWriteWord(MODBUS_DATA_DEVICE_D,
- MODBUS_RETAINED_D_START + index,
- 0U);
-
- ModbusBackupData->retainedD[index] = 0U;
- }
-
- /*
- * 数据初始化完成后最后写magic,避免初始化中途断电
- * 却把不完整数据标记成有效
- */
- ModbusBackupData->magic = MODBUS_BACKUP_MAGIC;
- }
- }
- void ModbusRetainedRegistersPoll(void)
- {
- uint16_t index;
- uint16_t value;
-
- for (index = 0; index < MODBUS_RETAINED_D_COUNT; index++)
- {
- (void)ModbusDataReadWord(MODBUS_DATA_DEVICE_D,
- MODBUS_RETAINED_D_START + index,
- &value);
- if (value != ModbusRetainedSnapshot[index])
- {
- ModbusBackupData->retainedD[index] = value;
- }
-
- ModbusRetainedSnapshot[index] = value;
- }
- }
|