STM32H743-ARM例程28-UDP
本文介绍了基于STM32H743开发板的UDP通信实现。实验使用银杏科技GT7000开发板和iToolXE仿真器,配合STM32CubeMX、MDK开发环境。文章详细解析了UDP协议特性:无连接传输、数据报分组、简单校验机制,并对比了UDP与TCP的区别。重点阐述了UDP的单播、广播和组播三种通信方式,以及UDP在视频流媒体、在线游戏等实时性要求高的场景中的优势。最后通过CubeMX配置以太网RM
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35,TCP&UDP测试工具
网盘资料包:链接: https://pan.baidu.com/s/1Y3nwaY4SMxfoUsdqPm2R3w?pwd=inba 提取码: inba
UDP
在之前TCP_CLIENT章节我们介绍了物联网的OSI七层模型,其中传输层包括TCP、UDP,本章我们来详细介绍下UDP。
UDP是User Datagram Protocol的简称,中文名是用户数据报协议,是OSI参考模型中的传输层协议,它是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。UDP的正式规范是IETF RFC768。UDP在IP报文的协议号是17。
UDP 是一个无连接的、不可靠的传输层协议。它专注于快速地将数据包从源端发送到目的端,但不保证数据包一定能到达、不保证按顺序到达,也不提供重传或拥塞控制机制。
可以把它想象成寄送明信片:
-
你写好就扔进邮筒(发送出去)。
-
你不会确认对方是否收到(无连接)。
-
明信片可能会丢失(不可靠)。
-
如果你寄了很多张,它们到达的顺序可能和你寄出的顺序不同(不保证顺序)。
-
但它非常快捷,省去了挂号信的繁琐手续(开销小)。
UDP报文

1)源端口(2 字节):发送方端口号
2)目的端口(2 字节 ):接收方端口号
3)报文长度(2 字节):UDP 用户数据报的总长度,以字节为单位。
4)校验和(2 字节):检测 UDP 用户数据报在传输中是否有错,有错就丢弃。
用于校验 UDP 数据报的数字段和包含 UDP 数据报首部的“伪首部”。
伪首部, 又称为伪包头(Pseudo Header):是指在 TCP 的分段或 UDP 的数据报格式中,在数据报首部前面增加源 IP 地址、目的 IP 地址、IP 分组的协议字段、TCP 或 UDP 数据报的总长度等共12字节,所构成的扩展首部结构。此伪首部是一个临时的结构,它既不向上也不向下传递,仅仅只是为了保证可以校验套接字的正确性。
5)数据:UDP 的数据部分如果不为偶数需要用 0 填补,就是说,如果数据长度为奇数,数据长度加“1”。
UDP的单播、广播、组播
1、UDP特点单播:用于两个主机之间端对端的通信。即一对一(客户端与服务器端点到点连接)。
2、广播:用于一个主机对整个局域网上所有主机通信。即一对所有。广播禁止在Internet宽带网上传输(广播风暴)。
3、组播(多播):对一组特定的主机进行通信,而不是整个局域网上的所有主机。即一对一组
将网络中同一业务类型主机进行了逻辑上的分组,进行数据收发的时候其数据仅仅在同一分组中进行,其他的主机没有加入此分组不能收发对应的数据。
单播和广播是两个极端,要么对一个主机进行通信,要么对整个局域网上的主机进行通信。实际情况下,经常需要对一组特定的主机进行通信,而不是整个局域网上的所有主机,这就是组播的用途。
注意:只有UDP才有广播、组播的传递方式。而TCP是一对一连接通信。组播的重点是高效的把同一个包尽可能多的发送到不同的,甚至可能未知的设备。但是TCP连接是一对一明确的。
想了解UDP更多内容的读者可以阅读UDP协议是什么?作用是什么?这篇文章
UDP特点
无连接传输
UDP的无连接特性使得数据可以快速传输。发送方无需等待接收方的响应,可以直接发送数据报。这种机制减少了延迟,但也带来了数据包丢失的风险。
数据报分组与组装
UDP将数据分割成独立的数据报,每个数据报包含源端口、目标端口、长度和校验和。接收方接收到数据报后,负责将其组装成完整的数据。如果数据报丢失或顺序错乱,接收方无法自动恢复。
简单校验和机制
UDP使用简单的校验和机制来检测数据报在传输过程中的错误。校验和覆盖了UDP头部和数据部分,但它的错误检测能力有限,无法像TCP那样确保数据的完整性。
UDP的应用场景
UDP适用于对传输速度要求较高、容忍一定数据丢失的场景:
视频流媒体:如YouTube和Netflix,视频流媒体对数据传输的实时性要求高,UDP能够减少延迟,提高用户体验。
在线游戏:游戏中的实时交互对延迟非常敏感,UDP的低延迟特性使其成为在线游戏的首选。
DNS查询:DNS查询需要快速响应,UDP的简单机制能够满足这一需求。
TCP和UDP区别
| 特征点 | TCP | UDP |
|---|---|---|
| 传输可靠性 | 面向连接 | 面向非连接 |
| 应用场景 | 传输数据量大 | 传输量小 |
| 速度 | 慢 | 快 |
UDP传输与IP传输非常类似,它的传输方式也是"Best Effort"的,所以UDP协议也是不可靠的。我们知道TCP就是为了解决IP层不可靠的传输层协议,既然UDP是不可靠的,为什么不直接使用IP协议而要额外增加一个UDP协议呢?
1、一个重要的原因是IP协议中并没有端口(port)的概念。IP协议进行的是IP地址到IP地址的传输,这意味者两台计算机之间的对话。但每台计算机中需要有多个通信通道,并将多个通信通道分配给不同的进程使用。一个端口就代表了这样的一个通信通道。UDP协议实现了端口,从而让数据包可以在送到IP地址的基础上,进一步可以送到某个端口。
2、对于一些简单的通信,我们只需要“Best Effort”式的IP传输就可以了,而不需要TCP协议复杂的建立连接的方式(特别是在早期网络环境中,如果过多的建立TCP连接,会造成很大的网络负担,而UDP协议可以相对快速的处理这些简单通信)
3、在使用TCP协议传输数据时,如果一个数据段丢失或者接收端对某个数据段没有确认,发送端会重新发送该数据段。TCP重新发送数据会带来传输延迟和重复数据,降低了用户的体验。对于迟延敏感的应用,少量的数据丢失一般可以被忽略,这时使用UDP传输将能够提升用户的体验。
STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示。我们来看配置MPU配置、以太网部分和Lwip部分配置如下图所示:
配置以太网。选用RMII(精简的独立于介质接口)模式



MPU配置
LWIP配置
选择PHY芯片型号。LAN8742向下兼容LAN8720
这里需要是能IPV6
实验代码
1. 主函数
int main(void)
{
MPU_Config();
SCB_EnableICache();
SCB_EnableDCache();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART6_UART_Init();
MX_LWIP_Init();
uart6.initialize(115200);
uart6.printf("\x0c");
uart6.printf("GT7000 OK!\r\n");
HAL_GPIO_WritePin(PHYAD0_GPIO_Port,PHYAD0_Pin,GPIO_PIN_RESET);
eth_udp.initialize();
while (1)
{
MX_LWIP_Process();
if(eth_udp.receive_ok_flag == 1){
eth_udp.receive_ok_flag = 0;
eth_udp.send_data(eth_udp.udppcb);
}
}
}
2. LwIP初始化
void MX_LWIP_Init(void)
{
/* IP addresses initialization */
IP_ADDRESS[0] = 192;
IP_ADDRESS[1] = 168;
IP_ADDRESS[2] = 0;
IP_ADDRESS[3] = 11;
NETMASK_ADDRESS[0] = 255;
NETMASK_ADDRESS[1] = 255;
NETMASK_ADDRESS[2] = 255;
NETMASK_ADDRESS[3] = 0;
GATEWAY_ADDRESS[0] = 192;
GATEWAY_ADDRESS[1] = 168;
GATEWAY_ADDRESS[2] = 0;
GATEWAY_ADDRESS[3] = 1;
/* Initilialize the LwIP stack without RTOS */
lwip_init();
/* IP addresses initialization without DHCP (IPv4) */
IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
/* add the network interface (IPv4/IPv6) without RTOS */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
/* Registers the default network interface */
netif_set_default(&gnetif);
/* We must always bring the network interface up connection or not... */
netif_set_up(&gnetif);
/* Set the link callback function, this function is called on change of link status*/
netif_set_link_callback(&gnetif, ethernet_link_status_updated);
/* Create the Ethernet link handler thread */
}
3. UDP相关函数
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/tcp.h"
#include "lwip/init.h"a
#include "netif/etharp.h"
#include "lwip/udp.h"
#include "lwip/pbuf.h"
#include <stdio.h>
#include <string.h>
#include "eth_udp.h"
#define PC_PORT 30001
#define LOCAL_PORT 30000
//--------------------------- Variable --------------------------//
struct udp_pcb *UdpPcb;
ip4_addr_t rip_addr; //远端IP
uint8_t PCIP_ADDRESS[4] = {192, 168, 0, 10};
//UDP接收数据
void UDP_Receive_Callback(void *arg, struct udp_pcb *upcb, struct pbuf *p,
const ip4_addr_t *addr, u16_t port)
{
struct pbuf *pb;
if(p != NULL) /* 如果收到的数据不为空 */
{
pb= p; //保存当前控制块
UDP_SendData(pb->payload,pb->tot_len);
UdpPcb->remote_ip = *addr; // 获取远端IP地址
UdpPcb->remote_port = port; //记录端口
/* Free the p buffer */
pbuf_free(p);
}
else
{
}
}
//UDP发送数据
void UDP_SendData(uint8_t *buff, uint16_t len)
{
struct pbuf *p;
p = pbuf_alloc(PBUF_TRANSPORT,strlen((char*)buff), PBUF_POOL);
if (p != NULL)
{
/* copy data to pbuf */
pbuf_take(p, (char*)buff, strlen((char*)buff));
/* send udp data */
udp_send(UdpPcb, p);
/* free pbuf */
pbuf_free(p);
}
}
//UDP初始化
void udp_Init(void)
{
err_t err;
/* Create a new UDP control block */
UdpPcb = udp_new(); //创建UDP控制块
if(UdpPcb != NULL)
{
IP4_ADDR(&rip_addr, PCIP_ADDRESS[0] ,PCIP_ADDRESS[1] ,PCIP_ADDRESS[2] ,PCIP_ADDRESS[3]); //设置远端IP地址
err = udp_bind(UdpPcb, IP_ADDR_ANY, LOCAL_PORT); //绑定本地IP地址,端口30000
if(err == ERR_OK) //绑定OK
{
err = udp_connect(UdpPcb, &rip_addr, PC_PORT); //连接远程主机,指定其IP和端口
udp_recv(UdpPcb, UDP_Receive_Callback, NULL); /* 设置数据接收时的回调函数*/
}
else
{
udp_remove(UdpPcb); //删除UDP控制块
}
}
}
实验现象
1、打开控制面板–>网络和Internet–>网络和共享中心–>更改适配器设置–>以太网属性
2、Internet协议版本4,选择使用下面的IP地址,然后更改IP地址和默认网关,IP地址与代码中设置的服务器IP(192.168.0.11)在同一网段下即可如下图所示
3、打开测试工具,点击创建连接,弹出设置端口的窗口,设置为30000和30001,30000为GT7000开发版端口号,30001为PC主机指定端口号
客户端已经创建完成,点击连接。
GT7000客户端自动连接服务器,即可通信。(若连接不成功,请关闭电脑防火墙)
5、在发送区编辑完要发送的数据信息后,点击发送即可收到发送的数据包。如图所示:
更多推荐



所有评论(0)