ZYNQ PS端lwip httpd开发记录

器件选型:xc7z020clg484-2

开发环境:Vivado2018.3;

参考资料:

(1)RT-Thread 使用 webserver (lwip协议栈 自带httpd )-CSDN博客

(2)lwip-2.1.3自带的httpd网页服务器使用教程(二)使用SSI动态生成网页部分内容_lwip 动态网页_巨大八爪鱼的博客-CSDN博客

(3)lwip-2.1.3自带的httpd网页服务器使用教程(三)使用CGI获取URL参数(GET类型表单)_巨大八爪鱼的博客-CSDN博客

目录

一:设置

1:PL端

2:PS端

3.PC端网络设置

二:HTTP协议实现

1:连接开发板的IP地址

2:fsdata.c文件生成

3:使用CGI获取URL参数(GET类型表单)

<1>认识URL参数

<2>CGI实现

<3>CGI实现界面​编辑

4:使用SSI动态生成网页部分内容

<1>SSI实现

<2>SSI实现结果​编辑

<3>通过回调函数直接处理所有标签

5:POST类型表单的解析和文件上传

6:ZYNQ_FSBL


一:设置

1:PL端

新建一个工程,创建Blockdesign,进行ZYNQ的配置

打开Ehernet0、MDIO0、UART0使能

设置完成后,ExportHardware、Launch SDK、进入到SDK界面。

2:PS端

本次开发是在LWIP Echo Server的基础上修改的。 SDK中lwip资源版本为2.0.2。

首先要在资源库中ps7_cortexa9_0/libsrc/lwip202_v1_2/src路径下找到Makefile和Makefile.lwip。

在Makefile.lwip中添加如下

CORE_HTTPD_SRCS = $(LWIP_DIR)/src/apps/httpd/fs.c \
		$(LWIP_DIR)/src/apps/httpd/httpd.c

LWIP_INCLUDES = $(LWIP_DIR)/src/include/lwip/apps/fs.h 
//在LWIP_INCLUDES末尾添加这一配置

LWIP_SRCS += $(CORE_HTTPD_SRCS)

在Makefile中添加如下

VPATH = $(LWIP_DIR)/src/core/ $(LWIP_DIR)/src/core/ipv4/ $(LWIP_DIR)/src/core/ipv6 \
	$(LWIP_DIR)/src/netif  $(LWIP_DIR)/src/apps/httpd $(LWIP_DIR)/src/api $(PORT) $(PORT)/netif

3.PC端网络设置

要实现HTTP协议,需要先将以太网配置成固定IP,配置如下。

二:HTTP协议实现

1:连接开发板的IP地址

要在应用层使用HTTP协议,需要先创建一个TCP连接,在上面提到的echo例程中已经使用了tcp,所以直接使用就好了。

在echoserver例程中的main.c修改如下: 

#include <stdio.h>
#include <string.h> /* memset */
#include <stdlib.h> /* atoi */
#include "lwip/init.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/apps/fs.h"
#include "lwip/apps/httpd_structs.h"
#include "lwip/def.h"
#include "lwip/ip.h"
#include "xparameters.h"
#include "netif/xadapter.h"
#include "platform.h"
#include "platform_config.h"
#if defined (__arm__) || defined(__aarch64__)
#include "xil_printf.h"
#endif
#include "lwip/udp.h"
#include "lwip/err.h"
#include "lwip/tcp.h"
#include "xil_cache.h"
#include "lwip/apps/httpd.h"


void print_app_header();
int start_application();
void lwip_init();

#if LWIP_IPV6==0
#if LWIP_DHCP==1
extern volatile int dhcp_timoutcntr;
err_t dhcp_start(struct netif *netif);
#endif
#endif

extern volatile int TcpFastTmrFlag;
extern volatile int TcpSlowTmrFlag;
static struct netif server_netif;
struct netif *echo_netif;
struct http_state *hs;

#if LWIP_IPV6==1
void print_ip6(char *msg, ip_addr_t *ip)
{
	print(msg);
	xil_printf(" %x:%x:%x:%x:%x:%x:%x:%x\n\r",
			IP6_ADDR_BLOCK1(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK2(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK3(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK4(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK5(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK6(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK7(&ip->u_addr.ip6),
			IP6_ADDR_BLOCK8(&ip->u_addr.ip6));

}
#else
void
print_ip(char *msg, ip_addr_t *ip)
{
	print(msg);
	xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip),
			ip4_addr3(ip), ip4_addr4(ip));
}


void
print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw)
{

	print_ip("Board IP: ", ip);
	print_ip("Netmask : ", mask);
	print_ip("Gateway : ", gw);
}
#endif

#if defined (__arm__) && !defined (ARMR5)
#if XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1
int ProgramSi5324(void);
int ProgramSfpPhy(void);
#endif
#endif

#ifdef XPS_BOARD_ZCU102
#ifdef XPAR_XIICPS_0_DEVICE_ID
int IicPhyReset(void);
#endif
#endif

int main()
{
#if LWIP_IPV6==0
	ip_addr_t ipaddr, netmask, gw;

#endif
	/* the mac address of the board. this should be unique per board */
	unsigned char mac_ethernet_address[] =
	{ 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };

	echo_netif = &server_netif;
#if defined (__arm__) && !defined (ARMR5)
#if XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1
	ProgramSi5324();
	ProgramSfpPhy();
#endif
#endif

#ifdef XPS_BOARD_ZCU102
	if(IicPhyReset()) {
		xil_printf("Error performing PHY reset \n\r");
		return -1;
	}
#endif

	init_platform();

#if LWIP_IPV6==0
#if LWIP_DHCP==1
    ipaddr.addr = 0;
	gw.addr = 0;
	netmask.addr = 0;
#else
	IP4_ADDR(&ipaddr,  192, 168,   1, 10);
	IP4_ADDR(&netmask, 255, 255, 255,  0);
	IP4_ADDR(&gw,      192, 168,   1,  1);
#endif
#endif
	print_app_header();

	lwip_init();

#if (LWIP_IPV6 == 0)
	if (!xemac_add(echo_netif, &ipaddr, &netmask,
						&gw, mac_ethernet_address,
						PLATFORM_EMAC_BASEADDR)) {
		xil_printf("Error adding N/W interface\n\r");
		return -1;
	}
#else
	if (!xemac_add(echo_netif, NULL, NULL, NULL, mac_ethernet_address,
						PLATFORM_EMAC_BASEADDR)) {
		xil_printf("Error adding N/W interface\n\r");
		return -1;
	}
	echo_netif->ip6_autoconfig_enabled = 1;
	netif_create_ip6_linklocal_address(echo_netif, 1);
	netif_ip6_addr_set_state(echo_netif, 0, IP6_ADDR_VALID);
	print_ip6("\n\rBoard IPv6 address ", &echo_netif->ip6_addr[0].u_addr.ip6);

#endif
	netif_set_default(echo_netif);
	platform_enable_interrupts();
	netif_set_up(echo_netif);
#if (LWIP_IPV6 == 0)
#if (LWIP_DHCP==1)
	dhcp_start(echo_netif);
	dhcp_timoutcntr = 1;
	while(((echo_netif->ip_addr.addr) == 0) && (dhcp_timoutcntr > 0))
		xemacif_input(echo_netif);
	if (dhcp_timoutcntr <= 0) {
		if ((echo_netif->ip_addr.addr) == 0) {
			xil_printf("DHCP Timeout\r\n");
			xil_printf("Configuring default IP of 192.168.1.10\r\n");
			IP4_ADDR(&(echo_netif->ip_addr),  192, 168,   1, 10);
			IP4_ADDR(&(echo_netif->netmask), 255, 255, 255,  0);
			IP4_ADDR(&(echo_netif->gw),      192, 168,   1,  1);
		}
	}
	ipaddr.addr = echo_netif->ip_addr.addr;
	gw.addr = echo_netif->gw.addr;
	netmask.addr = echo_netif->netmask.addr;
#endif
	print_ip_settings(&ipaddr, &netmask, &gw);
#endif


	httpd_init();

	/* receive and process packets */
	while (1) {
		xemacif_input(echo_netif);
	}

	return 0;
}

这样修改就能关闭ECHO回环的功能,主要是while(1){}中的内容

在此之后,调用httpd_init();开启httpd例程网站如下

本地开发板为固定ip 192.168.1.10

到这一步就实现了PS端连接到开发板IP地址。

2:fsdata.c文件生成

在编写好网站的shtml后,放在makefsdata/fs文件夹中,点击make.bat,即可生成fsdata.c文件。

3:使用CGI获取URL参数(GET类型表单)

<1>认识URL参数

在上网的时候,我们经常会见到在网址后面带有?A=B&C=D这样的语法格式。

例如http://192.168.1.10/sets.cgi?TXfreq=40&RXfreq=50&Bandwidth=60

等号后面是参数的值。如果有多个参数的话,中间可用&符号连接。

另外我们也可以把网页中的表单的method属性设为get,表单提交后表单的内容也是以URL参数的方式呈现的。

		<form method="get" name="sets.cgi" action="/sets.cgi" >
		<table align="center"border="1px" width="60%" height="150px">
		    	<tr align="center">
				<td>TXfreq</td>
				<td><input type="text" name="TXfreq" value="<!--#TAG_TX-->">Hz</td>
			    </tr>
			<tr align="center">
				<td>RXfreq</td>
				<td><input type="text" name="RXfreq" value="<!--#TAG_RX-->">Hz</td>
			</tr>
			<tr align="center">
				<td>Bandwidth</td>
				<td><input type="text" name="Bandwidth" value="<!--#TAG_BW-->">Hz</td>
			</tr>
			<tr align="center">
				<td colspan="2">  
				    <input type="submit" value="Configure"/>
			            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
				    <input type="reset"  value="Reset"/>
				</td>
			</tr>
		</table>
		</form>

<input type="submit" value="Configure"/>是表单提交按钮,点击按钮后凡是带有name属性的控件的名称和值都会出现在URL中。

lwip httpd服务器提供的CGI功能就是用来获取这样的URL参数的。

注:

(1)这边出现<!--#TAG_TX-->是为了后续开展SSI(CGI不用TAG),所以在html中加上如:

<td><input type="text" name="TXfreq" value="<!--#TAG_TX-->">Hz</td>

(2)每次修改完html文件,都需要到fs.c文件中编译一下,否则没用

<2>CGI实现

首先在

该路径下修改文件

#if !defined LWIP_HTTPD_CGI || defined __DOXYGEN__
#define LWIP_HTTPD_CGI            1
#endif

在main.c中CGI代码如下

void httpd_cgi_init(void);
const char* SETS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]);
int TXfreq,RXfreq,Bandwidth;

//CGI
static const tCGI ppcURLs[]= //cgi程序
{
    {"/sets.cgi",SETS_CGI_Handler},
};

static int FindCGIParameter(const char *pcToFind,char *pcParam[],int iNumParams)
{
    int iLoop;
    for(iLoop = 0;iLoop < iNumParams;iLoop ++ )
    {
        if(strcmp(pcToFind,pcParam[iLoop]) == 0)
        {
            return (iLoop);
        }
    }
    return (-1);
}

//CGI句柄初始化
void httpd_cgi_init(void)
{
  http_set_cgi_handlers(ppcURLs, NUM_CONFIG_CGI_URIS);
}

//CGI sets控制句柄
const char* SETS_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
	printf("SETS_CGI_Handler\r\n");
	//txfreq
	iIndex = FindCGIParameter("TXfreq",pcParam,iNumParams);  //找到SET的索引号
	if (iIndex != -1)
	{
		TXfreq = atoi(pcValue[iIndex]);   //pcvalue是字符串 atoi将字符串转换成相应整数,存到TXfreq变量中
		printf("TXfreq: %d\n", TXfreq);
	}
	//rxfreq
	iIndex = FindCGIParameter("RXfreq",pcParam,iNumParams);
	if (iIndex != -1)
	{
		RXfreq = atoi(pcValue[iIndex]);
		printf("RXfreq: %d\n", RXfreq);
	}

	iIndex = FindCGIParameter("Bandwidth",pcParam,iNumParams);
	if (iIndex != -1)
	{
		Bandwidth = atoi(pcValue[iIndex]);
		printf("Bandwidth: %d\n", Bandwidth);
	}

	return "/AD9361.shtml";
}


//在main函数中加上
httpd_cgi_init();


<3>CGI实现界面

输入10/20/30的配置,点击configure按钮,结果如下:

4:使用SSI动态生成网页部分内容

<1>SSI实现

首先在该路径下修改httpd_opts.h文件

SSI功能默认是不开启的。开启SSI的方法是在lwipopts.h中定义下面的宏:

#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0

LWIP_HTTPD_SSI=1是开启SSI功能的意思。

LWIP_HTTPD_SSI_INCLUDE_TAG=0意思是不在最终生成的HTML网页中保留<!--#TAG-->标签。

在代码中,我们通过全局数组定义TAG标签列表

在main.c中添加如下:

#define NUM_CONFIG_SSI_TAGS (sizeof(ppcTAGs) / sizeof(char *))
void httpd_ssi_init(void);

int TXfreq=10,RXfreq=20,Bandwidth=30;

static const char *ppcTAGs[]=  //SSI的Tag
{
    "TAG_TX",
    "TAG_RX",
	"TAG_BW"
};

//SSIHandler中TXfreq/RXfreq/Bandwidth处理函数
int TXfreq_Handler(char *pcInsert)
{
     /* 准备添加到html中的数据 */
	int slen = sprintf(pcInsert,"%d",TXfreq);
	printf("TXfreq=%s\r\n",pcInsert);
	return slen;
}
int RXfreq_Handler(char *pcInsert)
{
	int slen = sprintf(pcInsert,"%d",RXfreq);
	printf("RXfreq=%s\r\n",pcInsert);
	return slen;

}
int Bandwidth_Handler(char *pcInsert)
{
	int slen = sprintf(pcInsert,"%d",Bandwidth);
	printf("Bandwidth=%s\r\n",pcInsert);
	return slen;
}

//SSI的 Handler 句柄
static u16_t SSIHandler(int iIndex,char *pcInsert,int iInsertLen)
{
	printf("SSIHandler,iIndex=%d,len=%d\r\n",iIndex,iInsertLen);
    switch(iIndex)
    {
        case 0:
                return TXfreq_Handler(pcInsert);   //获取TXfreq的数值,返回到pcInsert
                break;
        case 1:
                return RXfreq_Handler(pcInsert);
                break;
        case 2:
                return Bandwidth_Handler(pcInsert);
                break;
    }
    return 0;
}

//SSI句柄初始化
void httpd_ssi_init(void)
{
	http_set_ssi_handler(SSIHandler,ppcTAGs,NUM_CONFIG_SSI_TAGS);
}


//int main(){}中添加
httpd_ssi_init();

<2>SSI实现结果

配置后,动态显示配置的值,SSI实现成功

<3>通过回调函数直接处理所有标签

如果网页比较多的话,把所有网页用到的标签名都放到ssi_tags全局数组中也不太现实。

lwip允许我们开启LWIP_HTTPD_SSI_RAW选项,不用定义ssi_tags全局数组,直接在SSIHandler回调函数里面判断标签名就行。

首先在lwipopts.h中修改如下参数

// 配置HTTPD
#define LWIP_HTTPD_SSI 1
#define LWIP_HTTPD_SSI_INCLUDE_TAG 0
#define LWIP_HTTPD_SSI_RAW 1

注:修改完后到

路径下编译。否则LWIP_HTTPD_SSI_RAW开启无效

在main.c中修改代码如下:

//开启LWIP_HTTPD_SSI_RAW选项后,ppcTAGs全局数组就可以删了
//static const char *ppcTAGs[]=  //SSI的Tag
//{
//    "TAG_TX",
//    "TAG_RX",
//	"TAG_BW"
//};

//SSI的 Handler 句柄
//static u16_t SSIHandler(int iIndex,char *pcInsert,int iInsertLen)
//{
//	printf("SSIHandler,iIndex=%d,len=%d\r\n",iIndex,iInsertLen);
//    switch(iIndex)
//    {
//        case 0:
//                return TXfreq_Handler(pcInsert);   //获取TXfreq的数值,返回到pcInsert
//                break;
//        case 1:
//                return RXfreq_Handler(pcInsert);
//                break;
//        case 2:
        	    print("Bandwidth_Handler\n\r");
//                return Bandwidth_Handler(pcInsert);
//                break;
//    }
//    return 0;
//}

//SSI的 Handler 句柄
static u16_t SSIHandler(const char *ssi_tag_name, char *pcInsert, int iInsertLen)
{
	printf("SSIHandler\r\n");
	if (strcmp(ssi_tag_name, "TAG_TX") == 0)
		return TXfreq_Handler(pcInsert);
	else if (strcmp(ssi_tag_name, "TAG_RX") == 0)
		return RXfreq_Handler(pcInsert);
	else if (strcmp(ssi_tag_name, "TAG_BW") == 0)
		  return Bandwidth_Handler(pcInsert);
    else
    return 0;
}

//SSI句柄初始化
void httpd_ssi_init(void)
{
//	http_set_ssi_handler(SSIHandler,ppcTAGs,NUM_CONFIG_SSI_TAGS);
	http_set_ssi_handler(SSIHandler,NULL,0);
}

注意与RAW开启前的不同,不用全局数组ssi_tags。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值