stm32的LWIP在无操作系统下TCP功能加入双路IP

1.stm32f207,无操作系统,LWIP-1.3.2,开发TCP服务器下的双路ip;

实现前提:先调通,实现单路IP;

参考帖子(然而最后的感觉都是没啥卵用):

STM32 LwIP单网卡绑定多个IP地址-https://blog.csdn.net/sinat_20006769/article/details/80847989   

LWIP 双IP实现-https://blog.csdn.net/weixin_38307270/article/details/80873032

参考资料2:https://www.cnblogs.com/x_wukong/p/5924798.html

参考资料1:零死角玩转STM32—F407霸天虎.pdf  ; STM32F4开发指南-库函数版本_V1.1.pdf

2.简述执行过程

2.1初始化: 

void LwIP_Init(void)---LwIP_Init 函数用于初始化 LwIP 协议栈,一般在 main 函数中调用。

  • 首先是内存相关初始化,mem_init 函数是动态内存堆初始化,memp_init 函数是存储池初始化,LwIP 是实现内存的高效利用,内部需要不同形式的内存管理模式。
    • 接下来为 ipaddr、netmask 和 gw 结构体变量赋值,设置本地 IP 地址、子网掩码和网关,如果使用 DHCP 功能直接赋值为 0 即可。                                                                                                                                                                                       netif_add 是以太网设备添加函数,即向 LwIP 协议栈申请添加一个网卡设备,函数有 7 个形参,第一个为 netif 结构体类型变量指针,这里赋值为 gnetif 地址,该网卡设备属性就存放在 gnetif 变量中;第二个为 ip_addr 结构体类型变量指针,用于设置网卡 IP 地址;第三个 ip_addr 结构体类型变量指针,用于设置子网掩码;第四个为 ip_addr 结构体类型变量指针,用于设置网关;第五个为 void 变量,用户自定义字段,一般不用直接赋值 NULL;                                                        第六个为 netif_init_fn 类型函数指针,用于指向网卡设备初始化函数,这里赋值为指向 ethernetif_init 函数,该函数在 ethernetif.c 文件定义,初始化 LwIP 与 ETH 外设连接函数;                                                                                                           最后一个参数为 netif_input_fn 类型函数指针,用于指向以太网帧接收函数,这里赋值为指向 ethernet_input 函数,该函数定义在 etharp.c 文件中。                                                                                                                                             netif_set_default 函数用于设置指定网卡为默认的网络通信设备。--两个以上设置要注意!!
  • 在无硬件连接错误时,调用 ETH_BSP_Config(优先 LwIP_Init 函数被调用)时会将EthStatus 变量对应的 ETH_LINK_FLAG 位使能,所以在 LwIP_INIT 函数中会执行 if 判断语句代码,置位网卡设备标志位以及运行 netif_set_up 函数启动网卡设备。否则执行netif_set_down 函数停止网卡设备。
  • 最后,根据需要调用 netif_set_link_callback 函数实在当链路状态发生改变时需要调用的回调函数配置    

  • 第六个参数-ethernetif_init()引入硬件初始化函数--low_level_init()

      low_level_init 函数是网络接口初始化函数,在 ethernetif_init 函数被调用,在系统启动时被运行一次,用于初始化与网络接口相关硬件。

      函数先是给 netif 结构体成员赋值配置网卡参数,调用 ETH_MACAddressConfig 函数绑定网卡 MAC 地址,ETH_DMATxDescChainInit 和 ETH_DMARxDescChainInit 初始化网络数据帧发送和接收描述符,设置为链模式。

调用ETH_DMARxDescReceiveITConfig 函数使能 DMA 数据接收相关中断。通过定义宏 CHECKSUM_BY_HARDWARE,可以使能发送数据硬件校验和,这个需要硬件支持,STM32F4xx 控制器是支持的。

调用 sys_thread_new 函数创建一个任务,设置任务函数是 ethernetif_input,该函数用于讲接收到数据包转入到 LwIP 内部缓存区,这里还传递了 netif 结构体变量。

最后,调用 ETH_Start 函数使能 ETH。low_level_output 和 low_level_input 两个函数内容(备注:无操作系统时网络接口函数--主要有三个部分函数,一个是 low_level_init,用于初始化 MAC 相关工作环境、初始化 DMA 描述符链表,并使能 MAC 和 DMA;一个是low_level_output,它是最底层发送一帧数据函数;最后一个是 low_level_input,它是最底层接收一帧数据函数。)

  • 第七个函数ethernet_input

参考帖子理解:http://blog.sina.com.cn/s/blog_62a85b950101anvx.html

看到是上面的第一个截图,网络接口层的第一个函数为netif->input,接收到的数据,有两种,一个是以太网+ip包,一个是arp包;如下左图:

    LWIP利用netif.input指向的函数接收以太网数据包,通常这个函数是ethernet_input。注意,这里并不是说ethernet_input直接与底层硬件交互接收数据包,而是更底层的函数接收到数据包后将数据包递交给ethernet_input,ethernet_input再对其进行处理。
    以太网的帧类型可以是:IP,ARP,甚至可以是pppoe, wlan等。这里主要分析IP和ARP两种类型的数据包。ethernet_input根据以太网首部的类型字段判断收到的数据包的类型(如下图所示!),如果是IP包,则将该包递交给etharp_ip_input,如果是ARP包,则将该包递交给etharp_arp_input。如上左图;                                                                                                                                                                  ARP从功能上来说可以简单的分成两个部分:

     当有数据包输入时,更新arp表,如果是ip包则递交给ip层,如果是arp包,则针对不同的arp包类型做相应的响应;当向目的ip发送一个数据包的时候,需要通过arp实现ip到MAC地址的映射,必要时,需要发送广播数据包获得目标机器的MAC地址。

      对于arp类型的数据包,etharp_arp_input函数首先利用数据包头信息更新arp表的内容,然后再判断该ARP数据包的类型,如果是ARP请求包,则首先判断这个包是不是给自己的,如果是给自己的,则在原有包的基础上重组一个ARP应答包发送出去(注意此处并没有重新分配一个pbuf,而是借用了原来的缓冲结构)。如果不是给自己的,则直接忽略。如果是ARP应答包,主要的工作就是更新arp表,但是这一步已经在arp包刚进来的时候就处理了,所以这里不需要再重复做,这样ARP包的处理也完毕。
     对于ip类型的数据包,etharp_ip_input首先检查是否开启了ETHARP_TRUST_IP_MAC这个选项,如果开启了就是要用这个帧中的信息和update_arp_entry函数来更新arp表(利用帧首部的源mac地址和帧数据中ip报文中的源ip地址),然后丢弃以太网帧首部,将IP报文通过ip_input函数递交给ip层。

代码说明如下

err_t ethernet_input(struct pbuf *p,struct netif *netif)
        {
            struct eth_hdr *ethhdr;                  // 以太网数据包头结构体
            u16_t type;
            s16_t ip_hdr_offset = SIZEOF_ETH_HDR;    // 包头固定值14字节
            ethhdr = (eth_hdr *)p->payload;
            type = htons(ethhdr->type);
            
            switch(type)
            {
                case ETHTYPE_IP:      // IP数据包 0x0800
                    etharp_ip_input(netif,p);  // 使用收到的IP包更新ARP缓存表,详见《lwip之ARP协议》
                    pbuf_header(p, -ip_hdr_offset);  // 调整以太网数据包指针,使掠过包头,指向IP协议包头
                    ip_input(p,netif);// 提交IP层,ip_input为IP层主要函数,解析见下文
                case ETHTYPE_ARP:  // ARP数据包 0806
etharp_arp_input(netif,(struct eth_addr *)netif->hwaddr,p);  // ARP数据包处理,第二个形参是本机MAC,详见《lwip之ARP协议》
                    break;
                default:
                    break;
            }
        }

2.2 数据帧结构

  •  前导字段,也称报头,这是一段方波,用于使收发节点的时钟同步。内容为连续 7 个字节的 0x55。字段和帧起始定界符在 MAC 收到数据包后会自动过滤掉。
  •  帧起始定界符(SFD):用于区分前导段与数据段的,内容为 0xD5。
  •  MAC 地址: MAC 地址由 48 位数字组成,它是网卡的物理地址,在以太网传输的最底层,就是根据 MAC 地址来收发数据的。部分 MAC 地址用于广播和多播,在同一个网络里不能有两个相同的 MAC 地址。PC 的网卡在出厂时已经设置好了 MAC 地址,但也可以通过一些软件来进行修改,在嵌入式的以太网控制器中可由程序进行配置。
  • 数据包中的 DA 是目标地址,SA 是源地址。
  •  数据包类型:本区域可以用来描述本 MAC 数据包是属于 TCP/IP 协议层的 IP 包、ARP包还是 SNMP 包,也可以用来描述本 MAC 数据包数据段的长度。如果该值被设置大于 0x0600,不用于长度描述,而是用于类型描述功能,表示与以太网帧相关的 MAC客户端协议的种类。
  •  数据段:数据段是 MAC 包的核心内容,它包含的数据来自 MAC 的上层。其长度可以从 0~1500 字节间变化。
  •  填充域:由于协议要求整个 MAC 数据包的长度至少为 64 字节(接收到的数据包如果少于 64 字节会被认为发生冲突,数据包被自动丢弃),当数据段的字节少于 46 字节时,在填充域会自动填上无效数据,以使数据包符合长度要求。
  •  校验和域:MAC 数据包的尾部是校验和域,它保存了 CRC 校验序列,用于检错。

        以上是标准的 MAC 数据包,IEEE 802.3 同时还规定了扩展的 MAC 数据包,它是在标准的 MAC 数据包的 SA 和数据包类型之间添加 4 个字节的 QTag 前缀字段,用于获取标志的 MAC 帧。前 2 个字节固定为 0x8100,用于识别 QTag 前缀的存在;后两个字节内容分别为 3 个位的用户优先级、1 个位的标准格式指示符(CFI)和一个 12 位的 VLAN 标识符。


   

2.3 ping IP 的基本流程:

当主机A要与主机B通信时,ARP协议可以将主机B的IP地址解析成主机B的MAC地址,工作流程如下:    
        第一步:主机A先检查自己的ARP缓冲,看是否存在主机B匹配的MAC地址,如果没有,就会向外广播一个ARP请求包
        第二步:其他主机收到后,发现请求的IP地址与自己的IP地址不匹配,则丢弃ARP请求
        第三步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射到本地ARP缓存中
        第四步:主机B将包含其MAC地址的ARP回复发回给主机A
        第五步:主机A收到从主机B发来的ARP回复时,会将主机B的IP地址和MAC地址映射更新到本地ARP缓存中。主机B的MAC地址一旦确定,主机A就可以向主机B发送IP通信了

参考:lwip之数据收发流程 https://blog.csdn.net/banruoju/article/details/54340684

2.4 主函数修改

  

  •  LwIP_Pkt_Handle 函数用于从以太网存储器读取一个以太网帧并将其发送给 LwIP,它在接收到以太网帧时被调用,它是直接调用 ethernetif_input 函数实现的,该函数定义在ethernetif.c 文件中。

  • 获取活跃的网卡号

以上是所有的改动点,Ping通是通讯的第一步!

跟另外两个帖子处理不一样;

3 TCP服务器网络通讯的收发

...待续!

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
Stm32LwIP TCP是指在Stm32微控制器上使用LwIP协议栈进行TCP通信。根据引用\[1\]中的描述,你在调试tcp_connect()函数时遇到了一些问题。其中可能的原因有几个。首先,网上的资料大多是将Stm32作为服务器使用,而很少是将其作为客户端使用,这可能导致配置PC服务器变得困难。其次,你可能没有找到相关的资料,只能自己调试。最后,你怀疑自己搭建的服务器有问题,或者电脑的防火墙可能会影响连接。 根据引用\[2\]中的代码片段,你在初始化函数中创建了一个TCP控制块,并将其绑定到本地IP地址和端口号。然后将连接状态设置为LISTEN,并指定在建立连接时调用的函数。 根据引用\[3\]中的描述,tcp_bind()函数用于绑定端口号和IP地址,tcp_listen()函数用于进入监听状态并检查连接,tcp_accept()函数用于处理客户端连接后的回调函数。 综上所述,你遇到的问题可能是由于配置问题、代码错误或网络设置问题导致的。你可以检查你的网络设置、代码逻辑和防火墙设置来解决这些问题。 #### 引用[.reference_title] - *1* [利用stm32lwip TCP/IP协议栈的通信的思路](https://blog.csdn.net/weixin_31313629/article/details/119470770)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [STM32 LWIP TCP以太网传输数据](https://blog.csdn.net/u012246376/article/details/45893235)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxilang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值