【HALライブラリ】STM32CubeMX開発----STM32F407----LAN8720A----FreeModbusを移植してModbusTCPを実現


序文

この実験では、STM32F407VET6チップが MCU として使用され、25MHz の外部クロック ソースが使用されます。
Ethernet PHY層チップはLAN8720Aで、FreeModbusを移植してModbusTCPネットワークポート通信を実現しています。
具体的な内容については、記事を参照してください: [HAL ライブラリ] STM32CubeMX 開発----STM32F407----ETH+LAN8720A+LWIP----ping

今回はFreeModbusのTCP機能をクライアント(スレーブ)として移植し、ネットワークポートTCP-Modbus通信を実現します。

1.FreeModbusソースコードのダウンロード

FreeModbus ソース コードのダウンロード リンク: https://www.embedded-experts.at/en/freemodbus-downloads/

クリックしてダウンロード
ここに画像の説明を挿入

ソースコードパッケージは次のとおりです。

ここに画像の説明を挿入

2. FreeModbus ソース コードの移植 ----新しい TCP 関数ファイルを作成します

今回の実験では、FreeModbus の TCP 機能を実現するために、 FreeModbus_TCPフォルダを新規作成し、必要なファイルをすべて移植します。

ステップ1

freemodbus-v1.6フォルダーを開き、 modbusフォルダーをクリックします。

ここに画像の説明を挿入

ステップ2

modbusフォルダー内のすべてのファイルを、新しく作成したFreeModbus_TCPフォルダーに移行します
ここに画像の説明を挿入

ステップ3

freemodbus-v1.6\demo\STR71XTCPにあるポートファイルを、新しく作成したFreeModbus_TCPフォルダーに移植します

ここに画像の説明を挿入

ステップ4

移植の最終結果、新しく作成されたFreeModbus_TCPフォルダーの内容は次のとおりです。

ここに画像の説明を挿入

3. FreeModbus ソース コードの移植 ---- TCP 関数ファイルが STM32 プロジェクト ファイルに移植されます。

今回はEthernet pingを実現できるSTM32F407プロジェクトを使用します。
プロジェクトのソース コード: STM32F407-ETH+LAN8720A+LWIP-オペレーティング システムなし-ping----プログラム ソース コード

ステップ1

FreeModbus_TCPフォルダーをプロジェクト ファイルにコピーします。

ここに画像の説明を挿入

ステップ2

keil5 を使用してプロジェクトを開き、FreeModbus_TCPフォルダーにファイルをインポートします。

ここに画像の説明を挿入

FreeModbus_TCPフォルダーのfunctionフォルダーにあるすべての .c ファイルを選択します。

ここに画像の説明を挿入

FreeModbus_TCPフォルダー内のポートフォルダーのすべての .c ファイルを選択します。

ここに画像の説明を挿入
FreeModbus_TCPフォルダー内のtcpフォルダーのすべての .c ファイルを選択します。FreeModbus_TCPフォルダー内の mb.c ファイル
ここに画像の説明を挿入
を選択します。
ここに画像の説明を挿入

最終的な結果は次のとおりです

ここに画像の説明を挿入
ここに画像の説明を挿入

ステップ3

魔法の杖をクリックしC/C++を選択し、ファイル パスを追加します。

ここに画像の説明を挿入

ファイルパスを追加

ここに画像の説明を挿入

次のように結果を追加します

ここに画像の説明を挿入
プログラムをコンパイルするといくつかのエラーが発生します。エラーを取り除くために以下のプログラムを編集します。

4. 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    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;
}

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

ステップ 7: main 関数で ModbusTCP を呼び出す

メイン関数の初期化ではModbusTCPInit() を呼び出し、
実行中のメイン関数ではModbusTCPMain() を呼び出します。

ここに画像の説明を挿入

5. FreeModbus ソース コードの移植 ----テスト検証

Modbus Poll ソフトウェアを使用して、 ModbusTCP機能をテストします
Modbus ポーリング ソフトウェア ----ダウンロードしてインストールする

ステップ 1: Modbus ポーリング ソフトウェアを開く

ここに画像の説明を挿入

ステップ 2: 接続構成ウィンドウを開き、接続を構成します

メニューバーの「接続」→「接続...」をクリック(またはショートカットキーF3を押す)して、接続設定ウィンドウをポップアップ表示します。

ここに画像の説明を挿入
[ModbusTCP/IP]を選択しIP アドレスを構成し、ポートを選択します。その他の場合はデフォルト値を使用し、[ OK]をクリックします。

ここに画像の説明を挿入

ステップ 3: ウィンドウ情報を構成する

[セットアップ] -> [読み取り/書き込み定義...] をクリックするか、ショートカット キー F8 を押します。

ここに画像の説明を挿入

スレーブアドレス、ファンクションコード、スタートアドレス、レジスタ数などを設定し、OKをクリックします。

ここに画像の説明を挿入

ステップ 4: テスト結果

ファンクションコード 01、コイルを読み取り、テスト結果はプログラムと一致します。

ここに画像の説明を挿入

ファンクション コード 02、ディスクリート レジスタの読み取り、テスト結果はプログラムと一致します。

ここに画像の説明を挿入

ファンクションコード 03、保持レジスタを読み取ります。テスト結果はプログラムと一致します。

ここに画像の説明を挿入

ファンクションコード 04、入力レジスタを読み取ります。テスト結果はプログラムと一致します。

ここに画像の説明を挿入


おすすめ

転載: blog.csdn.net/MQ0522/article/details/132011066