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

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

目录

  • LWIP
    • LWIP简介
    • LWIP主要特性
  • ENC28J60
    • ENC28J60简介
    • ENC28J60特点
  • 无操作系统LWIP移植
    • 在说移植之前,先说下几个重要的函数功能和数据结构
      • enc28j60.c文件
      • 主要结构体
        • *netif*结构体定义(netif.h)只列出了比较重要的字段
    • lwip__comm.c文件
      • __lwip_dev(lwip__commm.h)存放一些重要信息
      • lwip_comm.c文件的API
        • u8 lwip_comm_init(void)
        • 然后看一个DHCP动态分配IP过程:
    • ethernetif.c文件
      • 主要函数
      • LWIP 的裁剪与配置
  • RAW编程接口UDP
    • UDP协议
      • UDP中的协议函数
      • LWIP中RAW API编程接口中与UDP相关的函数。
        • udp_demo_test()

LWIP

LWIP简介

lwip,是由Adam Dunkels开发的小型开源的TCP/IP协议栈;支持TCPIP协议族的核心协议;主要包括:ARP/ICMP/TCP/UDP/IPV4/IPV6/DHCP等;最主要的特点是运行所需的RAM和ROM少(只需十几KB的RAM和四十KB的ROM就可以运行),这使LWIP协议栈适合在低端的嵌入式系统中使用.

LWIP主要特性

  • ARP 协议,以太网地址解析协议;
  • IP 协议,包括 IPv4 IPv4 和 IPv6 IPv6 ,支持 IP 分片与重装,支持多网络接口下数据转发;
  • ICMP 协议,用于网络调试与维护;
  • IGMP IGMP 协议,用于网络组管理可以实现多播数据的接收;
  • UDP 协议,用户数据报;
  • TCP 协议,支持 TCPTCP 拥塞控制, 拥塞控制, RTT估计,快速恢复与重传等;
  • 提供三种用户编程接口方式: raw/callback APIraw/callback;
  • DNS ,域名解析;
  • SNMP ,简单网络管理协议
  • DHCP,动态主机配置协议;
  • AUTOTP,IP地址自动配置;
  • PPP,点对点协议,支持PPPoE

ENC28J60

ENC28J60简介

ENC
对于stm32f103来说没有网口,通信就得靠enc28j60

ENC28J60特点

  • 兼容 IEEE802.3 IEEE802.3 协议的以太网控制器 。

  • 集成 MAC MAC和 10 BASE 10 BASE 10 BASE 10 BASE-T物理层 。

  • 支持全双工和半模式 。

  • 数据冲突时可编程自动重发 。

  • SPI 接口速度可达 接口速度可达 10Mbps 10Mbps 。

  • 8K 数据接收和发送双端口 数据接收和发送双端口 RAM 。

  • 提供快速数据移动的内部 DMA 控制器 控制器 。

  • 可配置的接收和发送缓冲区大小 。

  • 两个可编程 LED 输出 。

  • 带 7个中断源的两引脚 个中断源的两引脚 。

  • TTL TTL 电平输入 。

  • 提供多种封装: SOIC/SSOP/SPDIP/QFN SOIC/SSOP/SPDIP/QFN 等。
    ENC28J60典型应用电路

  • SPI接口,充当主控制器和ENC28J60之间的通道

  • 控制寄存器,用于控制和监视ENC28J60.

  • 双端口RAM缓冲器,用于接收和发送数据包。

  • 判优器,当DMA、发送和接受模块发出请求时对RAM缓冲器的访问进行控制。

  • 总线接口,对通过SPI接收的数据和命令进行解析。

  • MAC(Medium Access Control),实现符合IEEE802.3标准的MAC逻辑。

  • PHY(物理层)模块,对双绞线上的模拟数据进行编码和译码。
    ENC28J60功能框图

无操作系统LWIP移植

在说移植之前,先说下几个重要的函数功能和数据结构

enc28j60.c文件

主要函数及功能enc28j60.c文件函数

主要结构体

因为LWIP可以支持的结构体,因为LWIP可以支持多个网络接口,当设备
有多个网络接口的话LWIP就会把所有的netif结构体组成链表来管理这些网络接口

netif结构体定义(netif.h)只列出了比较重要的字段

struct netif {
struct netif *next; //指向下过一个netif 结构体
ip_addr_t ip_addr; //网络接口IP 地址
ip_addr_t netmask; //子网掩码
ip_addr_t gw; //默认网关
netif_input_fn input; //IP 层接收数据函数
netif_output_fn output; //IP 层发送数据包调用
netif_linkoutput_fn linkoutput; //底层数据包发送
void *state; //设备的状态信息
u16_t mtu; //该网络接口最大允许传输的数据长度
u8_t hwaddr_len; //物理地址长度
u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; //该网络接口的物理地址
u8_t flags; //该网络接口的状态和属性
char name[2]; //该网络接口的名字
u8_t num; //该网络接口的编号
}
  • input:此字段为一个函数,这个函数将网卡接收到的数据交给IP 层。
  • output:此字段为一个函数,当IP 层向接口发送一个数据包时调用此函数。这个函数通常首先解析硬件地址,然后发送数据包。此字段我们一般使用etharp.c 中的etharp_output()函数。
  • linkoutput:此字段为一个函数,该函数被ARP 模块调用,完成网络数据的发送。上面说的etharp_output 函数将IP 数据包封装成以太网数据帧以后就会调用linkoutput 函数将数据发送出去。

lwip__comm.c文件

__lwip_dev(lwip__commm.h)存放一些重要信息

typedef struct
{ u8 mac[6]; //MAC 地址u8 remoteip[4];  // 远端主机 IP 地址u8 ip[4]; u8 ip[4]; // 本机 IP 地址u8 netmask[4];u8 netmask[4];// 子网掩码 u8 gateway[4];// 默认网关的 IP 地址 vu8 dhcpstatus;//dhcp状态//0, 未获取DHCP 地址 ; //1,进入 DHCP获取状态 //2,成功获取 DHCP地址 //0XFF,获取失败
}__lwip_dev;

lwip_comm.c文件的API

  • u8 lwip_comm_mem_malloc(void) //完成对mem.c和memp.c中内存堆ram_heap和内存池memp_memory的内存分配。
  • void lwip_comm_mem_free(void)//释放上个函数申请的内存 mem.c 和memp.c
  • void lwip_comm_default_ip_set( lwip_dev *lwipx) //设置本地默认地址和远端默认地址。网关 IP MAC 子网掩码。和DHCP状态
  • u8 lwip_comm_init(void)//内核初始化,设置网卡并打开指定的网卡。,下面重点说此函数。
  • void lwip_pkt_handle(void) //从网络缓冲区接受数据包并将其发送给LWIP处理
  • void lwip_periodic_handle()//完成对LWIP内核的定时器处理函数的周期性调用
  • void lwip_dhcp_process_handle(void)//DHCP处理函数,为设备分配IP地址,网关等信息

u8 lwip_comm_init(void)

//LWIP初始化(LWIP启动的时候使用)
//返回值:0,成功
//      1,内存错误
//      2,DM9000初始化失败
//      3,网卡添加失败.
u8 lwip_comm_init(void)
{struct netif *Netif_Init_Flag;		//调用netif_add()函数时的返回值,用于判断网络初始化是否成功struct ip_addr ipaddr;  			//ip地址struct ip_addr netmask; 			//子网掩码struct ip_addr gw;      			//默认网关 if(lwip_comm_mem_malloc())return 1;	//内存申请失败if(ENC28J60_Init())return 2;		//初始化ENC28J60lwip_init();						//初始化LWIP内核lwip_comm_default_ip_set(&lwipdev);	//设置默认IP等信息#if LWIP_DHCP		//使用动态IPipaddr.addr = 0;netmask.addr = 0;gw.addr = 0;
#else				//使用静态IPIP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
#endifNetif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&ethernet_input);//向网卡列表中添加一个网口#if LWIP_DHCP			//如果使用DHCP的话lwipdev.dhcpstatus=0;	//DHCP标记为0dhcp_start(&lwip_netif);	//开启DHCP服务
#endifif(Netif_Init_Flag==NULL)return 3;//网卡添加失败 else//网口添加成功后,设置netif为默认值,并且打开netif网口{netif_set_default(&lwip_netif); //设置netif为默认网口netif_set_up(&lwip_netif);		//打开netif网口}return 0;//操作OK.
} 

上面这个函数主要完成以下功能:
1 、调用 lwip_comm_mem_malloc() 函数完成前面提到的内存堆 ram_heap 和内存池
memp_memory 的内存分配。
2、调用 ENC28J60_Init ()函数完成对 ENC28J60 的初始化,ENC28J60_Init ()函数在
enc28j60.c 文件中,由 ALIENTEK 提供,前面已经介绍过了。
3、调用 lwip_init 函数完成 LWIP 的内核初始化,lwip_init()通过调用各个模块的初始化函数来完成各个模块的初始化,比如内存初始化函数、数据包结构初始化函数、网络接口初始化函数、IP 和 TCP 等的初始化函数,lwip_init()在 init.c 文件中,属于 LWIP 源码。
4、调用 ip_comm_default_ip_set()函数设置静态地址等信息,此函数由 ALIENTEK 提供。
5、判断是否使用 DHCP,如果使用 DHCP 的话就通过 DHCP 服务来获取 IP 地址、子网掩码和默认网关等信息,如果使用静态 IP 地址的话就用 ip_comm_default_ip_se()函数设置的地址信息。
6、我们通过 netif_add()函数来完成网卡的注册,netif_add()在 netif.c 文件中,此函数属于LWIP 源码。netif_add()函数中的参数 lwip_netif 是我们定义的一个网络接口,这个函数除了使用上面说的 IP 地址,子网掩码和默认网关作为参数外,还使用了两个函数地址作为参数:ethernetif_init 和 ethernet_input , 这两个函数地址会被赋值给 netif 结构体的相关字段,
ethernetif_init()在下一节会讲到,这个函数由 ALIENTEK 提供,ethernet_input()函数在 etharp.c文件中,属于 LWIP 源码,是 ARP 层数据包输入函数。
7、如果使用 DHCP 的话就开启 DHCP 服务,通过调用 dhcp_start()函数开启 DHCP 服务。
8、当网卡注册成功后使用netif_set_default()设置此网卡为默认网卡,并且使用netif_set_up()函数打开此网卡。
此函数也是我们所写的函数硬件驱动完后,的第一个函数。也是对LWIP初始化的函数,此函数运行成功则LWIP初始化成功。此函数完后就要定时去处理内核维护函数。lwip_periodic_handle();

然后看一个DHCP动态分配IP过程:

**void lwip_dhcp_process_handle(void)**此函数定时执行放在内核维护的定时中断里面。状态机模式执行。

//DHCP处理任务 
void lwip_dhcp_process_handle(void)
{u32 ip=0,netmask=0,gw=0;switch(lwipdev.dhcpstatus){case 0: 	//开启DHCPdhcp_start(&lwip_netif);lwipdev.dhcpstatus = 1;		//等待通过DHCP获取到的地址printf("正在查找DHCP服务器,请稍等...........\r\n");  break;case 1:		//等待获取到IP地址{ip=lwip_netif.ip_addr.addr;		//读取新IP地址netmask=lwip_netif.netmask.addr;//读取子网掩码gw=lwip_netif.gw.addr;			//读取默认网关 if(ip!=0)			//正确获取到IP地址的时候{lwipdev.dhcpstatus=2;	//DHCP成功printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);//解析出通过DHCP获取到的IP地址lwipdev.ip[3]=(uint8_t)(ip>>24); lwipdev.ip[2]=(uint8_t)(ip>>16);lwipdev.ip[1]=(uint8_t)(ip>>8);lwipdev.ip[0]=(uint8_t)(ip);printf("通过DHCP获取到IP地址..............%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);//解析通过DHCP获取到的子网掩码地址lwipdev.netmask[3]=(uint8_t)(netmask>>24);lwipdev.netmask[2]=(uint8_t)(netmask>>16);lwipdev.netmask[1]=(uint8_t)(netmask>>8);lwipdev.netmask[0]=(uint8_t)(netmask);printf("通过DHCP获取到子网掩码............%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);//解析出通过DHCP获取到的默认网关lwipdev.gateway[3]=(uint8_t)(gw>>24);lwipdev.gateway[2]=(uint8_t)(gw>>16);lwipdev.gateway[1]=(uint8_t)(gw>>8);lwipdev.gateway[0]=(uint8_t)(gw);printf("通过DHCP获取到的默认网关..........%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);}else if(lwip_netif.dhcp->tries>LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数{lwipdev.dhcpstatus=0XFF;//DHCP超时失败.//使用静态IP地址IP4_ADDR(&(lwip_netif.ip_addr),lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);IP4_ADDR(&(lwip_netif.netmask),lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);IP4_ADDR(&(lwip_netif.gw),lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);printf("DHCP服务超时,使用静态IP地址!\r\n");printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);}}break;default : break;}
}

我们通过判断 lwipdev 结构体的 dhcpstatus 字段判断是否使用 DHCP 服务,当 dhcpstatus=0 时表示开启 DHCP,我们调用 dhcp_start()函数开启相应网络接口的 DHCP 服务 dhcp_start()函数由 LWIP 源码提供。开启 DHCP 以后让 dhcpstatus=1,表示开始进行 DHCP,等待 DHCP 完成。当 DHCP
完成以后让 dhcpstatus=2 ,表示 DHCP 成功。下次执行此函数直default,但是当 DHCP 重试次数大于LWIP_MAX_DHCP_TRIES 时,意味着 DHCP 失败,这时dhcpstatus=0XFF,表示 DHCP 失败, 并且使用静态 IP 地址。

ethernetif.c文件

主要函数

  • static err_t low_level_init(struct netif *netif)//网卡的复位、协议栈网络接口管理结构体netif中相关字段的初始化
  • static err_t low_level_output(struct netif *netif, struct pbuf *p)
  • static struct pbuf * low_level_input(struct netif *netif) //从网卡中提取数据,并将其封装在pbuf结构体中工LWIP使用。函数 low_level_input()首先调用函数 ENC28J60_Packet_Receive()接收网络数据,然后将数据打包成 pbuf 结构,然后将这个 pbuf 返回,low_level_input 函数就是完成这个功能的,
  • err_t ethernetif_input(struct netif *netif)//对 low_level_input()函数做封装,然后将接收到的数据送入指定的网卡结构中
  • err_t ethernetif_init(struct netif *netif)//ethernetif_init()函数为 low_level_init()函数的简单封装,并且初始化了 netif 的相关字段,

LWIP 的裁剪与配置

在 LWIP 的源码中有个 opt.h 的文件,这个文件是裁剪和配置 LWIP 的,不过我们最好不要直接在 opt.h 里面做修改,我们可以打开 opt.h 文件看一下,里面的配置都是条件编译的,如果我们在其他地方有定义过的话那么在 opt.h 里面的定义不起作用了。所以我们可以新建一个.h 的文件来裁剪和配置 LWIP,我们前面提过在 LWIP->lwip_app->lwip_comm 下有一个 lwipopts.h 的文件,这个文件就是用来裁剪与配置LWIP

RAW编程接口UDP

UDP协议

——UDP 协议是 TCP/IP 协议栈中传输层协议,是一个简单的面向数据报的协议,在传输层中还有另一个重要的协议,那就是 TCP 协议。UDP 不提供数据包分组、组装,不能对数据包进行排序,当报文发送出去后无法知道是否安全、完整的到达。UDP 除了这些缺点外肯定有它自身的优势,由于 UDP 不属于连接型协议,因而消耗资源小,处理速度快,所以通常在音频、视频和普通数据传输时使用 UDP 较多。
数据报结构如下图所示
 UDP数据报结构
校验和首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算和将不会相符,由此 UDP 协议可以检测是否出错。

UDP中的协议函数

——在 LWIP 源码中 udp.c 和 udp.h 这两个文件是关于 UDP 协议的,这两个文件中有很多函数, 要想对 UDP 有详细的了解的话可以查阅这两个文件。在这里推荐一本我本人认为很好的 LWIP 参考书《嵌入式网络那些事 LWIP 协议栈深度剖析与实战演练》,作者朱升林,在这本书中对于 LWIP 的源码做了非常详细的讲解,想要研究 LWIP 源码的可以看看这本书,不过这本书中采用的是 1.3.2 版本的 LWIP,本教程采用的是 1.4.1 版本的 LWIP,所以有些函数会有差别,这一点大家一定要注意一下。

udp.c 中与 UDP 报文处理有关的函数之间的关系如图

UDP层函数关系及与应用程序接口函数

LWIP中RAW API编程接口中与UDP相关的函数。

LWIP的RAW API编程方式是基于回调机制,当我们初始化应用的时候我们必须为内核中的不同时间注册相应的回调函数,当相应的时间发生的时候这些回调函数就会被调用。

RAW API 函数 描述UDP的RAW APi 功能函数
———— ———————UDP 的 RAW API 功能函数

  • udp_demo.c里面的一些重要的函数
    UDP实验函数
    这里我们着重介绍一下 udp_demo_test() 这个函数。

udp_demo_test()

//UDP测试
void udp_demo_test(void)
{err_t err;struct udp_pcb *udppcb;  	//定义一个UDP服务器控制块struct ip_addr rmtipaddr;  	//用来保存远端ip地址u8 *tbuf;u8 key;u8 res=0;		
//	u8 t=0; //initsardata();udp_demo_set_remoteip();//设置远端IPtbuf=mymalloc(SRAMIN,200);	//申请内存if(tbuf==NULL)return ;		//内存申请失败了,直接退出sprintf((char*)tbuf,"Local IP:%d.%d.%d.%d",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);//服务器IP//LCD_ShowString(30,150,210,16,16,tbuf);  sprintf((char*)tbuf,"Remote IP:%d.%d.%d.%d",lwipdev.remoteip[0],lwipdev.remoteip[1],lwipdev.remoteip[2],lwipdev.remoteip[3]);//远端IP//LCD_ShowString(30,170,210,16,16,tbuf);  sprintf((char*)tbuf,"Remote Port:%d",UDP_DEMO_PORT);//客户端端口号//LCD_ShowString(30,190,210,16,16,tbuf);POINT_COLOR=BLUE;//字体为蓝色//LCD_ShowString(30,210,210,16,16,"STATUS:Disconnected"); //如果本地准备成功会覆盖此数据//创建一个UDP控制块udppcb=udp_new();//他自己的内存池创建initsardata();if(udppcb)//创建成功{ IP4_ADDR(&rmtipaddr,lwipdev.remoteip[0],lwipdev.remoteip[1],lwipdev.remoteip[2],lwipdev.remoteip[3]);//打包成u32 位err=udp_connect(udppcb,&rmtipaddr,UDP_DEMO_PORT);//UDP客户端连接到指定IP地址和端口号的服务器if(err==ERR_OK){err=udp_bind(udppcb,IP_ADDR_ANY,UDP_DEMO_PORT);//绑定本地IP地址与端口号if(err==ERR_OK)	//绑定完成{udp_recv(udppcb,udp_demo_recv,NULL);//注册接收回调函数  给相关的udppcb结构体元素赋值printf("STATUS:Connected   \r\n");//标记连接上了(UDP是非可靠连接,这里仅仅表示本地UDP已经准备好)udp_demo_flag |= 1<<5;			//标记已经连接上POINT_COLOR=RED;//LCD_ShowString(30,230,lcddev.width-30,lcddev.height-190,16,"Receive Data:");//提示消息		POINT_COLOR=BLUE;//蓝色字体}else res=1;}else res=1;		}else res=1;while(res==0)//连接成功{//Open_Elengine_2();key=KEY_Scan(0);if(key==WKUP_PRES)break;if(key==KEY0_PRES)//KEY0按下了,发送数据{udp_demo_senddata(udppcb);}
//		if((sendrecv.humi&0x0f)==0x01)//如果humi标志位为1则发送数据到上位机
//		{
//			
//			sendrecv.humi=sendrecv.humi&0xf0;//将该位清零
//		}if(udp_demo_flag&1<<6)//是否收到数据?{udp_demo_senddata(udppcb);elengine_wyx = 1;//LCD_Fill(30,250,lcddev.width-1,lcddev.height-1,WHITE);//清上一次数据//LCD_ShowString(30,250,lcddev.width-30,lcddev.height-230,16,udp_demo_recvbuf);//显示接收到的数据			udp_demo_flag&=~(1<<6);//标记数据已经被处理了.} lwip_periodic_handle();//轮询任务 维持LWIP内核delay_ms(2);//DHT11_test();}udp_demo_connection_close(udppcb); myfree(SRAMIN,tbuf);
} 

在上面代码中我们完成了一下几个功能:
1、调用 udp_new()函数创建一个 UDP 控制块 upcb。 2、当创建成功以后,调用 udp_connect()函数连接到指定的 IP 地址和端口号的主机,如果
UDP 控制块 upcb 创建失败的话 res 等于 1。
3、连接成功以后调用 udp_bind()函数绑定本地 IP 地址和端口号,连接失败的话就让 res 等于 1。
4、绑定成功以后使用 udp_recv()函数注册 UDP 的接收函数,并且置位
udp_demo_flag 的bit5,表示已经连接上(UDP 是非可靠连接,这里仅仅表示本地 UDP 已经准备好),如果绑定失败的话令 res 等 1。
5、如果 res 等于 0 的话那么表示以上 4 个步骤都成功了,成功以后就进入 while 循环,失败的话就调用 udp_demo_connection_close()函数断开连接,并且释放内存。
6、进入 while 以后我们通过按键来做不同的处理,当按下 KWY_UP 键的时
候退出循环,当按下 KWY0 键的时候发送数据。通过读取 udp_demo_flag 的 bit6 来判断是否接收到数据,当接收到数据的时候就在 LCD 上显示接收到的数据。
7、这点特别重要!在 while 循环中一定要调用函lwip_periodic_handle(),如果不调用这两个函数那么 LWIP 的内核就不能运行,那么网络肯定不会工作!
至此,我们关于 LWIP的RAW API UDP通信就说完了,关于RAW编程接口UDP的代码及实现部分请看


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

相关文章

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语言教材。 虽然它只讲解最基本的语法&…

新概念c语言周二强07答案,新概念C语言能力教程(普通高等教育十二五规划教材)...

导语 内容提要 周二强编写的《新概念C语言能力教程(普通高等教育十二五规划教材)》以先进的教学理念为指导&#xff0c;以培养编程能力与学习能力为目标&#xff0c;从全新的角度解析了C语言&#xff0c;高屋建瓴地阐释了C语言学习中的诸多难点&#xff0c;对序列点、指针等概念…