当前位置:   article > 正文

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

stm32cube移植freemodbus

前言

本次实验以 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

步骤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    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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206

需要加上printf重定向(关于printf重定向的文章),不加上,程序就会卡死,我也不知道什么原因,有哪位大神知道,可以评论说一下,非常感谢。

User_modbus_TCP.h文件

#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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

步骤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,读取输入寄存器,测试结果与程序一致。

在这里插入图片描述


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/743232
推荐阅读
相关标签
  

闽ICP备14008679号