1098996_ja-JP

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

1098996_ja-JP

1098996_ja-JP

Sequential APIを使用したLwIPアプリケーションの開発<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

LwIP は、メインループ モード ("NO_SYS") (ターゲット システムで OS/RTOS が実行されていない) と OS モード (TCPIP スレッド) (ターゲット システムで OS が実行されている) の 2 つの基本モードで使用できます。mainloop モードでは、raw API のみを使用できます。OSモードでは、raw APIとSequential APIを使用できます。

OS モードでは、lwip スタックとアプリケーションは別々のタスクで実行されます。このアプリケーションは、プロセス間通信のためにRTOSメールボックスメカニズムを訴えるシーケンシャルAPI呼び出しを介してLwIPスタックと通信します。

この投稿では、MCUXpresso SDKのシーケンシャルAPIを使用してOSモードでLwIPアプリケーションを設計する方法に焦点を当てています。LwIP初心者向けです。コード snipperts は MCUXpresso SDK2.6 からのものです。

raw APIを使用してメインループモード(ベアメタルモード)でLwIPアプリケーションを設計する方法については、以下のリンクを参照してください。

Raw API を使用した LwIP アプリケーションの開発  



一般に、LwIP アプリケーションには、ネットワーク インターフェイスの設定、LwIP スタックの初期化、LwIP API の使用、および構成が含まれます。


1. ネットワークインターフェースの起動

新しいネットワークインターフェースを作成するには、ユーザーは新しい 構造体netif にスペースを割り当て(ただし、その一部は初期化しません)、 netifapi_netif_addを呼び出します。

    IP4_ADDR(&fsl_netif0_ipaddr, configIP_ADDR0, configIP_ADDR1, configIP_ADDR2, configIP_ADDR3);

    IP4_ADDR(&fsl_netif0_netmask, configNET_MASK0, configNET_MASK1, configNET_MASK2, configNET_MASK3);

IP4_ADDR(&fsl_netif0_gw, configGW_ADDR0, configGW_ADDR1, configGW_ADDR2, configGW_ADDR3);

 

    netifapi_netif_add(&fsl_netif0, &fsl_netif0_ipaddr, &fsl_netif0_netmask, &fsl_netif0_gw, &fsl_enet_config0,

                       ethernetif0_init, tcpip_input);

tcpip_input API を netif_add API に入力コールバック関数として渡します。このコールバック関数は、入力パケットをプロトコル層スタックに渡します


次に、インターフェースを有効にする必要があります

「アップ」状態のインターフェースはアプリケーションで入力と出力に使用でき、「ダウン」状態はその反対の状態です。したがって、インターフェースを使用する前に、起動する必要があります。これは、インターフェースがIPアドレスを取得する方法に応じて達成できます。静的IPアドレスまたはDHCPを使用することができます。

ネットワーク・インターフェースをデフォルトのネットワーク・インターフェースとして設定します

netifapi_netif_set_default(&fsl_netif0);

 

インターフェースを起動し、処理が可能な状態にします

    netifapi_netif_set_up(&fsl_netif0);

2. LwIPスタックの初期化

 

tcpip_threadを作成するために tcpip_init を呼び出すと、このスレッドはLwIPコア関数に排他的にアクセスできます。他のスレッドは、メッセージ ボックスを使用してこのスレッドと通信します。また、すべてのタイマーを開始して、適切なスレッドコンテキストで実行されていることを確認します。

 

tcpip_init(NULL, NULL);

 

void   tcpip_init(tcpip_init_done_fn initfunc, void *arg)

{

  lwip_init();

 

  tcpip_init_done = initfunc;

  tcpip_init_done_arg = arg;

if (sys_mbox_new(&tcpip_mbox, TCPIP_MBOX_SIZE) != ERR_OK) {

LWIP_ASSERT ("mbox の作成に失敗しましたtcpip_thread", 0);

  }

#if LWIP_TCPIP_CORE_LOCKING

if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {

LWIP_ASSERT ("lock_tcpip_coreの作成に失敗しました", 0);

  }

#endif /* LWIP_TCPIP_CORE_LOCKING */

 

sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);

}

ユーザータスクの優先度は、tcpip_threadの優先度を超えてはなりません

- lwipopts.h では、tcpip_threadの優先順位

#define TCPIP_THREAD_PRIO 2


3. シーケンシャルAPIの利用

 次の図に示すように、クライアント側で TCP 接続を確立する手順は次のとおりです。

  • netconn_new() 関数を使用して接続を作成します。
  • netconn_connect() 関数を使用してサーバーのアドレスに接続します。
  • netconn_recv() 関数と netconn_write() 関数を使用してデータを送受信します。
  • netconn_close()関数で接続を閉じてください。

サーバー側でTCP接続を確立するための手順は次のとおりです。

  • netconn_new() 関数を使用して TCP 接続を作成します。
  • netconn_bind() 関数を使用してサーバーをアドレスにバインドします。
  • netconn_listen() 関数で接続をリッスンします。
  • netconn_accept() 関数で接続を受け入れます。通常、この呼び出しは、クライアントがサーバーに接続するまでブロックされます。
  • netconn_write() と netconn_recv() を使用してデータを送受信します。
  • netconn_close()関数で接続を閉じてください。

pastedImage_1.png

ミドルウェア/lwip/contrib/appa/tcpecho/tcpecho.c

スタティックボイド

tcpecho_thread(void *arg)

{

  struct netconn *conn, *newconn;

  err_t err;

LWIP_UNUSED_ARG(引数);

 

 /* 新しい接続識別子を作成します。

 /* 既知のポート番号 7 への接続をバインドします。 */

#if LWIP_IPV6

conn = netconn_new(NETCONN_TCP_IPV6);

netconn_bind(コネチカット州、IP6_ADDR_ANY、7);

#else /* LWIP_IPV6 */

conn = netconn_new(NETCONN_TCP);

  netconn_bind(コネチカット州、IP_ADDR_ANY、7);

#endif /* LWIP_IPV6 */

  LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;);

 

 /* 接続にリスニングモードに入るように指示します。

  netconn_listen(conn);

 

 (1) {

 

   /* 新しい接続を取得します。

    err = netconn_accept(conn, &newconn);

   /*printf("新しい接続 %p\nを受け入れました", newconn);*/

   /* 新しい接続を処理します。 */

    if (err == ERR_OK) {

     構造体 netbuf *buf;

      void *data;

      u16_t len;

     

     while ((err = netconn_recv(Newconn, &buf)) == ERR_OK) {

       /*printf("受信済み\n");*/

        do {

netbuf_data(buf, &data, &len);

err = netconn_write(newconn, data, len, NETCONN_COPY);

#if 0

            if (err != ERR_OK) {

printf("tcpecho: netconn_write: エラー \"%s\"\n", lwip_strerr(エラー));

            }

#endif

} while (netbuf_next(buf) >= 0);

        netbuf_delete(buf);

}

     /*printf("EOF を取得しました。ループしています\n");*/

     /* 接続を閉じ、接続識別子を破棄します。

      netconn_close(ニューコン);

      netconn_delete(ニューコン);

    }

  }

}


tcpechoスレッドから、

まず、API netconn_newによってパラメータ NETCONN_TCP を使用して 1 つの新しい TCP 接続が呼び出されました。

#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)

構造体 netconn *

netconn_new_with_proto_and_callback(列挙型 netconn_type t、u8_t proto、netconn_callback コールバック)

{

構造体 netconn *conn;

  API_MSG_VAR_DECLARE(msg);

  API_MSG_VAR_ALLOC_RETURN_NULL(msg);

 

conn = netconn_alloc(t, コールバック);

if (conn != NULL) {

    err_t err;

 

    API_MSG_VAR_REF(msg).msg.n.proto = proto;

    API_MSG_VAR_REF(msg).conn = conn;

    err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));

    if (err != ERR_OK) {

LWIP_ASSERT ("pcb を解放せずに conn を解放", conn->pcb.tcp == NULL);

LWIP_ASSERT("conn には recvmbox がありません", sys_mbox_valid(&conn->recvmbox));

#if LWIP_TCP

LWIP_ASSERT("conn->acceptmbox は存在すべきではありません", !sys_mbox_valid(&conn->acceptmbox));

#endif /* LWIP_TCP */

#if !LWIP_NETCONN_SEM_PER_THREAD

LWIP_ASSERT ("conn にはop_completedがありません", sys_sem_valid(&conn->op_completed));

sys_sem_free(&conn->op_completed);

#endif /* !LWIP_NETCONN_SEM_PER_THREAD */

sys_mbox_free(&conn->recvmbox);

      memp_free(MEMP_NETCONN, conn);

      API_MSG_VAR_FREE(msg);

      return NULL;

    }

  }

  API_MSG_VAR_FREE(msg);

  return conn;

}

 

次に、新しく作成された接続は、API 関数 netconn_bind を呼び出すことによってポート 7 (エコー プロトコル) にバインドされます。

 

次に、アプリケーションは API 関数 netconn_listen を呼び出して、接続のリッスン プロセスを開始します。

無限の while(1) ループでは、アプリケーションは API 関数 netconn_accept を呼び出して新しい接続を待ちます。この API は、着信接続がない場合にアプリケーション タスクをブロックします。

受信接続がある場合、アプリケーションは API 関数 netconn_recv を呼び出してデータの受信を開始できます。着信データは netbuf で受信されます。

 

 

 

アプリケーションは、netbuf API 関数netbuf_dataを呼び出すことで、受信したデータを取得できます。

err_t

netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)

{

LWIP_ERROR ("netbuf_data: 無効な buf", (buf != NULL), return ERR_ARG;);

  LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);

  LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);

 

if (buf->ptr == NULL) {

    return ERR_BUF;

  }

*dataptr = buf->ptr->ペイロード;

  *len = buf->ptr->len;

  return ERR_OK;

}

 

受信したデータは、API 関数 netconn_write を呼び出すことによって、リモート TCP クライアントに送り返されます (エコーされます)。

Netconn_close と netconn_delete は、それぞれ netconn 接続を閉じて削除するために使用されます

 

 

4. LwIPの設定

lwipopts.h は、lwIP とそのすべてのモジュールを完全に設定するために使用できるユーザーファイルです。lwIP が提供するすべてのオプションを定義する必要はありません。オプションを定義しない場合は、デフォルト値が使用されます。したがって、あなたのlwipopts.hlwIP の動作の多くをオーバーライドする方法を提供します。

マルチヘッドモードでは、.0 に #define NO_SYS する必要があります。

evkbimxrt1050_lwip_tcpecho_freertos\source\lwipopts.hを参照してください。

#if USE_RTOS

 

/**

* SYS_LIGHTWEIGHT_PROT==1: 特定のタスク 保護が必要な場合

* バッファの割り当て、割り当て解除、およびメモリ中の重要な領域

* 割り当てと割り当て解除。

 */

#define SYS_LIGHTWEIGHT_PROT 1

 

/**

* NO_SYS==0: RTOSを使用

 */

#define NO_SYS 0

/**

* LWIP_NETCONN==1: Netconn API を有効にする (api_lib.c の使用が必要)

 */

#define LWIP_NETCONN 1

/**

* LWIP_SOCKET==1: ソケットAPIを有効にする(sockets.cの使用が必要)

 */

#define LWIP_SOCKET 1

 

/**

* LWIP_SO_RCVTIMEO==1: ソケット/ネットコンの受信 タイムアウトを有効にし、

※SO_RCVTIMEO加工中です。

 */

#define LWIP_SO_RCVTIMEO 1





 

Re: Sequential APIを使用したLwIPアプリケーションの開発<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

Daniel,

netif API呼び出しを行う前に、ネットワーク接続がアクティブである必要はありませんか?netif_dhcp_start() を呼び出すときにネットワーク ケーブルが接続されていないのに、後で接続すると、dhcp 状態変数が DHCP_STATE_BOUND になるのが見えません。だから、リンクがアップまたはダウンしたときに知らせるコールバックまたはステータス変数があると思いますが、MCUXpresso SDKまたはLwIPでそれを見つけることができないようです。リンクステータスはどのように検出および報告されますか?

ありがとうございます!

Tags (1)
No ratings
Version history
Last update:
‎01-05-2026 10:14 AM
Updated by: