1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

STM32で始めるFreeRTOSとLwIP(5) -LwIPの処理まとめ

Posted at

下記の記事で、STM32でFreeRTOS+LwIPのサンプルソースコードを見てきました。

  1. CubeIDEで行うFreeRTOS+LwIPハンズオン
  2. サンプルソースにおけるLwIPの初期化処理の解説
  3. サンプルソースにおけるLwIPのDHCP Clientの解説
  4. サンプルソースにおけるLHTTP Serverの解説

上記の記事ではLwIPの使い方だけを記載しているわけではないので、この記事ではLwIPの使い方に関してまとめます。
なお、FreeRTOSを使う前提としているので、OSレスでLwIPを使う方法は想定していません。

LwIPの初期化処理

初期化時に行う処理としては、下記となります。

  • TCPIPのプロトコルスタックの初期化
  • ネットワークインターフェースの作成
  • Ethernetドライバの送信関数、受信関数、接続に変化があった際に呼ばれるコールバック関数を、ネットワークインターフェースに登録

実際に呼び出すLwIPは下記のようになります。

  1. tcpipの初期化
  2. IPアドレスを設定
  3. ネットワークインターフェースを作成
    • netif_add()呼び出し。第六引数に登録する関数は、Ethernetドライバの受信と送信を行う関数をネットワークインターフェースに登録する処理を実装しておく
  4. 作成したネットワークインターフェースをデフォルトで使用するよう設定
  5. ethernetが接続された際のコールバックを登録

下記に初期化のサンプルを示します。

#include "lwip/netif.h"
#include "lwip/tcpip.h"

void lwIPInitSample()
{
     ip_addr_t ipaddr;
     ip_addr_t netmask;
     ip_addr_t gw;
     struct netif gnetif; /* network interface structure */
    
    
    /* TCP/IPのプロトコルスタックの初期化 */
     tcpip_init(NULL, NULL);
    
    #if LWIP_DHCP
    /* DHCP clientに対応する場合 */
    /* IPアドレス、ネットマスク、ゲートウェイを全て0で初期化 */
     ip_addr_set_zero_ip4(&ipaddr);
     ip_addr_set_zero_ip4(&netmask);
     ip_addr_set_zero_ip4(&gw);
    #else
    /* DHCP clientに対応しない場合 */
    /* ユーザーのIPアドレス、ネットマスク、ゲートウェイを設定 */
     IP_ADDR4(&ipaddr,IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3);
     IP_ADDR4(&netmask,NETMASK_ADDR0,NETMASK_ADDR1,NETMASK_ADDR2,NETMASK_ADDR3);
     IP_ADDR4(&gw,GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_ADDR3);
    #endif /* LWIP_DHCP */
    
     /*設定したIPアドレス、ネットマスク、ゲートウェイのネットワークインターフェースを作成 */
     /* ethernet_initはイーサネットドライバで実装したもの */
     netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);
     /*  作成したネットワークインターフェースをデフォルトで使用するように設定 */
     netif_set_default(&gnetif);
    /* 接続状況に変化があった際のコールバックを登録 */
    /* ethernet_link_status_upodatedはイーサネットドライバで実装したもの */
     netif_set_link_callback(&gnetif, ethernet_link_status_updated);
}

LwIPのDHCP Client処理

主に行うのは、DHCP ClientのAPIを呼び出すのみです。IPアドレスを取得下かどうかに関しては、APIを呼び出して確認します。

  1. Ethernetケーブルが接続されている状態で、IPアドレスの設定を要求するパケット(DHCP Discover)を送信する
  2. DHCPでIPアドレスを取得できたかどうかを確認する。
    • dhcp_supplied_address()を呼び出す。返り値がtrueの場合はIPアドレスが取得できた状態となる

DHCP Offer、DHCP Request、DHCP ACKの処理はLwIP内で行っているため、アプリケーション側から何か別のAPIを呼び出す必要はありません。
下記にDHCP Clientのサンプルを示します。

void DHCP_Thread( struct netif *netif)
{
        /* DHCP Discoverパケットを送信(受信処理はLwIPの内部処理で行う) */
       dhcp_start(netif);
 
       while (!dhcp_supplied_address(netif))
       {
           osDelay(500);
       }
}

LwIPのTCP通信処理

Client側とサーバー側で振る舞いが違うので分けて示します。

Server型処理

行うこととしては、下記となります。

  • 制御用の構造体の作成
  • 通信するIPアドレスとポート番号を、制御用構造体と紐づける
  • TCPコネクション確立要求待ち
  • TCPコネクションの確立
  • データ通信

実際のフローとしては下記の通りとなります。

  1. TCPの通信制御用の構造体の作成
    • netconn_newを、引数に「NETCONN_TCP」を指定して呼び出す
  2. 通信制御用の構造体と、IPアドレスとポート番号を紐づける
    • netconn_bindを、第一引数をnetconn_new()で生成した構造体を指定し、第二引数で通信に使うIPアドレス、第三引数で通信に使うポート番号を指定して呼び出す
  3. TCPのコネクションの確立要求を待つ
    • netconn_listenを、引数にnetconn_new()で生成した構造体を指定して呼び出す
  4. コネクションの確立要求があったデバイスを承認して接続する
    • netconn_acceptを、第一引数をnetconn_new()で生成した構造体を指定し、第二引数で空のnetconn型構造体を指定する。接続先の情報は第二引数で指定したnetconn型構造体に格納される
  5. データの送受信を行う
    • netconn_recvnetconn_writeを使ってデータの送受信を行う
      • netconn_recvは第一引数をnetconn_new()で生成した構造体を指定し、第二引数でnetbuf型のポインタのポインタを指定する。受信データは第二引数で指定したnetbuf型のポインタのポインタが指し示すインスタンスに格納される
      • netconn_writeは第一引数をnetconn_new()で生成した構造体を指定し、第二引数に送信データのポインタ、第三引数に送信データのサイズを指定する。第四引数では、送信時のオプション設定を指定する。
  6. 通信を終了し、TCPのコネクションを切断する(削除は行わない)
    • netconn_closeを、引数にnetconn_accept()の第二引数に格納された構造体を指定して呼び出す
  7. 現在通信を行っている接続状態を完全に削除する
    • netconn_deleteを、引数にnetconn_accept()の第二引数に格納された構造体を指定して呼び出す

下記にサンプルを示します。

static void server_netconn_thread(void *arg)
{
    struct netconn *conn, *newconn;
    err_t err, accept_err;
    struct netbuf *inbuf;
    err_t recv_err;
    char* buf;
    u16_t buflen;
    
    /* 制御用構造体の作成 */
    conn = netconn_new(NETCONN_TCP);
    if (conn!= NULL)
    {
        /* ポート番号とIPアドレスを紐づけ(ここでは例としてポート番号80のみ指定) */
        err = netconn_bind(conn, NULL, 80);
        if (err == ERR_OK)
        {
            /* TCPコネクション確立要求待ち状態にする */
            netconn_listen(conn);
            while(1)
            {
                /* TCPコネクション確立 */
                accept_err = netconn_accept(conn, &newconn);
                if(accept_err == ERR_OK)
                {
                     /* データ通信(サンプルなので受信データをそのまま返す) */
                     recv_err = netconn_recv(conn, &inbuf);
                     if (netconn_err(conn) == ERR_OK)
                     {
                         netbuf_data(inbuf, (void**)&buf, &buflen);
                     }
                     netconn_write(conn, (const unsigned char*)buf, (size_t)buflen, NETCONN_NOCOPY);
                
                     /* TCPコネクション切断 */
                     netconn_delete(newconn);
                }
            }
        }
    }
}

Client型処理

行うこととしては、下記となります。

  • 制御用の構造体の作成
  • 通信するIPアドレスとポート番号を、制御用構造体と紐づける
  • TCPコネクション確立要求送信
  • データ通信

実際のフローとしては下記の通りとなります。

    1. TCPの通信制御用の構造体の作成
    • netconn_newを、引数に「NETCONN_TCP」を指定して呼び出す
  1. コネクションの確立要求を指定したIPアドレスとポート番号に送る
    • netconn_connectを、第一引数をnetconn_new()で生成した構造体を指定し、第二引数と第三引数で接続先のIPアドレスとポート番号を指定する
  2. データの送受信を行う
    • netconn_recvnetconn_writeを使う
      • netconn_recvは第一引数をnetconn_new()で生成した構造体を指定し、第二引数でnetbuf型のポインタのポインタを指定する。受信データは第二引数で指定したnetbuf型のポインタのポインタが指し示すインスタンスに格納される
      • netconn_writeは第一引数をnetconn_new()で生成した構造体を指定し、第二引数に送信データのポインタ、第三引数に送信データのサイズを指定する。第四引数では、送信時のオプション設定を指定する
  3. 通信を終了し、TCPのコネクションを切断する(削除は行わない)
    • netconn_closeを、引数にnetconn_accept()の第二引数に格納された構造体を指定して呼び出す
  4. 現在通信を行っている接続状態を完全に削除する
    • netconn_deleteを、引数にnetconn_accept()の第二引数に格納された構造体を指定して呼び出す

下記にサンプルを示します。

static void client_netconn_thread(void *arg)
{
    struct netif *netif = (struct netif *) arg;   /* 引数としてnetifをもらう前提 */
    struct netconn *conn;
    err_t err;
    
    
    /* Create a new TCP connection handle */
    conn = netconn_new(NETCONN_TCP);
    
    
    /* DHCPでIPアドレスが設定されるのを待つ */
    while(!dhcp_supplied_address(netif))
    {
        osDelay(100);
    }
    
    if (conn != NULL)
    {
        ip_addr_t targetIpaddr;
        IP_ADDR4(&targetIpaddr,192,168,11,195); /* 例としてIPアドレスを192.168.11.195としている */
        /* create connection */
        while(1)
        {
            err = netconn_connect(conn,&targetIpaddr,1024);
            if (err == ERR_OK)
            {
                break;
            }
            /* wait 500 ms */
            osDelay(500);
        }
        /* send */
        char buf[] = "test";
        netconn_write(conn, (const unsigned char*)(buf), strlen(buf), NETCONN_NOCOPY);
        /* Close the connection (server closes in HTTP) */
        netconn_close(conn);
        /* delete connection */
        netconn_delete(conn);
    }
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?