|
- /**
- * @file modbus.c
- * @brief Modbus RTU 从站协议实现。
- * @version 2.0
- * @author zengbingjie
- * @date 2026-07-29
- * @copyright 版权所有信捷科技股份有限公司
- * @note 修改记录:2.0 增加严格 RTU 校验和广播处理。
- */
-
- #include "modbus.h"
-
- /* RTU ADU 布局为:地址和功能码在前,后面是 PDU 和两字节 CRC。
- * 以下偏移量由所有请求和响应处理路径共用。 */
- #define MODBUS_RTU_ADDRESS_INDEX (0U)
- #define MODBUS_RTU_FUNCTION_INDEX (1U)
- #define MODBUS_RTU_DATA_INDEX (2U)
- #define MODBUS_RTU_BYTE_COUNT_INDEX (6U)
- #define MODBUS_RTU_WRITE_DATA_INDEX (7U)
- #define MODBUS_RTU_EXCEPTION_MASK (0x80U)
- #define MODBUS_RTU_CRC_INIT (0xFFFFU)
- #define MODBUS_RTU_CRC_POLYNOMIAL (0xA001U)
- #define MODBUS_BITS_PER_BYTE (8U)
- #define MODBUS_INITIAL_ON_COIL_COUNT (3U)
-
- typedef enum {
- MODBUS_REQUEST_OK = 0,
- MODBUS_REQUEST_ILLEGAL_ADDRESS = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDR,
- MODBUS_REQUEST_ILLEGAL_VALUE = MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,
- /* 0xFF 仅作为内部哨兵值,绝不会作为 Modbus 异常码发送。
- * 调用者会将上面的两类协议错误转换为异常响应 0x02 和 0x03。 */
- MODBUS_REQUEST_RESPONSE_ERROR = 0xFFU
- } MODBUS_REQUEST_RESULT;
-
- /**
- * @brief 判断地址是否为有效的单播从站地址。
- * @param[in] slaveAddress 待校验的从站地址。
- * @return 地址有效时返回 1,否则返回 0。
- */
- static uint8_t ModbusIsUnicastAddress(uint8_t slaveAddress)
- {
- if ((slaveAddress < MODBUS_SLAVE_ADDRESS_MIN)
- || (slaveAddress > MODBUS_SLAVE_ADDRESS_MAX)) {
- return 0U;
- }
-
- return 1U;
- }
-
- /**
- * @brief 判断地址范围是否位于 Modbus 数据区内。
- * @param[in] startAddress 范围内的首个地址。
- * @param[in] dataCount 连续数据点数量。
- * @return 地址范围有效时返回 1,否则返回 0。
- */
- static uint8_t ModbusIsDataRangeValid(uint16_t startAddress,
- uint16_t dataCount)
- {
- /* 使用减法而不是 startAddress + dataCount,避免 16 位溢出后错误地将越界
- * 请求判断为有效。 */
- if ((dataCount == 0U) || (startAddress > MODBUS_DATA_ADDRESS_MAX)) {
- return 0U;
- }
-
- if (dataCount > (MODBUS_DATA_POINT_COUNT - startAddress)) {
- return 0U;
- }
-
- return 1U;
- }
-
- /**
- * @brief 校验私有 32 位地址是否唯一指向扩展保持寄存器。
- * @param[in] slave 从站数据对象。
- * @param[in] address 私有功能码帧内的 32 位地址。
- * @return 地址为 0x00010000 且扩展存储已绑定时返回 1,否则返回 0。
- */
- static uint8_t ModbusIsExtendedRegisterAddressValid(
- const MODBUS_SLAVE *slave, uint32_t address)
- {
- if ((slave == NULL) || (slave->extendedHoldingRegister == NULL)
- || (address != MODBUS_EXTENDED_HOLDING_ADDRESS)) {
- return 0U;
- }
-
- return 1U;
- }
-
- /**
- * @brief 从 Modbus ADU 中读取一个大端序 16 位数值。
- * @param[in] buffer 数据缓冲区。
- * @return 解析得到的 16 位数值。
- */
- static uint16_t ModbusReadU16(const uint8_t *buffer)
- {
- uint16_t value;
-
- /* Modbus 字段采用大端序(高字节在前),但 CRC 在总线上按低字节在前发送。 */
- value = ((uint16_t)buffer[0] << 8U) | (uint16_t)buffer[1];
-
- return value;
- }
-
- /**
- * @brief 从私有功能码 PDU 读取大端序 32 位地址。
- * @param[in] buffer 至少包含 4 个字节的 PDU 字段。
- * @return 解析后的 32 位地址。
- */
- static uint32_t ModbusReadU32(const uint8_t *buffer)
- {
- uint32_t value;
-
- value = ((uint32_t)buffer[0] << 24U)
- | ((uint32_t)buffer[1] << 16U)
- | ((uint32_t)buffer[2] << 8U)
- | (uint32_t)buffer[3];
-
- return value;
- }
-
- /**
- * @brief 向私有功能码 PDU 写入大端序 32 位地址。
- * @param[out] buffer 至少可容纳 4 个字节的 PDU 字段。
- * @param[in] value 待写入的 32 位地址。
- */
- static void ModbusWriteU32(uint8_t *buffer, uint32_t value)
- {
- buffer[0] = (uint8_t)(value >> 24U);
- buffer[1] = (uint8_t)(value >> 16U);
- buffer[2] = (uint8_t)(value >> 8U);
- buffer[3] = (uint8_t)value;
- }
-
- /**
- * @brief 向 Modbus ADU 中写入一个大端序 16 位数值。
- * @param[out] buffer 数据缓冲区。
- * @param[in] value 待写入的 16 位数值。
- */
- static void ModbusWriteU16(uint8_t *buffer, uint16_t value)
- {
- buffer[0] = (uint8_t)(value >> 8U);
- buffer[1] = (uint8_t)(value & 0x00FFU);
- }
-
- /**
- * @brief 在 ADU 尾部追加 Modbus CRC 校验字段。
- * @param[in,out] buffer ADU 缓冲区。
- * @param[in] capacity ADU 缓冲区容量,单位为字节。
- * @param[in,out] length 追加 CRC 前的 ADU 有效载荷长度。
- * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- static MODBUS_STATUS ModbusAppendCrc(uint8_t *buffer, uint16_t capacity,
- uint16_t *length)
- {
- uint16_t crcValue;
-
- if ((buffer == NULL) || (length == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if ((capacity < MODBUS_RTU_CRC_LENGTH)
- || (*length > (capacity - MODBUS_RTU_CRC_LENGTH))) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- /* 按 Modbus RTU 要求,以小端序追加 CRC 值。 */
- crcValue = ModbusCrc16(buffer, *length);
- buffer[*length] = (uint8_t)(crcValue & 0x00FFU);
- buffer[*length + 1U] = (uint8_t)(crcValue >> 8U);
- *length += MODBUS_RTU_CRC_LENGTH;
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 校验完整 Modbus RTU ADU 的 CRC 字段。
- * @param[in] buffer 完整的 ADU 缓冲区。
- * @param[in] length 完整 ADU 长度,单位为字节。
- * @return 校验成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- static MODBUS_STATUS ModbusValidateCrc(const uint8_t *buffer, uint16_t length)
- {
- uint16_t expectedCrc;
- uint16_t actualCrc;
-
- if (buffer == NULL) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if (length < MODBUS_RTU_ADU_MIN_LENGTH) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- /* 对去掉末尾两个字节的 ADU 重新计算 CRC,再与收到的低字节/高字节表示进行比较。 */
- expectedCrc = ModbusCrc16(buffer, length - MODBUS_RTU_CRC_LENGTH);
- actualCrc = (uint16_t)buffer[length - MODBUS_RTU_CRC_LENGTH]
- | ((uint16_t)buffer[length - 1U] << 8U);
- if (actualCrc != expectedCrc) {
- return MODBUS_STATUS_CRC_ERROR;
- }
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 组建 Modbus 异常响应 ADU。
- * @param[in] slaveAddress 响应使用的从站地址。
- * @param[in] functionCode 请求失败时的功能码。
- * @param[in] exceptionCode Modbus 异常码。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
- * @param[out] txLength 生成的响应长度,单位为字节。
- * @return 成功返回 MODBUS_STATUS_EXCEPTION,否则返回错误状态。
- */
- static MODBUS_STATUS ModbusBuildException(uint8_t slaveAddress,
- uint8_t functionCode,
- uint8_t exceptionCode,
- uint8_t *txBuffer,
- uint16_t txCapacity,
- uint16_t *txLength)
- {
- MODBUS_STATUS status;
-
- if ((txBuffer == NULL) || (txLength == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if ((functionCode & MODBUS_RTU_EXCEPTION_MASK) != 0U) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- if (txCapacity < MODBUS_RTU_EXCEPTION_LENGTH) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- /* 标准异常响应格式为:原功能码 | 0x80,后跟一个字节的异常码和重新计算的 CRC。 */
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = functionCode
- | MODBUS_RTU_EXCEPTION_MASK;
- txBuffer[MODBUS_RTU_DATA_INDEX] = exceptionCode;
- *txLength = MODBUS_RTU_DATA_INDEX + 1U;
- status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
- if (status != MODBUS_STATUS_OK) {
- return status;
- }
-
- return MODBUS_STATUS_EXCEPTION;
- }
-
- /**
- * @brief 从压缩存储区读取一个线圈位。
- * @param[in] slave 从站数据对象。
- * @param[in] address 线圈地址。
- * @return 线圈值,0 表示关闭,1 表示开启。
- */
- static uint8_t ModbusReadCoilBit(const MODBUS_SLAVE *slave, uint16_t address)
- {
- uint16_t byteIndex;
- uint8_t bitIndex;
- uint8_t value;
-
- /* 线圈按低位在前压缩:线圈 0 是字节 0 的 bit 0,线圈 8 是字节 1 的 bit 0。
- * 构造 0x01/0x0F 帧时使用相同的映射关系。 */
- byteIndex = address / MODBUS_BITS_PER_BYTE;
- bitIndex = (uint8_t)(address % MODBUS_BITS_PER_BYTE);
- value = (uint8_t)((slave->coilStorage[byteIndex] >> bitIndex) & 0x01U);
-
- return value;
- }
-
- /**
- * @brief 向压缩存储区写入一个线圈位。
- * @param[in,out] slave 从站数据对象。
- * @param[in] address 线圈地址。
- * @param[in] value 线圈值,0 表示关闭,非 0 表示开启。
- */
- static void ModbusWriteCoilBit(MODBUS_SLAVE *slave, uint16_t address,
- uint8_t value)
- {
- uint16_t byteIndex;
- uint8_t bitIndex;
- uint8_t bitMask;
-
- /* 只更新选中的位,保持同一压缩字节中相邻线圈的值不变。 */
- byteIndex = address / MODBUS_BITS_PER_BYTE;
- bitIndex = (uint8_t)(address % MODBUS_BITS_PER_BYTE);
- bitMask = (uint8_t)(1U << bitIndex);
- if (value != 0U) {
- slave->coilStorage[byteIndex] |= bitMask;
- }
- else {
- slave->coilStorage[byteIndex] &= (uint8_t)(~bitMask);
- }
- }
-
- /**
- * @brief 组建读取线圈的响应帧。
- * @param[in] slave 从站数据对象。
- * @param[in] startAddress 首个线圈地址。
- * @param[in] coilCount 待读取的线圈数量。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
- * @param[out] txLength 生成的响应长度,单位为字节。
- * @return 请求校验结果。
- */
- static MODBUS_REQUEST_RESULT ModbusBuildReadCoilsResponse(
- const MODBUS_SLAVE *slave, uint16_t startAddress, uint16_t coilCount,
- uint8_t *txBuffer, uint16_t txCapacity, uint16_t *txLength)
- {
- MODBUS_STATUS status;
- uint16_t byteCount;
- uint16_t coilOffset;
- uint16_t byteIndex;
-
- if ((coilCount == 0U) || (coilCount > MODBUS_MAX_READ_COIL_COUNT)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (ModbusIsDataRangeValid(startAddress, coilCount) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- byteCount = (coilCount + 7U) / MODBUS_BITS_PER_BYTE;
- if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_COILS;
- txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
- for (byteIndex = 0U; byteIndex < byteCount; byteIndex++) {
- txBuffer[3U + byteIndex] = 0U;
- }
-
- /* 响应将第一个请求线圈放入 bit 0,并保持最后一个数据字节中未使用的高位为 0。 */
- for (coilOffset = 0U; coilOffset < coilCount; coilOffset++) {
- if (ModbusReadCoilBit(slave, startAddress + coilOffset) != 0U) {
- byteIndex = coilOffset / MODBUS_BITS_PER_BYTE;
- txBuffer[3U + byteIndex] |= (uint8_t)(1U
- << (coilOffset % MODBUS_BITS_PER_BYTE));
- }
- }
-
- *txLength = 3U + byteCount;
- status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
- if (status != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 组建读取保持寄存器的响应帧。
- * @param[in] slave 从站数据对象。
- * @param[in] startAddress 首个保持寄存器地址。
- * @param[in] registerCount 待读取的保持寄存器数量。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
- * @param[out] txLength 生成的响应长度,单位为字节。
- * @return 请求校验结果。
- */
- static MODBUS_REQUEST_RESULT ModbusBuildReadRegistersResponse(
- const MODBUS_SLAVE *slave, uint16_t startAddress,
- uint16_t registerCount, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- MODBUS_STATUS status;
- uint16_t byteCount;
- uint16_t registerOffset;
-
- if ((registerCount == 0U)
- || (registerCount > MODBUS_MAX_READ_REGISTER_COUNT)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (ModbusIsDataRangeValid(startAddress, registerCount) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- byteCount = registerCount * 2U;
- if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_HOLDING_REGS;
- txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
- for (registerOffset = 0U; registerOffset < registerCount;
- registerOffset++) {
- ModbusWriteU16(&txBuffer[3U + (registerOffset * 2U)],
- slave->holdingRegisters[startAddress + registerOffset]);
- }
-
- *txLength = 3U + byteCount;
- status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
- if (status != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 处理写单个线圈请求。
- * @param[in,out] slave 从站数据对象。
- * @param[in] rxBuffer 请求 ADU。
- * @param[in] rxLength 请求 ADU 长度。
- * @param[in] isBroadcast 广播请求时为 1,否则为 0。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量。
- * @param[out] txLength 生成的响应长度。
- * @return 请求校验结果。
- */
- static MODBUS_REQUEST_RESULT ModbusProcessWriteSingleCoil(
- MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
- uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- uint16_t address;
- uint16_t coilValue;
- uint16_t index;
-
- /* 功能码 0x05 的 ADU 固定为 8 字节,必须检查两种合法线圈值;接受任意非零值
- * 会违反 Modbus 规范。 */
- if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- address = ModbusReadU16(&rxBuffer[2U]);
- coilValue = ModbusReadU16(&rxBuffer[4U]);
- if (ModbusIsDataRangeValid(address, 1U) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- if ((coilValue != MODBUS_COIL_ON_VALUE)
- && (coilValue != MODBUS_COIL_OFF_VALUE)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if ((isBroadcast == 0U)
- && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- ModbusWriteCoilBit(slave, address, (uint8_t)(coilValue != 0U));
- if (isBroadcast != 0U) {
- *txLength = 0U;
- return MODBUS_REQUEST_OK;
- }
-
- /* 单播写响应必须完整回显请求(包括原 CRC)。广播写入会执行,但按规范不发送响应。 */
- for (index = 0U; index < rxLength; index++) {
- txBuffer[index] = rxBuffer[index];
- }
- *txLength = rxLength;
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 处理写单个保持寄存器请求。
- * @param[in,out] slave 从站数据对象。
- * @param[in] rxBuffer 请求 ADU。
- * @param[in] rxLength 请求 ADU 长度。
- * @param[in] isBroadcast 广播请求时为 1,否则为 0。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量。
- * @param[out] txLength 生成的响应长度。
- * @return 请求校验结果。
- */
- static MODBUS_REQUEST_RESULT ModbusProcessWriteSingleRegister(
- MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
- uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- uint16_t address;
- uint16_t index;
-
- if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- address = ModbusReadU16(&rxBuffer[2U]);
- if (ModbusIsDataRangeValid(address, 1U) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- if ((isBroadcast == 0U)
- && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- slave->holdingRegisters[address] = ModbusReadU16(&rxBuffer[4U]);
- if (isBroadcast != 0U) {
- *txLength = 0U;
- return MODBUS_REQUEST_OK;
- }
-
- for (index = 0U; index < rxLength; index++) {
- txBuffer[index] = rxBuffer[index];
- }
- *txLength = rxLength;
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 处理写多个线圈请求。
- * @param[in,out] slave 从站数据对象。
- * @param[in] rxBuffer 请求 ADU。
- * @param[in] rxLength 请求 ADU 长度。
- * @param[in] isBroadcast 广播请求时为 1,否则为 0。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量。
- * @param[out] txLength 生成的响应长度。
- * @return 请求校验结果。
- */
- static MODBUS_REQUEST_RESULT ModbusProcessWriteMultipleCoils(
- MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
- uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- uint16_t address;
- uint16_t coilCount;
- uint16_t byteCount;
- uint16_t coilOffset;
- uint8_t coilValue;
-
- /* 读取数据或修改线圈前,先校验数量、字节数和总长度,防止畸形帧只修改请求范围
- * 的前一部分。 */
- if (rxLength < MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- address = ModbusReadU16(&rxBuffer[2U]);
- coilCount = ModbusReadU16(&rxBuffer[4U]);
- byteCount = rxBuffer[MODBUS_RTU_BYTE_COUNT_INDEX];
- if ((coilCount == 0U) || (coilCount > MODBUS_MAX_WRITE_COIL_COUNT)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (byteCount != ((coilCount + 7U) / MODBUS_BITS_PER_BYTE)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (rxLength != (MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH + byteCount)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (ModbusIsDataRangeValid(address, coilCount) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- if ((isBroadcast == 0U)
- && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- /* 请求数据区中的位采用与 0x01 相同的低位在前压缩方式。 */
- for (coilOffset = 0U; coilOffset < coilCount; coilOffset++) {
- coilValue = (uint8_t)((rxBuffer[MODBUS_RTU_WRITE_DATA_INDEX
- + (coilOffset / MODBUS_BITS_PER_BYTE)]
- >> (coilOffset % MODBUS_BITS_PER_BYTE)) & 0x01U);
- ModbusWriteCoilBit(slave, address + coilOffset, coilValue);
- }
-
- if (isBroadcast != 0U) {
- *txLength = 0U;
- return MODBUS_REQUEST_OK;
- }
-
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_MULTIPLE_COILS;
- ModbusWriteU16(&txBuffer[2U], address);
- ModbusWriteU16(&txBuffer[4U], coilCount);
- *txLength = 6U;
- if (ModbusAppendCrc(txBuffer, txCapacity, txLength) != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 处理写多个保持寄存器请求。
- * @param[in,out] slave 从站数据对象。
- * @param[in] rxBuffer 请求 ADU。
- * @param[in] rxLength 请求 ADU 长度。
- * @param[in] isBroadcast 广播请求时为 1,否则为 0。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量。
- * @param[out] txLength 生成的响应长度。
- * @return 请求校验结果。
- */
- static MODBUS_REQUEST_RESULT ModbusProcessWriteMultipleRegisters(
- MODBUS_SLAVE *slave, const uint8_t *rxBuffer, uint16_t rxLength,
- uint8_t isBroadcast, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- uint16_t address;
- uint16_t registerCount;
- uint16_t byteCount;
- uint16_t registerOffset;
-
- /* 功能码 0x10 为每个寄存器携带一个大端序 16 位值。所有帧结构检查都在写入循环
- * 之前完成,避免部分写入。 */
- if (rxLength < MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- address = ModbusReadU16(&rxBuffer[2U]);
- registerCount = ModbusReadU16(&rxBuffer[4U]);
- byteCount = rxBuffer[MODBUS_RTU_BYTE_COUNT_INDEX];
- if ((registerCount == 0U)
- || (registerCount > MODBUS_MAX_WRITE_REGISTER_COUNT)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (byteCount != (registerCount * 2U)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (rxLength != (MODBUS_RTU_WRITE_REQUEST_BASE_LENGTH + byteCount)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (ModbusIsDataRangeValid(address, registerCount) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- if ((isBroadcast == 0U)
- && (txCapacity < MODBUS_RTU_WRITE_RESPONSE_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- for (registerOffset = 0U; registerOffset < registerCount;
- registerOffset++) {
- slave->holdingRegisters[address + registerOffset] = ModbusReadU16(
- &rxBuffer[MODBUS_RTU_WRITE_DATA_INDEX + (registerOffset * 2U)]);
- }
-
- if (isBroadcast != 0U) {
- *txLength = 0U;
- return MODBUS_REQUEST_OK;
- }
-
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_MULTIPLE_REGS;
- ModbusWriteU16(&txBuffer[2U], address);
- ModbusWriteU16(&txBuffer[4U], registerCount);
- *txLength = 6U;
- if (ModbusAppendCrc(txBuffer, txCapacity, txLength) != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 构建私有功能码 0x41 的扩展寄存器读取响应。
- * @note 该接口只接受地址 0x00010000 和数量 1,响应数据为一个 16 位值。
- */
- static MODBUS_REQUEST_RESULT ModbusBuildReadExtendedRegistersResponse(
- const MODBUS_SLAVE *slave, uint32_t startAddress,
- uint16_t registerCount, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- MODBUS_STATUS status;
-
- if (registerCount != 1U) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- if (ModbusIsExtendedRegisterAddressValid(slave, startAddress) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- if (txCapacity < 7U) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_EXTENDED_HOLDING_REGS;
- txBuffer[MODBUS_RTU_DATA_INDEX] = 2U;
- ModbusWriteU16(&txBuffer[3U], *slave->extendedHoldingRegister);
-
- *txLength = 5U;
- status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
- if (status != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 执行私有功能码 0x42 的扩展寄存器写入并回显地址和值。
- * @note 本函数只更新 RAM 中的扩展值;RTC 持久化由应用层在成功处理后触发。
- */
- static MODBUS_REQUEST_RESULT ModbusBuildWriteExtendedRegisterResponse(
- MODBUS_SLAVE *slave, uint32_t startAddress, uint16_t registerValue,
- uint8_t *txBuffer, uint16_t txCapacity, uint16_t *txLength)
- {
- MODBUS_STATUS status;
-
- if (ModbusIsExtendedRegisterAddressValid(slave, startAddress) == 0U) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- if (txCapacity < 10U) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- /* 私有扩展使用 32 位地址字段表示 0x00010000,不改变标准功能码的 16 位地址模型。 */
- *slave->extendedHoldingRegister = registerValue;
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS;
- ModbusWriteU32(&txBuffer[2U], startAddress);
- ModbusWriteU16(&txBuffer[6U], registerValue);
- *txLength = 8U;
- status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
- if (status != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 构建私有功能码 0x43 的连续奇数地址读取响应。
- * @note 数量范围为 1 至 123,奇数和偶数均可。偶数起始基准地址自动上调到
- * 下一奇数,例如起始地址 0 依次返回地址 1、3、5 等的寄存器值。
- */
- static MODBUS_REQUEST_RESULT ModbusBuildReadOddAddressRegistersResponse(
- const MODBUS_SLAVE *slave, uint16_t startAddress,
- uint16_t registerCount, uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- MODBUS_STATUS status;
- uint16_t byteCount;
- uint16_t firstOddAddress;
- uint16_t readAddress;
- uint16_t registerOffset;
-
- if ((registerCount == 0U)
- || (registerCount > MODBUS_MAX_ODD_COUNT_REGISTER_COUNT)) {
- return MODBUS_REQUEST_ILLEGAL_VALUE;
- }
-
- /* 请求数量表示返回的奇数地址寄存器个数,而不是连续地址跨度。从偶数地址开始时
- * 会跳过该地址,从下一个奇数地址开始;后续每个地址递增 2。 */
- firstOddAddress = (uint16_t)(startAddress | 0x0001U);
- if ((firstOddAddress > MODBUS_DATA_ADDRESS_MAX)
- || (registerCount
- > (((MODBUS_DATA_ADDRESS_MAX - firstOddAddress) / 2U) + 1U))) {
- return MODBUS_REQUEST_ILLEGAL_ADDRESS;
- }
-
- byteCount = registerCount * 2U;
- if (txCapacity < (3U + byteCount + MODBUS_RTU_CRC_LENGTH)) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- txBuffer[MODBUS_RTU_ADDRESS_INDEX] = slave->slaveAddress;
- txBuffer[MODBUS_RTU_FUNCTION_INDEX] = MODBUS_FC_READ_ODD_COUNT_REGS;
- txBuffer[MODBUS_RTU_DATA_INDEX] = (uint8_t)byteCount;
- for (registerOffset = 0U; registerOffset < registerCount;
- registerOffset++) {
- readAddress = (uint16_t)(firstOddAddress + (registerOffset * 2U));
- ModbusWriteU16(&txBuffer[3U + (registerOffset * 2U)],
- slave->holdingRegisters[readAddress]);
- }
-
- *txLength = 3U + byteCount;
- status = ModbusAppendCrc(txBuffer, txCapacity, txLength);
- if (status != MODBUS_STATUS_OK) {
- return MODBUS_REQUEST_RESPONSE_ERROR;
- }
-
- return MODBUS_REQUEST_OK;
- }
-
- /**
- * @brief 计算 Modbus RTU 的 CRC16 校验值。
- * @param[in] buffer 不包含 CRC 字段的数据缓冲区。
- * @param[in] length 数据缓冲区长度,单位为字节。
- * @return CRC16 校验值,发送时低字节在前。
- */
- uint16_t ModbusCrc16(const uint8_t *buffer, uint16_t length)
- {
- uint16_t crcValue;
- uint16_t byteIndex;
- uint8_t bitIndex;
-
- if (buffer == NULL) {
- return 0U;
- }
-
- /* Modbus CRC-16 使用初值 0xFFFF 和反射多项式 0xA001。按最低有效位优先处理,
- * 与 RTU 总线字节序一致,也不依赖 MCU 本身的字节序。 */
- crcValue = MODBUS_RTU_CRC_INIT;
- for (byteIndex = 0U; byteIndex < length; byteIndex++) {
- crcValue ^= (uint16_t)buffer[byteIndex];
- for (bitIndex = 0U; bitIndex < MODBUS_BITS_PER_BYTE; bitIndex++) {
- if ((crcValue & 0x0001U) != 0U) {
- crcValue = (crcValue >> 1U) ^ MODBUS_RTU_CRC_POLYNOMIAL;
- }
- else {
- crcValue >>= 1U;
- }
- }
- }
-
- return crcValue;
- }
-
- /**
- * @brief 初始化全部从站数据点并配置从站地址。
- * @param[out] slave 待初始化的从站数据对象。
- * @param[in] slaveAddress 范围为 1 至 247 的单播从站地址。
- * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- MODBUS_STATUS ModbusSlaveInit(
- MODBUS_SLAVE *slave,
- uint8_t slaveAddress,
- volatile uint16_t *holdingRegisterStorage,
- volatile uint8_t *coilBitStorage)
- {
- uint16_t registerIndex;
- uint16_t coilByteIndex;
- uint16_t coilAddress;
-
- if ((slave == NULL) || (holdingRegisterStorage == NULL)
- || (coilBitStorage == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if (ModbusIsUnicastAddress(slaveAddress) == 0U) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- slave->slaveAddress = slaveAddress;
- slave->holdingRegisters = holdingRegisterStorage;
- slave->coilStorage = coilBitStorage;
- slave->extendedHoldingRegister = NULL;
- /* 启动时清空完整的普通数据模型。扩展值单独初始化,因为随后可能从 RTC 备份
- * 寄存器恢复。 */
- for (registerIndex = 0U; registerIndex < MODBUS_DATA_POINT_COUNT;
- registerIndex++) {
- slave->holdingRegisters[registerIndex] = 0U;
- }
-
- for (coilByteIndex = 0U; coilByteIndex < MODBUS_COIL_STORAGE_SIZE;
- coilByteIndex++) {
- slave->coilStorage[coilByteIndex] = 0U;
- }
-
- /* 线圈按位压缩存储,初始置位线圈 0x0 至 0x2。 */
- for (coilAddress = 0U; coilAddress < MODBUS_INITIAL_ON_COIL_COUNT;
- coilAddress++) {
- ModbusWriteCoilBit(slave, coilAddress, 1U);
- }
-
- slave->holdingRegisters[10] = 0x0000;
- slave->holdingRegisters[11] = 0x0001;
- slave->holdingRegisters[12] = 0x0010;
- slave->holdingRegisters[13] = 0x0011;
- slave->holdingRegisters[14] = 0x0100;
- slave->holdingRegisters[15] = 0x0110;
-
- return MODBUS_STATUS_OK;
- }
-
- MODBUS_STATUS ModbusConfigureExtendedHoldingRegister(
- MODBUS_SLAVE *slave, volatile uint16_t *storage)
- {
- if ((slave == NULL) || (storage == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- slave->extendedHoldingRegister = storage;
- /* 首次启动的默认值;AppTaskModbus 随后可能用 RTC 备份值覆盖它。 */
- /* 只有不存在有效 RTC 备份时才使用这个默认值。 */
- *slave->extendedHoldingRegister = 0x1000U;
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 从从站数据模型中读取线圈值。
- * @param[in] slave 从站数据对象。
- * @param[in] address 范围为 0 至 0x270F 的线圈地址。
- * @param[out] value 线圈值,0 表示关闭,1 表示开启。
- * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- MODBUS_STATUS ModbusGetCoil(const MODBUS_SLAVE *slave, uint16_t address,
- uint8_t *value)
- {
- if ((slave == NULL) || (value == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if (ModbusIsDataRangeValid(address, 1U) == 0U) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- *value = ModbusReadCoilBit(slave, address);
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 向从站数据模型中写入线圈值。
- * @param[in,out] slave 从站数据对象。
- * @param[in] address 范围为 0 至 0x270F 的线圈地址。
- * @param[in] value 线圈值,0 表示关闭,非 0 表示开启。
- * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- MODBUS_STATUS ModbusSetCoil(MODBUS_SLAVE *slave, uint16_t address,
- uint8_t value)
- {
- if (slave == NULL) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if (ModbusIsDataRangeValid(address, 1U) == 0U) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- ModbusWriteCoilBit(slave, address, value);
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 从从站数据模型中读取保持寄存器值。
- * @param[in] slave 从站数据对象。
- * @param[in] address 范围为 0 至 0x270F 的保持寄存器地址。
- * @param[out] value 保持寄存器值。
- * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- MODBUS_STATUS ModbusGetHoldingRegister(const MODBUS_SLAVE *slave,
- uint16_t address, uint16_t *value)
- {
- if ((slave == NULL) || (value == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if (ModbusIsDataRangeValid(address, 1U) == 0U) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- *value = slave->holdingRegisters[address];
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 向从站数据模型中写入保持寄存器值。
- * @param[in,out] slave 从站数据对象。
- * @param[in] address 范围为 0 至 0x270F 的保持寄存器地址。
- * @param[in] value 保持寄存器值。
- * @return 成功返回 MODBUS_STATUS_OK,否则返回错误状态。
- */
- MODBUS_STATUS ModbusSetHoldingRegister(MODBUS_SLAVE *slave,
- uint16_t address, uint16_t value)
- {
- if (slave == NULL) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- if (ModbusIsDataRangeValid(address, 1U) == 0U) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- slave->holdingRegisters[address] = value;
-
- return MODBUS_STATUS_OK;
- }
-
- /**
- * @brief 解析 Modbus RTU 请求并生成从站响应。
- * @param[in,out] slave 从站数据对象。
- * @param[in] rxBuffer 包含 CRC 的完整请求 ADU。
- * @param[in] rxLength 请求 ADU 长度,单位为字节。
- * @param[out] txBuffer 响应 ADU 缓冲区。
- * @param[in] txCapacity 响应 ADU 缓冲区容量,单位为字节。
- * @param[out] txLength 生成的响应长度,单位为字节。
- * @return 请求处理状态;广播写入请求不生成响应。
- * @note 处理顺序固定为参数、帧长度、CRC、站号、功能码和字段校验,
- * 因而 CRC 错误或非本机帧绝不会写入数据区。
- */
- MODBUS_STATUS ModbusProcessFrame(MODBUS_SLAVE *slave,
- const uint8_t *rxBuffer, uint16_t rxLength,
- uint8_t *txBuffer, uint16_t txCapacity,
- uint16_t *txLength)
- {
- MODBUS_STATUS status;
- MODBUS_REQUEST_RESULT requestResult;
- uint8_t requestAddress;
- uint8_t functionCode;
- uint8_t isBroadcast;
- uint16_t startAddress;
- uint16_t dataCount;
- uint16_t registerValue;
- uint32_t extendedAddress;
-
- if ((slave == NULL) || (rxBuffer == NULL) || (txBuffer == NULL)
- || (txLength == NULL)) {
- return MODBUS_STATUS_INVALID_ARGUMENT;
- }
-
- /* 始终从空响应开始。被忽略的帧、CRC 错误帧和广播写入都必须保持 txLength 为 0。 */
- *txLength = 0U;
- if (rxLength > MODBUS_RTU_ADU_MAX_LENGTH) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- /* 校验顺序是有意设计的:先拒绝格式错误或 CRC 错误的帧,再检查地址,最后才访问
- * 数据模型。 */
- status = ModbusValidateCrc(rxBuffer, rxLength);
- if (status != MODBUS_STATUS_OK) {
- return status;
- }
-
- requestAddress = rxBuffer[MODBUS_RTU_ADDRESS_INDEX];
- if ((requestAddress != slave->slaveAddress)
- && (requestAddress != MODBUS_BROADCAST_ADDRESS)) {
- return MODBUS_STATUS_IGNORED;
- }
-
- functionCode = rxBuffer[MODBUS_RTU_FUNCTION_INDEX];
- if ((functionCode & MODBUS_RTU_EXCEPTION_MASK) != 0U) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- /* Modbus RTU 的广播地址 0 只允许写入。广播读取和私有读取扩展会被忽略且不响应。 */
- isBroadcast = (uint8_t)(requestAddress == MODBUS_BROADCAST_ADDRESS);
- requestResult = MODBUS_REQUEST_OK;
- /* 每个处理函数负责校验自身的固定/可变帧格式和数据边界,然后返回内部结果,
- * 由下面的逻辑映射为标准异常响应。 */
- switch (functionCode) {
- case MODBUS_FC_READ_COILS:
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
- requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
- break;
- }
- startAddress = ModbusReadU16(&rxBuffer[2U]);
- dataCount = ModbusReadU16(&rxBuffer[4U]);
- requestResult = ModbusBuildReadCoilsResponse(
- slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
- break;
-
- case MODBUS_FC_READ_HOLDING_REGS:
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
- requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
- break;
- }
- startAddress = ModbusReadU16(&rxBuffer[2U]);
- dataCount = ModbusReadU16(&rxBuffer[4U]);
- requestResult = ModbusBuildReadRegistersResponse(
- slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
- break;
-
- case MODBUS_FC_READ_EXTENDED_HOLDING_REGS:
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- if (rxLength != 10U) {
- requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
- break;
- }
- extendedAddress = ModbusReadU32(&rxBuffer[2U]);
- dataCount = ModbusReadU16(&rxBuffer[6U]);
- requestResult = ModbusBuildReadExtendedRegistersResponse(
- slave, extendedAddress, dataCount, txBuffer, txCapacity,
- txLength);
- break;
-
- case MODBUS_FC_WRITE_EXTENDED_HOLDING_REGS:
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- if (rxLength != 10U) {
- requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
- break;
- }
- extendedAddress = ModbusReadU32(&rxBuffer[2U]);
- registerValue = ModbusReadU16(&rxBuffer[6U]);
- requestResult = ModbusBuildWriteExtendedRegisterResponse(
- slave, extendedAddress, registerValue, txBuffer, txCapacity,
- txLength);
- break;
-
- case MODBUS_FC_READ_ODD_COUNT_REGS:
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- if (rxLength != MODBUS_RTU_READ_REQUEST_LENGTH) {
- requestResult = MODBUS_REQUEST_ILLEGAL_VALUE;
- break;
- }
- startAddress = ModbusReadU16(&rxBuffer[2U]);
- dataCount = ModbusReadU16(&rxBuffer[4U]);
- requestResult = ModbusBuildReadOddAddressRegistersResponse(
- slave, startAddress, dataCount, txBuffer, txCapacity, txLength);
- break;
-
- case MODBUS_FC_WRITE_SINGLE_COIL:
- requestResult = ModbusProcessWriteSingleCoil(
- slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
- txLength);
- break;
-
- case MODBUS_FC_WRITE_SINGLE_REG:
- requestResult = ModbusProcessWriteSingleRegister(
- slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
- txLength);
- break;
-
- case MODBUS_FC_WRITE_MULTIPLE_COILS:
- requestResult = ModbusProcessWriteMultipleCoils(
- slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
- txLength);
- break;
-
- case MODBUS_FC_WRITE_MULTIPLE_REGS:
- requestResult = ModbusProcessWriteMultipleRegisters(
- slave, rxBuffer, rxLength, isBroadcast, txBuffer, txCapacity,
- txLength);
- break;
-
- default:
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- return ModbusBuildException(slave->slaveAddress, functionCode,
- MODBUS_EXCEPTION_ILLEGAL_FUNC,
- txBuffer, txCapacity, txLength);
- }
-
- /* 响应缓冲区或内部处理失败不是协议异常,应该报告给应用层,而不能伪造一帧
- * 发送到总线上的异常响应。 */
- if (requestResult == MODBUS_REQUEST_RESPONSE_ERROR) {
- return MODBUS_STATUS_FRAME_ERROR;
- }
-
- if (requestResult != MODBUS_REQUEST_OK) {
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_IGNORED;
- }
- return ModbusBuildException(slave->slaveAddress, functionCode,
- (uint8_t)requestResult, txBuffer,
- txCapacity, txLength);
- }
-
- if (isBroadcast != 0U) {
- return MODBUS_STATUS_BROADCAST;
- }
-
- return MODBUS_STATUS_OK;
- }
|