当前位置: 首页 > wzjs >正文

玉溪做网站建设的公司外贸怎么找客户资源

玉溪做网站建设的公司,外贸怎么找客户资源,怎么做网页制作如何插入水平线,wordpress日志图片溢出怎么办Modbus通信协议简介 1. Modbus概述 Modbus是一种广泛应用于工业控制领域的通信协议,由Modicon公司(现为施耐德电气)于1979年发明。它是一种开放的、基于主从架构的通信协议,被广泛应用于工业自动化、楼宇自动化、能源管理等领域…

Modbus通信协议简介

1. Modbus概述

Modbus是一种广泛应用于工业控制领域的通信协议,由Modicon公司(现为施耐德电气)于1979年发明。它是一种开放的、基于主从架构的通信协议,被广泛应用于工业自动化、楼宇自动化、能源管理等领域。Modbus具有以下特点:

  • 简单且健壮:协议结构简单,易于实现,且通信可靠
  • 开放性:作为一种公开的工业标准,不需要支付版权费用
  • 少量的报文类型:基本的报文类型较少,易于学习和使用
  • 主从架构:明确的主站和从站关系,避免冲突
  • 支持多种物理层:如RS-232、RS-485、TCP/IP等

2. Modbus通信模式

Modbus协议支持多种通信模式,主要包括:

  1. Modbus RTU:基于串行通信(如RS-232/RS-485),使用二进制编码方式,以提高传输效率。
  2. Modbus ASCII:基于串行通信,使用ASCII编码方式,易于调试但传输效率较低。
  3. Modbus TCP/IP:基于以太网通信,将Modbus帧封装在TCP报文中传输。

本项目主要实现的是Modbus RTU模式。

3. Modbus数据模型

Modbus定义了四种基本的数据表,每个表可能对应不同类型的对象:

数据表对象类型访问描述
线圈(Coils)单个位读/写离散输出量,可读可写
离散输入(Discrete Inputs)单个位只读离散输入量,只读
保持寄存器(Holding Registers)16位字读/写可读可写的16位寄存器
输入寄存器(Input Registers)16位字只读只读的16位寄存器

4. Modbus功能码

Modbus使用功能码来指示要执行的操作,常用功能码包括:

功能码描述操作的数据类型
0x01读线圈线圈(离散输出)
0x02读离散输入离散输入
0x03读保持寄存器保持寄存器
0x04读输入寄存器输入寄存器
0x05写单个线圈线圈
0x06写单个寄存器保持寄存器
0x0F写多个线圈线圈
0x10写多个寄存器保持寄存器

5. Modbus RTU帧格式

Modbus RTU的帧格式如下:

[从站地址] [功能码] [数据] [CRC校验(低字节)] [CRC校验(高字节)]
  • 从站地址:占1字节,范围0~247,其中0为广播地址
  • 功能码:占1字节,表示要执行的操作
  • 数据:长度不定,根据功能码不同而不同
  • CRC校验:占2字节,用于错误检测

6. 本项目实现的功能

本项目基于STM32F103RCT6实现了Modbus RTU从站功能,包括:

  1. 支持所有标准Modbus功能码(01、02、03、04、05、06、0F、10)
  2. 实现了线圈、离散输入、保持寄存器和输入寄存器四种数据类型
  3. 使用UART2(PA2/PA3)作为Modbus通信接口,默认波特率9600,8位数据位,无校验,1位停止位
  4. 从站地址为0x01

7. 如何使用

7.1 硬件连接

将Modbus总线连接到STM32的PA2(TX)和PA3(RX)引脚。如果使用RS-485通信,需要外接RS-485转换芯片(如MAX485)。

7.2 Modbus寄存器和线圈定义

本项目中定义的Modbus数据模型默认值如下:

  • 线圈(地址范围0-63):默认全部为0
  • 离散输入(地址范围0-63):默认全部为0
  • 保持寄存器(地址范围0-31):
    • 寄存器0 = 0x1234
    • 寄存器1 = 0x5678
    • 其余默认为0
  • 输入寄存器(地址范围0-31):
    • 寄存器0 = 0xABCD
    • 寄存器1 = 0xEF01
    • 其余默认为0

可以根据实际需求在modbus_rtu.c的Modbus_Init函数中修改这些默认值。

7.3 测试方法

可以使用Modbus调试工具(如Modbus Poll、ModScan等)作为主站,连接到STM32进行测试。

测试时请注意以下配置:

  • 通信参数:9600,8,N,1
  • 从站地址:1
  • Modbus模式:RTU

8. 扩展与修改

如需扩展或修改本项目,可以考虑以下方面:

  1. 增加寄存器数量:修改modbus_rtu.h中的MAX_COILS、MAX_DISCRETE_INPUTS、MAX_HOLDING_REGISTERS和MAX_INPUT_REGISTERS宏定义。
  2. 修改通信参数:在modbus_rtu.c的Modbus_Init函数中修改UART配置。
  3. 添加新功能码:在Modbus_ProcessMessage函数中添加新的case分支,并实现相应的处理函数。
  4. 集成实际功能:可以将实际的传感器数据写入输入寄存器,或者通过保持寄存器控制设备。

9. 常见问题与解决方法

  1. 通信无响应

    • 检查物理连接是否正确
    • 确认通信参数(波特率、校验位等)是否一致
    • 确认从站地址是否正确
  2. CRC错误

    • 检查通信线路是否有干扰
    • 降低通信速率
    • 确认主站的CRC计算方式是否正确
  3. 接收数据不完整

    • 可能是3.5个字符时间判断有误,尝试调整t35Timeout的值
    • 检查接收中断处理逻辑

10. 进一步学习资源

  • Modbus官方网站
  • Modbus协议规范
  • STM32 HAL库文档
  • FreeRTOS官方文档

11.项目源码

modbus_rtu.h文件

/********************************************************************************
* @file    modbus_rtu.h
* @brief   Modbus RTU协议实现头文件
******************************************************************************
*/#ifndef __MODBUS_RTU_H
#define __MODBUS_RTU_H#ifdef __cplusplus
extern "C" {
#endif/* 包含头文件 */
#include "main.h"#ifndef  USE_FREERTOS_COMPONENT
#else 
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#endif/* 宏定义 */
#define MODBUS_RTU_SLAVE_ADDRESS    0x01        /* 从机地址 */
#define MODBUS_MAX_BUFFER_SIZE      256         /* 最大缓冲区大小 *//* ModBus功能码 */
typedef enum {MODBUS_FC_READ_COILS               = 0x01,  /* 读线圈 */MODBUS_FC_READ_DISCRETE_INPUTS     = 0x02,  /* 读离散输入 */MODBUS_FC_READ_HOLDING_REGISTERS   = 0x03,  /* 读保持寄存器 */MODBUS_FC_READ_INPUT_REGISTERS     = 0x04,  /* 读输入寄存器 */MODBUS_FC_WRITE_SINGLE_COIL        = 0x05,  /* 写单个线圈 */MODBUS_FC_WRITE_SINGLE_REGISTER    = 0x06,  /* 写单个寄存器 */MODBUS_FC_WRITE_MULTIPLE_COILS     = 0x0F,  /* 写多个线圈 */MODBUS_FC_WRITE_MULTIPLE_REGISTERS = 0x10   /* 写多个寄存器 */
} ModbusFunctionCode;/* Modbus错误码 */
typedef enum {MODBUS_EXCEPTION_ILLEGAL_FUNCTION     = 0x01, /* 非法功能 */MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 0x02, /* 非法数据地址 */MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE   = 0x03, /* 非法数据值 */MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE = 0x04, /* 从设备故障 */MODBUS_EXCEPTION_ACKNOWLEDGE          = 0x05, /* 确认 */MODBUS_EXCEPTION_SLAVE_DEVICE_BUSY    = 0x06, /* 从设备忙 */MODBUS_EXCEPTION_MEMORY_PARITY_ERROR  = 0x08, /* 内存奇偶错误 */MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE = 0x0A, /* 网关路径不可用 */MODBUS_EXCEPTION_GATEWAY_TARGET_FAILED    = 0x0B  /* 网关目标设备无响应 */
} ModbusExceptionCode;/* Modbus数据模型 */
#define MAX_COILS              64   /* 线圈数量 */
#define MAX_DISCRETE_INPUTS    64   /* 离散输入数量 */
#define MAX_INPUT_REGISTERS    32   /* 输入寄存器数量 */
#define MAX_HOLDING_REGISTERS  32   /* 保持寄存器数量 *//* UART配置 */
#define MODBUS_UART            huart2
#define MODBUS_UART_TIMEOUT    1000/* 函数原型 */
void Modbus_Init(void);#ifndef  USE_FREERTOS_COMPONENT
void Modbus_app(void);
#else
void Modbus_Task(void *pvParameters);
#endif
uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t length);
void Modbus_ProcessMessage(uint8_t *frame, uint16_t length);
void Modbus_SendException(uint8_t slaveAddr, uint8_t functionCode, ModbusExceptionCode exceptionCode);
void Modbus_SendResponse(uint8_t *response, uint16_t length);/* 功能码处理函数 */
void Modbus_ReadCoils(uint8_t *frame, uint16_t length);
void Modbus_ReadDiscreteInputs(uint8_t *frame, uint16_t length);
void Modbus_ReadHoldingRegisters(uint8_t *frame, uint16_t length);
void Modbus_ReadInputRegisters(uint8_t *frame, uint16_t length);
void Modbus_WriteSingleCoil(uint8_t *frame, uint16_t length);
void Modbus_WriteSingleRegister(uint8_t *frame, uint16_t length);
void Modbus_WriteMultipleCoils(uint8_t *frame, uint16_t length);
void Modbus_WriteMultipleRegisters(uint8_t *frame, uint16_t length);void Modbus_PrintData(uint8_t *buffer, uint16_t length, const char *description);/* 外部变量 */
extern UART_HandleTypeDef huart2;#ifdef __cplusplus
}
#endif#endif /* __MODBUS_RTU_H */ 

modbus_rtu.c文件

/********************************************************************************* @file    modbus_rtu.c* @brief   Modbus RTU协议实现源文件*******************************************************************************//* 包含头文件 */
#include "modbus_rtu.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"/* 私有变量 */
UART_HandleTypeDef huart2;static uint32_t lastActivity = 0;
/* Modbus从机数据 */
static uint8_t coils[MAX_COILS / 8];                  /* 线圈 */
static uint8_t discreteInputs[MAX_DISCRETE_INPUTS / 8]; /* 离散输入 */
static uint16_t inputRegisters[MAX_INPUT_REGISTERS];    /* 输入寄存器 */
static uint16_t holdingRegisters[MAX_HOLDING_REGISTERS]; /* 保持寄存器 *//* 接收和发送缓冲区 */
static uint8_t rxBuffer[MODBUS_MAX_BUFFER_SIZE];
static uint8_t txBuffer[MODBUS_MAX_BUFFER_SIZE];
static uint16_t rxCount = 0;/*** @brief  初始化Modbus* @param  无* @retval 无*/
void Modbus_Init(void)
{/* 配置Modbus使用的串口 */GPIO_InitTypeDef GPIO_InitStruct = {0};/* 使能USART2和GPIOA时钟 */__HAL_RCC_USART2_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/* 配置USART2 TX引脚(PA2) */GPIO_InitStruct.Pin = GPIO_PIN_2;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* 配置USART2 RX引脚(PA3) */GPIO_InitStruct.Pin = GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* 配置UART参数 */huart2.Instance = USART2;huart2.Init.BaudRate = 9600;huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_1;huart2.Init.Parity = UART_PARITY_NONE;huart2.Init.Mode = UART_MODE_TX_RX;huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart2.Init.OverSampling = UART_OVERSAMPLING_16;HAL_UART_Init(&huart2);/* 配置USART2中断 */HAL_NVIC_SetPriority(USART2_IRQn, 5, 0);  /* 设置中断优先级,需要低于FreeRTOS的系统调用 */HAL_NVIC_EnableIRQ(USART2_IRQn);          /* 使能USART2中断 *//* 初始化Modbus数据区 */memset(coils, 0, sizeof(coils));memset(discreteInputs, 0, sizeof(discreteInputs));memset(inputRegisters, 0, sizeof(inputRegisters));memset(holdingRegisters, 0, sizeof(holdingRegisters));/* 设置一些测试数据 */holdingRegisters[0] = 0x1234;holdingRegisters[1] = 0x5678;inputRegisters[0] = 0xABCD;inputRegisters[1] = 0xEF01;/* 开始接收数据 */HAL_UART_Receive_IT(&huart2, &rxBuffer[0], 1);printf("Modbus RTU initialized on USART2 (9600-8-N-1)\r\n");
}#ifndef USE_FREERTOS_COMPONENT
void Modbus_app(void)
{const uint32_t t35Timeout = 4; /* 3.5个字符时间,按9600波特率大约4ms *//* 初始化Modbus */Modbus_Init();for(;;){/* 检查是否接收到数据,并且已经过了3.5个字符时间 */uint32_t currentTick = HAL_GetTick();if (rxCount > 0 && (currentTick - lastActivity) >= t35Timeout){/* 有一个完整的帧,处理它 */if (rxCount >= 4) /* 最小Modbus帧长度:从机地址(1)+功能码(1)+CRC(2) */{/* 验证CRC */uint16_t crc = Modbus_CRC16(rxBuffer, rxCount - 2);uint16_t receivedCrc = (rxBuffer[rxCount - 1] << 8) | rxBuffer[rxCount - 2];if (crc == receivedCrc){/* CRC正确,检查是否是发给本机的 */if (rxBuffer[0] == MODBUS_RTU_SLAVE_ADDRESS || rxBuffer[0] == 0) /* 0是广播地址 */{/* 处理Modbus消息 */Modbus_ProcessMessage(rxBuffer, rxCount);Modbus_PrintData(rxBuffer, rxCount, NULL);}}else{printf("Modbus CRC error\r\n");}}/* 重置接收计数器,准备接收下一帧 */rxCount = 0;HAL_UART_Receive_IT(&huart2, &rxBuffer[0], 1);}else{//printf(" rxCount:%d currentTick - lastActivity:%d\r\n", rxCount, (currentTick - lastActivity));}//HAL_Delay(1);}
}
#else
/*** @brief  Modbus任务函数* @param  pvParameters 任务参数* @retval 无*/
void Modbus_Task(void *pvParameters)
{uint32_t lastActivity = 0;const uint32_t t35Timeout = 4; /* 3.5个字符时间,按9600波特率大约4ms *//* 初始化Modbus */Modbus_Init();for(;;){/* 检查是否接收到数据,并且已经过了3.5个字符时间 */uint32_t currentTick = xTaskGetTickCount();if (rxCount > 0 && (currentTick - lastActivity) >= t35Timeout){/* 有一个完整的帧,处理它 */if (rxCount >= 4) /* 最小Modbus帧长度:从机地址(1)+功能码(1)+CRC(2) */{/* 验证CRC */uint16_t crc = Modbus_CRC16(rxBuffer, rxCount - 2);uint16_t receivedCrc = (rxBuffer[rxCount - 1] << 8) | rxBuffer[rxCount - 2];if (crc == receivedCrc){/* CRC正确,检查是否是发给本机的 */if (rxBuffer[0] == MODBUS_RTU_SLAVE_ADDRESS || rxBuffer[0] == 0) /* 0是广播地址 */{/* 处理Modbus消息 */Modbus_ProcessMessage(rxBuffer, rxCount);}}else{printf("Modbus CRC error\r\n");}}/* 重置接收计数器,准备接收下一帧 */rxCount = 0;HAL_UART_Receive_IT(&huart2, &rxBuffer[0], 1);}/* 休眠一小段时间 */vTaskDelay(1);}
}#endif
/*** @brief  计算Modbus CRC16校验* @param  buffer 数据缓冲区* @param  length 数据长度* @retval CRC16校验值*/
uint16_t Modbus_CRC16(uint8_t *buffer, uint16_t length)
{uint16_t crc = 0xFFFF;uint16_t i;while (length--){crc ^= *buffer++;for (i = 0; i < 8; i++){if (crc & 0x0001){crc >>= 1;crc ^= 0xA001;}else{crc >>= 1;}}}return crc;
}/*** @brief  处理Modbus消息* @param  frame 数据帧* @param  length 数据长度* @retval 无*/
void Modbus_ProcessMessage(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint8_t functionCode = frame[1];/* 如果是广播地址,不需要响应 */if (slaveAddr == 0){return;}/* 根据功能码调用相应的处理函数 */switch (functionCode){case MODBUS_FC_READ_COILS:Modbus_ReadCoils(frame, length);break;case MODBUS_FC_READ_DISCRETE_INPUTS:Modbus_ReadDiscreteInputs(frame, length);break;case MODBUS_FC_READ_HOLDING_REGISTERS:Modbus_ReadHoldingRegisters(frame, length);break;case MODBUS_FC_READ_INPUT_REGISTERS:Modbus_ReadInputRegisters(frame, length);break;case MODBUS_FC_WRITE_SINGLE_COIL:Modbus_WriteSingleCoil(frame, length);break;case MODBUS_FC_WRITE_SINGLE_REGISTER:Modbus_WriteSingleRegister(frame, length);break;case MODBUS_FC_WRITE_MULTIPLE_COILS:Modbus_WriteMultipleCoils(frame, length);break;case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:Modbus_WriteMultipleRegisters(frame, length);break;default:/* 不支持的功能码 */Modbus_SendException(slaveAddr, functionCode, MODBUS_EXCEPTION_ILLEGAL_FUNCTION);break;}
}/*** @brief  发送Modbus异常响应* @param  slaveAddr 从机地址* @param  functionCode 功能码* @param  exceptionCode 异常码* @retval 无*/
void Modbus_SendException(uint8_t slaveAddr, uint8_t functionCode, ModbusExceptionCode exceptionCode)
{uint16_t crc;txBuffer[0] = slaveAddr;txBuffer[1] = functionCode | 0x80; /* 异常响应功能码 = 功能码 | 0x80 */txBuffer[2] = exceptionCode;/* 计算CRC */crc = Modbus_CRC16(txBuffer, 3);txBuffer[3] = crc & 0xFF;txBuffer[4] = crc >> 8;/* 发送异常帧 */Modbus_SendResponse(txBuffer, 5);
}/*** @brief  发送Modbus响应* @param  response 响应数据* @param  length 数据长度* @retval 无*/
void Modbus_SendResponse(uint8_t *response, uint16_t length)
{HAL_UART_Transmit(&huart2, response, length, MODBUS_UART_TIMEOUT);
}/*** @brief  读线圈状态处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_ReadCoils(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t startAddress = (frame[2] << 8) | frame[3];uint16_t coilCount = (frame[4] << 8) | frame[5];uint16_t byteCount = (coilCount + 7) / 8; /* 计算需要的字节数 */uint16_t i, j;uint16_t crc;/* 检查地址和数量是否有效 */if (startAddress + coilCount > MAX_COILS || coilCount == 0 || coilCount > 2000){Modbus_SendException(slaveAddr, MODBUS_FC_READ_COILS, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 构建响应 */txBuffer[0] = slaveAddr;txBuffer[1] = MODBUS_FC_READ_COILS;txBuffer[2] = byteCount;/* 填充线圈状态数据 */memset(&txBuffer[3], 0, byteCount);for (i = 0; i < coilCount; i++){uint16_t byteIndex = i / 8;uint8_t bitIndex = i % 8;/* 获取线圈状态 */if ((coils[(startAddress + i) / 8] >> ((startAddress + i) % 8)) & 0x01){txBuffer[3 + byteIndex] |= (1 << bitIndex);}}/* 计算CRC */crc = Modbus_CRC16(txBuffer, 3 + byteCount);txBuffer[3 + byteCount] = crc & 0xFF;txBuffer[3 + byteCount + 1] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 3 + byteCount + 2);
}/*** @brief  读离散输入状态处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_ReadDiscreteInputs(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t startAddress = (frame[2] << 8) | frame[3];uint16_t inputCount = (frame[4] << 8) | frame[5];uint16_t byteCount = (inputCount + 7) / 8; /* 计算需要的字节数 */uint16_t i, j;uint16_t crc;/* 检查地址和数量是否有效 */if (startAddress + inputCount > MAX_DISCRETE_INPUTS || inputCount == 0 || inputCount > 2000){Modbus_SendException(slaveAddr, MODBUS_FC_READ_DISCRETE_INPUTS, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 构建响应 */txBuffer[0] = slaveAddr;txBuffer[1] = MODBUS_FC_READ_DISCRETE_INPUTS;txBuffer[2] = byteCount;/* 填充离散输入状态数据 */memset(&txBuffer[3], 0, byteCount);for (i = 0; i < inputCount; i++){uint16_t byteIndex = i / 8;uint8_t bitIndex = i % 8;/* 获取离散输入状态 */if ((discreteInputs[(startAddress + i) / 8] >> ((startAddress + i) % 8)) & 0x01){txBuffer[3 + byteIndex] |= (1 << bitIndex);}}/* 计算CRC */crc = Modbus_CRC16(txBuffer, 3 + byteCount);txBuffer[3 + byteCount] = crc & 0xFF;txBuffer[3 + byteCount + 1] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 3 + byteCount + 2);
}/*** @brief  读保持寄存器处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_ReadHoldingRegisters(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t startAddress = (frame[2] << 8) | frame[3];uint16_t regCount = (frame[4] << 8) | frame[5];uint16_t byteCount = regCount * 2; /* 每个寄存器2个字节 */uint16_t i;uint16_t crc;/* 检查地址和数量是否有效 */if (startAddress + regCount > MAX_HOLDING_REGISTERS || regCount == 0 || regCount > 125){Modbus_SendException(slaveAddr, MODBUS_FC_READ_HOLDING_REGISTERS, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 构建响应 */txBuffer[0] = slaveAddr;txBuffer[1] = MODBUS_FC_READ_HOLDING_REGISTERS;txBuffer[2] = byteCount;/* 填充寄存器数据 */for (i = 0; i < regCount; i++){txBuffer[3 + i * 2] = holdingRegisters[startAddress + i] >> 8; /* 高字节 */txBuffer[3 + i * 2 + 1] = holdingRegisters[startAddress + i] & 0xFF; /* 低字节 */}/* 计算CRC */crc = Modbus_CRC16(txBuffer, 3 + byteCount);txBuffer[3 + byteCount] = crc & 0xFF;txBuffer[3 + byteCount + 1] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 3 + byteCount + 2);
}/*** @brief  读输入寄存器处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_ReadInputRegisters(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t startAddress = (frame[2] << 8) | frame[3];uint16_t regCount = (frame[4] << 8) | frame[5];uint16_t byteCount = regCount * 2; /* 每个寄存器2个字节 */uint16_t i;uint16_t crc;/* 检查地址和数量是否有效 */if (startAddress + regCount > MAX_INPUT_REGISTERS || regCount == 0 || regCount > 125){Modbus_SendException(slaveAddr, MODBUS_FC_READ_INPUT_REGISTERS, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 构建响应 */txBuffer[0] = slaveAddr;txBuffer[1] = MODBUS_FC_READ_INPUT_REGISTERS;txBuffer[2] = byteCount;/* 填充寄存器数据 */for (i = 0; i < regCount; i++){txBuffer[3 + i * 2] = inputRegisters[startAddress + i] >> 8; /* 高字节 */txBuffer[3 + i * 2 + 1] = inputRegisters[startAddress + i] & 0xFF; /* 低字节 */}/* 计算CRC */crc = Modbus_CRC16(txBuffer, 3 + byteCount);txBuffer[3 + byteCount] = crc & 0xFF;txBuffer[3 + byteCount + 1] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 3 + byteCount + 2);
}/*** @brief  写单个线圈处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_WriteSingleCoil(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t coilAddress = (frame[2] << 8) | frame[3];uint16_t coilValue = (frame[4] << 8) | frame[5];uint16_t crc;/* 检查地址是否有效 */if (coilAddress >= MAX_COILS){Modbus_SendException(slaveAddr, MODBUS_FC_WRITE_SINGLE_COIL, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 检查值是否有效 (0x0000或0xFF00) */if (coilValue != 0x0000 && coilValue != 0xFF00){Modbus_SendException(slaveAddr, MODBUS_FC_WRITE_SINGLE_COIL, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE);return;}/* 设置线圈状态 */uint8_t bitMask = 1 << (coilAddress % 8);if (coilValue == 0xFF00){/* 设置线圈为ON */coils[coilAddress / 8] |= bitMask;}else{/* 设置线圈为OFF */coils[coilAddress / 8] &= ~bitMask;}/* 构建响应(与请求相同) */memcpy(txBuffer, frame, 6);/* 计算CRC */crc = Modbus_CRC16(txBuffer, 6);txBuffer[6] = crc & 0xFF;txBuffer[7] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 8);
}/*** @brief  写单个寄存器处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_WriteSingleRegister(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t regAddress = (frame[2] << 8) | frame[3];uint16_t regValue = (frame[4] << 8) | frame[5];uint16_t crc;/* 检查地址是否有效 */if (regAddress >= MAX_HOLDING_REGISTERS){Modbus_SendException(slaveAddr, MODBUS_FC_WRITE_SINGLE_REGISTER, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 设置寄存器值 */holdingRegisters[regAddress] = regValue;/* 构建响应(与请求相同) */memcpy(txBuffer, frame, 6);/* 计算CRC */crc = Modbus_CRC16(txBuffer, 6);txBuffer[6] = crc & 0xFF;txBuffer[7] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 8);
}/*** @brief  写多个线圈处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_WriteMultipleCoils(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t startAddress = (frame[2] << 8) | frame[3];uint16_t coilCount = (frame[4] << 8) | frame[5];uint8_t byteCount = frame[6];uint16_t i;uint16_t crc;/* 检查地址和数量是否有效 */if (startAddress + coilCount > MAX_COILS || coilCount == 0 || coilCount > 1968 ||byteCount != (coilCount + 7) / 8){Modbus_SendException(slaveAddr, MODBUS_FC_WRITE_MULTIPLE_COILS, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 设置线圈状态 */for (i = 0; i < coilCount; i++){uint16_t byteIndex = i / 8;uint8_t bitIndex = i % 8;uint8_t bitMask = 1 << bitIndex;/* 清除旧值 */coils[(startAddress + i) / 8] &= ~(1 << ((startAddress + i) % 8));/* 设置新值 */if (frame[7 + byteIndex] & bitMask){coils[(startAddress + i) / 8] |= (1 << ((startAddress + i) % 8));}}/* 构建响应 */txBuffer[0] = slaveAddr;txBuffer[1] = MODBUS_FC_WRITE_MULTIPLE_COILS;txBuffer[2] = startAddress >> 8;txBuffer[3] = startAddress & 0xFF;txBuffer[4] = coilCount >> 8;txBuffer[5] = coilCount & 0xFF;/* 计算CRC */crc = Modbus_CRC16(txBuffer, 6);txBuffer[6] = crc & 0xFF;txBuffer[7] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 8);
}/*** @brief  写多个寄存器处理* @param  frame 请求帧* @param  length 帧长度* @retval 无*/
void Modbus_WriteMultipleRegisters(uint8_t *frame, uint16_t length)
{uint8_t slaveAddr = frame[0];uint16_t startAddress = (frame[2] << 8) | frame[3];uint16_t regCount = (frame[4] << 8) | frame[5];uint8_t byteCount = frame[6];uint16_t i;uint16_t crc;/* 检查地址和数量是否有效 */if (startAddress + regCount > MAX_HOLDING_REGISTERS || regCount == 0 || regCount > 123 ||byteCount != regCount * 2){Modbus_SendException(slaveAddr, MODBUS_FC_WRITE_MULTIPLE_REGISTERS, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);return;}/* 设置寄存器值 */for (i = 0; i < regCount; i++){holdingRegisters[startAddress + i] = (frame[7 + i * 2] << 8) | frame[7 + i * 2 + 1];}/* 构建响应 */txBuffer[0] = slaveAddr;txBuffer[1] = MODBUS_FC_WRITE_MULTIPLE_REGISTERS;txBuffer[2] = startAddress >> 8;txBuffer[3] = startAddress & 0xFF;txBuffer[4] = regCount >> 8;txBuffer[5] = regCount & 0xFF;/* 计算CRC */crc = Modbus_CRC16(txBuffer, 6);txBuffer[6] = crc & 0xFF;txBuffer[7] = crc >> 8;/* 发送响应 */Modbus_SendResponse(txBuffer, 8);
}/*** @brief  UART接收完成回调函数* @param  huart UART句柄* @retval 无*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if (huart->Instance == USART2){lastActivity = HAL_GetTick();if (rxCount < MODBUS_MAX_BUFFER_SIZE){/* 继续接收下一个字节 */rxCount++;HAL_UART_Receive_IT(&huart2, &rxBuffer[rxCount], 1);}else{/* 缓冲区已满,重置接收 */rxCount = 0;HAL_UART_Receive_IT(&huart2, &rxBuffer[0], 1);}//printf("recv data:%s \r\n", rxBuffer);}
} /*** @brief  打印Modbus数据* @param  buffer 数据缓冲区* @param  length 数据长度* @param  description 数据描述* @retval 无*/
void Modbus_PrintData(uint8_t *buffer, uint16_t length, const char *description)
{printf("%s,长度: %d 字节\r\n", description, length);printf("数据内容(HEX): ");for (uint16_t i = 0; i < length; i++){printf("%02X ", buffer[i]);}printf("\r\n");
}

12.注意事项

需要在stm32f1xxx_it.c文件中定义UART2的接收中断

extern UART_HandleTypeDef huart2;
void USART2_IRQHandler(void)
{HAL_UART_IRQHandler(&huart2);
} 

13.测试结果

在这里插入图片描述

http://www.dtcms.com/wzjs/191852.html

相关文章:

  • 晋江做鞋子批发的网站优化大师兑换码
  • 为公司做网站要做什么准备西安疫情最新消息
  • 会展相关网站建设福州百度网站快速优化
  • 哪几个网站做acm题目站长网站提交
  • 西安学校网站建设国际新闻头条最新消息
  • 网站中医建设每日新闻快报
  • 手机网站建南京seo优化公司
  • 新手学做网站百度云百度数据开放平台
  • 国家企业信息年报系统seo优化包括哪些内容
  • 设计公司取名大全最新版的移动网站推广如何优化
  • 四川省建设工程质量安全监督总站网站广州seo网站
  • 静安网站建设91永久免费海外地域网名
  • 如何分析一个网站做的怎么样宁波seo公司网站推广
  • 新乡做网站多少钱百度竞价托管代运营
  • 绍兴 网站建设关键字挖掘
  • 智能建站系统官网企业网站的基本功能
  • 建设平面设计工作室网站方案企业网站seo诊断报告
  • 湛江企业网站建设公司免费网站或软件
  • 二手网站建设论文太原全网推广
  • php抗议小卫士填报代码网页制作网站网站推广软件免费版下载
  • 群晖 做网站服务器南京百度快速排名优化
  • 做推广任务的网站有哪些网络课程
  • 建设网站制作首码项目推广平台
  • 网站被清空了怎么办百度站长工具使用方法
  • 做收费视频网站个人博客网站怎么做
  • 阿米纳网站建设免费推广的方式
  • 淮南网站建设好公司官网优化方案
  • 网站建设周期计划哪个搜索引擎最好
  • 手机上的软件网站建设chrome 谷歌浏览器
  • 独立购物网站建设百度品牌专区怎么收费