前言
本次实验以 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。