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

article/2025/11/5 12:55:53

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

  • 1. 前言
  • 2. 实验简介
  • 3. uip简介
  • 4. icmp简介
    • 4.1 icmp介绍
    • 4.2 请求回显或回显应答报文格式介绍
  • 5. 实验环境
  • 6. 实验内容
    • 6.1 实验方案
    • 6.2 请求回显报文的发送
    • 6.3 回显应答报文的接收与校验
  • 7. 实验结果
  • 8. 实验结果分析
  • 9. 总结

1. 前言

临近毕业,多年在csdn等各大论坛闯荡(学习)的我,终于下定决心,开始写自己人生中的第一篇博客。

在学习了一段时间的uip协议栈后,走了很多弯路,所以想与大家分享自己的学习经历。本人没啥文笔,只能将自己所学所感与大家分享,本文的部分内容也是通过csdn等各大论坛收集整理而来,忠心希望大家能将意见或者建议在评论区与我分享,与大家共勉。

2. 实验简介

本次实验主要采用stm32最小系统开发板,MCU为stm32f103c8t6,搭载了enc28j60以太网模块,工程代码基于uip协议栈,实现了实现单片机 ping PC端。

3. uip简介

关于uip的学习,可参考xukai871105大神的博客—【uIP学习笔记】

4. icmp简介

4.1 icmp介绍

ICMP(Internet Control Message Protocol),网络控制消息协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。ICMP的协议号为1。

ICMP协议的功能主要有:
(1)确认IP包是否成功到达目标地址。
(2)通知在发送过程中IP包被丢弃的原因。

ICMP报文是在IP报文内部的!!!
ICMP报文分为查询报文和差错报文。

4.2 请求回显或回显应答报文格式介绍

注:本次实验主要实现的是ping功能,用到的是ICMP查询报文中的请求回显或回显应答报文(Echo or Echo Reply Message),所以对icmp其他报文类型不做展开。

报文内容的开始,是以太网帧头,包括目的主机的mac地址,源主机的mac地址,协议类型,共14Bytes(PC端的网卡MAC地址可通过cmd命令:ipconfig/all 查看,由于enc28j60没有唯一的mac标识,在实验时可随机设置)。

其次是报文类型,该处字符若为0x8000,说明该报文是IPv4类型。

接着是IP首部字段(IP Header),总长20Bytes。首部字段包括:

(1)IP Version:4,说明是IPv4),1Bytes;
(2)包头长度(Header Length),1Bytes;
(3)区分服务领域(Differentiated Services Field),1Bytes;
(4)总长度(Total Length),1Bytes;
(5)标识符(Identification),2Bytes;
(6)标记字段(Flags),2Bytes;
(8)报文生存时间(TTL),1Bytes;
(9)报文所用协议类型(Protocol),1Bytes;
(10)IP包头检验和(IP Header checksum),2Bytes,
(11)源IP(发送方IP),4Bytes;
(12)目的IP(接收方IP),4Bytes。

其中,IP包头检验和计算方法如下:
1.checksum的初始值自动被设置为0
2.接着,以16bit为单位,两两相加,对于该例子,即为:E34F + 2396 + 4427 + 99F3 = 1E4FF
3.若计算结果大于0xFFFF,则将,高16位加到低16位上,对于该例子,即为0xE4FF + 0x0001 = E500

注:校验和部分很重要,如果校验和出错,会导致报文被过滤,从而使得接收方接收不到该报文。

再接着是ICMP字段,总长40Bytes。其中包括:
(1)类型Type(Type: 8 表示icmp echo request,请求回显),1Bytes;
(2)代码值(code,code: 0x00表示请求回显),1Bytes;
(3)校验和(checksum),2Bytes;
(4)Identifier(用于区分不同的PING进程),2Bytes,对于unix以及类unix操作系统来说,icmp Identifier的内容就是ping的进程号,对于windows系统来说,具体参考如下:
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系统的icmp Identifier是固定不变的,那么系统如何区别不同的Ping进程呢?实际上windows系统就不在根据Identifier来区别ping进程了,它是根据Sequence Number field来区分的。
(5)序列号(Sequence number),2Bytes,区分发送顺序,与IP Header中的标识符类似。
(6)数据段(data),32Bytes,作为icmp 请求回显或回显应答报文的话,发送数据Data的内容可以是随机的。

看完了格式内容之后,同学们可以动动手,用wireshark抓取icmp包,看看报文中各个部分的具体内容。

5. 实验环境

单片机部分:stm32+enc28j60

PC端部分:win10,串口调试助手,wireshark

其他:单片机与PC端网线直连(并保证单片机与PC在同一网段)
单片机IP: 192.168.1.8
PC端IP: 192.168.1.5
网关: 192.168.1.1

6. 实验内容

6.1 实验方案

本次实验,主要分为请求回显报文的发送和回显应答报文的接收两部分,已经知道了报文的具体内容之后,我们便可以自己构建报文内容。模仿uip协议栈的uip_buf机制,构建请求回显报文内容,往uip_buf(或者自己定义的buf变量)中填充数据,再通过enc20j60底层发送函数进行发送;对于接收回显应答报文,可以分步对其进行数据解析,最后通过串口打印ping的结果。

6.2 请求回显报文的发送

构造请求回显报文,主要有以下几个方面:

  1. 定义相关结构体,这些结构体中的变量是根据报文的格式内容来定义的;
  2. 声明相关全局变量,如报文各个部分的长度;
  3. 校验和函数的定义;
  4. 报文内容的封装;
  5. 对封装好的报文进行预发送处理,在预发送过程中,要判断在arp表中是否有目的ip的mac地址,如果有,则以封装好的请求回显报文进行发送;如果没有,就要构造ARP请求进行发送。

详细代码如下:

/************************  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;		//校验和u8_t ide[2];			//用于区分不同ping进程u8_t seq[2];			//echo 序列号char data[28];			//数据段
};/**********************************************************************/
/********************   icmp echo request      ************************/
#define UIP_ICMP_BUFSIZE		200
#define ICMP_DATA_SIZE			32
#define ICMP_IPD_LLH_LEN		17			//以太网+IP
#define ICMP_ETH_LEN     		14			//以太网帧头长度
#define ICMP_IPH_LEN			20			//IPHead长度
#define UIP_ICMP_LEN			40			//ICMP帧长度
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])		//主动连接时,替换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  构造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;//构造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;											//计算校验和之前,先将校验和清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表中找到目标IP地址并构造以太网头。如果目标IP地址不在本地网络,我们使用默认路由器的IP地址。如果找不到ARP表项,我们用对IP地址的ARP请求覆盖原始IP包 *//* 首先检查目的地是否是本地广播。 */if(uip_ipaddr_cmp(ICMP_IP_BUF->destipaddr, broadcast_ipaddr)) {memcpy(ICMP_IP_BUF->ethhdr.dest.addr, broadcast_ethaddr.addr, 6);	} else {/* 检查目标地址是否在本地网络上 */if(!uip_ipaddr_maskcmp(ICMP_IP_BUF->destipaddr, uip_hostaddr, uip_netmask)) {/* 目标地址不在本地网络上,因此在确定MAC地址时,我们需要使用默认路由器的IP地址而不是目标地址。 */uip_ipaddr_copy(ipaddr, uip_draddr);} else {/* 否则,我们使用目标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请求覆盖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;}/* 构建以太网标头。 */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  构造icmp包头									                      *
* @param  void																  *
* @retval void.                                                               *
******************************************************************************/
void icmp_icmphead(void)
{//构造icmp echo request字段//icmp echo headerICMP_BUF->type 			= 0x08;								//echo 类型ICMP_BUF->code 		 	= 0x00;								//该字段用来查找错误原因icmp_ide = 0x01;ICMP_BUF->ide[0]		= (icmp_ide >> 8);		//区分不同的ping进程ICMP_BUF->ide[1] 		= (icmp_ide & 0xff);++ icmp_seq;ICMP_BUF->seq[0]	 	= (icmp_seq >> 8);		//echo 序列号ICMP_BUF->seq[1] 		= (icmp_seq & 0xff);memcpy(ICMP_BUF->data, icmp_data, ICMP_DATA_SIZE);ICMP_BUF->icmpchksum 	= 0;									//校验和ICMP_BUF->icmpchksum	= ~(icmp_icmpchksum());
}/******************************************************************************
* @brief  icmp request报文发送									              *                                                
* @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);										//构造icmp IPv4 header	icmp_arp();														//如果arp table中没有目标ip的mac地址,就要构造ARP请求,加以太网头结构if(FLAG_icmp_arpout == 0)										//如果arp table中有目标ip的mac地址{icmp_icmphead();											//构造icmp报头			enc28j60PacketSend(uip_icmp_len, (uchar *)uip_icmp_buf);	//发送报文}else															//如果构造的是arp request包{enc28j60PacketSend(uip_len,uip_buf);						//发送arp request包到以太网printf("ip:%d.%d.%d.%d no mac addr,arp request sent!\r\n", a, b, c, d);}}

6.3 回显应答报文的接收与校验

由于一些原因,接收方并不能在收到回显请求后,立即发送回显应答,而且发送方也不能在发送后,就立刻能收到,所以对于回显应答报文的接收,要设置一个轮询机制,在一定时间内反复查询是否有收到回显应答,如果收到了就进行回显应答校验,没收到就轮询直到定时器超时。

对于回显应答报文的数据校验,主要有以下几个方面:

  1. 通过底层的enc28j60PacketReceive函数,看是否有收到数据,如果有,在进行下一步的判断;
  2. 判断是否是ip包,是的话,进行下一步判断;
  3. 判断icmp字段的内容长度是否正确,正确的话再进行下一步判断;
  4. 查看icmp字段的类型,如果是icmp echo reply,再接着下一步;
  5. 判断报文中的目标ip是否是本机ip,如果上述条件都符合,则确认是发送给本机的icmp echo reply;
  6. 然后判断icmpchksum是否正确,icmp id标识符是否相同,icmp seq序列号是否相同,这三者的判断顺序无先后关系,1-5点要从1至5依次判断。

详细代码如下:

/********************   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 请求应答报文中的数据校验                    *
* @param  void																  *
* @retval void.                                                               *
******************************************************************************/
void uip_icmp_reply_check(void)
{u16_t icmp_chksum = 0;u16_t new_icmpchksum = 0;//接收数据icmp_reply_len = enc28j60PacketReceive(UIP_ICMP_BUFSIZE, icmp_reply_buf);if(icmp_reply_len > 0)					//有收到数据{	//处理IP数据包(只有校验通过的IP包才会被接收)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;/* 首先,判断报文中的目标ip是否是本机ip*/if(uip_ipaddr_cmp(ICMP_REPLY_IP_BUF->destipaddr, ICMP_IP_BUF->srcipaddr)){//上述条件都符合,则确认是发送给本机的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标识符是否相同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序列号是否相同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	报文处理结果判断									  *
* @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 校验,函数运行结束释放,所以不声明为staic变量timer_set(&icmp_timer, CLOCK_SECOND / 2);	   	//创建1个0.5秒的定时器while(1){if(FLAG_icmp_arpout > 0){break;}loop_feed_softdog();					//喂软件看门狗uip_icmp_reply_check();					//icmp reply 报文校验if(timer_expired(&icmp_timer))  		//0.5s秒定时器超时{timer_reset(&icmp_timer);			//复位定时器if(flag_icmp_reply_run == 0){		//没接收到icmp echo reply 报文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;							//计数标志清零flag_icmp_reply_run = 0;flag_icmp_reply_checkOK = 0;			flag_icmp_reply_outtimes = 0;return;		
}

7. 实验结果

1.依次ping5个不同的ip地址

网页输入5个ip地址(网页部分代码根据例程修改得到的,这里就不展开说明了):
图1 网页输入ip
图1 网页输入ip

图2 串口打印信息
图2 串口打印信息

图3 wireshark抓包结果
图3 wireshark抓包结果

2.连续ping5次相同的ip
图4 网页输入5个相同ip
图4 网页输入5个相同ip

图5 串口打印信息
图5 串口打印信息

图6 wireshark抓包结果

图6 wireshark抓包结果

8. 实验结果分析

1.依次ping5个不同的ip地址

从图2和图3来看,192.168.1.5是PC端的IP,其余IP均无具体主机。所以在发送icmp请求时,对于无具体主机的ip的icmp请求报文改写成了arp请求报文,并以广播的形式发送;对于192.168.1.5,因为未收到pc端的回复,所以发送no receive icmp reply,并返回ping 失败。

2.连续ping5次相同的ip地址

从图6的结果来看,均成功发送icmp请求,并成功返回icmp 应答。

9. 总结

1.对于嵌入式以太网的学习,一开始是从直接从例程入手,学习缓慢;随后开始在csdn等论坛查阅资料,一步一个脚印,但也走了很多弯路,有时候解决一个问题也花费了好些天,但最终靠着耐心和坚持,收获良多。

2.对实验例程和源码详细阅读很重要,可以加快实验的理解,也可以让实验代码功能更加完善。

3.对于实验中的技术出错与改错方法,这里就不一一说明了,如果你也遇到什么技术问题,可以在评论区发布,我会与大家解决。

最后,感谢大家看完这篇文章,如有不足,望大家积极指出,我也会改进,与 大家共勉!


http://chatgpt.dhexx.cn/article/S3IvnyWS.shtml

相关文章

LWIP的RAW API UDP通信详解(stm32f103---enc28j60)

目录 LWIPLWIP简介LWIP主要特性 ENC28J60ENC28J60简介ENC28J60特点 无操作系统LWIP移植在说移植之前&#xff0c;先说下几个重要的函数功能和数据结构enc28j60.c文件主要结构体*netif*结构体定义&#xff08;netif.h&#xff09;只列出了比较重要的字段 lwip__comm.c文件__lwip…

Ardunio开发实例-ENC28J60以太网模块实现Web服务器

ENC28J60以太网模块实现Web服务器 ENC28J60采用与以太网兼容的IEEE 802.3或可用作以太网控制器。 换句话说,ENC28J60是具有一种功能的微芯片,该功能能够根据芯片使用Internet或Intranet网络封装(重新封装,通常是将一种类型的数据网络数据包转换为其他数据类型)数据的过程…

【STM32+cubemx】0020 HAL库开发:以太网ENC28J60芯片和TCP、UDP简单应用

MCU通过以太网通信有很多种方式&#xff0c;有的内部自带以太网接口&#xff08;如stm32f107的某些型号&#xff09;&#xff1b;如果没有&#xff0c;也可以在外围连接以太网芯片来实现。外接的以太网芯片&#xff0c;又分为带网络协议栈和不带协议栈的&#xff0c;带网络协议…

如何使用Arduino Web服务器连接ENC28J60以太网模块

在本篇文章中&#xff0c;我们将学习如何将ENC28J60以太网模块与Arduino Web服务器进行连接&#xff0c;以在LAN局域网或无线网络中的计算机与Arduino之间建立通信。我们将ENC28J60以太网控制器与Arduino连接&#xff0c;以便我们的Arduino成为该网络的成员之一。一旦完成&…

ENC28J60学习

ENC28J60符合IEEE802.3的全部规范&#xff0c;采用了一系列包过滤机制以对传入数据包进行限制&#xff0c;内部有一个NMA模块&#xff0c;与主控制器通过两个中断脚和SPI实现通信。 寄存器&#xff1a; 共有三种不同形式的寄存器——控制寄存器&#xff0c;以太网寄存器 和PH…

STM32开源代码——ENC28J60程序

正点原子精英开发板 模块化封装&#xff0c;入口函数简明&#xff0c;易上手操作 展示main.c代码&#xff0c;完整代码请下载 网路调试工具与库函数讲解已放在压缩包中 点击下载代码 开机检测ENC28J60&#xff0c;如果检测不成功&#xff0c;则提示报错。在成功检测到ENC2…

51单片机 ENC28J60 TCP/IP通信

转载地址 https://blog.csdn.net/a_a666/article/details/78277628 51单片机 ENC28J60 TCP/IP通信 单片机&#xff1a;STC90C516RDENC28J60模块&#xff1a;mini(3.3V供电&#xff09;设备&#xff1a;PC、开发板、两个网线、路由器 首先先介绍一下本文的移植针对于单片机做…

STM32+ENC28J60+UIP协议栈实现WEB服务器示例

一、环境介绍 MCU: STM32F103ZET6 网卡: ENC28J60 协议栈: UIP 开发软件: Keil5 二、功能介绍 完整项目源码下载链接&#xff1a;https://download.csdn.net/download/xiaolong1126626497/18617694 STM32控制ENC28J60UIP协议栈创建TCP服务器(WEB服务器)&#xff0c;支持…

ENC28J60+STM32F103在STM32CubeIDE上移植lwIP2.1.2

ENC28J60STM32F103在STM32CubeIDE上移植lwIP2.1.2 为了学习lwIP&#xff0c;网购了一块正点原子的Mini STM32开发板和一个ENC28J60以太网模块&#xff0c;发现正点原子所给的示例代码是基于lwIP1.4.1的&#xff0c;有点偏老&#xff0c;最新版本的lwIP是2.1.2&#xff0c;使用的…

RT-Thread—STM32—enc28j60

!!! 本文已同步到码云 - 点击此链接获取最新 - 可进入码云提交修改 !!! 概述 本教程主要是讲解如何使用ENC28J60模块介入以太网 本例程的模板使用通用模板环境搭建里面的模板RT-Thread—STM32—环境搭建 文末也有我移植好的例程&#xff0c;不过建议大家从头开始移植&#xf…

GD32F4xx 以太网芯片(enc28j60)驱动移植

1、enc28j60 简介 ENC28J60 是带有行业标准串行外设接口(SPI)的独立以太网控制器。 主要特性: (1)SPI最高通信速率:10Mb/s。只支持SPI的模式0,0,且SPI端口要求SCK在空闲状态时为低电平。 (2)支持全双工和半双工模式。 (3)8KB 发送/接收数据包双端口RAM.,可配置发送…

enc28j60 linux 驱动_linux enc28j60网卡驱动移植(硬件spi和模拟spi)

本来想移植DM9000网卡的驱动&#xff0c;无奈硬件出了点问题&#xff0c;通过杜邦线链接开发板和DM9000网卡模块&#xff0c;系统上电&#xff0c;还没加载网卡驱动就直接崩溃了&#xff0c;找不到原因。。。刚好手上有一个enc28j60的网卡模块&#xff0c;于是就着手移植enc28j…

lwip-2.1.3在STM32F103ZE+ENC28J60有线网卡上无操作系统移植(使用STM32 HAL库)

程序下载链接&#xff1a;百度网盘 请输入提取码&#xff08;提取码&#xff1a;k6tz&#xff09; 【重要说明】 连接方式一&#xff08;推荐&#xff09;&#xff1a; 电脑有线网卡断开&#xff0c;无线网卡连无线路由器&#xff0c;无线网卡配置成自动获取IP地址。 板子的E…

enc28j60是带SPI接口的独立以太网控制器(即网卡),兼容IEEE 802.3,集成MAC和10 BASE-T PHY.而KSZ8081只是PHY芯片和网口扫盲三:以太网芯片MAC和PHY的关系

百度百科中介绍以太网控制器也称以太网适配器&#xff0c;就是我们通常称的“网卡”。电脑中网卡通过PCI和CPU相连&#xff0c;网卡上RJ45插网线水晶头。教程中的ENC28J60通过SPI和单片机相连&#xff0c;ENC28J60模块的RJ45插网线的水晶头 以太网控制器_百度百科 1.概述 enc2…

基于enc28j60的学习心得

1.概述 enc28j60是带SPI接口的独立以太网控制器&#xff0c;兼容IEEE 802.3&#xff0c;集成MAC和10 BASE-T PHY&#xff0c;最高速度可达10Mb/s。基于enc28j60控制器的理解可阅读文章&#xff1a; ENC28J60学习笔记&#xff0c;在该文章内详细介绍控制器的使用方法&#xff0c…

单片机学习:手把手教你移植LWIP(ENC28J60)

这里只是移植&#xff0c;所以LWIP那么多的协议都不需要管&#xff0c;只要知道哪里需要我们修改&#xff0c;为什么修改就可以了。 上图就是整个移植的基本思路&#xff0c;非常清晰的三个层次。其实想想&#xff0c;本质上就是收发数据&#xff0c;只是LWIP协议通过对数据的…

单片机 STM32 HAL 网络模块 ENC28J60

文章目录 一、 简介二、特性三、示例代码 一、 简介 ENC28J60 是带有行业标准串行外设接口&#xff08;Serial Peripheral Interface&#xff0c;SPI&#xff09;的独立以太网控制器。它可作为任何配备有 SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3的全部规范&#x…

ENC28J60 简介

单片机以太网方案 单片机想要使用以太网的话&#xff0c;通常有以下几种方案&#xff1a; 如果 MCU 内部集成 MAC 控制器&#xff0c;则只需外接一个 PHY 芯片就可以了如果 MCU 内部没有 MAC 控制器&#xff0c;需要外接 MAC 芯片和 PHY 芯片&#xff0c;这两颗芯片可以分立也…

c语言程序设计 国外教材,标准C程序设计(第7版国外计算机科学经典教材)

导语 内容提要 E.巴拉古路萨米著李周芳译的《标准C程序设计(第7版国外计算机科学经典教材)》专门用于满足渴望成为程序员的学生&#xff0c;最新版按照Bloom分类法所定的学习目标来呈现主题&#xff0c;支持基于学习的成果。本书解释了基本概念和高级内容&#xff0c;且主要关注…

学习C语言的教材

作者&#xff1a; 阮一峰 日期&#xff1a; 2011年9月18日 我的C语言是自学的&#xff0c;这些年看过不少教材。 下面&#xff0c;我对其中一些教材做个点评。 1. How to Think Like a Computer Scientist: C version 这是我读过最易懂的C语言教材。 虽然它只讲解最基本的语法&…