foreword
In this experiment, the STM32F407VET6 chip is used as the MCU, and a 25MHz external clock source is used.
The Ethernet PHY layer chip is LAN8720A, and FreeModbus is transplanted to realize ModbusTCP network port communication.
Refer to the article for specific content: [HAL library] STM32CubeMX development----STM32F407----ETH+LAN8720A+LWIP----ping
This time transplant the TCP function in FreeModbus, as a client (slave), and realize the network port TCP-Modbus communication.
1. FreeModbus source code download
FreeModbus source code download link: https://www.embedded-experts.at/en/freemodbus-downloads/
click to download
The source code package is as follows:
2. Transplant FreeModbus source code----create new TCP function file
In this experiment, to realize the TCP function of FreeModbus, create a new FreeModbus_TCP folder, and transplant all the required files. The specific files are as follows:
step 1
Open the freemodbus-v1.6 folder and click the modbus folder.
step 2
Migrate all the files in the modbus folder to the newly created FreeModbus_TCP folder.
step 3
Transplant the port file in freemodbus-v1.6\demo\STR71XTCP to the newly created FreeModbus_TCP folder.
step 4
The final result of transplantation, the content of the newly created FreeModbus_TCP folder is as follows:
3. Transplant FreeModbus source code ---- TCP function files are transplanted into STM32 project files.
This time, the STM32F407 project that can achieve Ethernet ping is used.
Project source code: STM32F407-ETH+LAN8720A+LWIP-no operating system-ping----program source code
step 1
Copy the FreeModbus_TCP folder to the project file.
step 2
Use keil5 to open the project and import the files in the FreeModbus_TCP folder.
Select all .c files in the functions folder in the FreeModbus_TCP folder.
Select all .c files of the port folder in the FreeModbus_TCP folder.
Select all .c files of the tcp folder in the FreeModbus_TCP folder. Select the mb.c file in the FreeModbus_TCP folder.
The final result is as follows
step 3
Click the magic wand , select C/C++ , and add the file path .
add file path
Add the result as follows
Compile the program, there will be some errors, edit the program below to eliminate the errors.
4. Transplant FreeModbus source code----editing program
Step 1: Modify mbconfig.h
Turn off MB_ASCII and MB_RTU, and turn on MB_TCP .
Step 2: Modify port.h
Comment out line 27: #include "71x_type.h" .
Open the comments from line 39 to line 46.
The specific code is as follows:
Step 3: Modify portevent.c
Replace the original program with the following program.
/* ----------------------- 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;
}
Step 4: Modify porttcp.c
On line 24, add #include "string.h" .
On line 43, add #define NETCONN_COPY 0x01 .
Comment out vPortEnterCritical( ); on lines 120 and 135 .
Comment out vMBPortEventClose( ); on line 148 .
Step 5: Modify mb.c
Comment out ENTER_CRITICAL_SECTION( ); on line 232 . Comment out EXIT_CRITICAL_SECTION( );
on line 261 .
Step 6: Create a new file
User_modbus_TCP.c file
#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;
}
Need to add printf redirection ( article about printf redirection ), if not added, the program will freeze, I don’t know why, whoever knows, you can comment, thank you very much.
User_modbus_TCP.h file
#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
Step 7: Call ModbusTCP in the main function
In the main function initialization, call ModbusTCPInit();
in the main function while running, call ModbusTCPMain();
5. Transplant FreeModbus source code----test verification
Use Modbus Poll software to test the ModbusTCP function.
Modbus Poll Software----Download and Install
Step 1: Open the Modbus Poll software
Step 2: Open the connection configuration window and configure the connection
Click on the menu bar "Connection"->"Connect..." (or press the shortcut key F3) to pop up the connection configuration window.
Select ModbusTCP/IP , then configure the IP address , then select the port , the other times are default values, and then click OK .
Step 3: Configure window information
Click "Setup" -> "Read/Write Definition...", or press the shortcut key F8.
Set the slave address, function code, start address, number of registers and other information, and then click OK.