STM32 + UIP + ENC28J60 實現TCP 簡單通訊


MCU: STM32F103C6T6

 背景

上次介紹了怎么把UIP移植到STM32中來,並最后實現一個ping操作,這次在上次基礎上實現MCU當TCP服務端,電腦當客戶端通過TCP端鏈接MCU,實現通訊。

為保證程序盡量精簡,程序在接受到TCP數據后,會原封不動返回給客戶端(電腦), 並通過串口打印。

在使用UIP TCP功能前,需要可以讓MCU獲取當前時間,主要為實現每10ms毫秒處理一次TCP連接,和每5s秒刷新一次ARP;例如HAL庫中有一個HAL_GetTick(),可以獲取當前毫秒時間。

操作流程

整體TCP使用流程:

1. 初始化enc28j60、UIP

2. 設置IP、網關、子網掩碼

3. 開啟端口監聽

4. 處理ARP請求、響應

5. 每10ms處理一次TCP請求

6. 每5秒刷新一次ARP

7. 通過UIP提供的UIP_APPCALL回調,接收TCP數據、發送TCP數據

main主循環中每10ms處理一批TCP請求、每5s 刷新一次ARP.在操作前,需保證網卡初始化正常,UIP運行正常。

TCP操作

網卡部分初始化、UIP初始化、設置IP、網關等信息不在重復介紹,主要介紹TCP操作部分。

設置TCP端口

通過調用uip的uip_listen函數,來設置要監聽的TCP端口:

uip_listen(HTONS(8099));//8099為端口號

這里注意,uip_listen內套了一個HTONS函數(宏),端口號是用HTONS包裹的。

聲明UIP回調函數UIP_APPCALL實現

UIP_APPCALL也是一個宏。UIP會在各種事件觸發的時候調用它(個人理解..)

TCP的接收、發送也是在此處做;

首先,需要先創建一個函數,例如在main.c中聲明一個main_appcall:

//處理接收到的TCP消息
void main_appcall() {
     //處理UIP各種事件,TCP數據接收、發送,就在此處處理
     if( uip_newdata() ){
         //有新數據,uip_appdata為TCP數據、uip_len為TCP數據長度
         print3(uip_appdata, uip_len);//可取消,調試信息
         uip_send(uip_appdata,uip_len);//發送TCP數據
     }
}

然后在uip-conf.h中聲明UIP_APPCALL宏

void main_appcall();
#define UIP_APPCALL main_appcall

最后在設置UIP監聽、主循環中處理TCP請求:

uip_listen(HTONS(1234));//設置監聽

...
while(1){
//UIP_COUNTS : TCP最大連接數
            for(uint8_t i = 0; i < UIP_CONNS; i++)
                {
                        uip_periodic(i);
                        if(uip_len > 0)
                        {
                                //有TCP新數據
                                print("tcp data handler...");
                                uip_arp_out();
                                tapdev_send();
                        }
                }
}

最后附上main.c部分代碼:

#define UIP_BUF ((struct uip_eth_hdr *)&uip_buf[0]);
void main_appcall() {
     //處理UIP各種事件,TCP數據接收、發送,就在此處處理
     if( uip_newdata() ){
         //有新數據,uip_appdata為TCP數據、uip_len為TCP數據長度
         print3(uip_appdata, uip_len);//可取消,調試信息
         uip_send(uip_appdata,uip_len);//發送TCP數據
     }
}

//記錄ARP、TCP刷新時間,目的實現定時處理TCP請求、刷新ARP
uint32_t lastTimer = 0,lastTimerARP = 0;

int main(void)
{
  /* USER CODE BEGIN 1 */
    
  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    
  MX_USART1_UART_Init();
    //設置開始時間
    lastTimer = HAL_GetTick();
    lastTimerARP = HAL_GetTick();
    //等待一些時間,准備初始化ENC28J60
  /* USER CODE BEGIN 2 */
    for(int i = 0;i < 20;i++) {
        //enc28j60PhyWrite(PHLCON,0x7a4);    
        HAL_Delay(500);
        print("begin init enc28j60...");
    }
    //開始初始化,如果初始化不成功會阻塞
    //enc28j60_init(my_mac);
    tapdev_init();//初始化enc28j60
    //表示初始化成功,說明接線正常
    print("init enc28j60 success!!!");
    uip_init();
    uip_ipaddr_t ipaddr;
    uip_ipaddr(ipaddr, 192, 168, 1, 8);
    uip_sethostaddr(ipaddr);
    uip_ipaddr(ipaddr, 192, 168, 1, 1);
    uip_setdraddr(ipaddr);
    uip_ipaddr(ipaddr, 255, 255, 252, 0);
    uip_setnetmask(ipaddr);
    
    //開啟指定端口監聽, 注意端口處HTONS,不是直接寫的端口號!
    uip_listen(HTONS(1234)); /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    
  while (1)
  {
    /* USER CODE END WHILE */
        //處理IP、ARP、Ping消息
        uip_len = tapdev_read();
        if(uip_len != 0){
            if(UIP_BUF->type == htons(UIP_ETHTYPE_ARP)) {
                uip_arp_arpin();
                if(uip_len>0){
                    print("rarp package send!!!");
                    tapdev_send();
                }
            }
            if(UIP_BUF->type == htons(UIP_ETHTYPE_IP)) {
                uip_arp_ipin();
                uip_input();
                if(uip_len > 0){
                    uip_arp_out();
                    tapdev_send();
                }
            }
        }
        
        //處理TCP鏈接,10毫秒處理一次
        if((HAL_GetTick() - lastTimer) > 10) {
            //UIP_COUNTS : TCP最大連接數
            for(uint8_t i = 0; i < UIP_CONNS; i++)
                {
                        uip_periodic(i);
                        if(uip_len > 0)
                        {
                                //有TCP新數據
                                print("tcp data handler...");
                                uip_arp_out();
                                tapdev_send();
                        }
                }
                lastTimer = HAL_GetTick();
        }
        //ARP 刷新
        if((HAL_GetTick() - lastTimerARP) > 5000) {
            lastTimerARP = HAL_GetTick();
            uip_arp_timer();
        }
        
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 

最后附上全部代碼(keil5),已放在藍奏雲:https://wwb.lanzouw.com/ieZu2y933pe

非常感謝xukai871105總結的筆記,本次TCP成功跑通全靠這篇文章,推薦去看一看,寫的很詳細: https://blog.csdn.net/xukai871105/article/details/17471865


免責聲明!

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



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