首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

STM32NET学习笔记 ARP和Ethernet部分

STM32NET学习笔记 ARP和Ethernet部分

嵌入式以太网开发是一个很有挑战性的工作。通过几个月的学习,个人觉得大致有两条途径。第一条途径,通过高级语言熟悉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 plaincopy

  • // 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 plaincopy

  • // 初始化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 plaincopy

  • // 协议类型 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 plaincopy

  • void 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;  
  •         // avr_mac为全局变量
  •         rxtx_buffer[ ETH_SRC_MAC_P + i ] = stm32_mac.byte;  
  •     }  
  •     // 配置协议类型 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地址作为了全局变量,可以在函数内部填充到缓冲区中。
继承事业,薪火相传
返回列表