設置 | 登錄 | 註冊

作者共發了10篇帖子。

【程序】lwip-2.0.2通過DHCP自動從路由器獲取IP位址

1樓 巨大八爪鱼 2017-3-29 22:04
原始程序:
https://zh.arslanbar.net/post.php?t=24571
打開lwipopts.h,把#define LWIP_DHCP 0改成#define LWIP_DHCP 1,啟用DHCP。
然後修改main.c文件:
#include <stdio.h>
#include <stm32f10x.h>
#include "lwip/etharp.h" // etharp_tmr函數所在的頭文件
#include "lwip/init.h" // lwip_init函數所在的頭文件

// 與DHCP有關的頭文件
#include "lwip/dhcp.h"
#include "lwip/prot/dhcp.h" // DHCP狀態常量所在的頭文件

#include "lwip/priv/tcp_priv.h" // tcp_tmr函數所在的頭文件
#include "netif/ethernet.h" // ethernet_input函數所在頭文件
#include "ENC28J60.h"

// 這兩個函數位於ethernetif.c中, 但沒有頭文件聲明
err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif);

// 聲明httptest.c中的函數
void init_http(void);

// printf串口輸出
// 必須要在項目屬性里勾選Use MicroLIB後才能使用
int fputc(int ch, FILE *fp)
{
    if (fp == &__stdout)
    {
        if (ch == '\n')
        {
            // 遇到\n自動在前面添加\r
            USART1->DR = '\r';
            while ((USART1->SR & USART_SR_TXE) == 0);
        }
        USART1->DR = ch;
        while ((USART1->SR & USART_SR_TXE) == 0);
    }
    return ch;
}

void show_addr(struct netif *netif)
{
    // 獲取網卡的DHCP信息
    //struct dhcp *dhcp = netif->client_data[LWIP_NETIF_CLIENT_DATA_INDEX_DHCP];
    struct dhcp *dhcp = netif_dhcp_data(netif); // 這個宏相當於上面的語句
   
    // 顯示IP位址
    static uint8_t displayed = 0;
    if (dhcp->state == DHCP_STATE_BOUND)
    {
        // 如果DHCP分配成功就顯示IP位址
        if (displayed == 0) // 只顯示一次
        {
            printf("DHCP分配成功!\n");
            printf("IP位址: %s\n", ip4addr_ntoa(&dhcp->offered_ip_addr));
            printf("子網掩碼: %s\n", ip4addr_ntoa(&dhcp->offered_sn_mask));
            printf("網關: %s\n", ip4addr_ntoa(&dhcp->offered_gw_addr));
            //printf("DHCP狀態碼: %d\n", dhcp->state);
            displayed = 1;
        }
    }
    else
        displayed = 0;
}

int main(void)
{
    struct ip4_addr ipaddr, netmask, gw;
    struct netif enc28j60;
    uint8_t cnt = 0;
   
    // 配置PA口
    RCC->APB2ENR = RCC_APB2ENR_IOPAEN;
    GPIOA->CRH = 0x000004b3; // PA8為開發板上的一個LED燈
    GPIOA->CRL = 0xb4bb0080;
    GPIOA->BSRR = GPIO_BSRR_BS1; // PA1為網卡中斷輸出
   
    // 配置SPI
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_BR_1; // 主機模式, 時鐘至少需要8分頻(BR=010), 也就是72MHz/8=9MHz
    SPI1->CR2 = SPI_CR2_SSOE; // 開CS片選輸出
   
    // 配置串口
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    USART1->BRR = 0x271; // 波特率: 115200
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
   
    // 配置定時器
    RCC->APB1ENR = RCC_APB1ENR_TIM6EN;
    TIM6->ARR = 2499; // 共2500個數, 2500*0.1ms=250ms
    TIM6->PSC = 7199; // 72MHz/7200=10kHz -> 0.1ms
    TIM6->CR1 = TIM_CR1_URS; // 防止UG=1時UIF置位
    TIM6->EGR = TIM_EGR_UG; // 應用上述設置
    TIM6->CR1 |= TIM_CR1_CEN; // 開定時器
   
    lwip_init();
    ip4_addr_set_zero(&ipaddr); // 暫時將IP位址設為0
    ip4_addr_set_zero(&netmask); // 子網掩碼設為0
    ip4_addr_set_zero(&gw); // 網關也要設為0
   
    netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ethernetif_init, ethernet_input);
    netif_set_default(&enc28j60); // 設為默認網卡
    netif_set_up(&enc28j60);
   
    dhcp_start(&enc28j60); // 啟動DHCP
    init_http(); // 初始化HTTP服務
    while (1)
    {
        if (ENC28J60_GetPacketNum() != 0)
        {
            GPIOA->ODR ^= GPIO_ODR_ODR8; // PA8上的LED燈閃爍表明系統正常工作
            ethernetif_input(&enc28j60);
        }
       
        show_addr(&enc28j60); // 如果DHCP分配地址成功就顯示IP位址
       
        // 若定時器溢出
        if (TIM6->SR & TIM_SR_UIF)
        {
            // 250ms
            TIM6->SR &= ~TIM_SR_UIF; // 清除溢出標誌
            cnt++;
            if (cnt >= 250)
            {
                cnt = 0; // 250ms*240=1min
                dhcp_coarse_tmr();
            }
           
            tcp_tmr(); // TCP定時處理
            if (cnt % 2 == 0)
                dhcp_fine_tmr(); // 250ms*2=500ms
            if (cnt % 4 == 0)
            {
                // 250ms*4=1s
                etharp_tmr(); // ARP定時處理
            }
            if (cnt % 20 == 0)
                GPIOA->ODR ^= GPIO_ODR_ODR8; // LED燈狀態切換(250ms*20=5s)
        }
    }
}
2樓 巨大八爪鱼 2017-3-29 22:08
路由器中可以查看到DHCP分配的IP位址:
3樓 巨大八爪鱼 2017-3-29 22:09

用分配的IP位址訪問HTTP網站:

4樓 巨大八爪鱼 2017-3-29 22:10
串口輸出的內容:

5樓 巨大八爪鱼 2017-3-29 22:15
【注意】
[1] lwipopts.h中不需要開ARP,也就是說不需要將LWIP_ARP定義為1
[2] 網卡驅動文件無需修改。不需要將ERXFCON_BCEN位設為1。
6樓 巨大八爪鱼 2017-3-29 22:18

手機連接無線路由器後,用瀏覽器通過自動分配的IP位址訪問HTTP網站的截圖:

7樓 巨大八爪鱼 2017-3-30 10:07

回復5樓 @巨大八爪鱼 的內容:

【注意】
[1] lwipopts.h中不需要開ARP,也就是說不需要將LWIP_ARP定義為1
[2] 網卡驅動文件無需修改。不需要將ERXFCON_BCEN位設為1。

雖然沒有在lwipopts.h中開ARP,但在opts.h頭文件中有這樣的定義:
#if !defined LWIP_ARP || defined __DOXYGEN__
#define LWIP_ARP                        1
#endif
也就是說只要沒有在lwipopts.h中定義LWIP_ARP,就會自動將LWIP_ARP定義為1。

所以其實ARP是自動打開了的。

8樓 巨大八爪鱼 2017-3-31 16:09

【配置說明】
定時器設為250ms溢出一次,主循環while(1)中添加了一些與DHCP有關的響應函數。
請在ethernetif.c中設置一個合適的網卡MAC地址,以免路由器無法識別導致網卡只能接收不能發送數據:
/* set MAC hardware address */
netif->hwaddr[0] = 0;
netif->hwaddr[1] = 'R';
netif->hwaddr[2] = 'M';
netif->hwaddr[3] = 'N';
netif->hwaddr[4] = 'E';
netif->hwaddr[5] = 'T';


另外,由於現在IP位址是自動分配,所以初始IP位址、子網掩碼和網關都為0,調用netif_add函數時可直接用IP_ADDR_ANY代替,不再單獨設3個地址變量:
lwip_init();
netif_add(&enc28j60, IP_ADDR_ANY, IP_ADDR_ANY, IP_ADDR_ANY, NULL, ethernetif_init, ethernet_input);
netif_set_default(&enc28j60);
netif_set_up(&enc28j60);
之前聲明的struct ip4_addr ipaddr, netmask, gw可以刪除了。
以下三句話也要去掉:
ip4_addr_set_zero(&ipaddr);

ip4_addr_set_zero(&netmask);
ip4_addr_set_zero(&gw);

9樓 巨大八爪鱼 2017-3-31 18:36
【設備名的配置】
現在,在路由器的DHCP設備列表頁面顯示的客戶端名稱是Unknown。為了讓路由器能知道MAC地址對應的設備名稱,可先在lwipopts.h中添加如下的定義:
#define LWIP_NETIF_HOSTNAME 1
然後在main.c的dhcp_start(&enc28j60)前面加上這樣一句話:
enc28j60.hostname = "ENC28J60_Device";
編譯並下載程序後,路由器的頁面就可以看到新的設備名稱了:


10樓 巨大八爪鱼 2017-4-2 21:40
if (dhcp->state == DHCP_STATE_BOUND)
可以用下面的語句代替:
if (dhcp_supplied_address(netif))
修改後可以去掉包含如下頭文件的語句:
#include "lwip/prot/dhcp.h" // DHCP狀態常量所在的頭文件

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。