器件选型: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端
新建一个工程,创建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"/>
<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实现界面![](https://i-blog.csdnimg.cn/blog_migrate/40baf245b8bfd094bcf29231068edb70.png)
输入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实现结果![](https://i-blog.csdnimg.cn/blog_migrate/bb5e8af35ec763c13fadf513a50bb439.png)
配置后,动态显示配置的值,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。