当前位置: 首页 > 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,读取输入寄存器,测试结果与程序一致。

在这里插入图片描述


http://www.lryc.cn/news/107701.html

相关文章:

  • 11-矩阵(matrix)_方阵_对称阵_单位阵_对角阵
  • AWS多账户单点登录 IAM Identity Center(AWS SSO)
  • 实验2-3-3 求奇数分之一序列前N项和 (15 分)
  • 关于Android studio中的自动化测试脚本UiAutomator框架以及UiAutomatorViewer工具的使用——项目案例
  • OA办公自动化系统设计与实现(论文+源码)_kaic
  • ansible——playbook
  • DDS中间件设计
  • aws的EC2云服务器自己操作记录
  • 基本ACL 和高级ACL配置
  • 【uniapp 报错 Cannot read properties of null (reading ‘offsetWidth‘)解决办法】
  • 6.s081/6.1810(Fall 2022)Lab2: System calls
  • Git在VSCode中的使用
  • 【双指针_移动零_C++】
  • 【网络安全】网络安全威胁实时地图 - 2023
  • 视频过大如何压缩变小?文件压缩技巧分享
  • 组合模式(Composite)
  • grid map学习笔记3之详解grid_map_pcl库实现point cloud点云转换成grid map栅格地图
  • ebpf开发问题汇总
  • 认识 mysql 命令
  • IK(Inverse Kinematics,逆运动学)
  • Cadence 小技巧系列(持续更新)
  • 【unity】Pico VR 开发笔记(基础篇)
  • 竞争之王CEO商战课,聚百家企业在京举行
  • 【shell】获取ping的时延数据并分析网络情况及常用命令学习
  • 石子合并一章通(环形石子合并,四边形不等式,GarsiaWachs算法)(内附封面)
  • Docker快速入门笔记
  • 【Excel】记录Match和Index函数的用法
  • SolidUI社区-从开源社区角度思考苹果下架多款ChatGPT应用
  • 插入排序讲解
  • 杀疯了的ChatGPT——开启AI智能交流新纪元 「文末有彩蛋」