stm 32 f 103 w 5500 tcp server

35669 ワード

背景
プロジェクトの中でネットを使う必要があります。STm 32 f 4+lwipの方案を使い始めます。しかし、ハードウエアのコストが少し高いです。もっと主要なのはlwipがよくないので、いつも切れています。長い間の研究論証を経て、最終的にw 5500というチップを選択しました。TCP/IPネットワークプロトコルスタックをハードウェアチップに硬化させ、ユーザーのためにアプリケーション層インターフェースを残して、簡単で安定している。
移植プロセス
まずはhttps://w5500.com/チップマニュアルと参照コードをダウンロードしてチップの原理と基本的な使い方を理解します。内容はそんなに複雑ではありません。プロジェクトには、TCP ServerとしてW 5500が必要です。三つのクライアントが接続されます。関連コードを参照して、プロジェクトに移植します。具体的なコードは以下の通りです。
設定
static void net_set_config(void)
{
    unsigned char mac[6] = {0x00, 0x08, 0xdc, 0x11, 0x11, 0x11};
    unsigned char ip[4]  = {192, 168, 1, 100};
    unsigned char sub[4] = {255, 255, 255, 0};
    unsigned char gw[4]  = {192, 168, 1, 1};
    unsigned char tx_size[8] = {2,2,2,2,2,2,2,2};
    unsigned char rx_size[8] = {2,2,2,2,2,2,2,2};

    /*   Mac   */
    set_mac_addr(mac);
    /*   IP */
    set_source_ip_addr(ip);
    /*        */
    set_subnet_mask(sub);
    /*      */
    set_gateway(gw);

    /*    8 socket */
    sys_init(tx_size, rx_size);
    /*        */
    set_retrans_time(2000);
    /*            */
    set_retrans_num(3);
	
    /*        */
    set_keepalive(SOCKET_ID_0);
	set_keepalive(SOCKET_ID_1);
	set_keepalive(SOCKET_ID_2);	
}
ステートマシン
int net_process_socket(SOCKET sock, unsigned short port)
{
    int ret = -1;
    unsigned char state = SOCK_CLOSED;
    unsigned short len = 0;
    unsigned char data[MAX_MSG_LEN];

    state = get_sock_status(sock);
    switch (state) {
        case SOCK_INIT:
            listen(sock);
            break;
        case SOCK_ESTABLISHED:
            if (get_sock_interrupt_status(sock) & Sn_IR_CON) {
                set_sock_interrupt_status(sock, Sn_IR_CON);
            }
            len = get_sock_rx_free_buff_size(sock);
            if (len > 0) {
                recv(sock, data, MAX_MSG_LEN);
				/* process the recv data */
            }
            break;
        case SOCK_CLOSE_WAIT:
            disconnect(sock);
            break;
        case SOCK_CLOSED:
            ret = socket(sock, Sn_MR_TCP, port, Sn_MR_ND);
            break;
        default:
            break;
    }

    return 0;
}
実行
void net_task(void *arg)
{    
    /*        */
    net_set_config();

    while (1) { 
        net_process_socket(SOCKET_ID_0, 12345);
        net_process_socket(SOCKET_ID_1, 12345);
        net_process_socket(SOCKET_ID_2, 12345);
		
		/*   20ms */
        vTaskDelay(100);
    }
}
w 5500は最大8つのsocket接続をサポートしています。プロジェクトに三つのクライアントが必要なので、三つのsocketを配置しました。そして、それぞれのsocketに対応するステートマシンを個々のスレッドで順次実行します。具体的な基礎知識は詳しく説明しないで、公式サイトとブログの中でとても多い説明があります。
問題
公式サイトの設定により、正常に運行できるはずです。確かに実行できます。三つのクライアントも接続できます。しかし、デバイスの長時間テストで、クライアントとサーバの間の接続が常に切断されていることが分かりました。いったん切断したら、あるクライアントは接続できます。あるクライアントはもう接続できなくなりました。解決の方法は設備を再起動するしかないです。これはきっとだめです。
クライアントは再接続機構があります。定期的に心拍カバンをサーバーに送ります。タイムアウト後にサーバーに再接続します。全体の仕組みは大丈夫です。問題はサーバーに接続できなくなります。採用した有線接続は、本来切断すべきではないです。この問題は一ヶ月以上繰り返しましたが、まだ分からないです。設備の出荷日も延期するしかないです。
ソリューション
まず、ネットワークデバッグアシスタントを通じてクライアントをシミュレートして、長時間運転してみます。Wirestharkでカバンをひっかきましたが、keepaliveときめきバッグがないことに気づきました。なぜサーバから送られてきませんでしたか?同僚と浩然電子杜工の提案を参考にして、次のように修正しました。問題は解決されました。現在十数日間運行しています。設備は一回も切れたことがありません。
  • 予備のsocketは3つのクライアントがあります。6つのクライアントを開きます。各クライアントは正常に使用されています。一つの予備のものがあります。接続が切断されると、もう一つは即時に有効になり、シームレスに切り替えられます。
  • 心拍処理は自動的に心拍を送信して手動送信に変更されます。
  • 延長公式サイトの資料パッケージは全部裸機によるデモンストレーションプログラムです。私達のプロジェクトはマルチスレッドです。だから、死ぬなどのところで延長する必要があります。
  • 設定
    static void net_set_config(void)
    {
        unsigned char mac[6] = {0x00, 0x08, 0xdc, 0x11, 0x11, 0x11};
        unsigned char ip[4]  = {192, 168, 1, 100};
        unsigned char sub[4] = {255, 255, 255, 0};
        unsigned char gw[4]  = {192, 168, 1, 1};
        unsigned char tx_size[8] = {2,2,2,2,2,2,2,2};
        unsigned char rx_size[8] = {2,2,2,2,2,2,2,2};
    
        /*   Mac   */
        set_mac_addr(mac);
        /*   IP */
        set_source_ip_addr(ip);
        /*        */
        set_subnet_mask(sub);
        /*      */
        set_gateway(gw);
    
        /*    8 socket */
        sys_init(tx_size, rx_size);
        /*        */
        set_retrans_time(2000);
        /*            */
        set_retrans_num(3);	
    }
    
    動悸自動送信機構を開けません。
    ステートマシン
    int net_process_socket(SOCKET sock, unsigned short port)
    {
        int ret = -1;
        unsigned char state = SOCK_CLOSED;
        unsigned short len = 0;
        unsigned char data[MAX_MSG_LEN];
        static unsigned char sock_est_flag[8] = {1, 1, 1, 1, 1, 1, 1, 1};
        SOCKET another_sock;
    
        state = get_sock_status(sock);
        switch (state) {
            case SOCK_INIT:
                listen(sock);
                sock_est_flag[sock] = 1;
                break;
            case SOCK_ESTABLISHED:
                if (sock_est_flag[sock] == 1) {
                    sock_est_flag[sock] = 0;
                    if ((sock % 2) == 0) {
                        another_sock = sock + 1;
                    } else {
                        another_sock = sock - 1;
                    }
                    if (get_sock_status(another_sock) == SOCK_ESTABLISHED) {
                        close(another_sock);
                    }
                }
                
                if (get_sock_interrupt_status(sock) & Sn_IR_CON) {
                    set_sock_interrupt_status(sock, Sn_IR_CON);
                }
                len = get_sock_rx_free_buff_size(sock);
                if (len > 0) {
                    recv(sock, data, MAX_MSG_LEN);
                    /* process the recv data */
                }
                break;
            case SOCK_CLOSE_WAIT:
                disconnect(sock);
                break;
            case SOCK_CLOSED:
                ret = socket(sock, Sn_MR_TCP, port, Sn_MR_ND);
                break;
            default:
                break;
        }
    
        return 0;
    }
    
    バックアップsocketを追加します。予備のsocketはいつもlisten状態です。
    実行
    void net_task(void *arg)
    {    
    	int i = 0;
    	int counter = 0;
    
        /*        */
        net_set_config();
    
        while (1) { 
            net_process_socket(SOCKET_ID_0, 12345);
            net_process_socket(SOCKET_ID_1, 12345);
            net_process_socket(SOCKET_ID_2, 12346);
            net_process_socket(SOCKET_ID_3, 12346);
            net_process_socket(SOCKET_ID_4, 12347);
            net_process_socket(SOCKET_ID_5, 12347);
    
    		if ((counter++ % 100) == 0) {
    			for (i = 0 ; i < 6; i++) {
    				iinchip_write_data(Sn_CR(i),Sn_CR_SEND_KEEP);
    			}
    		}
    		
    		/*   20ms */
            vTaskDelay(100);
        }
    }
    
    手动でkeepaliveときめきパックを送ります。10 sごとに一回送ります。
    時間を延ばす
    また、非常に重要な点は、socketの送信や受信関数の中で死などの場所にオペレーティングシステムの遅延を増やすことである。
    締め括りをつける
    問題があったら焦らないでください。解決策があります。今は時間がないだけです。現在把握している手がかりがまだ解決に足りないと説明し、引き続き分析研究を行う必要がある。窮地に陥ったら、同僚と相談してもいいです。他の人の話によっては、いい手がかりが提供されるかもしれません。
    再度浩然電子の杜工さんの親切な助けに感謝します。この記録で辛い過程を記録して、他の人に手伝いたいです。