STM32F407VET6 基於FreeRTOS實時操作系統和LwIP協議棧創建TCP客戶端


  在上一篇博客中我們移植好了FreeRTOS + LwIP + LAN8720網卡,現在我們在上一篇博客的工程基礎上創建一個TCP客戶端進行網絡通信。

  注:如果要自動獲取本地IP地址,那就要使能DHCP功能,在lwipopts.h文件配置。這里我不使用DHCP功能,而是使用靜態IP。

1、工程部分

  首先創建兩個文件,分別為tcp_client.c、tcp_client.h,然后保存在LwIP\app目錄下,然后在工程中添加tcp_client.c文件。如下圖所示:

  

 

 

2、代碼部分

 

tcp_client.h

#ifndef __TCP_CLIENT_H
#define __TCP_CLIENT_H
 

/********************************************************
 * 函數功能:創建TCP客戶端
 * 形    參:ip_msg:IP信息數據結構指針
 * 返 回 值:0=成功
             1=TCP客戶端線程創建失敗
             2=TCP客戶端數據列隊創建失敗
 ********************************************************/
unsigned int tcp_client_init(void *ip_msg);

/********************************************************
 * 函數功能:TCP客戶端重連
 * 形    參:無
 * 返 回 值:0=成功
             1=失敗
 ********************************************************/
unsigned int tcp_client_reconnect(void);

/********************************************************
 * 函數功能:獲取TCP客戶端連接狀態
 * 形    參:無
 * 返 回 值:0=連接正常
             1=連接異常
 ********************************************************/
unsigned int tcp_client_connect_status_get(void);

/********************************************************
 * 函數功能:TCP客戶端向網口發送數據
 * 形    參:pbuf:數據緩存地址
             length:發送數據字節數
 * 返 回 值:0=成功
             1=數據緩存地址為NULL
             2=數據長度錯誤
             3=客戶端未啟動
             4=連接異常
 ********************************************************/
unsigned int tcp_client_write(const unsigned char *pbuf, unsigned int length);

/********************************************************
 * 函數功能:獲取TCP客戶端數據列隊句柄
 * 形    參:無
 * 返 回 值:數據列隊句柄
 ********************************************************/
void *tcp_client_queue(void);
 
 
#endif
 tcp_clent.c

 

#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "sys_eth.h"
#include "lwip/api.h"
#include "tcp_client.h"

// TCP客戶端接收數據結構
#pragma pack(push, 1)
typedef struct _sTcpClientRxMsg
{
   unsigned char *pbuf;
   unsigned int length;
}sTcpClientRxMsg_t;
#pragma pack(pop)

static struct netconn *socket = NULL; // TCP客戶端套接字
static xQueueHandle TcpClientQueue = NULL; // 接收數據列隊
static unsigned char connect_flag = pdFALSE; // TCP客戶端連接標志
 

/********************************************************
 * 函數功能:TCP客戶端數據接收線程
 * 形    參:arg:線程形參
 * 返 回 值:無
 ********************************************************/
static void tcp_client_thread(void *parg)
{
   sLwipDev_t *psLwipDev = (sLwipDev_t *)parg;
 
   while(psLwipDev != NULL)
   {
      // 創建一個套接字
      socket = netconn_new(NETCONN_TCP);
      if(socket == NULL)
      {
         vTaskDelay(100);
         continue; // TCP連接創建失敗則從新創建
      }
  
      // 綁定IP端口,並連接服務器
      ip_addr_t LocalIP = {0};
      ip_addr_t RemoteIP = {0};
      IP4_ADDR(&LocalIP, psLwipDev->localip[0], psLwipDev->localip[1], psLwipDev->localip[2], psLwipDev->localip[3]);
      IP4_ADDR(&RemoteIP, psLwipDev->remoteip[0], psLwipDev->remoteip[1], psLwipDev->remoteip[2], psLwipDev->remoteip[3]);
      netconn_bind(socket, &LocalIP, psLwipDev->localport); // 綁定本地端口和IP,綁定后使用此IP和端口對外發送
      if(netconn_connect(socket, &RemoteIP, psLwipDev->remoteport) != ERR_OK) // 連接服務器
      {
         netconn_delete(socket); // 服務器連接失敗,刪除連接
         vTaskDelay(100);
         continue;
      }
      err_t recv_err;
      struct pbuf *q;
      struct netbuf *recvbuf;
      socket->recv_timeout = 10;
      connect_flag = pdTRUE; // 連接標志置位
      while(connect_flag == pdTRUE)
      {
         // 接收數據(注:實測每次接收的數據量最大為1460個字節)
         recv_err = netconn_recv(socket, &recvbuf);
         if(recv_err == ERR_OK) // 接收到數據
         {
            unsigned int length = 0;
            sTcpClientRxMsg_t msg = {0};
    
            taskENTER_CRITICAL(); // 進入臨界區
            {
               length = recvbuf->p->tot_len;
               msg.pbuf = pvPortMalloc(length);
               if(msg.pbuf != NULL)
               {
                  for(q = recvbuf->p; q != NULL; q = q->next)
                  {
                     if((msg.length + q->len) > length)
                     {
                        break; // 緩存不夠,直接退出
                     }
       
                     unsigned char *pload = (unsigned char *)q->payload;
                     for(unsigned int i = 0; i < q->len; i++)
                     {
                        msg.pbuf[msg.length++] = pload[i];
                     }
                  }
      
                  netbuf_delete(recvbuf);
               }
            }
            taskEXIT_CRITICAL(); // 退出臨界區
            xQueueSend(TcpClientQueue, &msg, 0);
         }
         else if(recv_err == ERR_CLSD) // 服務器主動關閉連接
         {
            tcp_client_reconnect();
         }
      }
  
      // 連接異常
      netconn_close(socket); // 關閉套接字
      netconn_delete(socket); // 釋放資源
   }
}

/********************************************************
 * 函數功能:創建TCP客戶端
 * 形    參:ip_msg:IP信息數據結構指針
 * 返 回 值:0=成功
             1=TCP客戶端線程創建失敗
             2=TCP客戶端數據列隊創建失敗
 ********************************************************/
unsigned int tcp_client_init(void *ip_msg)
{
   vPortFree(TcpClientQueue);
   TcpClientQueue = xQueueCreate(50, sizeof(sTcpClientRxMsg_t));
   if(TcpClientQueue == NULL)
   {
      return 2;
   }
 
   // 創建任務,參數:任務函數、任務名稱、任務堆棧大小、任務函數形參、任務優先級、任務句柄
   if(xTaskCreate(tcp_client_thread, "tcp_client_thread", 256, ip_msg, 5, NULL) != pdPASS)
   {
      return 1; // 創建失敗
   }
   return 0;
}

/********************************************************
 * 函數功能:獲取TCP客戶端數據列隊句柄
 * 形    參:無
 * 返 回 值:數據列隊句柄
 ********************************************************/
void *tcp_client_queue(void)
{
   return TcpClientQueue;
}

/********************************************************
 * 函數功能:TCP客戶端重連
 * 形    參:無
 * 返 回 值:0=成功
             1=失敗
 ********************************************************/
unsigned int tcp_client_reconnect(void)
{
   connect_flag = pdFALSE; // 清除連接標志
   if(connect_flag == pdFALSE)
   {
      return 0;
   }
   else
   {
      return 1;
   }
}

/********************************************************
 * 函數功能:獲取TCP客戶端連接狀態
 * 形    參:無
 * 返 回 值:0=連接正常
             1=連接異常
 ********************************************************/
unsigned int tcp_client_connect_status_get(void)
{
   if(connect_flag == pdFALSE)
   {
      return 1;
   }
   else
   {
      return 0;
   }
}

/********************************************************
 * 函數功能:TCP客戶端向網口發送數據
 * 形    參:pbuf:數據緩存地址
             length:發送數據字節數
 * 返 回 值:0=成功
             1=數據緩存地址為NULL
             2=數據長度錯誤
             3=客戶端未啟動
             4=連接異常
 ********************************************************/
unsigned int tcp_client_write(const unsigned char *pbuf, unsigned int length)
{
   unsigned char retry = 5;
 
   if(pbuf == NULL)
   {
      return 1;
   }
 
   if(length == 0)
   {
      return 2;
   }
 
   if(connect_flag == pdFALSE)
   {
      return 3;
   }
 
   while(netconn_write(socket, pbuf, length, NETCONN_COPY) != ERR_OK) // 發送數據
   {
      vTaskDelay(100);
      if(--retry == 0)
      {
         tcp_client_reconnect();
         return 4;
      }
   }
 
   return 0; // 發送成功
}
 
main.c
 
#include "stm32f4xx.h"
#include "delay.h"
#include "led.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
#include "sys_eth.h"
#include "tcp_client.h"
// TCP客戶端接收數據結構
#pragma pack(push, 1)
typedef struct _sTcpClientRxMsg
{
   unsigned char *pbuf;
   unsigned int length;
}sTcpClientRxMsg_t;
#pragma pack(pop)

static void tcp_client(void *parg)
{
   sLwipDev_t sLwipDev = {0};
   sTcpClientRxMsg_t msg = {0};
 
   // 以太網、LwIP協議棧初始化
   eth_default_ip_get(&sLwipDev);
   while(eth_init(&sLwipDev) != 0)
   {
      vTaskDelay(500);
   }
 
   tcp_client_init(&sLwipDev);
 
   while(1)
   {
      if(xQueueReceive(tcp_client_queue(), &msg, portMAX_DELAY) == pdPASS)
      {
         tcp_client_write(msg.pbuf, msg.length);
       vPortFree(msg.pbuf);
      }
   }
}

int main(void)
{
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
   systick_init();
 
   // 參數:任務函數指針、任務名稱、任務棧大小、任務函數形參、任務優先級、任務句柄
   xTaskCreate(tcp_client, "tcp_client", 512, NULL, 3, NULL); // 創建TCP客戶端任務
 
   // 啟動任務調度器
   vTaskStartScheduler();
   return 1; // 運行到此處表示系統異常
}
 
3、運行結果 
  

 

   可以看到運行結果正常。

 

  至此TCP客戶端創建完成。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM