您的位置 首页 汽车

STM32之 IP ICMP ETHERNET的完成

STM32之 IP ICMP ETHERNET的实现-嵌入式以太网开发是一个很有挑战性的工作。通过几个月的学习,个人觉得大致有两条途径。第一条途径,通过高级语言熟悉socket编程,例如C#或C++,熟悉bind,listen,connect,accept等函数,在嵌入式系统中应用 lwIP协议栈。第二种途径,通过分析嵌入式以太网代码,结合TCPIP协议栈规范逐步实践协议栈代码。

1.前语

嵌入式以太网开发是一个很有挑战性的作业。经过几个月的学习,个人觉得大致有两条途径。榜首条途径,经过高档言语了解socket编程,例如C++#或C++,了解bind,listen,connect,accept等函数,在嵌入式体系中运用 lwIP协议栈。第二种途径,经过剖析嵌入式以太网代码,结合TCPIP协议栈标准逐渐实践协议栈代码。榜首种途径效率高,开发周期短,编写出来的代码功用安稳,第二种途径花的时间长,开宣布来的代码功用不完善,可是由于紧紧结合TCPIP标准,能够了解的内容较多,合适学习。本文经过剖析和修正AVRNET源码并移植到STM32渠道,逐渐完成TCPIP协议栈的各个子部分,包含ETHERNET部分,ARP部分,IP部分,ICMP部分,UDP部分,TCP部分和HTTP部分。

【 STM32NET学习笔记——索引】【代码库房】

本文先完成ethernet部分和ARP部分。

1.2 其他阐明

【硬件渠道】 STM32+ENC28J60

【编译渠道】 IAR 6.5

【IP地址】在实践之前,需求经过ipconfig指令查看PC机的IP地址和MAC地址,AVR的IP地址设定有必要和PC机在同一个网段中。例如 :

PC机IP:192.168.1.102

AVR IP: 192.168.1.115

【局域网拜访 】

假如有STM32开发板或许其他CPU的开发板的话,能够把开发板的以太网端口衔接到路由器LAN端口,只需确保开发板的IP地址和PC机的IP地址在同一个网段。

【广域网拜访 】

假如有固定的电信网IP地址的话,能够在路由器中设置静态端口映射,把某个端口映射成局域网内的IP地址和端口号。若没有固定IP地址的话,可运用花生壳软件虚拟一个域名。

1.3 代码库房

【代码库房】——CSDN Code代码库房。

2.初始化

以太网协议栈的完成离不开以太网驱动芯片。以太网驱动怎么完成请参阅——ENC28J60学习笔记。TCPIP的完成离不开两个根本地址,IP地址和MAC地址。在本例中经过以下代码界说和完成。

struct.h头文件中 相关界说:

[cpp] view plain copy// MAC地址结构体

#pragma pack(1)

typedef struct _MAC_ADDR

{

BYTE byte[6];

}MAC_ADDR;

// IP地址结构体

#pragma pack(1)

typedef struct _IP_ADDR

{

BYTE byte[4];

}IP_ADDR;

main.c函数中的初始化代码:

[cpp] view plain copy// 初始化MAC地址

stm32_mac.byte[0] = ‘S’;

stm32_mac.byte[1] = ‘T’;

stm32_mac.byte[2] = ‘M’;

stm32_mac.byte[3] = ‘N’;

stm32_mac.byte[4] = ‘E’;

stm32_mac.byte[5] = ‘T’;

// 初始化IP地址,固定IP地址

stm32_ip.byte[0] = 192;

stm32_ip.byte[1] = 168;

stm32_ip.byte[2] = 1;

stm32_ip.byte[3] = 115;

MAC地址和IP地址均为自界说的结构体,结构体中为一个字节数组。严格来说,MAC地址不能胡乱界说,应严格遵守相关标准,假如条件答应的话能够运用带有全球仅有的MAC地址的EEPROM芯片。

3.完成ETHERNET

TCPIP是一系列协议的组合,其间最有名的为TCP协议和IP协议。可是千万不要忽视最底层的协议结构——ETHERNET。ETHERNET包含14个字节,称之为以太网首部,其间前六个字节为方针MAC地址,紧着的6个字节为源MAC地址,最终的两个字节为协议类型。以太网的完成通讯时有必要要知道两边的MAC地址,发送方不清晰接纳方的地址便经过ARP协议寻觅方针MAC地址,假如仍然没有成果则可只能把该报文转发给路由器,让路由器处理该报文。协议类型只需关怀两种,0800的IP协议和0806的ARP协议。

ethernet.h中相关宏界说

[cpp] view plain copy// 协议类型 ARP报文

#define ETH_TYPE_ARP_V 0x0806

#define ETH_TYPE_ARP_H_V 0x08

#define ETH_TYPE_ARP_L_V 0x06

// 协议类型 以太网报文

#define ETH_TYPE_IP_V 0x0800

#define ETH_TYPE_IP_H_V 0x08

#define ETH_TYPE_IP_L_V 0x00

// 以太网报文头部长度 14

#define ETH_HEADER_LEN 14

// 方针MAC地址

#define ETH_DST_MAC_P 0

// 源MAC地址

#define ETH_SRC_MAC_P 6

// 协议类型

#define ETH_TYPE_H_P 12

#define ETH_TYPE_L_P 13

ethernet.c中相关函数

[cpp] view plain copyvoid eth_generate_header ( BYTE *rxtx_buffer, WORD_BYTES type, BYTE *dest_mac )

{

BYTE i;

// 装备以太网报文 方针MAC地址和源MAC地址

for ( i=0; i《sizeof(MAC_ADDR); i++)

{

rxtx_buffer[ ETH_DST_MAC_P + i ] = dest_mac[i];

// avr_mac为全局变量

rxtx_buffer[ ETH_SRC_MAC_P + i ] = stm32_mac.byte[i];

}

// 装备协议类型 IP报文或ARP报文

rxtx_buffer[ ETH_TYPE_H_P ] = type.byte.high;

rxtx_buffer[ ETH_TYPE_L_P ] = type.byte.low;

}

eth_generate_header函数完成了填充以太网首部的功用,榜首个输入参数为发送接纳缓冲区。第二个参数为IP类型,在AVRNET项目中传入的参数不是0800的IP协议类型就是0806的ARP协议类型。第三个参数为方针MAC地址,由于本机MAC地址作为了全局变量,能够在函数内部填充到缓冲区中。

4.完成ARP

为了运用最少的代码完成TCPIP功用,假定经过IP发送报文时现已承认了方针的IP地址,设备总是先被迫的经过ARP先让PC机知道其MAC地址,这样当PC机发送UDP或许TCP报文时,在报文中现已包含了PC机的IP地址,设备仅需从rxtx_buffer中取出PC机IP地址。ARP协议是一个找街坊的进程,是一个播送找MAC的进程。宣布者经过播送报文承认某个IP的MAC地址。ARP首部包含,2字节硬件类型,2字节协议类型,1字节硬件长度,1字节协议长度,2字节操作码,6字节发送者硬件地址,4字节发送者IP地址,6字节方针硬件地址和4字节方针IP地址。

在运用ARP协议时需求留意三点:

榜首,操作码分为两种——ARP恳求和ARP呼应,ARP恳求的编码为1,ARP呼应的编码为2,先有恳求后有呼应。第二,发送ARP协议恳求时恳求方清晰对方IP地址,可是不清晰对方MAC地址,所以在恳求报文中MAC地址悉数以0代替。第三,由于不知道对方的MAC地址,所以只能经过播送帧发送以太网数据,所以以太网首部的前6个字节被FF填充。

为了便于ARP功用的完成,在arp.h文件中界说了以下宏界说

[cpp] view plain copy#define ARP_PACKET_LEN 28

// ARP恳求

#define ARP_OPCODE_REQUEST_V 0x0001

#define ARP_OPCODE_REQUEST_H_V 0x00

#define ARP_OPCODE_REQUEST_L_V 0x01

// ARP呼应

#define ARP_OPCODE_REPLY_V 0x0002

#define ARP_OPCODE_REPLY_H_V 0x00

#define ARP_OPCODE_REPLY_L_V 0x02

// 硬件类型 10M以太网

#define ARP_HARDWARE_TYPE_H_V 0x00

#define ARP_HARDWARE_TYPE_L_V 0x01

// 协议类型 IPV4

#define ARP_PROTOCOL_H_V 0x08

#define ARP_PROTOCOL_L_V 0x00

// 硬件地址长度

#define ARP_HARDWARE_SIZE_V 0x06

// 协议地址长度

#define ARP_PROTOCOL_SIZE_V 0x04

// 硬件类型 2字节

#define ARP_HARDWARE_TYPE_H_P 0x0E

#define ARP_HARDWARE_TYPE_L_P 0x0F

// 协议类型 2字节

#define ARP_PROTOCOL_H_P 0x10

#define ARP_PROTOCOL_L_P 0x11

// 硬件地址 1字节

#define ARP_HARDWARE_SIZE_P 0x12

// 协议地址长度 1字节

#define ARP_PROTOCOL_SIZE_P 0x13

// 操作码 2字节

#define ARP_OPCODE_H_P 0x14

#define ARP_OPCODE_L_P 0x15

// 发送者硬件地址 6字节

#define ARP_SRC_MAC_P 0x16

// 发送者IP地址 4字节

#define ARP_SRC_IP_P 0x1C

// 方针硬件地址 6字节

#define ARP_DST_MAC_P 0x20

// 方针IP地址 6字节

#define ARP_DST_IP_P 0x26

在没有操作体系的支持下,一般经过一个无限循环完成子功用的完成。项目中经过某个process不断查询是否存在网卡数据,假如有网卡数据则马上保存源MAC地址。由于项目中没有保护ARP表,所以有必要及时记载发送方的MAC地址,以便向它回来数据。紧着就是查询该报文是否为ARP恳求,假如是ARP恳求则回来ARP呼应。详细代码如下 :

[cpp] view plain copyvoid server_process ( void )

{

MAC_ADDR client_mac;

IP_ADDR client_ip;

WORD plen;

// 取得新的IP报文

plen = enc28j60_packet_receive( (BYTE*)&rxtx_buffer, MAX_RXTX_BUFFER );

if(plen==0) return;

// 保存客服端的MAC地址

memcpy ( (BYTE*)&client_mac, &rxtx_buffer[ ETH_SRC_MAC_P ], sizeof( MAC_ADDR) );

// 查看该报文是不是ARP报文

if ( arp_packet_is_arp( rxtx_buffer, (WORD_BYTES){ARP_OPCODE_REQUEST_V} ) )

{

// 向客户端回来ARP报文

arp_send_reply ( (BYTE*)&rxtx_buffer, (BYTE*)&client_mac );

return;

}

}

4.1 查询ARP报文

查询该报文是否是针对设备的ARP报文需求承认三点,榜首:承认以太网首部中的协议类型是否为ARP协议类型,ARP协议类型的值为0806H。第二,查询该ARP报文是否为ARP恳求,该进程需求到ARP首部中查询ARP操作码,ARP恳求的操作码为1。第三,查询该ARP恳求中的MAC地址是否和本机MAC匹配。

最终经过宏界说ARP_DEBUD决议是否经过串口输出发起者IP地址和MAC地址。经过串口打印能够承认该ARP报文的发起者。

[cpp] view plain copyBYTE arp_packet_is_arp ( BYTE *rxtx_buffer, WORD_BYTES opcode )

{

BYTE i;

// 该报文为ARP报文

if( rxtx_buffer[ ETH_TYPE_H_P ] != ETH_TYPE_ARP_H_V || rxtx_buffer[ ETH_TYPE_L_P ] != ETH_TYPE_ARP_L_V)

return 0;

// 承认ARP操作码 ARP恳求 1 ARP应对2

if ( rxtx_buffer[ ARP_OPCODE_H_P ] != opcode.byte.high || rxtx_buffer[ ARP_OPCODE_L_P ] != opcode.byte.low )

return 0;

// 匹配IP地址

for ( i=0; i《sizeof(IP_ADDR); i++ )

{

if ( rxtx_buffer[ ARP_DST_IP_P + i] != stm32_ip.byte[i] )

return 0;

}

// 经过串口输出

#if ARP_DEBUG

printf(“ARP Message!
”);

printf(“Source IP:%d.%d.%d.%d
”,

rxtx_buffer[ARP_SRC_IP_P+0],rxtx_buffer[ARP_SRC_IP_P+1],

rxtx_buffer[ARP_SRC_IP_P+2],rxtx_buffer[ARP_SRC_IP_P+3]);

printf(“Source MAC:%02X-%02X-%02X-%02X-%02X-%02X
”,

rxtx_buffer[ARP_SRC_MAC_P+0],rxtx_buffer[ARP_SRC_MAC_P+1],

rxtx_buffer[ARP_SRC_MAC_P+2],rxtx_buffer[ARP_SRC_MAC_P+3],

rxtx_buffer[ARP_SRC_MAC_P+4],rxtx_buffer[ARP_SRC_MAC_P+5]);

#endif

return 1;

}

4.2 生成ARP首部

生成ARP首部仍是紧紧围绕两个地址打开,即方针MAC地址和方针IP地址,在ARP呼应进程中,源MAC地址和IP地址现在改变为了方针MAC地址和IP地址。

[cpp] view plain copyvoid arp_generate_packet ( BYTE *rxtx_buffer, BYTE *dest_mac, BYTE *dest_ip )

{

unsigned char i;

// 硬件类型 0001 10M以太网

rxtx_buffer[ ARP_HARDWARE_TYPE_H_P ] = ARP_HARDWARE_TYPE_H_V;

rxtx_buffer[ ARP_HARDWARE_TYPE_L_P ] = ARP_HARDWARE_TYPE_L_V;

// 协议类型

rxtx_buffer[ ARP_PROTOCOL_H_P ] = ARP_PROTOCOL_H_V;

rxtx_buffer[ ARP_PROTOCOL_L_P ] = ARP_PROTOCOL_L_V;

// 硬件地址长度

rxtx_buffer[ ARP_HARDWARE_SIZE_P ] = ARP_HARDWARE_SIZE_V;

// 协议地址长度

rxtx_buffer[ ARP_PROTOCOL_SIZE_P ] = ARP_PROTOCOL_SIZE_V;

// 方针硬件地址和源硬件地址

for ( i=0; i《sizeof(MAC_ADDR); i++)

{

rxtx_buffer[ ARP_DST_MAC_P + i ] = dest_mac[i];

rxtx_buffer[ ARP_SRC_MAC_P + i ] = stm32_mac.byte[i];

}

// 方针IP地址和源IP地址

for ( i=0; i《sizeof(IP_ADDR); i++)

{

rxtx_buffer[ ARP_DST_IP_P + i ] = dest_ip[i];

rxtx_buffer[ ARP_SRC_IP_P + i ] = stm32_ip.byte[i];

}

}

4.3 呼应ARP恳求

ARP呼应能够体现出TCP IP报文发生的根本进程,即层层包装。先包装以太网首部,在包装ARP首部,最终经过ENC28J60发送即可。

[cpp] view plain copyvoid arp_send_request ( BYTE *rxtx_buffer, BYTE *dest_ip )

{

unsigned char i;

MAC_ADDR dest_mac;

// generate ethernet header

for ( i=0; i《sizeof(MAC_ADDR); i++)

dest_mac.byte[i] = 0xff;

eth_generate_header ( rxtx_buffer, (WORD_BYTES){ETH_TYPE_ARP_V}, (BYTE*)&dest_mac );

// generate arp packet

for ( i = 0 ; i 《 sizeof(MAC_ADDR) ; i++)

dest_mac.byte[i] = 0x00;

// set arp opcode is request

rxtx_buffer[ ARP_OPCODE_H_P ] = ARP_OPCODE_REQUEST_H_V;

rxtx_buffer[ ARP_OPCODE_L_P ] = ARP_OPCODE_REQUEST_L_V;

arp_generate_packet ( rxtx_buffer, (BYTE*)&dest_mac, dest_ip );

// send arp packet to network

enc28j60_packet_send ( rxtx_buffer, sizeof(ETH_HEADER) + sizeof(ARP_PACKET) );

}

5.测验

PC机经过ping指令发送一个ICMP报文,ping指令是承认网络是否衔接的指令,例如发送ping 192.168.1.115,由于PC机不清晰该IP地址的MAC地址,所以会先发送一个ARP恳求。STM32设备可捕获该ARP恳求,并经过串口输出发送ARP恳求的设备的IP地址和MAC地址。此刻先不必理睬是否能够ping通,由于会在今后的文章中完成。

在开端之前能够经过ipconfig /all指令查询本机的IP地址和MAC地址,经过arp -a指令查询PC机中ARP缓冲表。假如有必要可运用arp –d铲除缓冲表的一切内容。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qiche/347916.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部