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()関数で接続を閉じてください。 ミドルウェア/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でそれを見つけることができないようです。リンクステータスはどのように検出および報告されますか? ありがとうございます!
查看全文