1. 程式人生 > >LwIP例項程式碼分析

LwIP例項程式碼分析

//開發環境IAR 
int APP_Main(void)
{
#if C_USE_MCU_STM32F4xx
 /* Ethernet configuration --------------------------------------*/
Ethernet_Initial();
while(1)
{
   Ethernet_Handler();    
}
#endif  /*end C_USE_MCU_STM32F4xx */
}

/** @param  netif: the network interface//網路介面*/
void Ethernet_Initial(void)
{
    /* config the Ethernet Reset Pin*///配置乙太網復位引腳
    Ethernet_Reset_Config();

 /* config the Ethernet Link interrupt*///配置乙太網鏈路中斷
    Ethernet_Config();

    /* Initilaize the LwIP stack */ //初始化LwIP堆疊
    lwip_init();

    //netif_remove(&gnetif);
    /* Configure the Network interface */ //配置網路介面
    Netif_Config();

    /* Notify user about the network interface config *///通知使用者有關網路介面配置
    User_notification(&gnetif);
    /*Connects to the TCP echo server*///連線到TCP echo伺服器
    tcp_echoclient_connect();
}

void Ethernet_Reset_Config(void)
{
  GPIO_Set_Mode(ETHERNET_RST_CTRL_PORT,ETHERNET_RST_CTRL_PIN,GPIO_MODE_OUTPUT_PP, GPIO_NOPULL);  //PA4

    GPIO_RESET(ETHERNET_RST_CTRL_PORT, ETHERNET_RST_CTRL_PIN);
    delay_cycles_ms(1);
    GPIO_SET(ETHERNET_RST_CTRL_PORT, ETHERNET_RST_CTRL_PIN);
    delay_cycles_ms(10);
}

void Ethernet_Config(void)
{
    //congig the ETH_INT 需要配置,斷網自動聯網機制
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Enable PX.x to IT mode: Ethernet Link interrupt */
    ETHERNET_MII_INT_CLK_ENABLE();
    GPIO_InitStructure.Pin = ETHERNET_MII_INT_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStructure.Pull = GPIO_NOPULL ;
    HAL_GPIO_Init(ETHERNET_MII_INT_PORT, &GPIO_InitStructure);      //PB9

 /* Enable EXTI Line interrupt for ETH MII pin */
    HAL_NVIC_SetPriority(ETHERNET_MII_INT_IRQ , 0x5, 0x0);
    HAL_NVIC_EnableIRQ(ETHERNET_MII_INT_IRQ);
}

void lwip_init(void)
{
  stats_init();
  mem_init();//將堆歸零並初始化start,end和free-free
  memp_init();//將memp_memory劃分為每個池型別的連結列表。
  pbuf_init();//初始化pbuf模組
  netif_init();// 網路介面 netif
  udp_init();//清除UDP PCB列表。
  tcp_init();//清除TCP PCB列表並清除一些內部定時器。注:在這個初始化函式之後,你必須按預先確定的每個週期內呼叫       tcp_fasttmr() 和 tcp_slowtmr()函式。
  sys_timeouts_init();//初始化lwip超時時間限制

}


void Netif_Config(void)
{
    struct ip_addr ipaddr;
    struct ip_addr netmask;
    struct ip_addr gw;
    uint8_t temp_addr[4];

//自動獲取IP
    if(g_Config.Ethernet.DHCP[0] == 0x01)
    {
        ipaddr.addr = 0;
        netmask.addr = 0;
        gw.addr = 0;
    }else{
        //IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
        if(F_Conver_IP_Addr(g_Config.Ethernet.LocalIP, temp_addr))
        {
            IP4_ADDR(&ipaddr, temp_addr[0], temp_addr[1], temp_addr[2], temp_addr[3]);
        }else{//格式錯誤使用預設引數
            IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
        }
        //IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
        if(F_Conver_IP_Addr(g_Config.Ethernet.NetMask, temp_addr))
        {
            IP4_ADDR(&netmask, temp_addr[0], temp_addr[1], temp_addr[2], temp_addr[3]);
        }else{//格式錯誤使用預設引數
            IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
        }
        //IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
        if(F_Conver_IP_Addr(g_Config.Ethernet.GateWay, temp_addr))
        {
            IP4_ADDR(&gw, temp_addr[0], temp_addr[1], temp_addr[2], temp_addr[3]);
        }else{//格式錯誤使用預設引數
            IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
        }
    }
    /* Add the network interface */
    netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);

    /* Registers the default network interface */
    netif_set_default(&gnetif);//註冊預設網路介面

    if (netif_is_link_up(&gnetif))
    {
        /*  Creates a new DHCP client for this interface on the first call.
        Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
        the predefined regular intervals after starting the client.
        You can peek in the netif->dhcp struct for the actual DHCP status.*/
        if(g_Config.Ethernet.DHCP[0] == 0x01)
        {
           dhcp_start(&gnetif); 
        }
        /* When the netif is fully configured this function must be called */
        netif_set_up(&gnetif);
    }
    else
    {
        /* When the netif link is down this function must be called */
        netif_set_down(&gnetif);
    }

    /* Set the link callback function, this function is called on change of link status */
    netif_set_link_callback(&gnetif, ethernetif_update_config);

}

void tcp_echoclient_connect(void)
{
    struct ip_addr DestIPaddr;
    uint8_t temp_addr[4];

    if(echoclient_pcb != NULL)
    {//只建立一個TCP Client
        tcp_echoclient_connection_close(echoclient_pcb, echoclient_es);
        tcp_pcb_remove(&tcp_active_pcbs, echoclient_pcb);
        memp_free(MEMP_TCP_PCB, echoclient_pcb);
        echoclient_pcb = NULL;
        echoclient_es = NULL;
    }

    echoclient_pcb = tcp_new();

    if (echoclient_pcb != NULL)
    {//new an echoclient_pcb Success
        //IP4_ADDR( &DestIPaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3 );
        if(F_Conver_IP_Addr(g_Config.Net.Server1_IP, temp_addr))
        {
            IP4_ADDR(&DestIPaddr, temp_addr[0], temp_addr[1], temp_addr[2], temp_addr[3]);
        }else{//格式錯誤使用預設引數
            IP4_ADDR(&DestIPaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3);
        }

        /* connect to destination address/port */
        //tcp_connect(echoclient_pcb,&DestIPaddr,DEST_PORT,tcp_echoclient_connected);
        tcp_connect(echoclient_pcb, &DestIPaddr,(uint16_t)F_Conver_PORT_Addr(g_Config.Net.Server1_Port),
                    tcp_echoclient_connected);
    }
    else
    {//new an echoclient_pcb fail
        /* deallocate the pcb */
        memp_free(MEMP_TCP_PCB, echoclient_pcb);
    }
}

void Ethernet_Handler(void)
{
    /* Check TCP Connect */
    Check_TCP_Client_Connect();//檢查客戶端是否與伺服器相連

    /* Read a received packet from the Ethernet buffers and send it
    to the lwIP for handling */ //從乙太網緩衝區讀取接收到的資料包併發送到lwIP進行處理
    ethernetif_input(&gnetif);

    /* Handle timeouts *///超時處理
    sys_check_timeouts();
    //LwIP定時處理任務(心跳、收發)
    LwIP_Periodic_Handle(Ethernet_LocalTime);
}

struct tcp_pcb *Check_TCP_Client_Connect(void)
{//檢查客戶端是否與伺服器相連
    struct tcp_pcb *cpcb = NULL;
    err_t err;
    cpcb = tcp_active_pcbs;
    if(cpcb != NULL)//
    {//裝置連線成功
        g_TCPLinkClient_Timeout = 300;
        if( g_TCPLinkCheck_Cnt == 0 )
        {//超時未收到資料
            tcp_echoclient_connect();//重新連線
            g_TCPLinkCheck_Cnt = 100;
            //g_TCPLinkCheck_Cnt = g_EthTcp_Proto.HeartInterval_Set+60;   //斷網檢測時間
        }else{

        }
        return cpcb;
    }else{//連線斷開或者未連線
        //-----------------------------------------
        if((g_TCPLinkConnect_flag == 0) && (g_TCPLinkCheck_Cnt == 0))
        {
            tcp_echoclient_connect();//重新連線
            g_TCPLinkCheck_Cnt = 20;//60;
        }else if(g_TCPLinkConnect_flag == 1) {
            /* send data */
            //通過傳送一組資料來偵測remote 是否斷開
            //sprintf((char*)data, "message %d", (int)message_count);
            sprintf((char*)data, "");
            err = tcp_write(cpcb, data, strlen((char*)data), TCP_WRITE_FLAG_MORE);//ecoh 資料
            if(err == ERR_OK)
            {//server 意外斷開,而client 沒有偵測到,重新連線
                g_TCPLinkCheck_Cnt = 5;
                g_TCPLinkConnect_flag = 0;
            }
            else
            {//server 意外斷開,重新連線
                g_TCPLinkCheck_Cnt = 15;
                g_TCPLinkConnect_flag = 0;
            }
        }
        return cpcb;
    }
}


/**
  * @brief This function should be called when a packet is ready to be read
  * from the interface. It uses the function low_level_input() that
  * should handle the actual reception of bytes from the network
  * interface. Then the type of the received packet is determined and
  * the appropriate input function is called.

當資料包準備好被讀取時,應呼叫此函式
   *來自介面。 它使用函式low_level_input()
   *應該處理來自網路的實際位元組接收
   *介面。 然後確定接收分組的型別
   *呼叫適當的輸入函式。
  *
  * @param netif the lwip network interface structure for this ethernetif
  */
void ethernetif_input(struct netif *netif)
{
  err_t err;
  struct pbuf *p;

  /* move received packet into a new pbuf */ / *將收到的資料包移動到新的pbuf * /
  p = low_level_input(netif);

  /* no packet could be read, silently ignore this *// *無法讀取資料包,默默忽略此* /
  if (p == NULL) return;

  /* entry point to the LwIP stack */ / * LwIP堆疊的入口點* /
  err = netif->input(p, netif);

  if (err != ERR_OK)//輸入錯誤
  {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
    pbuf_free(p);
    p = NULL;
  }
}

/ **
* @brief LwIP定期任務
* @param localtime當前的LocalTime值
* @retval沒有
* /
void LwIP_Periodic_Handle(__ IO uint32_t localtime)
{
    / *每250毫秒TCP週期性過程* /
    if(localtime - TCPTimer> = TCP_TMR_INTERVAL)
    {
        TCPTimer = localtime;
        tcp_tmr();
    }

    / *每5秒ARP週期程序* /
    if((localtime - ARPTimer)> = ARP_TMR_INTERVAL)
    {
        ARPTimer = localtime;
        etharp_tmr();
    }
    if(g_Config.Ethernet.DHCP [0] == 0x01)
    {
        / *每500ms精細DHCP定期程序* /
        if(localtime - DHCPfineTimer> = DHCP_FINE_TIMER_MSECS)
        {
            DHCPfineTimer = localtime;
            dhcp_fine_tmr();
            if((DHCP_state!= DHCP_ADDRESS_ASSIGNED)&&(DHCP_state!= DHCP_TIMEOUT))
            {
                / *切換LED1以指示DHCP正在進行的過程* /
                // STM_EVAL_LEDToggle(LED1);

                / *程序DHCP狀態機* /
                LwIP_DHCP_Process_Handle();
            }
        }

        / * DHCP每60秒進行一次粗略週期過程* /
        if(localtime - DHCPcoarseTimer> = DHCP_COARSE_TIMER_MSECS)
        {
            DHCPcoarseTimer = localtime;
            dhcp_coarse_tmr();
        }
    }