FGPA-01-TCP-Client实现

主要使用LWIP实现TCP的数据传输,和主机建立通讯。

主要参考https://bestfpga.blog.csdn.net/article/details/88775286


SDK程序设计
按照前文方法,新建工程后启用lwIP 1.4.1库,其余配置都保持默认即可(使用RAW API)。使用lwIP需要启动中断系统,sys_intr.h和sys_intr.c文件代码与UDP实例中的相同。

整个代码可以借用lwip TCP Perf Client作为模板创建

 

此外lwIP要求每250ms调用一次tcp_tmr()函数(具体原因后面小节分析)因此我们要添加定时器资源。定时器使用方法参考本系列第10篇,这里不再讲述。
 

timer_intr.h文件代码如下:
#include <stdio.h>
#include "xadcps.h"
#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"

extern volatile int TcpTmrFlag;

#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR

void Timer_start(XScuTimer *TimerPtr);
void Timer_Setup_Intr_System(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId);
int Timer_init(XScuTimer *TimerPtr,u32 Load_Value,u32 DeviceId);

 timer_intr.c文件的代码如下:

#include "timer_intr.h"

volatile int TcpTmrFlag;

//---------------------------------------------------------
//                   定时器中断处理函数
//---------------------------------------------------------
static void TimerIntrHandler(void *CallBackRef)
{

    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
	TcpTmrFlag = 1;
}

//---------------------------------------------------------
//                    启动定时器函数
//---------------------------------------------------------
void Timer_start(XScuTimer *TimerPtr)
{
XScuTimer_Start(TimerPtr);
}

//---------------------------------------------------------
//                   定时器中断设置函数
//---------------------------------------------------------
void Timer_Setup_Intr_System(XScuGic *GicInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{
XScuGic_Connect(GicInstancePtr, TimerIntrId,
         (Xil_ExceptionHandler)TimerIntrHandler, (void *)TimerInstancePtr);

XScuGic_Enable(GicInstancePtr, TimerIntrId);
XScuTimer_EnableInterrupt(TimerInstancePtr);
}

//---------------------------------------------------------
//                    定时器初始化函数
//---------------------------------------------------------
int Timer_init(XScuTimer *TimerPtr,u32 Load_Value,u32 DeviceId)
{
     XScuTimer_Config *TMRConfigPtr;
     TMRConfigPtr = XScuTimer_LookupConfig(DeviceId);
     XScuTimer_CfgInitialize(TimerPtr,TMRConfigPtr,TMRConfigPtr->BaseAddr);
     XScuTimer_LoadTimer(TimerPtr, Load_Value);
     XScuTimer_EnableAutoReload(TimerPtr);

     return 1;
}

mian.c文件中的代码

#include "timer_intr.h"
#include "sys_intr.h"
#include "user_tcp.h"
#include "sleep.h"

#define TIMER_LOAD_VALUE    XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ / 8 //0.25S

static  XScuGic Intc; //GIC
static  XScuTimer Timer;//timer
extern volatile unsigned tcp_client_connected;
extern int tcp_trans_cnt;

//--------------------------------------------------
//                中断与定时器初始化
//--------------------------------------------------
void System_Init()
{
	Timer_init(&Timer,TIMER_LOAD_VALUE,TIMER_DEVICE_ID);
	Init_Intr_System(&Intc); // initial DMA interrupt system
	Setup_Intr_Exception(&Intc);
	Timer_Setup_Intr_System(&Intc,&Timer,TIMER_IRPT_INTR);
	Timer_start(&Timer);
	TcpTmrFlag = 0;
}

//--------------------------------------------------
//                     主程序
//--------------------------------------------------
int main(void)
{
	struct netif *netif, server_netif;   //用于lwIP网络接口的通用数据结构
	struct ip_addr ipaddr, netmask, gw;  //unsigned int 

	//开发板的MAC地址
	unsigned char mac_ethernet_address[] = {0x00,0x0a,0x35,0x00,0x01,0x02};
	System_Init();

	netif = &server_netif;
	
	//将4byte结构的IP地址转换为unsigned int
	IP4_ADDR(&ipaddr,  192, 168,   1, 10);  //IP地址(开发板)
	IP4_ADDR(&netmask, 255, 255, 255,  0);  //网络掩码
	IP4_ADDR(&gw,      192, 168,   1,  1);  //网关
	
	lwip_init();    //初始化lwIP
	//将网络接口添加到netif_list中
	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);       //启动网络接口
	tcp_send_init();           //初始化TCP PCB
	
	while(1) {
		/* call tcp timer every 250ms */
		if(TcpTmrFlag)
		{
			tcp_tmr();
			TcpTmrFlag = 0;
		}
		xemacif_input(netif);     //将MAC队列中的packets传输到lwIP栈中
		if (tcp_client_connected) {  //连接成功则发送数据
			sleep(1);
			send_data();
			xil_printf("tran_cnt:%d\n\r", tcp_trans_cnt);
		}	
	}
}

与UDP相同的lwIP配置流程代码中给出了详细注释。最大区别在于下面这部分:

while(1) {
		/* call tcp timer every 250ms */
		if(TcpTmrFlag)
		{
			tcp_tmr();
			TcpTmrFlag = 0;
		}
		user_function();  //用户功能
}

别小瞧这个函数,tcp_tmr对TCP的稳定使用至关重要,可查看本文后面小节的测试。

user_tcp.h文件代码如下(为了避免混淆,尽量不要取名为lwIP库中已用过的udp.h/c和tcp.h/c):

 

#include <stdio.h>
#include <string.h>
#include "lwip/err.h"
#include "lwip/tcp.h"
#include "lwipopts.h"
#include "netif/xadapter.h"
#include "lwip/init.h"
#include "lwip/tcp_impl.h"
#include "xil_printf.h"

extern volatile unsigned tcp_client_connected;
extern int tcp_trans_cnt;

int tcp_send_init();
void send_data(void);

#include "user_tcp.h"

#define SEND_SIZE 12

static struct tcp_pcb *connected_pcb = NULL;
volatile unsigned tcp_client_connected = 0;
int tcp_trans_cnt = 0;
char sendBuffer[12]="Hello World!";

//--------------------------------------------------
//            TCP数据发送成功的回调函数
//--------------------------------------------------
static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	tcp_trans_cnt++;     //统计发送数据的次数
	xil_printf("send int");
	return ERR_OK;
}

//--------------------------------------------------
//             TCP连接成功的回调函数
//--------------------------------------------------
static err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err)
{
	xil_printf("txperf: Connected to iperf server\r\n");

	connected_pcb = tpcb;   //存储连接的TCP状态

	tcp_nagle_disable(connected_pcb);
	tcp_arg(tpcb, NULL);    //指定应该传递回调函数的参数
	//设置当TCP数据成功传递到远程主机时调用回调函数tcp_sent_callback
	tcp_sent(tpcb, tcp_sent_callback);

	tcp_client_connected = 1;  //置1表示连接已建立
	xil_printf("Connect Success.\r\n");
	return ERR_OK;
}

//--------------------------------------------------
//              TCP PCB初始化函数
//--------------------------------------------------
int tcp_send_init()
{
	struct tcp_pcb *pcb;
	struct ip_addr ipaddr;
	err_t err;
	u16_t port;

	pcb = tcp_new(); 	//创建新的TCP PCB
	if (!pcb) {
		xil_printf("txperf: Error creating PCB. Out of Memory\r\n");
		return -1;
	}

	IP4_ADDR(&ipaddr, 192, 168, 1, 100);   //服务器的IP地址
	port = 7;					           //服务器的默认端口
    tcp_client_connected = 0;

    //连接主机,连接建立后调用回调函数tcp_connected_callback
	err = tcp_connect(pcb, &ipaddr, port, tcp_connected_callback);
	if (err != ERR_OK) {
		xil_printf("txperf: tcp_connect returned error: %d\r\n", err);
		return err;
	}
	xil_printf("%d\r\n",err);

	return 0;
}

//--------------------------------------------------
//                TCP数据发送函数
//--------------------------------------------------
void send_data(void)
{
	err_t err;
	struct tcp_pcb *tpcb = connected_pcb;

	if (!connected_pcb)
			return;

	err = tcp_write(tpcb, sendBuffer, SEND_SIZE, 3);
	if (err != ERR_OK) {
		xil_printf("txperf: Error on tcp_write: %d\r\n", err);
		connected_pcb = NULL;
		return;
	}
	err = tcp_output(tpcb);
	if (err != ERR_OK) {
		xil_printf("txperf: Error on tcp_output: %d\r\n",err);
		return;
	}
}

本设计的TCP工作在客户端模式,将远程主机当作服务器,主动请求连接。TCP client和TCP server在lwIP中的连接流程和区别可参考本系列前面与lwIP相关的文章。


请注意上面是以Vivado2017的某个版本来设计的;我这边使用了Vivado2019.1,结果上面的程序需要基础修改

1. LWIP1.4.1---LWIP2.x需要进行一定的修改

2. 移植完发现还是跑不起来,原因在查找

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值