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

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


前言

本次实验以 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    10uint8_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    20uint8_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    30uint16_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    20uint16_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 4002extern 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,读取输入寄存器,测试结果与程序一致。

在这里插入图片描述


相关文章:

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

前言 本次实验以 STM32F407VET6 芯片为MCU&#xff0c;使用 25MHz 外部时钟源。 以太网PHY层芯片为 LAN8720A&#xff0c;移植FreeModbus实现ModbusTCP网口通信。 具体内容参考文章&#xff1a;【HAL库】STM32CubeMX开发----STM32F407----ETHLAN8720ALWIP----ping通 本次移植…...

11-矩阵(matrix)_方阵_对称阵_单位阵_对角阵

矩阵及其运算 [ a 11 ⋯ a 1 n ⋯ ⋯ ⋯ a m 1 ⋯ a m n ] \begin{bmatrix} a_{11} & \cdots & a_{1n} \\ \cdots & \cdots & \cdots \\ a_{m1} & \cdots & a_{mn} \\ \end{bmatrix} ​a11​⋯am1​​⋯⋯⋯​a1n​⋯amn​​ ​ 矩阵就是二维数组&…...

AWS多账户单点登录 IAM Identity Center(AWS SSO)

需求场景 多个aws账户&#xff0c;登陆麻烦且不安全&#xff0c;SSO单点功能并且外部身份提供者 — 如果您要管理外部身份提供者&#xff08;IdP&#xff09;&#xff08;例如 Okta 或 Active Directory&#xff09;中的用户。 官方文档&#xff1a;https://docs.aws.amazon.c…...

实验2-3-3 求奇数分之一序列前N项和 (15 分)

实验2-3-3 求奇数分之一序列前N项和 &#xff08;15 分&#xff09; 本题要求编写程序&#xff0c;计算序列 1 1/3 1/5 … 的前N项之和。 输入格式: 输入在一行中给出一个正整数N。 输出格式: 在一行中按照“sum S”的格式输出部分和的值S&#xff0c;精确到小数点后6位。…...

关于Android studio中的自动化测试脚本UiAutomator框架以及UiAutomatorViewer工具的使用——项目案例

加入依赖 implementation androidx.test.uiautomator:uiautomator:2.2.0创建CalcActivity页 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"...

OA办公自动化系统设计与实现(论文+源码)_kaic

摘要 随着信息化建设的日益深入&#xff0c;无论是政府还是企事业单位&#xff0c;部门之间的信息沟通与协调工作越来越重要。人们迫切需要一个能充分利用网络优势&#xff0c;并可以管理企业的各种重要信息的软件平台&#xff0c;利用该平台快速建立自己的信息网络和办公管理系…...

ansible——playbook

playbook playbook是剧本的意思 通过 task 调用 ansible 的模块将多个 play 组织在一 个playbook中运行。 playbook本身由以下各部分组成&#xff1a; Tasks: 任务&#xff0c;即调用模块完成的某操作 Variables: 变量 Templates: 模板 Handlers: 处理器&#xff0c;当某条件…...

DDS中间件设计

OpenDDS、FastDDS数据分发服务中间件设计 软件架构 应用层DDS层RTPS层传输层 软件层次 FastDDS整体架构如下&#xff0c;这里可以看到DDS和RTPS的关系。另外缺少一部分IDL&#xff08;统一描述语言&#xff09;&#xff0c;其应该是Pub、Sub的反序列化、序列化工具。 在RT…...

aws的EC2云服务器自己操作记录

亚马逊官网有免费试用1年的服务器 以下内容参考 1. 启动生成实例 1.1 创建实例时需要生成 使用的默认的 Debian 和 一个.pem后缀的秘钥 1.2 网上下一个Mobaxterm ,实例名是公有 IPv4 DNS 地址 ,使用SSH连接,登录名是admin 1.3 登录进去后 输入用户名 admin 后进去,sudo …...

基本ACL 和高级ACL配置

基本ACL 一、要求 1.全网可达 2.在1的基础上使PC1不能访问PC2 二、思路 1.通过写静态的方式使全网可达 2.配置acl主要是拒绝源IP的访问 3.在靠近目标的地方配置acl及使用 三、操作配置 1.IP及静态配置 [r1]int g0/0/0 [r1-GigabitEthernet0/0/0]ip add 123.0.0.1 24 [r…...

【uniapp 报错 Cannot read properties of null (reading ‘offsetWidth‘)解决办法】

该错误通常是由于访问了一个空值的offsetWidth而引起的。解决方法如下&#xff1a; 检查代码中是否有访问了空值的情况&#xff0c;比如变量未初始化或者传入了空值参数或者事件未定义。 在操作元素之前&#xff0c;确保元素已经被正确加载。可以使用如下方法&#xff1a; <…...

6.s081/6.1810(Fall 2022)Lab2: System calls

文章目录 前言其他篇章参考链接0. 前置准备1. System call tracing (moderate)1.1 简单分析1.2 Hint 11.3 Hint 21.4 Hint 31.5 Hint 41.6 Hint 51.7 测试 2. Sysinfo (moderate)2.1 声明2.2 实现2.2.1 框架2.2.2 用户态与内核态交互2.2.3 计算空闲内存的大小2.2.4 计算非UNUSE…...

Git在VSCode中的使用

1.Git图像化界面进行项目初始化&#xff08;git init&#xff09; 2. Git图形化界面对文件进行操作 当我们创建一个文件时&#xff0c;该文件后面有一个U&#xff0c;表示文件未跟踪。 我们在管理工具中输入日志并提交代码&#xff0c;相当于做了两件事&#xff0c;将文件由“…...

【双指针_移动零_C++】

题目解析 移动零 nums [0,1,0,3,12] [1,3,12,0,0]算法原理 数组划分&#xff08;数组分块&#xff09; 双指针算法&#xff08;利用数组下标来充当指针&#xff09;使用两个指针的作用&#xff1a; cur指针&#xff1a;从左往右扫描数组&#xff0c;就是遍历数组。 dest指针…...

【网络安全】网络安全威胁实时地图 - 2023

文章目录 [TOC] ① 360 安全大脑360 APT全景雷达 ② 瑞星杀毒瑞星云安全瑞星网络威胁态势感知平台 ③ 比特梵德 Bitdefender④ 飞塔防火墙 FortiGuard⑤ 音墙网络 Sonicwall⑥ 捷邦 Check Point⑦ AO卡巴斯基实验室全球模拟隧道模拟 ⑧ 数字攻击地图⑨ Threatbutt互联网黑客攻击…...

视频过大如何压缩变小?文件压缩技巧分享

如何压缩视频是许多视频编辑者、视频上传者经常遇到的问题&#xff0c;如果你也遇到了这个问题&#xff0c;不用担心&#xff0c;下面将就给大家分享几个视频压缩方法&#xff0c;可以帮助大家轻松地压缩视频&#xff0c;同时保持视频的高清晰度和音频质量。 一、嗨格式压缩大师…...

组合模式(Composite)

组合模式是一种结构型设计模式&#xff0c;主要用来将多个对象组织成树形结构以表示“部分-整体”的层次结构&#xff0c;因此该模式也称为“部分-整体”模式。简言之&#xff0c;组合模式就是用来将一组对象组合成树状结构&#xff0c;并且能像使用独立对象一样使用它们。 Co…...

grid map学习笔记3之详解grid_map_pcl库实现point cloud点云转换成grid map栅格地图

文章目录 0 引言1 grid_map_pcl示例1.1 主要文件1.2 示例数据1.3 启动文件1.4 配置文件1.5 主要实现流程1.6 启动示例1.7 示例结果 2 D435i 点云生成栅格地图2.1 D435i 点云文件2.2 修改启动文件2.3 测试和结果2.4 修改配置文件2.5 重新测试和结果 0 引言 grid map学习笔记1已…...

ebpf开发问题汇总

不同Programs之间通信 用bpf_obj_get来获取MAP的描述符&#xff0c;然后用bpf_map_reuse_fd函数来在不同program之间复用 kernel 与 user space之间 需要pin the BPF MAP to the BPF Virtual File System (VFS),来持久化存储&#xff0c;否则如果map用不到会被destory 引用…...

认识 mysql 命令

文章目录 1.简介2.选项3.子命令4.小结参考文献 1.简介 mysql 是 MySQL 的命令行客户端工具&#xff0c;用于连接到 MySQL 服务器并执行 SQL 语句。 它支持交互式和非交互式两种使用方式。以交互方式使用时&#xff0c;查询结果以 ASCII 表格式呈现。 当以非交互方式使用时&am…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋&#xff0c;无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话&#xff0c;配置.bahs_profile后也能解决上下翻页这些&#xff0c;但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...