本文在PS的Standalone程序中使用lwip库通过TCP协议实现网络数据传输。
包含TCP Server和TCP Client两种形式
本文主要参考lwip的SDK内部说明和工程模板,xapp1026, xapp1306, SDK以及3篇博客:
https://www.cnblogs.com/54zorb/p/9609021.html
https://www.cnblogs.com/54zorb/p/9609111.html
https://blog.csdn.net/firefly_cjd/article/details/79826818
本文配套源码已上传至
https://download.csdn.net/download/botao_li/11002024
Block Design
建立zcu102的Vivado工程,新建Block Design,并且添加zynq模块
运行Run Block Automation,双击打开zynq模块配置查看GEM接口配置
GEM3的配置与ug1182一致
保留UART和GEM3,关闭其它外设接口以及PS-PL接口
确保TTC 0已使能
保存后依次运行Generate Output Products和Create HDL Wrapper
在Flow Navigator中选择Generate Bitstream,完成后先Export Hardware,再Launch SDK
PS程序
在SDK中可以使用lwip模板新建包含lwip库示例代码的模板工程
注意zuc102开发板生成的模板工程会在IicPhyReset()函数编译出错,将此函数注释掉,用开发板上下电代替复位(试验中发现即使不上下电能正常工作)
建完首个工程后,其它工程建立时可以选择使用已有BSP工程
本文新建空工程用于试验
建立工程后SDK进行自动编译,完成之后在BSP工程配置lwip库(否则可能出现不可理解的异常错误)
在BSP工程上,右键菜单选择Board Support Package Settings
进入Settings窗口,在Overview界面选中lwip库(如果建立的是lwip模板工程,则BSP工程已自动添加lwip库)
配置完成后点击OK
打开BSP工程中的system.mss文件,可以在最下方发现lwip库,选择Documentation可以打开说明文档
在工作工程的src文件夹上右键菜单新建C Source File
在弹出窗口选择Default C Source Template
进入新建的source文件main.c,双击打开main.c文件,写入以下代码
#include <stdio.h>
#include "xparameters.h"
#include "netif/xadapter.h"
#include "lwipopts.h"
#include "xil_printf.h"
#include "sleep.h"
#include "lwip/priv/tcp_priv.h"
#include "lwip/init.h"
#include "lwip/inet.h"
#include "lwip/ip_addr.h"
#include "lwip/udp.h"
#include "xil_cache.h"
#include "xscugic.h"
#include "xttcps.h"
//定时器相关定义***************************************************
static XTtcPs TimerInstance;
static u16 Interval;
static u8 Prescaler;
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_XTTCPS_0_INTR
#define INTC_BASE_ADDR XPAR_SCUGIC_0_CPU_BASEADDR
#define INTC_DIST_BASE_ADDR XPAR_SCUGIC_0_DIST_BASEADDR
#define PLATFORM_TIMER_INTR_RATE_HZ (4)
//中断响应函数与main函数都访问的变量,加volatile前缀,不进入cache
volatile int TcpFastTmrFlag = 0;
volatile int TcpSlowTmrFlag = 0;
void platform_clear_interrupt( XTtcPs * TimerInstance )
{
u32 StatusEvent;
StatusEvent = XTtcPs_GetInterruptStatus(TimerInstance);
XTtcPs_ClearInterruptStatus(TimerInstance, StatusEvent);
}
void timer_callback(XTtcPs * TimerInstance)
{
/* we need to call tcp_fasttmr & tcp_slowtmr at intervals specified
* by lwIP. It is not important that the timing is absoluetly accurate.
*/
static int odd = 1;
TcpFastTmrFlag = 1;
odd = !odd;
if (odd)
{
TcpSlowTmrFlag = 1;
}
platform_clear_interrupt(TimerInstance);
}
void platform_setup_timer(void)
{
int Status;
XTtcPs * Timer = &TimerInstance;
XTtcPs_Config *Config;
//设置Timer之前保证Timer已停止
XTtcPs_Stop(Timer);
Config = XTtcPs_LookupConfig(TIMER_DEVICE_ID);
Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);
if (Status != XST_SUCCESS) {
xil_printf("In %s: Timer Cfg initialization failed...\r\n",
__func__);
return;
}
XTtcPs_SetOptions(Timer, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE);
XTtcPs_CalcIntervalFromFreq(Timer, PLATFORM_TIMER_INTR_RATE_HZ, &Interval, &Prescaler);
XTtcPs_SetInterval(Timer, Interval);
XTtcPs_SetPrescaler(Timer, Prescaler);
}
void platform_setup_interrupts(void)
{
Xil_ExceptionInit();
XScuGic_DeviceInitialize(INTC_DEVICE_ID);
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_DeviceInterruptHandler