【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,使用 25MHz 外部时钟源。 以太网PHY层芯片为 LAN8720A,移植FreeModbus实现ModbusTCP网口通信。 具体内容参考文章:【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账户,登陆麻烦且不安全,SSO单点功能并且外部身份提供者 — 如果您要管理外部身份提供者(IdP)(例如 Okta 或 Active Directory)中的用户。 官方文档:https://docs.aws.amazon.c…...
实验2-3-3 求奇数分之一序列前N项和 (15 分)
实验2-3-3 求奇数分之一序列前N项和 (15 分) 本题要求编写程序,计算序列 1 1/3 1/5 … 的前N项之和。 输入格式: 输入在一行中给出一个正整数N。 输出格式: 在一行中按照“sum S”的格式输出部分和的值S,精确到小数点后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
摘要 随着信息化建设的日益深入,无论是政府还是企事业单位,部门之间的信息沟通与协调工作越来越重要。人们迫切需要一个能充分利用网络优势,并可以管理企业的各种重要信息的软件平台,利用该平台快速建立自己的信息网络和办公管理系…...
ansible——playbook
playbook playbook是剧本的意思 通过 task 调用 ansible 的模块将多个 play 组织在一 个playbook中运行。 playbook本身由以下各部分组成: Tasks: 任务,即调用模块完成的某操作 Variables: 变量 Templates: 模板 Handlers: 处理器,当某条件…...

DDS中间件设计
OpenDDS、FastDDS数据分发服务中间件设计 软件架构 应用层DDS层RTPS层传输层 软件层次 FastDDS整体架构如下,这里可以看到DDS和RTPS的关系。另外缺少一部分IDL(统一描述语言),其应该是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而引起的。解决方法如下: 检查代码中是否有访问了空值的情况,比如变量未初始化或者传入了空值参数或者事件未定义。 在操作元素之前,确保元素已经被正确加载。可以使用如下方法: <…...

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图像化界面进行项目初始化(git init) 2. Git图形化界面对文件进行操作 当我们创建一个文件时,该文件后面有一个U,表示文件未跟踪。 我们在管理工具中输入日志并提交代码,相当于做了两件事,将文件由“…...

【双指针_移动零_C++】
题目解析 移动零 nums [0,1,0,3,12] [1,3,12,0,0]算法原理 数组划分(数组分块) 双指针算法(利用数组下标来充当指针)使用两个指针的作用: cur指针:从左往右扫描数组,就是遍历数组。 dest指针…...

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

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

组合模式(Composite)
组合模式是一种结构型设计模式,主要用来将多个对象组织成树形结构以表示“部分-整体”的层次结构,因此该模式也称为“部分-整体”模式。简言之,组合模式就是用来将一组对象组合成树状结构,并且能像使用独立对象一样使用它们。 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的描述符,然后用bpf_map_reuse_fd函数来在不同program之间复用 kernel 与 user space之间 需要pin the BPF MAP to the BPF Virtual File System (VFS),来持久化存储,否则如果map用不到会被destory 引用…...
认识 mysql 命令
文章目录 1.简介2.选项3.子命令4.小结参考文献 1.简介 mysql 是 MySQL 的命令行客户端工具,用于连接到 MySQL 服务器并执行 SQL 语句。 它支持交互式和非交互式两种使用方式。以交互方式使用时,查询结果以 ASCII 表格式呈现。 当以非交互方式使用时&am…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

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

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...

【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...