实验介绍
LWIP是嵌入式设备中较为常用的TCP/IP协议栈,本文将使用UDP协议传输较大的txt文件并写入PS端的DDR中,实验对文件传输的速率和准确率要求不高因此调用简单的UDP协议即可。
实验难点:
- LWIP的pbuf的理解。
- 对UDP接收回调函数的使用。
实验基本设置
如果不知道如何创建和使用SDK可以参考该文章:如何创建PS-PL工程
1.Vivado中搭建硬件环境,启用UART和以太网外设,UART也是需要的将打印一些提示信息。
这个要根据自己使用的板子选择
2.SDK设置
工程中的需要使用支持lwip的BSP包。这个BSP包可以是重新生成也可以添加lwip到已有的BSP包中。
如果需要配置lwip可以在以下界面中设置,这里只说一下api_modle,lwip支持3种api接口(接口区别在本文参考资料中有写),这里只能选择前两种API接口。
- RAW/Callback API
- Socket API
- NETCONN API
SDK代码
工程配置好后需要添加4个代码文件才能使用udp
- lwIP需要启动中断系统,需要sys_intr.c与sys_intr.h
- UDP使用则需要添加一些初始化和回调函数包含在udp.process.c与udp.process.h
这4个文件我附在了最后的完整代码中
主要代码介绍
main.c
- 设置开发板MAC地址
- 开启中断系统
- 设置本地IP地址
- 初始化lwIP
- 添加网络接口 设置默认网络接口
- 启动网络
- 初始化UDP连接
- 进行数据传输
#include "sys_intr.h"
#include "udp_process.h"
#include "xil_printf.h"
/* 基本参数设置 */
#define MAX_LEN 2500
u8 UDPsentBuffer[MAX_LEN]={0};
u8 UDPrecvBuffer[MAX_LEN]={0};
//udp传输来的指令存放数组
u8 PC2PScmd[MAX_LEN];
u8 PS2PCcmd[MAX_LEN];
//收到的数据长度
u16 revnum=0;
extern unsigned udp_connected_flag;
static XScuGic Intc; //GIC 中断管理
int main(void)
{
/* UDP init*/
struct netif *netif, server_netif;
ip_addr_t ipaddr, netmask, gw;
/* 开发板MAC地址 */
unsigned char mac_ethernet_address [] ={0x00, 0x0a, 0x35, 0x00, 0x01, 0x02};
/* 开启中断系统 */
Init_Intr_System(&IntcInstPtr);
Setup_Intr_Exception(&IntcInstPtr);
/* 添加本机IP地址,远端设备IP在udp.process.c中设置 */
netif = &server_netif;
IP4_ADDR(&ipaddr, 192, 168, 1, 30);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);
lwip_init(); //初始化lwIP库
/* 添加网络接口并将其设置为默认接口 */
if (!xemac_add(netif, &ipaddr, &netmask, &gw, mac_ethernet_address, XPAR_XEMACPS_0_BASEADDR)) {
xil_printf("Error adding N/W interface\r\n");
return -1;
}
netif_set_default(netif);
netif_set_up(netif); //启动网络
user_udp_init(); //初始化UDP
while (1)
{
/* 将MAC队列中的包传输的LwIP/IP栈中 */
xemacif_input(netif);
revnum=udp_ReadData(UDPrecvBuffer);
/* 接收到的数据PC2PScmd即存在其中 */
memcpy(PC2PScmd,UDPrecvBuffer,revnum);
/* 将值通过udp发送回去 */
if(revnum>0)
{
memcpy(UDPsentBuffer,PC2PScmd,revnum);
udp_SentData(revnum,UDPsentBuffer);
}
}
return 0;
}
udp_recv_callback函数
该函数是重要的接收回调函数
void udp_recv_callback(void *arg,struct udp_pcb *tpcb,struct pbuf *p, ip_addr_t *addr, u16_t port)
{
struct pbuf *p_r;
xil_printf("Received from %d.%d.%d.%d port %d\r\n", (addr->addr) & 0xFF,
(addr->addr>>8) & 0xFF, (addr->addr>>16) & 0xFF, (addr->addr>>24) & 0xFF, port);
/* Tell the client that we have accepted it */
//memcpy(RxBuffer,(unsigned char*)p->payload,p->len);
//xil_printf("Received:%d,len=%d",RxBuffer,p->len);
if(p != NULL)
{
//默认内存块为530字节左右,接收数据超过后会被存到多个内存块中这里将读取所有的内存块读出完整的数据
for(p_r = p; p_r != NULL; p_r = p_r->next)
{
memcpy(RxBuffer,(unsigned char*)p_r->payload,p_r->len);
RxCount += p_r->len;
}
}
/* Free the p buffer */
pbuf_free(p);
}
实验效果
设置网络调试助手发送hello world,ZYNQ返回hello world。
主机地址端口和远端地址端口要与代码写的一致,PC主机的地址在网络中心中可以设置
参考资料
学会Zynq(14)UDP发送Hello World
STM32 LWIP UDP3000字节接收
STM32以太网通信-LWIP简介
完整代码
sys_intr.c
#include "sys_intr.h"
void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
{
/* Enable interrupts from the hardware */
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
(void *)IntcInstancePtr);
Xil_ExceptionEnable();
}
int Init_Intr_System(XScuGic * IntcInstancePtr)
{
int Status;
XScuGic_Config *IntcConfig;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
sys_intr.h
#ifndef SYS_INTR_H_
#define SYS_INTR_H_
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
int Init_Intr_System(XScuGic * IntcInstancePtr);
void setup_Intr_Exception(XScuGic * IntcInstancePtr);
#endif
udp.process.c文章来源:https://www.toymoban.com/news/detail-766168.html
#include "udp_process.h"
//---------------------------------------------------------
// 变量定义
//---------------------------------------------------------
struct udp_pcb *connected_pcb = NULL;
static struct pbuf *pbuf_to_be_sent = NULL;
static unsigned local_port = 9000; //本地端口
static unsigned remote_port = 9000; //远程端口
volatile unsigned udp_connected_flag = 0; //连接标志
#define MAX_LEN 2500
//缓冲数组
uint8_t TxBuffer[MAX_LEN] = {0};
uint8_t RxBuffer[MAX_LEN] = {0};
uint16_t RxCount = 0;
void udp_recv_callback(void *arg,struct udp_pcb *tpcb,struct pbuf *p, ip_addr_t *addr, u16_t port)
{
struct pbuf *p_r;
xil_printf("Received from %d.%d.%d.%d port %d\r\n", (addr->addr) & 0xFF,
(addr->addr>>8) & 0xFF, (addr->addr>>16) & 0xFF, (addr->addr>>24) & 0xFF, port);
/* Tell the client that we have accepted it */
//memcpy(RxBuffer,(unsigned char*)p->payload,p->len);
//xil_printf("Received:%d,len=%d",RxBuffer,p->len);
if(p != NULL)
{
for(p_r = p; p_r != NULL; p_r = p_r->next)
{
memcpy(RxBuffer,(unsigned char*)p_r->payload,p_r->len);
RxCount += p_r->len;
}
}
/* Free the p buffer */
pbuf_free(p);
}
//---------------------------------------------------------
// UDP连接初始化函数
//---------------------------------------------------------
int user_udp_init(void)
{
struct udp_pcb *pcb;
ip_addr_t ipaddr;
err_t err;
udp_connected_flag = 0;
/* 创建UDP控制块 */
pcb = udp_new();
if (!pcb) {
xil_printf("Error Creating PCB.\r\n");
return -1;
}
/* 绑定本地端口 */
err = udp_bind(pcb, IP_ADDR_ANY, local_port);
if (err != ERR_OK) {
xil_printf("Unable to bind to port %d\r\n", local_port);
return -2;
}
/* 连接远程地址 */
IP4_ADDR(&ipaddr, 192, 168, 1, 28);
err = udp_connect(pcb, &ipaddr, remote_port);
if (err != ERR_OK) {
xil_printf("Unable to connect remote port.\r\n");
return -3;
}
else {
xil_printf("Connected Success.\r\n");
connected_pcb = pcb;
udp_connected_flag = 1;
}
udp_recv(pcb, udp_recv_callback, NULL); //设置接收回调函数
return 0;
}
//---------------------------------------------------------
// UDP接收数据函数
//---------------------------------------------------------
uint16_t udp_ReadData(uint8_t *buff)
{
uint16_t len = RxCount;
if(len > 0)
memcpy(buff,RxBuffer,len);
memset(RxBuffer,0,len); //memset过大的重置内存块会对UDP会有异常
RxCount = 0;
return len;
}
//---------------------------------------------------------
// UDP发送数据函数
//---------------------------------------------------------
void udp_SentData(uint32_t num, u8 * send_buff)
{
err_t err;
struct udp_pcb *tpcb = connected_pcb;
if (!tpcb) {
xil_printf("error connect.\r\n");
}
/* 申请pbuf资源 */
pbuf_to_be_sent = pbuf_alloc(PBUF_TRANSPORT, num, PBUF_POOL);
memset(pbuf_to_be_sent->payload, 0, num);
memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, num);
/* 发送字符串 */
err = udp_send(tpcb, pbuf_to_be_sent);
if (err != ERR_OK) {
xil_printf("Error on udp send : %d\r\n", err);
pbuf_free(pbuf_to_be_sent);
return;
}
pbuf_free(pbuf_to_be_sent); //释放pbuf
}
udp.process.h文章来源地址https://www.toymoban.com/news/detail-766168.html
#ifndef SRC_USER_UDP_H_
#define SRC_USER_UDP_H_
#include "lwip/err.h"
#include "lwip/udp.h"
#include "lwip/init.h"
#include "lwipopts.h"
#include "lwip/err.h"
#include "lwipopts.h"
#include "netif/xadapter.h"
#include "xil_printf.h"
int user_udp_init(void);
void udp_SentData(uint32_t num, u8 * send_buff);
uint16_t udp_ReadData(uint8_t *buff);
#endif /* SRC_USER_UDP_H_ */
到了这里,关于ZYNQ实验 基于LWIP的UDP传输实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!