在STM32F407VGT6 + LAN8720 + LWIP基础上实现Modbus TCP

[复制链接]
2174|1
keer_zu 发表于 2025-9-2 23:13 | 显示全部楼层 |阅读模式

在STM32F407VGT6 + LAN8720 + LWIP基础上实现Modbus TCP

在STM32F407VGT6微控制器上结合LAN8720以太网PHY和LWIP协议栈实现Modbus TCP协议,需要完成以下几个关键步骤:

1. 硬件和基础网络设置

首先确保你的硬件连接正确,并已配置好LWIP协议栈:

// 网络接口初始化
void netif_config(void)
{
    ip_addr_t ipaddr;
    ip_addr_t netmask;
    ip_addr_t gw;
  
    // 初始化IP地址(根据你的网络配置调整)
    IP4_ADDR(&ipaddr, 192, 168, 1, 10);
    IP4_ADDR(&netmask, 255, 255, 255, 0);
    IP4_ADDR(&gw, 192, 168, 1, 1);
  
    // 添加网络接口
    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);
  
    // 注册默认网络接口
    netif_set_default(&gnetif);
    netif_set_up(&gnetif);
}

2. 实现Modbus TCP服务器

创建一个Modbus TCP服务器任务来处理Modbus请求:

// Modbus TCP服务器任务
void modbus_tcp_server_task(void *arg)
{
    struct netconn *conn, *newconn;
    err_t err;
  
    // 创建新的TCP连接
    conn = netconn_new(NETCONN_TCP);
    netconn_bind(conn, IP_ADDR_ANY, 502); // Modbus TCP默认端口502
  
    // 监听连接
    netconn_listen(conn);
  
    while(1) {
        // 接受新连接
        err = netconn_accept(conn, &newconn);
      
        if(err == ERR_OK) {
            // 为新连接创建处理任务
            sys_thread_new("modbus_handler", modbus_connection_handler, newconn, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
        }
    }
}

3. Modbus连接处理程序

// Modbus连接处理程序
void modbus_connection_handler(void *arg)
{
    struct netconn *conn = (struct netconn *)arg;
    struct netbuf *buf;
    void *data;
    u16_t len;
    err_t err;
  
    do {
        // 接收数据
        err = netconn_recv(conn, &buf);
      
        if(err == ERR_OK) {
            // 获取数据和长度
            netbuf_data(buf, &data, &len);
          
            // 处理Modbus请求
            process_modbus_request(conn, data, len);
          
            // 释放缓冲区
            netbuf_delete(buf);
        }
    } while(err == ERR_OK);
  
    // 关闭连接
    netconn_close(conn);
    netconn_delete(conn);
}

4. Modbus请求处理

// 处理Modbus请求
void process_modbus_request(struct netconn *conn, void *data, u16_t len)
{
    modbus_tcp_header_t *header = (modbus_tcp_header_t *)data;
    modbus_pdu_t *pdu = (modbus_pdu_t *)(header + 1);
  
    // 验证事务标识符和协议标识符
    if(header->protocol_id != 0) {
        // 不是Modbus TCP协议
        return;
    }
  
    // 处理不同的功能码
    switch(pdu->function_code) {
        case MB_FUNC_READ_COILS:
            handle_read_coils(conn, header, pdu);
            break;
        case MB_FUNC_READ_DISCRETE_INPUTS:
            handle_read_discrete_inputs(conn, header, pdu);
            break;
        case MB_FUNC_READ_HOLDING_REGISTERS:
            handle_read_holding_registers(conn, header, pdu);
            break;
        case MB_FUNC_READ_INPUT_REGISTERS:
            handle_read_input_registers(conn, header, pdu);
            break;
        case MB_FUNC_WRITE_SINGLE_COIL:
            handle_write_single_coil(conn, header, pdu);
            break;
        case MB_FUNC_WRITE_SINGLE_REGISTER:
            handle_write_single_register(conn, header, pdu);
            break;
        // 添加其他功能码处理...
        default:
            // 返回异常响应
            send_exception_response(conn, header, pdu, MB_EX_ILLEGAL_FUNCTION);
            break;
    }
}

5. 示例功能处理程序

// 处理读取保持寄存器请求
void handle_read_holding_registers(struct netconn *conn, modbus_tcp_header_t *header, modbus_pdu_t *pdu)
{
    read_registers_req_t *req = (read_registers_req_t *)pdu->data;
    read_registers_resp_t *resp;
    modbus_tcp_header_t *resp_header;
    uint8_t *response;
    uint16_t start_addr = ntohs(req->start_addr);
    uint16_t reg_count = ntohs(req->reg_count);
    uint16_t length;
  
    // 检查请求的有效性
    if(reg_count < 1 || reg_count > 125) {
        send_exception_response(conn, header, pdu, MB_EX_ILLEGAL_DATA_VALUE);
        return;
    }
  
    // 准备响应
    length = sizeof(modbus_tcp_header_t) + sizeof(modbus_pdu_t) + 1 + reg_count * 2;
    response = mem_malloc(length);
  
    if(!response) {
        return; // 内存分配失败
    }
  
    // 填充响应头
    resp_header = (modbus_tcp_header_t *)response;
    resp_header->transaction_id = header->transaction_id;
    resp_header->protocol_id = 0;
    resp_header->length = htons(sizeof(modbus_pdu_t) + 1 + reg_count * 2);
    resp_header->unit_id = header->unit_id;
  
    // 填充PDU
    resp = (read_registers_resp_t *)(response + sizeof(modbus_tcp_header_t));
    resp->function_code = pdu->function_code;
    resp->byte_count = reg_count * 2;
  
    // 填充寄存器数据(这里需要从你的设备数据中获取真实值)
    for(int i = 0; i < reg_count; i++) {
        uint16_t reg_value = get_holding_register(start_addr + i);
        resp->registers[i] = htons(reg_value);
    }
  
    // 发送响应
    netconn_write(conn, response, length, NETCONN_COPY);
  
    // 释放内存
    mem_free(response);
}

6. 主函数初始化

int main(void)
{
    // HAL初始化
    HAL_Init();
  
    // 系统时钟配置
    SystemClock_Config();
  
    // 初始化LWIP
    lwip_init();
  
    // 配置网络接口
    netif_config();
  
    // 创建Modbus TCP服务器任务
    sys_thread_new("modbus_tcp", modbus_tcp_server_task, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO);
  
    while(1) {
        // 主循环 - 处理其他任务
        ethernetif_input(&gnetif);
        sys_check_timeouts();
        HAL_Delay(1);
    }
}

注意事项

  1. 内存管理:LWIP使用自己的内存管理,确保正确使用mem_malloc/mem_free。
  2. 字节序:Modbus TCP使用大端字节序,STM32是小端架构,需要使用ntohs/htons等函数进行转换。
  3. 并发处理:Modbus TCP服务器需要处理多个并发连接,确保你的实现是线程安全的。
  4. 超时处理:实现适当的超时机制,断开不活动的连接。
  5. 错误处理:正确处理各种异常情况,并返回适当的Modbus异常响应。
  6. 硬件寄存器映射:根据你的实际应用,实现与硬件寄存器/线圈的映射。

这个实现提供了一个基本的Modbus TCP服务器框架,你可以根据具体需求进行扩展和优化。

 楼主| keer_zu 发表于 2025-9-3 00:23 | 显示全部楼层

在STM32F407VGT6 + LAN8720 + LWIP上实现Modbus TCP的实施方案

下面是一个完整的、可实施的Modbus TCP实现方案,基于STM32F407VGT6微控制器、LAN8720以太网PHY和LWIP协议栈。

1. 系统架构设计

1.1 硬件连接

  • STM32F407VGT6通过RMII接口连接LAN8720
  • 正确配置时钟和引脚(参考STM32CubeMX配置)
  • 确保LAN8720的nINT/REFCLKO引脚正确连接

1.2 软件架构

应用层: Modbus TCP服务器
    |
协议层: Modbus协议处理
    |
传输层: TCP Socket (LWIP)
    |
网络层: IP协议 (LWIP)
    |
链路层: Ethernet (LWIP + LAN8720驱动)
    |
硬件层: STM32F407 + LAN8720

2. 代码实现

2.1 必要的头文件定义

// modbus_tcp.h
#ifndef __MODBUS_TCP_H
#define __MODBUS_TCP_H

#include "lwip/api.h"
#include "lwip/opt.h"
#include "lwip/tcp.h"
#include "lwip/tcpip.h"
#include "lwip/netif.h"
#include "lwip/dhcp.h"
#include "netif/etharp.h"

// Modbus功能码定义
#define MB_FUNC_READ_COILS                0x01
#define MB_FUNC_READ_DISCRETE_INPUTS      0x02
#define MB_FUNC_READ_HOLDING_REGISTERS    0x03
#define MB_FUNC_READ_INPUT_REGISTERS      0x04
#define MB_FUNC_WRITE_SINGLE_COIL         0x05
#define MB_FUNC_WRITE_SINGLE_REGISTER     0x06
#define MB_FUNC_WRITE_MULTIPLE_COILS      0x0F
#define MB_FUNC_WRITE_MULTIPLE_REGISTERS  0x10

// Modbus异常码定义
#define MB_EX_NONE                        0x00
#define MB_EX_ILLEGAL_FUNCTION            0x01
#define MB_EX_ILLEGAL_DATA_ADDRESS        0x02
#define MB_EX_ILLEGAL_DATA_VALUE          0x03
#define MB_EX_SLAVE_DEVICE_FAILURE        0x04
#define MB_EX_ACKNOWLEDGE                 0x05
#define MB_EX_SLAVE_DEVICE_BUSY           0x06
#define MB_EX_MEMORY_PARITY_ERROR         0x08
#define MB_EX_GATEWAY_PATH_UNAVAILABLE    0x0A
#define MB_EX_DEVICE_FAILED_TO_RESPOND    0x0B

// Modbus TCP头部结构
typedef struct {
    uint16_t transaction_id;
    uint16_t protocol_id;
    uint16_t length;
    uint8_t unit_id;
} modbus_tcp_header_t;

// Modbus PDU结构
typedef struct {
    uint8_t function_code;
    uint8_t data[1];
} modbus_pdu_t;

// 寄存器读取请求结构
typedef struct {
    uint16_t start_addr;
    uint16_t reg_count;
} read_registers_req_t;

// 寄存器读取响应结构
typedef struct {
    uint8_t byte_count;
    uint16_t registers[1];
} read_registers_resp_t;

// 线圈读取响应结构
typedef struct {
    uint8_t byte_count;
    uint8_t coils[1];
} read_coils_resp_t;

// 单寄存器写入请求结构
typedef struct {
    uint16_t reg_addr;
    uint16_t reg_value;
} write_single_register_req_t;

// 多寄存器写入请求结构
typedef struct {
    uint16_t start_addr;
    uint16_t reg_count;
    uint8_t byte_count;
    uint16_t registers[1];
} write_multiple_registers_req_t;

// Modbus连接上下文
typedef struct {
    struct tcp_pcb *pcb;
    modbus_tcp_header_t current_header;
    uint8_t exception_code;
    uint8_t is_connected;
} modbus_connection_t;

// 函数声明
void modbus_tcp_init(void);
err_t modbus_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
err_t modbus_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
void modbus_tcp_error(void *arg, err_t err);
err_t modbus_tcp_poll(void *arg, struct tcp_pcb *tpcb);
err_t modbus_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);

// 应用回调函数
uint16_t get_holding_register(uint16_t addr);
uint8_t get_coil(uint16_t addr);
uint16_t get_input_register(uint16_t addr);
uint8_t get_discrete_input(uint16_t addr);
void set_holding_register(uint16_t addr, uint16_t value);
void set_coil(uint16_t addr, uint8_t value);

#endif /* __MODBUS_TCP_H */

2.2 Modbus TCP服务器实现

// modbus_tcp.c
#include "modbus_tcp.h"
#include "string.h"

#define MODBUS_TCP_PORT 502
#define MODBUS_MAX_CONNECTIONS 3

static struct tcp_pcb *modbus_pcb = NULL;
static modbus_connection_t connections[MODBUS_MAX_CONNECTIONS];

// 初始化Modbus TCP服务器
void modbus_tcp_init(void)
{
    err_t err;
  
    // 创建新的TCP控制块
    modbus_pcb = tcp_new();
    if (!modbus_pcb) {
        printf("Error creating PCB\n");
        return;
    }
  
    // 绑定到Modbus TCP端口
    err = tcp_bind(modbus_pcb, IP_ADDR_ANY, MODBUS_TCP_PORT);
    if (err != ERR_OK) {
        printf("Error binding to port %d: %d\n", MODBUS_TCP_PORT, err);
        memp_free(MEMP_TCP_PCB, modbus_pcb);
        return;
    }
  
    // 开始监听
    modbus_pcb = tcp_listen(modbus_pcb);
    if (!modbus_pcb) {
        printf("Error listening\n");
        return;
    }
  
    // 设置接受连接回调
    tcp_accept(modbus_pcb, modbus_tcp_accept);
  
    // 初始化连接数组
    memset(connections, 0, sizeof(connections));
  
    printf("Modbus TCP server started on port %d\n", MODBUS_TCP_PORT);
}

// 接受新连接回调
err_t modbus_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    int i;
    static int connection_count = 0;
  
    LWIP_UNUSED_ARG(arg);
    LWIP_UNUSED_ARG(err);
  
    // 查找空闲连接槽
    for (i = 0; i < MODBUS_MAX_CONNECTIONS; i++) {
        if (connections[i].pcb == NULL) {
            break;
        }
    }
  
    if (i >= MODBUS_MAX_CONNECTIONS) {
        printf("Max connections reached, rejecting\n");
        tcp_abort(newpcb);
        return ERR_ABRT;
    }
  
    // 设置接收回调
    tcp_recv(newpcb, modbus_tcp_recv);
  
    // 设置错误回调
    tcp_err(newpcb, modbus_tcp_error);
  
    // 设置轮询回调
    tcp_poll(newpcb, modbus_tcp_poll, 4);
  
    // 设置发送完成回调
    tcp_sent(newpcb, modbus_tcp_sent);
  
    // 初始化连接上下文
    connections[i].pcb = newpcb;
    connections[i].is_connected = 1;
    connections[i].exception_code = MB_EX_NONE;
  
    connection_count++;
    printf("New Modbus TCP connection (%d active)\n", connection_count);
  
    return ERR_OK;
}

// 接收数据回调
err_t modbus_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    modbus_connection_t *conn = (modbus_connection_t *)arg;
    modbus_tcp_header_t *header;
    modbus_pdu_t *pdu;
    uint16_t pdu_length;
  
    if (!p) {
        // 连接关闭
        printf("Connection closed\n");
        modbus_tcp_connection_close(conn, tpcb);
        return ERR_OK;
    }
  
    if (err != ERR_OK) {
        printf("Receive error: %d\n", err);
        pbuf_free(p);
        return err;
    }
  
    // 确认我们收到了数据
    tcp_recved(tpcb, p->tot_len);
  
    // 检查数据长度是否足够
    if (p->tot_len < sizeof(modbus_tcp_header_t)) {
        printf("Packet too short: %d bytes\n", p->tot_len);
        pbuf_free(p);
        return ERR_OK;
    }
  
    // 获取Modbus TCP头部
    header = (modbus_tcp_header_t *)p->payload;
  
    // 检查协议标识符
    if (header->protocol_id != 0) {
        printf("Invalid protocol ID: %d\n", ntohs(header->protocol_id));
        pbuf_free(p);
        return ERR_OK;
    }
  
    // 保存当前头部信息
    conn->current_header.transaction_id = header->transaction_id;
    conn->current_header.protocol_id = header->protocol_id;
    conn->current_header.unit_id = header->unit_id;
  
    // 计算PDU长度
    pdu_length = ntohs(header->length) - 1; // 减去单元标识符
  
    // 获取PDU
    pdu = (modbus_pdu_t *)((uint8_t *)p->payload + sizeof(modbus_tcp_header_t));
  
    // 处理Modbus请求
    process_modbus_request(conn, pdu, pdu_length);
  
    pbuf_free(p);
    return ERR_OK;
}

// 处理Modbus请求
void process_modbus_request(modbus_connection_t *conn, modbus_pdu_t *pdu, uint16_t pdu_length)
{
    // 根据功能码处理请求
    switch (pdu->function_code) {
        case MB_FUNC_READ_HOLDING_REGISTERS:
            handle_read_holding_registers(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_READ_COILS:
            handle_read_coils(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_READ_DISCRETE_INPUTS:
            handle_read_discrete_inputs(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_READ_INPUT_REGISTERS:
            handle_read_input_registers(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_WRITE_SINGLE_REGISTER:
            handle_write_single_register(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_WRITE_SINGLE_COIL:
            handle_write_single_coil(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_WRITE_MULTIPLE_REGISTERS:
            handle_write_multiple_registers(conn, pdu, pdu_length);
            break;
          
        case MB_FUNC_WRITE_MULTIPLE_COILS:
            handle_write_multiple_coils(conn, pdu, pdu_length);
            break;
          
        default:
            // 不支持的功能码
            printf("Unsupported function code: 0x%02X\n", pdu->function_code);
            send_exception_response(conn, pdu->function_code, MB_EX_ILLEGAL_FUNCTION);
            break;
    }
}

// 处理读取保持寄存器请求
void handle_read_holding_registers(modbus_connection_t *conn, modbus_pdu_t *pdu, uint16_t pdu_length)
{
    read_registers_req_t *req = (read_registers_req_t *)pdu->data;
    read_registers_resp_t *resp;
    modbus_tcp_header_t *resp_header;
    struct pbuf *p_resp;
    uint16_t start_addr, reg_count;
    uint16_t length;
    uint8_t *payload;
    int i;
  
    // 检查请求长度
    if (pdu_length < sizeof(read_registers_req_t)) {
        printf("Invalid read holding registers request length: %d\n", pdu_length);
        send_exception_response(conn, pdu->function_code, MB_EX_ILLEGAL_DATA_VALUE);
        return;
    }
  
    // 获取起始地址和寄存器数量
    start_addr = ntohs(req->start_addr);
    reg_count = ntohs(req->reg_count);
  
    // 验证请求参数
    if (reg_count < 1 || reg_count > 125) {
        printf("Invalid register count: %d\n", reg_count);
        send_exception_response(conn, pdu->function_code, MB_EX_ILLEGAL_DATA_VALUE);
        return;
    }
  
    // 检查地址范围
    if ((start_addr + reg_count) > MODBUS_HOLDING_REGISTERS_MAX) {
        printf("Address out of range: %d + %d > %d\n", 
               start_addr, reg_count, MODBUS_HOLDING_REGISTERS_MAX);
        send_exception_response(conn, pdu->function_code, MB_EX_ILLEGAL_DATA_ADDRESS);
        return;
    }
  
    // 计算响应长度
    length = sizeof(modbus_tcp_header_t) + sizeof(modbus_pdu_t) + 1 + (reg_count * 2);
  
    // 分配响应缓冲区
    p_resp = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_RAM);
    if (!p_resp) {
        printf("Failed to allocate response buffer\n");
        return;
    }
  
    payload = (uint8_t *)p_resp->payload;
  
    // 填充响应头
    resp_header = (modbus_tcp_header_t *)payload;
    resp_header->transaction_id = conn->current_header.transaction_id;
    resp_header->protocol_id = 0;
    resp_header->length = htons(sizeof(modbus_pdu_t) + 1 + (reg_count * 2));
    resp_header->unit_id = conn->current_header.unit_id;
  
    // 填充PDU
    resp = (read_registers_resp_t *)(payload + sizeof(modbus_tcp_header_t));
    resp->function_code = pdu->function_code;
    resp->byte_count = reg_count * 2;
  
    // 填充寄存器值
    for (i = 0; i < reg_count; i++) {
        uint16_t reg_value = get_holding_register(start_addr + i);
        resp->registers[i] = htons(reg_value);
    }
  
    // 发送响应
    tcp_write(conn->pcb, p_resp->payload, p_resp->len, 1);
    tcp_output(conn->pcb);
  
    // 释放缓冲区
    pbuf_free(p_resp);
}

// 发送异常响应
void send_exception_response(modbus_connection_t *conn, uint8_t function_code, uint8_t exception_code)
{
    modbus_tcp_header_t *header;
    modbus_pdu_t *pdu;
    struct pbuf *p;
    uint8_t payload[sizeof(modbus_tcp_header_t) + sizeof(modbus_pdu_t) + 1];
  
    // 准备异常响应
    header = (modbus_tcp_header_t *)payload;
    header->transaction_id = conn->current_header.transaction_id;
    header->protocol_id = 0;
    header->length = htons(3); // 单元ID + 功能码 + 异常码
    header->unit_id = conn->current_header.unit_id;
  
    pdu = (modbus_pdu_t *)(payload + sizeof(modbus_tcp_header_t));
    pdu->function_code = function_code | 0x80; // 设置异常标志
    pdu->data[0] = exception_code;
  
    // 分配pbuf
    p = pbuf_alloc(PBUF_TRANSPORT, sizeof(payload), PBUF_RAM);
    if (!p) {
        printf("Failed to allocate exception response buffer\n");
        return;
    }
  
    // 复制数据到pbuf
    pbuf_take(p, payload, sizeof(payload));
  
    // 发送响应
    tcp_write(conn->pcb, p->payload, p->len, 1);
    tcp_output(conn->pcb);
  
    // 释放pbuf
    pbuf_free(p);
}

// 关闭连接
void modbus_tcp_connection_close(modbus_connection_t *conn, struct tcp_pcb *pcb)
{
    int i;
  
    // 查找连接并标记为断开
    for (i = 0; i < MODBUS_MAX_CONNECTIONS; i++) {
        if (connections[i].pcb == pcb) {
            connections[i].pcb = NULL;
            connections[i].is_connected = 0;
            break;
        }
    }
  
    // 关闭TCP连接
    tcp_arg(pcb, NULL);
    tcp_sent(pcb, NULL);
    tcp_recv(pcb, NULL);
    tcp_err(pcb, NULL);
    tcp_poll(pcb, NULL, 0);
  
    if (tcp_close(pcb) != ERR_OK) {
        tcp_abort(pcb);
    }
  
    printf("Modbus TCP connection closed\n");
}

// 错误回调
void modbus_tcp_error(void *arg, err_t err)
{
    modbus_connection_t *conn = (modbus_connection_t *)arg;
  
    LWIP_UNUSED_ARG(err);
  
    if (conn) {
        printf("Modbus TCP connection error: %d\n", err);
        conn->pcb = NULL;
        conn->is_connected = 0;
    }
}

// 轮询回调
err_t modbus_tcp_poll(void *arg, struct tcp_pcb *tpcb)
{
    modbus_connection_t *conn = (modbus_connection_t *)arg;
  
    if (!conn) {
        tcp_abort(tpcb);
        return ERR_ABRT;
    }
  
    // 可以在这里实现超时检测
  
    return ERR_OK;
}

// 发送完成回调
err_t modbus_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
    LWIP_UNUSED_ARG(arg);
    LWIP_UNUSED_ARG(tpcb);
    LWIP_UNUSED_ARG(len);
  
    return ERR_OK;
}

2.3 应用回调函数实现

// modbus_app.c
#include "modbus_tcp.h"

// 定义Modbus数据区大小
#define MODBUS_HOLDING_REGISTERS_MAX 100
#define MODBUS_INPUT_REGISTERS_MAX 50
#define MODBUS_COILS_MAX 100
#define MODBUS_DISCRETE_INPUTS_MAX 50

// Modbus数据存储
static uint16_t holding_registers[MODBUS_HOLDING_REGISTERS_MAX];
static uint16_t input_registers[MODBUS_INPUT_REGISTERS_MAX];
static uint8_t coils[MODBUS_COILS_MAX];
static uint8_t discrete_inputs[MODBUS_DISCRETE_INPUTS_MAX];

// 初始化Modbus数据区
void modbus_data_init(void)
{
    int i;
  
    // 初始化保持寄存器
    for (i = 0; i < MODBUS_HOLDING_REGISTERS_MAX; i++) {
        holding_registers[i] = 0;
    }
  
    // 初始化输入寄存器
    for (i = 0; i < MODBUS_INPUT_REGISTERS_MAX; i++) {
        input_registers[i] = 0;
    }
  
    // 初始化线圈
    for (i = 0; i < MODBUS_COILS_MAX; i++) {
        coils[i] = 0;
    }
  
    // 初始化离散输入
    for (i = 0; i < MODBUS_DISCRETE_INPUTS_MAX; i++) {
        discrete_inputs[i] = 0;
    }
}

// 获取保持寄存器值
uint16_t get_holding_register(uint16_t addr)
{
    if (addr < MODBUS_HOLDING_REGISTERS_MAX) {
        return holding_registers[addr];
    }
    return 0;
}

// 设置保持寄存器值
void set_holding_register(uint16_t addr, uint16_t value)
{
    if (addr < MODBUS_HOLDING_REGISTERS_MAX) {
        holding_registers[addr] = value;
    }
}

// 获取输入寄存器值
uint16_t get_input_register(uint16_t addr)
{
    if (addr < MODBUS_INPUT_REGISTERS_MAX) {
        return input_registers[addr];
    }
    return 0;
}

// 获取线圈状态
uint8_t get_coil(uint16_t addr)
{
    if (addr < MODBUS_COILS_MAX) {
        return coils[addr];
    }
    return 0;
}

// 设置线圈状态
void set_coil(uint16_t addr, uint8_t value)
{
    if (addr < MODBUS_COILS_MAX) {
        coils[addr] = value ? 1 : 0;
    }
}

// 获取离散输入状态
uint8_t get_discrete_input(uint16_t addr)
{
    if (addr < MODBUS_DISCRETE_INPUTS_MAX) {
        return discrete_inputs[addr];
    }
    return 0;
}

2.4 主函数和初始化

// main.c
#include "main.h"
#include "lwip.h"
#include "modbus_tcp.h"

// 全局网络接口
struct netif gnetif;

int main(void)
{
    // HAL初始化
    HAL_Init();
  
    // 系统时钟配置
    SystemClock_Config();
  
    // 初始化GPIO、ETH等外设
    MX_GPIO_Init();
    MX_ETH_Init();
  
    // 初始化LWIP
    MX_LWIP_Init();
  
    // 初始化Modbus数据区
    modbus_data_init();
  
    // 初始化Modbus TCP服务器
    modbus_tcp_init();
  
    // 主循环
    while (1) {
        // 处理以太网输入
        ethernetif_input(&gnetif);
      
        // 处理超时
        sys_check_timeouts();
      
        // 其他应用任务
        // ...
      
        // 短暂延时
        HAL_Delay(1);
    }
}

// 以太网初始化函数(由STM32CubeMX生成)
void MX_ETH_Init(void)
{
    // 以太网外设初始化代码
    // ...
}

// LWIP初始化函数
void MX_LWIP_Init(void)
{
    ip_addr_t ipaddr, netmask, gw;
  
    // 初始化IP地址(可以根据需要配置为静态或DHCP)
#if LWIP_DHCP
    ip_addr_set_zero_ip4(&ipaddr);
    ip_addr_set_zero_ip4(&netmask);
    ip_addr_set_zero_ip4(&gw);
#else
    IP4_ADDR(&ipaddr, 192, 168, 1, 10);
    IP4_ADDR(&netmask, 255, 255, 255, 0);
    IP4_ADDR(&gw, 192, 168, 1, 1);
#endif
  
    // 添加网络接口
    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);
  
    // 注册默认网络接口
    netif_set_default(&gnetif);
  
    // 启用网络接口
    netif_set_up(&gnetif);
  
#if LWIP_DHCP
    // 启动DHCP客户端
    dhcp_start(&gnetif);
#endif
}

3. 配置和优化建议

3.1 LWIP配置 (lwipopts.h)

// 重要配置选项
#define LWIP_TCP 1
#define TCP_MSS 1460
#define TCP_SND_BUF (4 * TCP_MSS)
#define TCP_WND (4 * TCP_MSS)
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define LWIP_SOCKET 0
#define MEM_SIZE (16000)
#define PBUF_POOL_SIZE (24)
#define PBUF_POOL_BUFSIZE (1524)
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1

3.2 性能优化建议

  1. 内存管理

    • 根据实际连接数调整 MEM_SIZEPBUF_POOL_SIZE
    • 使用内存池管理Modbus响应缓冲区
  2. 连接管理

    • 实现连接超时机制,自动关闭空闲连接
    • 限制最大连接数,防止资源耗尽
  3. 数据处理

    • 使用DMA进行以太网数据传输
    • 优化Modbus数据处理函数,减少内存拷贝
  4. 错误处理

    • 实现完整的异常处理机制
    • 添加日志记录功能,便于调试

4. 测试和验证

4.1 测试工具

  • 使用Modbus TCP客户端工具(如ModScan32、qModMaster等)
  • 使用网络调试工具(如Wireshark)捕获和分析数据包

4.2 测试步骤

  1. 验证网络连接和IP配置
  2. 测试基本Modbus功能(读取保持寄存器)
  3. 测试所有支持的功能码
  4. 测试异常情况处理(非法地址、非法值等)
  5. 测试多连接并发处理能力
  6. 测试长时间运行的稳定性

5. 部署注意事项

  1. 安全性

    • 考虑添加访问控制列表(ACL)
    • 实现连接速率限制,防止DoS攻击
  2. 可靠性

    • 添加看门狗机制,确保系统稳定性
    • 实现连接异常恢复机制
  3. 可维护性

    • 添加诊断功能,便于故障排查
    • 实现远程配置和管理接口

这个实施方案提供了一个完整的Modbus TCP服务器框架,可以根据具体应用需求进行扩展和优化。代码已经考虑了STM32F407的资源限制和LWIP协议栈的特性,可以直接在目标硬件上运行。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1488

主题

12949

帖子

55

粉丝
快速回复 在线客服 返回列表 返回顶部