【HAL库】STM32CubeMX开发----STM32F407----LAN8720A----移植FreeModbus实现ModbusTCP

news2024/9/23 11:24:45

前言

本次实验以 STM32F407VET6 芯片为MCU,使用 25MHz 外部时钟源。
以太网PHY层芯片为 LAN8720A,移植FreeModbus实现ModbusTCP网口通信。
具体内容参考文章:【HAL库】STM32CubeMX开发----STM32F407----ETH+LAN8720A+LWIP----ping通

本次移植FreeModbus中的TCP功能,做客户端(从机),实现网口TCP-Modbus通信。

一、FreeModbus源码下载

FreeModbus源码下载链接:https://www.embedded-experts.at/en/freemodbus-downloads/

点击下载
在这里插入图片描述

源码压缩包如下:

在这里插入图片描述

二、移植FreeModbus源码----新建TCP功能文件

本次实验,要实现FreeModbus的TCP功能,新建一个 FreeModbus_TCP 文件夹,将需要的文件都移植,具体文件移植如下:

步骤1

打开 freemodbus-v1.6 文件夹,点击 modbus 文件夹。

在这里插入图片描述

步骤2

modbus 文件夹中的全部文件移植到新建的 FreeModbus_TCP 文件夹中。
在这里插入图片描述

步骤3

freemodbus-v1.6\demo\STR71XTCP中的 port 文件,移植到新建的 FreeModbus_TCP 文件夹中。

在这里插入图片描述

步骤4

移植最终结果,新建的 FreeModbus_TCP 文件夹内容如下:

在这里插入图片描述

三、移植FreeModbus源码----TCP功能文件 移植 到STM32工程文件中。

本次使用的是能够实现以太网ping通的STM32F407工程。
工程源码:STM32F407-ETH+LAN8720A+LWIP-无操作系统-ping通----程序源码

步骤1

FreeModbus_TCP 文件夹复制到工程文件中。

在这里插入图片描述

步骤2

使用keil5打开工程,将FreeModbus_TCP 文件夹中的文件导入。

在这里插入图片描述

选择FreeModbus_TCP 文件夹中的 functions 文件夹的全部.c文件。

在这里插入图片描述

选择FreeModbus_TCP 文件夹中的 port 文件夹的全部.c文件。

在这里插入图片描述
选择FreeModbus_TCP 文件夹中的 tcp 文件夹的全部.c文件。
在这里插入图片描述
选择FreeModbus_TCP 文件夹中的mb.c文件。
在这里插入图片描述

最终结果如下

在这里插入图片描述
在这里插入图片描述

步骤3

点击魔法棒,选择 C/C++,添加文件路径

在这里插入图片描述

添加文件路径

在这里插入图片描述

添加结果如下

在这里插入图片描述
编译程序,会出现一些错误,下面编辑程序,消除错误。

四、移植FreeModbus源码----编辑程序

步骤1:修改 mbconfig.h

关闭 MB_ASCII 和 MB_RTU,打开 MB_TCP

在这里插入图片描述

步骤2:修改 port.h

将27行:#include “71x_type.h” 注释掉。
将39行到46行的注释,打开。
具体代码如下:

在这里插入图片描述

步骤3:修改 portevent.c

将以下程序,替换原来的程序。

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eQueuedEvent;
static BOOL     xEventInQueue;

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
    xEventInQueue = FALSE;
    return TRUE;
}

BOOL
xMBPortEventPost( eMBEventType eEvent )
{
    xEventInQueue = TRUE;
    eQueuedEvent = eEvent;
    return TRUE;
}

BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    BOOL            xEventHappened = FALSE;

    if( xEventInQueue )
    {
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
        xEventHappened = TRUE;
    }
    return xEventHappened;
}

步骤4:修改 porttcp.c

在24行,添加 #include “string.h”
在43行,添加 #define NETCONN_COPY 0x01

在这里插入图片描述

将120行和135行的 vPortEnterCritical( );,注释掉。

在这里插入图片描述
将148行的 vMBPortEventClose( ); 注释掉。
在这里插入图片描述

步骤5:修改 mb.c

将232行的 ENTER_CRITICAL_SECTION( );,注释掉。
将261行的 EXIT_CRITICAL_SECTION( );,注释掉。

在这里插入图片描述

步骤6:新建文件

User_modbus_TCP.c文件

#include <stdio.h>
#include <string.h>
#include "User_modbus_TCP.h"
#include "mb.h"
#include "mbutils.h"

void ModbusTCPInit(void)
{
    eMBTCPInit(MODBUS_TCP_PORT);
    eMBEnable();
}

void ModbusTCPDeInit(void)
{
    eMBDisable();
    eMBClose();
}

void ModbusTCPMain(void)
{
    if (MB_ENOERR != eMBPoll())
    {
        ModbusTCPDeInit();
        ModbusTCPInit();
    }
}

//线圈
#define REG_Coils_START   1
#define REG_Coils_SIZE    10

uint8_t  Coils_Data[REG_Coils_SIZE] = {1,1,0,1,0,0,1,1,1,0};

/**
 * @brief: 读线圈---01,写线圈---05
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNCoils      线圈数量
 * @param eMode         读写模式
 * @return eMBErrorCode 错误码
 */
eMBErrorCode eMBRegCoilsCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{
    uint16_t i = 0,byteOffset=0,bitOffset=0,RegIndex = usAddress - REG_Coils_START-1;
    if ((usAddress >= REG_Coils_START)&&(usAddress + usNCoils <= REG_Coils_START + REG_Coils_SIZE+1))
    {
        if (MB_REG_READ == eMode)
        {
          for(i=0;i<usNCoils;i++)
          {
              byteOffset = i / 8;
              bitOffset = i % 8;
              xMBUtilSetBits(&pucRegBuffer[byteOffset], bitOffset, 1, Coils_Data[RegIndex+i]);
          }
        }
        else
        {
          for(i=0;i<usNCoils;i++)
          {
              byteOffset = i / 8;
              bitOffset = i % 8;
              Coils_Data[RegIndex+i]=xMBUtilGetBits(&pucRegBuffer[byteOffset], bitOffset, 1);
          }
        }
    }
    else
    {
        return MB_ENOREG;
    }
    
    return MB_ENOERR;
}

 //离散寄存器
#define REG_DISCRETE_START   10
#define REG_DISCRETE_SIZE    20

uint8_t  Discrete_Data[REG_DISCRETE_SIZE] = {1,1,0,1,0,0,1,1,1,0,1,0,0,1};

/**
 * @brief:读离散寄存器---02
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNDiscrete   寄存器个数
 * @return eMBErrorCode 返回错误码
 */
eMBErrorCode eMBRegDiscreteCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
    uint16_t i = 0,byteOffset=0,bitOffset=0,RegIndex = usAddress - REG_DISCRETE_START-1;
  
    if ((usAddress >= REG_DISCRETE_START)&&(usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE+1))
    {
      for(i=0;i<usNDiscrete;i++)
      {
          byteOffset = i / 8;
          bitOffset = i % 8;
          xMBUtilSetBits(&pucRegBuffer[byteOffset], bitOffset, 1, Discrete_Data[RegIndex+i]);
      }
    }
    else
    {
        return MB_ENOREG;
    }

    return MB_ENOERR;
}

//保持寄存器
#define REG_HOLDING_REGISTER_START   10
#define REG_HOLDING_REGISTER_SIZE    30

uint16_t  Holding_Data[REG_HOLDING_REGISTER_SIZE] = 
{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12};

/**
 * @brief: 读保持寄存器---03,写保持寄存器---06
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNRegs       寄存器个数
 * @param eMode         读写模式
 * @return eMBErrorCode 返回错误码
 */

eMBErrorCode eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
    uint16_t i = 0,RegIndex = usAddress - REG_HOLDING_REGISTER_START-1;
  
    if ((usAddress >= REG_HOLDING_REGISTER_START )&&(usAddress + usNRegs <= REG_HOLDING_REGISTER_START  + REG_HOLDING_REGISTER_SIZE+1))
    {
        if (MB_REG_READ == eMode)//读
        {
          for(i=0;i<usNRegs;i++)
          {
            pucRegBuffer[i*2] = (UCHAR)(Holding_Data[RegIndex+i]>>8);
            pucRegBuffer[i*2+1] = (UCHAR)Holding_Data[RegIndex+i];
          }
        }
        else//写
        {
          for(i=0;i<usNRegs;i++)
          {
            Holding_Data[RegIndex+i]=(pucRegBuffer[i*2]<<8)|(pucRegBuffer[i*2+1]);
          }
        }
    }
    else
    {
        return MB_ENOREG;
    }

    return MB_ENOERR;
}

//输入寄存器
#define REG_INPUT_REGISTER_START    1
#define REG_INPUT_REGISTER_SIZE    20

uint16_t  Input_Data[REG_DISCRETE_SIZE] = 
{100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119};
/**
 * @brief: 读输入寄存器---04
 *
 * @param pucRegBuffer  缓存指针
 * @param usAddress     起始地址
 * @param usNRegs       寄存器个数
 * @return eMBErrorCode 返回错误码
 */
eMBErrorCode eMBRegInputCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
    uint16_t i = 0,RegIndex = usAddress - REG_INPUT_REGISTER_START-1;
    if ((usAddress >= REG_INPUT_REGISTER_START)&&(usAddress + usNRegs <= REG_INPUT_REGISTER_START + REG_INPUT_REGISTER_SIZE+1))
    {
        for(i=0;i<usNRegs;i++)
        {
          pucRegBuffer[i*2] = (UCHAR)(Input_Data[RegIndex+i]>>8);
          pucRegBuffer[i*2+1] = (UCHAR)Input_Data[RegIndex+i];
        }
    }
    else
    {
        return MB_ENOREG;
    }
    
    return MB_ENOERR;
}

/**********************printf重定向****************************/
//取消ARM的半主机工作模式
#pragma import(__use_no_semihosting)//标准库需要的支持函数                 
struct __FILE 
{ 
    int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) //定义_sys_exit()以避免使用半主机模式
{ 
    x = x; 
} 

int fputc(int ch, FILE *f)
{  
    return ch;
}

需要加上printf重定向(关于printf重定向的文章),不加上,程序就会卡死,我也不知道什么原因,有哪位大神知道,可以评论说一下,非常感谢。

User_modbus_TCP.h文件

#ifndef __User_modbbus_TCP_H__
#define	__User_modbbus_TCP_H__

#include "main.h"

#define MODBUS_TCP_PORT 4002

extern void ModbusTCPInit(void);
extern void ModbusTCPMain(void);

#endif

步骤7:在主函数中调用ModbusTCP

在主函数初始化中,调用 ModbusTCPInit();
在主函数while运行中,调用 ModbusTCPMain();

在这里插入图片描述

五、移植FreeModbus源码----测试验证

使用 Modbus Poll 软件,测试 ModbusTCP 功能。
Modbus Poll 软件----下载和安装

步骤1:打开Modbus Poll 软件

在这里插入图片描述

步骤2:打开连接配置窗口,配置连接

点击菜单栏"Connection"->“Connect…”(或者按快捷键F3)弹出连接配置窗口。

在这里插入图片描述
选择 ModbusTCP/IP,然后配置IP地址,然后选择端口,其他时间都是默认值,然后点击 OK

在这里插入图片描述

步骤3:配置窗口信息

点击"Setup"->“Read/Write Definition…”,或者按快捷键F8。

在这里插入图片描述

设置从机地址,功能码,起始地址,寄存器数量等信息,然后点击OK。

在这里插入图片描述

步骤4:测试结果

功能码 01,读取线圈,测试结果与程序一致。

在这里插入图片描述

功能码 02,读取离散寄存器,测试结果与程序一致。

在这里插入图片描述

功能码 03,读取保持寄存器,测试结果与程序一致。

在这里插入图片描述

功能码 04,读取输入寄存器,测试结果与程序一致。

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/834231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于开源模型搭建实时人脸识别系统(三):人脸关键点、对齐模型概览与模型选型

续 基于开源模型搭建实时人脸识别系统&#xff08;二&#xff09;&#xff1a;人脸检测概览与模型选型_CodingInCV的博客-CSDN博客 摘要 人脸对齐&#xff08;face alignment&#xff09;或者人脸关键点&#xff08;face alignment&#xff09;是定位人脸上的关键点&#xff…

chatGLM 本地部署(windows+linux)

chatGLM算是个相对友好的模型&#xff0c;支持中英文双语的对话交流&#xff0c;清华出的 我的教程无需特别的网络设置&#xff0c;不过部分情况因为国内网络速度慢&#xff0c;需要反复重复 chatGLM github地址 一、硬件需求 N卡8G显存以上&#xff0c;最好16G以上&#xff…

redis 集群 1:李代桃僵 —— Sentinel

目前我们讲的 Redis 还只是主从方案&#xff0c;最终一致性。读者们可思考过&#xff0c;如果主节点凌晨 3 点突发宕机怎么办&#xff1f;就坐等运维从床上爬起来&#xff0c;然后手工进行从主切换&#xff0c;再通知所有的程序把地址统统改一遍重新上线么&#xff1f;毫无疑问…

电路暂态过程

本文仅提取了课程的部分内容&#xff0c;原视频课程如下&#xff1a;姜三勇《电工学》暂态过程 电路暂态过程——产生的原因&#xff1a; 1、内部原因&#xff1a;电路内部含有储能元件&#xff08;如&#xff1a;电容、电感&#xff09;&#xff0c;其中存储的能量不能发生改…

vscode如何退出/切换 github 账号

退出/切换 github 账号 左下角点击头像按钮&#xff0c;选择注销&#xff0c;然后再重新登录

GateOne任意文件读取

人的生命力&#xff0c;是在痛苦的煎熬中强大起来的。 简介 GateOne 是一款使用 HTML5 技术编写的网页版 SSH 终端模拟器。基于现代的 HTML5技术&#xff0c;无需任何浏览器插件、支持多个 SSH 进程、可以嵌入到其他任意应用程序中、支持使用 JavaScript&#xff0c;Python 甚…

SaaS销售打法:常规有效的四种SaaS营销策略,落地执行增长突破

在外面常规的认知中&#xff1a;SaaS营销与常规的营销其实没有什么不同&#xff0c;都在遵循着传统的推广和销售策略&#xff0c;简单来说就是将自己的产品卖给更多的用户&#xff0c;从而换取利润。 SaaS营销策略 但是SaaS产品与其他产品不同的地方在于它本身是没有实体的&am…

如何基于eBPF实现跨语言、无侵入的流量录制?

测试是产品发布上线的一个重要环节&#xff0c;但随着业务规模和复杂度不断提高&#xff0c;每次上线需要回归的功能越来越多&#xff0c;给测试工作带来了巨大的压力。在这样的大背景下&#xff0c;越来越多的团队开始使用流量回放对服务进行回归测试。 在建设流量回放能力之前…

一台电脑给另外一台电脑共享网络

这里写自定义目录标题 有网的电脑上操作一根网线连接两台电脑没网的电脑上 有网的电脑上操作 右键->属性->共享 如同选择以太网&#xff0c;勾选。确认。 一根网线连接两台电脑 没网的电脑上 没网的电脑为mips&麒麟V10 新增个网络配置ww&#xff0c;设置如下。 …

产品设计中的小体验:带来大问题解决之道

在激烈的市场竞争中&#xff0c;产品的体验设计已成为区分优劣的重要标志。用户不仅仅关注产品的核心功能&#xff0c;更重视产品在使用过程中的舒适度、易用性和情感体验。产品设计中的细节体验&#xff0c;看似微不足道&#xff0c;却往往能带来意想不到的效果。这是因为&…

线上服务挂了 3 分钟

在一个风和日丽的下午&#xff0c;刚打算饮茶&#xff0c;线上就开始报警了&#xff0c;一看情况网关报 500 了。。 网关&#xff08;用的是Spring Cloud Gateway&#xff09;挂了可还行&#xff0c;这可是对外的们&#xff0c;门没了岂不是所有请求都进不来了&#xff01; 说…

Linux 系统编程 开篇/ 文件的打开/创建

从本节开始学习关于Linux系统编程的知识&#xff01; 学习Linux的系统编程有非常多的知识点&#xff0c;在应用层面&#xff0c;很重要的一点就是学习如何“用代码操作文件来实现文件创建&#xff0c;打开&#xff0c;编辑等自动化执行” 那如何自动化实现对文件的创建&#…

成本控制策略:加强企业安全

我们生活在一个不确定的时代。大多数经济学家预测&#xff0c;今年全球经济将继续放缓&#xff0c;亚太地区当然也不会逆势而上。 在供应链问题、大规模裁员、高通胀和高利率之间&#xff0c;我们毫不奇怪地看到大多数公司和行业采取谨慎态度&#xff0c;战略、增长计划和预算…

使用docker安装wordpress详细教程及出现数据库无法连接问题解决方法

1.获取wordpress镜像 docker pull wordpress 2.创建wordpress 的容器 a.创建wordpress的文件镜像卷文件夹 mkdir wordpress b.创建wordpress镜像 docker run --name wp -p8080:80 -v /home/wordpress/:/var/www/html -d wordpress c.查看容器运行情况 3.在本地或者其他服务器创…

AutoDL从0到1搭建stable-diffusion-webui

前言 AI绘画当前非常的火爆&#xff0c;随着Stable diffusion&#xff0c;Midjourney的出现将AI绘画推到顶端&#xff0c;各大行业均受其影响&#xff0c;离我们最近的AI绘画当属Stable diffusion&#xff0c;可本地化部署&#xff0c;只需电脑配备显卡即可完成AI绘画工作&…

Go语言并发编程(千锋教育)

Go语言并发编程&#xff08;千锋教育&#xff09; 视频地址&#xff1a;https://www.bilibili.com/video/BV1t541147Bc?p14 作者B站&#xff1a;https://space.bilibili.com/353694001 源代码&#xff1a;https://github.com/rubyhan1314/go_goroutine 1、基本概念 1.1、…

宋老板教我做人--背后少说别人

宋老板教我做人——背后少说别人 2000年&#xff5e;2004年间发生的事 让我很难忘&#xff0c;让我长记性 趣讲大白话&#xff1a;是不是传说中的&#xff0c;发自内心的善良&#xff1f; 【趣讲信息科技246期】 **************************** 真实故事1&#xff1a; 2000年5月…

Embedding入门介绍以及为什么Embedding在大语言模型中很重要

Embeddings技术简介及其历史概要 在机器学习和自然语言处理中&#xff0c;embedding是指将高维度的数据&#xff08;例如文字、图片、音频&#xff09;映射到低维度空间的过程。embedding向量通常是一个由实数构成的向量&#xff0c;它将输入的数据表示成一个连续的数值空间中…

【python】绘图代码模板

【python】绘图代码模板 pandas.DataFrame.plot( )画图函数Seaborn绘图 -数据可视化必备导入数据集可视化统计关系使用Seaborn绘制散点图抖动图箱线图小提琴图Pointplot群图 可视化数据集的分布绘制单变量分布柱状图直方图 绘制双变量分布Hex图KDE 图可视化数据集中的成对关系 …

【数据结构与算法】线索化二叉树

线索化二叉树 n 个节点的二叉链表中含有 n 1 【公式 2n - (n - 1) n 1】个空指针域。利用二叉链表中的空指针域&#xff0c;存放指向该节点在某种遍历次序下的前驱和后继节点的指针&#xff08;这种附加的指针称为“线索”&#xff09;。这种加上了线索的二叉链表称为线索链…