日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

STM32+enc28j60+uip 实现 单片机 ping PC端

發(fā)布時(shí)間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32+enc28j60+uip 实现 单片机 ping PC端 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

STM32+enc28j60+uip 實(shí)現(xiàn)單片機(jī) ping PC端

  • 1. 前言
  • 2. 實(shí)驗(yàn)簡(jiǎn)介
  • 3. uip簡(jiǎn)介
  • 4. icmp簡(jiǎn)介
    • 4.1 icmp介紹
    • 4.2 請(qǐng)求回顯或回顯應(yīng)答報(bào)文格式介紹
  • 5. 實(shí)驗(yàn)環(huán)境
  • 6. 實(shí)驗(yàn)內(nèi)容
    • 6.1 實(shí)驗(yàn)方案
    • 6.2 請(qǐng)求回顯報(bào)文的發(fā)送
    • 6.3 回顯應(yīng)答報(bào)文的接收與校驗(yàn)
  • 7. 實(shí)驗(yàn)結(jié)果
  • 8. 實(shí)驗(yàn)結(jié)果分析
  • 9. 總結(jié)

1. 前言

臨近畢業(yè),多年在csdn等各大論壇闖蕩(學(xué)習(xí))的我,終于下定決心,開始寫自己人生中的第一篇博客。

在學(xué)習(xí)了一段時(shí)間的uip協(xié)議棧后,走了很多彎路,所以想與大家分享自己的學(xué)習(xí)經(jīng)歷。本人沒啥文筆,只能將自己所學(xué)所感與大家分享,本文的部分內(nèi)容也是通過(guò)csdn等各大論壇收集整理而來(lái),忠心希望大家能將意見或者建議在評(píng)論區(qū)與我分享,與大家共勉。

2. 實(shí)驗(yàn)簡(jiǎn)介

本次實(shí)驗(yàn)主要采用stm32最小系統(tǒng)開發(fā)板,MCU為stm32f103c8t6,搭載了enc28j60以太網(wǎng)模塊,工程代碼基于uip協(xié)議棧,實(shí)現(xiàn)了實(shí)現(xiàn)單片機(jī) ping PC端。

3. uip簡(jiǎn)介

關(guān)于uip的學(xué)習(xí),可參考xukai871105大神的博客—【uIP學(xué)習(xí)筆記】

4. icmp簡(jiǎn)介

4.1 icmp介紹

ICMP(Internet Control Message Protocol),網(wǎng)絡(luò)控制消息協(xié)議。它是TCP/IP協(xié)議簇的一個(gè)子協(xié)議,用于在IP主機(jī)、路由器之間傳遞控制消息。ICMP的協(xié)議號(hào)為1。

ICMP協(xié)議的功能主要有:
(1)確認(rèn)IP包是否成功到達(dá)目標(biāo)地址。
(2)通知在發(fā)送過(guò)程中IP包被丟棄的原因。

ICMP報(bào)文是在IP報(bào)文內(nèi)部的!!!
ICMP報(bào)文分為查詢報(bào)文和差錯(cuò)報(bào)文。

4.2 請(qǐng)求回顯或回顯應(yīng)答報(bào)文格式介紹

注:本次實(shí)驗(yàn)主要實(shí)現(xiàn)的是ping功能,用到的是ICMP查詢報(bào)文中的請(qǐng)求回顯或回顯應(yīng)答報(bào)文(Echo or Echo Reply Message),所以對(duì)icmp其他報(bào)文類型不做展開。

報(bào)文內(nèi)容的開始,是以太網(wǎng)幀頭,包括目的主機(jī)的mac地址,源主機(jī)的mac地址,協(xié)議類型,共14Bytes(PC端的網(wǎng)卡MAC地址可通過(guò)cmd命令:ipconfig/all 查看,由于enc28j60沒有唯一的mac標(biāo)識(shí),在實(shí)驗(yàn)時(shí)可隨機(jī)設(shè)置)。

其次是報(bào)文類型,該處字符若為0x8000,說(shuō)明該報(bào)文是IPv4類型。

接著是IP首部字段(IP Header),總長(zhǎng)20Bytes。首部字段包括:

(1)IP Version:4,說(shuō)明是IPv4),1Bytes;
(2)包頭長(zhǎng)度(Header Length),1Bytes;
(3)區(qū)分服務(wù)領(lǐng)域(Differentiated Services Field),1Bytes;
(4)總長(zhǎng)度(Total Length),1Bytes;
(5)標(biāo)識(shí)符(Identification),2Bytes;
(6)標(biāo)記字段(Flags),2Bytes;
(8)報(bào)文生存時(shí)間(TTL),1Bytes;
(9)報(bào)文所用協(xié)議類型(Protocol),1Bytes;
(10)IP包頭檢驗(yàn)和(IP Header checksum),2Bytes,
(11)源IP(發(fā)送方IP),4Bytes;
(12)目的IP(接收方IP),4Bytes。

其中,IP包頭檢驗(yàn)和計(jì)算方法如下:
1.checksum的初始值自動(dòng)被設(shè)置為0
2.接著,以16bit為單位,兩兩相加,對(duì)于該例子,即為:E34F + 2396 + 4427 + 99F3 = 1E4FF
3.若計(jì)算結(jié)果大于0xFFFF,則將,高16位加到低16位上,對(duì)于該例子,即為0xE4FF + 0x0001 = E500

注:校驗(yàn)和部分很重要,如果校驗(yàn)和出錯(cuò),會(huì)導(dǎo)致報(bào)文被過(guò)濾,從而使得接收方接收不到該報(bào)文。

再接著是ICMP字段,總長(zhǎng)40Bytes。其中包括:
(1)類型Type(Type: 8 表示icmp echo request,請(qǐng)求回顯),1Bytes;
(2)代碼值(code,code: 0x00表示請(qǐng)求回顯),1Bytes;
(3)校驗(yàn)和(checksum),2Bytes;
(4)Identifier(用于區(qū)分不同的PING進(jìn)程),2Bytes,對(duì)于unix以及類unix操作系統(tǒng)來(lái)說(shuō),icmp Identifier的內(nèi)容就是ping的進(jìn)程號(hào),對(duì)于windows系統(tǒng)來(lái)說(shuō),具體參考如下:
Microsoft Windows NT - 256
Microsoft Windows 98/98SE - 512
Microsoft Windows 2000 - 512
Microsoft Windows ME - 768
Microsoft Windows 2000 Family with SP1 - 768
既然windows系統(tǒng)的icmp Identifier是固定不變的,那么系統(tǒng)如何區(qū)別不同的Ping進(jìn)程呢?實(shí)際上windows系統(tǒng)就不在根據(jù)Identifier來(lái)區(qū)別ping進(jìn)程了,它是根據(jù)Sequence Number field來(lái)區(qū)分的。
(5)序列號(hào)(Sequence number),2Bytes,區(qū)分發(fā)送順序,與IP Header中的標(biāo)識(shí)符類似。
(6)數(shù)據(jù)段(data),32Bytes,作為icmp 請(qǐng)求回顯或回顯應(yīng)答報(bào)文的話,發(fā)送數(shù)據(jù)Data的內(nèi)容可以是隨機(jī)的。

看完了格式內(nèi)容之后,同學(xué)們可以動(dòng)動(dòng)手,用wireshark抓取icmp包,看看報(bào)文中各個(gè)部分的具體內(nèi)容。

5. 實(shí)驗(yàn)環(huán)境

單片機(jī)部分:stm32+enc28j60

PC端部分:win10,串口調(diào)試助手,wireshark

其他:單片機(jī)與PC端網(wǎng)線直連(并保證單片機(jī)與PC在同一網(wǎng)段)
單片機(jī)IP: 192.168.1.8
PC端IP: 192.168.1.5
網(wǎng)關(guān): 192.168.1.1

6. 實(shí)驗(yàn)內(nèi)容

6.1 實(shí)驗(yàn)方案

本次實(shí)驗(yàn),主要分為請(qǐng)求回顯報(bào)文的發(fā)送和回顯應(yīng)答報(bào)文的接收兩部分,已經(jīng)知道了報(bào)文的具體內(nèi)容之后,我們便可以自己構(gòu)建報(bào)文內(nèi)容。模仿uip協(xié)議棧的uip_buf機(jī)制,構(gòu)建請(qǐng)求回顯報(bào)文內(nèi)容,往uip_buf(或者自己定義的buf變量)中填充數(shù)據(jù),再通過(guò)enc20j60底層發(fā)送函數(shù)進(jìn)行發(fā)送;對(duì)于接收回顯應(yīng)答報(bào)文,可以分步對(duì)其進(jìn)行數(shù)據(jù)解析,最后通過(guò)串口打印ping的結(jié)果。

6.2 請(qǐng)求回顯報(bào)文的發(fā)送

構(gòu)造請(qǐng)求回顯報(bào)文,主要有以下幾個(gè)方面:

  • 定義相關(guān)結(jié)構(gòu)體,這些結(jié)構(gòu)體中的變量是根據(jù)報(bào)文的格式內(nèi)容來(lái)定義的;
  • 聲明相關(guān)全局變量,如報(bào)文各個(gè)部分的長(zhǎng)度;
  • 校驗(yàn)和函數(shù)的定義;
  • 報(bào)文內(nèi)容的封裝;
  • 對(duì)封裝好的報(bào)文進(jìn)行預(yù)發(fā)送處理,在預(yù)發(fā)送過(guò)程中,要判斷在arp表中是否有目的ip的mac地址,如果有,則以封裝好的請(qǐng)求回顯報(bào)文進(jìn)行發(fā)送;如果沒有,就要構(gòu)造ARP請(qǐng)求進(jìn)行發(fā)送。
  • 詳細(xì)代碼如下:

    /************************ icmp ***************************************************/struct ethip_headr {struct uip_eth_hdr ethhdr;/* IP header. */u8_t vhl,tos,len[2],ipid[2],ipoffset[2],ttl,proto;u16_t ipchksum;u16_t srcipaddr[2],destipaddr[2]; };struct arp_header {struct uip_eth_hdr ethhdr;u16_t hwtype;u16_t protocol;u8_t hwlen;u8_t protolen;u16_t opcode;struct uip_eth_addr shwaddr;u16_t sipaddr[2];struct uip_eth_addr dhwaddr;u16_t dipaddr[2];};struct icmp_header {u8_t type; //icmp 類型u8_t code; //代碼值u16_t icmpchksum; //校驗(yàn)和u8_t ide[2]; //用于區(qū)分不同ping進(jìn)程u8_t seq[2]; //echo 序列號(hào)char data[28]; //數(shù)據(jù)段 };/**********************************************************************/ /******************** icmp echo request ************************/ #define UIP_ICMP_BUFSIZE 200 #define ICMP_DATA_SIZE 32 #define ICMP_IPD_LLH_LEN 17 //以太網(wǎng)+IP #define ICMP_ETH_LEN 14 //以太網(wǎng)幀頭長(zhǎng)度 #define ICMP_IPH_LEN 20 //IPHead長(zhǎng)度 #define UIP_ICMP_LEN 40 //ICMP幀長(zhǎng)度 u8_t uip_icmp_buf[UIP_ICMP_BUFSIZE + 2]; u16_t uip_icmp_len;#define ICMP_ARP_BUF ((struct arp_header *)&uip_icmp_buf[0]) //主動(dòng)連接時(shí),替換ICMP_IP_BUF #define ICMP_IP_BUF ((struct ethip_headr *)&uip_icmp_buf[0]) #define ICMP_BUF ((struct icmp_header *)&uip_icmp_buf[ICMP_ETH_LEN + ICMP_IPH_LEN])/**********************************************************************/ volatile u8_t FLAG_icmp_arpout = 0; extern u16_t chksum(u16_t sum, const u8_t *sdata, u16_t len); static u16_t icmp_ipid; static u16_t icmp_seq; static u8_t j; u16_t icmp_ide = 0;/**********************************************************************/ //iphead check static u16_t short_checksum(u16_t sum, const u8_t *sdata, u16_t len) {u16_t t;const u8_t *dataptr;const u8_t *last_byte;dataptr = sdata;last_byte = sdata + len - 1;while(dataptr < last_byte) { /* At least two more bytes */t = (dataptr[0] << 8) + dataptr[1];sum += t;if(sum < t) {sum++; /* carry */}dataptr += 2;}if(dataptr == last_byte) {t = (dataptr[0] << 8) + 0;sum += t;if(sum < t) {sum++; /* carry */}}/* Return sum in host byte order. */return sum; }/***************************** icmp IP checksum *********************************/ u16_t icmp_ipchksum(void) {u16_t sum;sum = short_checksum(0, &uip_icmp_buf[ICMP_ETH_LEN], ICMP_IPH_LEN);return (sum == 0) ? 0xffff : htons(sum); }/***************************** icmp checksum *********************************/u16_t icmp_icmpchksum(void) {u16_t sum;sum = short_checksum(0, &uip_icmp_buf[ICMP_ETH_LEN + ICMP_IPH_LEN], UIP_ICMP_LEN);return (sum == 0) ? 0xffff : htons(sum); } /*****************************************************************************//****************************************************************************** * @brief 構(gòu)造ip包頭 * * @param 接收方ip:a.b.c.d * * @retval void. * ******************************************************************************/ void icmp_iphead(u8_t a, u8_t b, u8_t c, u8_t d) {uip_ipaddr_t ipaddr;//構(gòu)造icmp IPv4 headerICMP_IP_BUF->vhl = 0x45;ICMP_IP_BUF->tos = 0;uip_icmp_len = 0x3c;ICMP_IP_BUF->len[0] = (uip_icmp_len >> 8);ICMP_IP_BUF->len[1] = (uip_icmp_len & 0xff);ICMP_IP_BUF->ipoffset[0] = ICMP_IP_BUF->ipoffset[1] = 0;++icmp_ipid;ICMP_IP_BUF->ipid[0] = icmp_ipid >> 8;ICMP_IP_BUF->ipid[1] = icmp_ipid & 0xff;ICMP_IP_BUF->ttl = UIP_TTL;ICMP_IP_BUF->proto = UIP_PROTO_ICMP; //0x01ICMP_IP_BUF->ipchksum = 0; //計(jì)算校驗(yàn)和之前,先將校驗(yàn)和清0ICMP_IP_BUF->ipchksum = ~(icmp_ipchksum());uip_ipaddr(ipaddr,a,b,c,d);uip_ipaddr_copy(ICMP_IP_BUF->destipaddr, (ipaddr));uip_ipaddr_copy(ICMP_IP_BUF->srcipaddr, uip_hostaddr); }const char icmp_data[ICMP_DATA_SIZE] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69 };void icmp_arp(void) {struct arp_entry *tabptr;FLAG_icmp_arpout = 0;/* 在ARP表中找到目標(biāo)IP地址并構(gòu)造以太網(wǎng)頭。如果目標(biāo)IP地址不在本地網(wǎng)絡(luò),我們使用默認(rèn)路由器的IP地址。如果找不到ARP表項(xiàng),我們用對(duì)IP地址的ARP請(qǐng)求覆蓋原始IP包 *//* 首先檢查目的地是否是本地廣播。 */if(uip_ipaddr_cmp(ICMP_IP_BUF->destipaddr, broadcast_ipaddr)) {memcpy(ICMP_IP_BUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6); } else {/* 檢查目標(biāo)地址是否在本地網(wǎng)絡(luò)上 */if(!uip_ipaddr_maskcmp(ICMP_IP_BUF->destipaddr, uip_hostaddr, uip_netmask)) {/* 目標(biāo)地址不在本地網(wǎng)絡(luò)上,因此在確定MAC地址時(shí),我們需要使用默認(rèn)路由器的IP地址而不是目標(biāo)地址。 */uip_ipaddr_copy(ipaddr, uip_draddr);} else {/* 否則,我們使用目標(biāo)IP地址 */uip_ipaddr_copy(ipaddr, ICMP_IP_BUF->destipaddr);}for(j = 0; j < UIP_ARPTAB_SIZE; ++j) {tabptr = &arp_table[j];if(uip_ipaddr_cmp(ipaddr, tabptr->ipaddr)) {break;} }if(j == UIP_ARPTAB_SIZE) {/* 目的地地址不在我們的ARP表中,所以我們用ARP請(qǐng)求覆蓋IP包。 */FLAG_icmp_arpout++ ;memset(BUF->ethhdr.dest.addr, 0xff, 6);memset(BUF->dhwaddr.addr, 0x00, 6);memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);uip_ipaddr_copy(BUF->dipaddr, ipaddr);uip_ipaddr_copy(BUF->sipaddr, uip_hostaddr);BUF->opcode = HTONS(ARP_REQUEST); /* ARP request. */BUF->hwtype = HTONS(ARP_HWTYPE_ETH);BUF->protocol = HTONS(UIP_ETHTYPE_IP);BUF->hwlen = 6;BUF->protolen = 4;BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);uip_appdata = &uip_buf[UIP_TCPIP_HLEN + UIP_LLH_LEN];uip_len = sizeof(struct arp_hdr);return;}/* 構(gòu)建以太網(wǎng)標(biāo)頭。 */memcpy(ICMP_IP_BUF->ethhdr.dest.addr, tabptr->ethaddr.addr, 6);}memcpy(ICMP_IP_BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);ICMP_IP_BUF->ethhdr.type = HTONS(UIP_ETHTYPE_IP);uip_icmp_len += sizeof(struct uip_eth_hdr);}/****************************************************************************** * @brief 構(gòu)造icmp包頭 * * @param void * * @retval void. * ******************************************************************************/ void icmp_icmphead(void) {//構(gòu)造icmp echo request字段//icmp echo headerICMP_BUF->type = 0x08; //echo 類型ICMP_BUF->code = 0x00; //該字段用來(lái)查找錯(cuò)誤原因icmp_ide = 0x01;ICMP_BUF->ide[0] = (icmp_ide >> 8); //區(qū)分不同的ping進(jìn)程ICMP_BUF->ide[1] = (icmp_ide & 0xff);++ icmp_seq;ICMP_BUF->seq[0] = (icmp_seq >> 8); //echo 序列號(hào)ICMP_BUF->seq[1] = (icmp_seq & 0xff);memcpy(ICMP_BUF->data, icmp_data, ICMP_DATA_SIZE);ICMP_BUF->icmpchksum = 0; //校驗(yàn)和ICMP_BUF->icmpchksum = ~(icmp_icmpchksum()); }/****************************************************************************** * @brief icmp request報(bào)文發(fā)送 * * @param 接收方ip:a.b.c.d * * @retval void. * ******************************************************************************/ void icmp_out(u8_t a, u8_t b, u8_t c, u8_t d) {printf("Ping %d.%d.%d.%d \r\n", a, b, c, d); //打印正在ping的ipicmp_iphead(a, b, c, d); //構(gòu)造icmp IPv4 header icmp_arp(); //如果arp table中沒有目標(biāo)ip的mac地址,就要構(gòu)造ARP請(qǐng)求,加以太網(wǎng)頭結(jié)構(gòu)if(FLAG_icmp_arpout == 0) //如果arp table中有目標(biāo)ip的mac地址{icmp_icmphead(); //構(gòu)造icmp報(bào)頭 enc28j60PacketSend(uip_icmp_len, (uchar *)uip_icmp_buf); //發(fā)送報(bào)文}else //如果構(gòu)造的是arp request包{enc28j60PacketSend(uip_len,uip_buf); //發(fā)送arp request包到以太網(wǎng)printf("ip:%d.%d.%d.%d no mac addr,arp request sent!\r\n", a, b, c, d);}}

    6.3 回顯應(yīng)答報(bào)文的接收與校驗(yàn)

    由于一些原因,接收方并不能在收到回顯請(qǐng)求后,立即發(fā)送回顯應(yīng)答,而且發(fā)送方也不能在發(fā)送后,就立刻能收到,所以對(duì)于回顯應(yīng)答報(bào)文的接收,要設(shè)置一個(gè)輪詢機(jī)制,在一定時(shí)間內(nèi)反復(fù)查詢是否有收到回顯應(yīng)答,如果收到了就進(jìn)行回顯應(yīng)答校驗(yàn),沒收到就輪詢直到定時(shí)器超時(shí)。

    對(duì)于回顯應(yīng)答報(bào)文的數(shù)據(jù)校驗(yàn),主要有以下幾個(gè)方面:

  • 通過(guò)底層的enc28j60PacketReceive函數(shù),看是否有收到數(shù)據(jù),如果有,在進(jìn)行下一步的判斷;
  • 判斷是否是ip包,是的話,進(jìn)行下一步判斷;
  • 判斷icmp字段的內(nèi)容長(zhǎng)度是否正確,正確的話再進(jìn)行下一步判斷;
  • 查看icmp字段的類型,如果是icmp echo reply,再接著下一步;
  • 判斷報(bào)文中的目標(biāo)ip是否是本機(jī)ip,如果上述條件都符合,則確認(rèn)是發(fā)送給本機(jī)的icmp echo reply;
  • 然后判斷icmpchksum是否正確,icmp id標(biāo)識(shí)符是否相同,icmp seq序列號(hào)是否相同,這三者的判斷順序無(wú)先后關(guān)系,1-5點(diǎn)要從1至5依次判斷。
  • 詳細(xì)代碼如下:

    /******************** icmp echo reply ***********************/ u8_t icmp_reply_buf[UIP_ICMP_BUFSIZE + 2]; u16_t icmp_reply_len;#define ICMP_REPLY_IP_BUF ((struct ethip_headr *)&icmp_reply_buf[0]) #define ICMP_REPLY_BUF ((struct icmp_header *)&icmp_reply_buf[ICMP_ETH_LEN + ICMP_IPH_LEN])volatile u8_t flag_icmp_reply_outtimes = 0; volatile u8_t flag_icmp_reply_checkOK = 0; volatile u8_t flag_icmp_reply_run = 0; extern void loop_feed_softdog(void); /***************************** icmp REPLY IP checksum *********************************/ u16_t icmp_reply_ipchksum(void) {u16_t sum;sum = short_checksum(0, &icmp_reply_buf[ICMP_ETH_LEN], ICMP_IPH_LEN);return (sum == 0) ? 0xffff : htons(sum); }/***************************** icmp REPLY checksum *********************************/u16_t icmp_reply_icmpchksum(void) {u16_t sum;sum = short_checksum(0, &icmp_reply_buf[ICMP_ETH_LEN + ICMP_IPH_LEN], UIP_ICMP_LEN);return (sum == 0) ? 0xffff : htons(sum); }/****************************************************************************** * @brief icmp_reply_check icmp 請(qǐng)求應(yīng)答報(bào)文中的數(shù)據(jù)校驗(yàn) * * @param void * * @retval void. * ******************************************************************************/ void uip_icmp_reply_check(void) {u16_t icmp_chksum = 0;u16_t new_icmpchksum = 0;//接收數(shù)據(jù)icmp_reply_len = enc28j60PacketReceive(UIP_ICMP_BUFSIZE, icmp_reply_buf);if(icmp_reply_len > 0) //有收到數(shù)據(jù){ //處理IP數(shù)據(jù)包(只有校驗(yàn)通過(guò)的IP包才會(huì)被接收)if(ICMP_REPLY_IP_BUF->ethhdr.type == htons(UIP_ETHTYPE_IP)) //判斷是否是IP包? {if(icmp_reply_len < sizeof(struct icmp_header)) {icmp_reply_len = 0;return;}icmp_reply_len = 0;switch(ICMP_REPLY_BUF->type) {case HTONS(0): //收到的包是icmp echo replyflag_icmp_reply_run = 1;/* 首先,判斷報(bào)文中的目標(biāo)ip是否是本機(jī)ip*/if(uip_ipaddr_cmp(ICMP_REPLY_IP_BUF->destipaddr, ICMP_IP_BUF->srcipaddr)){//上述條件都符合,則確認(rèn)是發(fā)送給本機(jī)的icmp echo replyicmp_chksum = ICMP_REPLY_BUF->icmpchksum; //其次,判斷 icmpchksum 是否正確ICMP_REPLY_BUF-> icmpchksum = 0;new_icmpchksum = ~(icmp_reply_icmpchksum());if(icmp_chksum == new_icmpchksum){flag_icmp_reply_checkOK++ ;}else{printf("reply icmp_chksum ERROR!\r\n");}if(ICMP_REPLY_BUF->ide[0] == ICMP_BUF->ide[0] && //接著判斷icmp id標(biāo)識(shí)符是否相同ICMP_REPLY_BUF->ide[1] == ICMP_BUF->ide[1]){flag_icmp_reply_checkOK++ ;}else{printf("icmp id is ERROR!\r\n"); }if(ICMP_REPLY_BUF->seq[0] == ICMP_BUF->seq[0] && //再接著判斷 icmp seq序列號(hào)是否相同ICMP_REPLY_BUF->seq[1] == ICMP_BUF->seq[1]){flag_icmp_reply_checkOK++ ;}else{printf("icmp seq is ERROR!\r\n");}return;}break;default:break; }}}return; }/****************************************************************************** * @brief icmp reply 報(bào)文處理結(jié)果判斷 * * @param ip 地址 a.b.c.d * * @retval void. * ******************************************************************************/ void uip_icmp_reply_in(u8_t a, u8_t b, u8_t c, u8_t d) {struct timer icmp_timer; //只用在icmp_reply 校驗(yàn),函數(shù)運(yùn)行結(jié)束釋放,所以不聲明為staic變量timer_set(&icmp_timer, CLOCK_SECOND / 2); //創(chuàng)建1個(gè)0.5秒的定時(shí)器while(1){if(FLAG_icmp_arpout > 0){break;}loop_feed_softdog(); //喂軟件看門狗uip_icmp_reply_check(); //icmp reply 報(bào)文校驗(yàn)if(timer_expired(&icmp_timer)) //0.5s秒定時(shí)器超時(shí){timer_reset(&icmp_timer); //復(fù)位定時(shí)器if(flag_icmp_reply_run == 0){ //沒接收到icmp echo reply 報(bào)文flag_icmp_reply_outtimes ++;printf("no receive icmp echo reply!\r\n");}break;}if((flag_icmp_reply_checkOK > 0) || (flag_icmp_reply_outtimes > 0)){break;}} if(flag_icmp_reply_checkOK == 3){printf("Ping %d.%d.%d.%d is SUCCESS !!\r\n", a, b, c, d);}else{printf("Ping %d.%d.%d.%d is ERROR !!\r\n", a, b, c, d);}FLAG_icmp_arpout = 0; //計(jì)數(shù)標(biāo)志清零flag_icmp_reply_run = 0;flag_icmp_reply_checkOK = 0; flag_icmp_reply_outtimes = 0;return; }

    7. 實(shí)驗(yàn)結(jié)果

    1.依次ping5個(gè)不同的ip地址

    網(wǎng)頁(yè)輸入5個(gè)ip地址(網(wǎng)頁(yè)部分代碼根據(jù)例程修改得到的,這里就不展開說(shuō)明了):

    圖1 網(wǎng)頁(yè)輸入ip


    圖2 串口打印信息


    圖3 wireshark抓包結(jié)果

    2.連續(xù)ping5次相同的ip

    圖4 網(wǎng)頁(yè)輸入5個(gè)相同ip


    圖5 串口打印信息

    圖6 wireshark抓包結(jié)果

    8. 實(shí)驗(yàn)結(jié)果分析

    1.依次ping5個(gè)不同的ip地址

    從圖2和圖3來(lái)看,192.168.1.5是PC端的IP,其余IP均無(wú)具體主機(jī)。所以在發(fā)送icmp請(qǐng)求時(shí),對(duì)于無(wú)具體主機(jī)的ip的icmp請(qǐng)求報(bào)文改寫成了arp請(qǐng)求報(bào)文,并以廣播的形式發(fā)送;對(duì)于192.168.1.5,因?yàn)槲词盏絧c端的回復(fù),所以發(fā)送no receive icmp reply,并返回ping 失敗。

    2.連續(xù)ping5次相同的ip地址

    從圖6的結(jié)果來(lái)看,均成功發(fā)送icmp請(qǐng)求,并成功返回icmp 應(yīng)答。

    9. 總結(jié)

    1.對(duì)于嵌入式以太網(wǎng)的學(xué)習(xí),一開始是從直接從例程入手,學(xué)習(xí)緩慢;隨后開始在csdn等論壇查閱資料,一步一個(gè)腳印,但也走了很多彎路,有時(shí)候解決一個(gè)問(wèn)題也花費(fèi)了好些天,但最終靠著耐心和堅(jiān)持,收獲良多。

    2.對(duì)實(shí)驗(yàn)例程和源碼詳細(xì)閱讀很重要,可以加快實(shí)驗(yàn)的理解,也可以讓實(shí)驗(yàn)代碼功能更加完善。

    3.對(duì)于實(shí)驗(yàn)中的技術(shù)出錯(cuò)與改錯(cuò)方法,這里就不一一說(shuō)明了,如果你也遇到什么技術(shù)問(wèn)題,可以在評(píng)論區(qū)發(fā)布,我會(huì)與大家解決。

    最后,感謝大家看完這篇文章,如有不足,望大家積極指出,我也會(huì)改進(jìn),與 大家共勉!

    總結(jié)

    以上是生活随笔為你收集整理的STM32+enc28j60+uip 实现 单片机 ping PC端的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。