OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 35737|回复: 72

【ALIENTEK 战舰STM32开发板例程系列连载+教学】第五十七章 ENC28J60网络实验

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
发表于 2013-4-26 22:44:41 | 显示全部楼层 |阅读模式

第五十七章 ENC28J60网络实验

 

本章,我们将向大家介绍ALIENTEK ENC28J60网络模块及其使用。本章,我们将使用ALIENTEK ENC28J60网络模块和uIP 1.0实现:TCP服务器、TCP客服端以及WEB服务器等三个功能。本章分为如下几个部分:

57.1 ENC28J60以及uIP简介

57.2 硬件设计

57.3 软件设计

57.4 下载验证


57.1 ENC28J60以及uIP简介

本章我们需要用到ENC28J60以太网控制器和uIP 1.0以太网协议栈。接下来分别介绍这两个部分。

57.1.1 ENC28J60简介

ENC28J60 是带有行业标准串行外设接口(Serial Peripheral InterfaceSPI)的独立以太网控制器。它可作为任何配备有SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3 的全部规范,采用了一系列包过滤机制以对传入数据包进行限制。 它还提供了一个内部DMA 模块,以实现快速数据吞吐和硬件支持的IP校验和计算。 与主控制器的通信通过两个中断引脚和

SPI 实现,数据传输速率高达10 Mb/s。两个专用的引脚用于连接LED,进行网络活动状态指示。

       ENC28J60的主要特点如下:

l  兼容IEEE802.3协议的以太网控制器

l  集成MAC10 BASE-T物理层

l  支持全双工和半双工模式

l  数据冲突时可编程自动重发

l  SPI接口速度可达10Mbps

l  8K数据接收和发送双端口RAM

l  提供快速数据移动的内部DMA控制器

l  可配置的接收和发送缓冲区大小

l  两个可编程LED输出

l  7个中断源的两个中断引脚

l  TTL电平输入

l  提供多种封装:SOIC/SSOP/SPDIP/QFN

ENC28J60的典型应用电路如图57.1.1.1所示:


57.1.1.1 ENC28J60典型应用电路

ENC28J60 由七个主要功能模块组成:

1)  SPI 接口,充当主控制器和ENC28J60 之间通信通道。

2)  控制寄存器,用于控制和监视ENC28J60

3)  双端口RAM缓冲器,用于接收和发送数据包。

4)  判优器,当DMA、发送和接收模块发出请求时对RAM 缓冲器的访问进行控制。

5)  总线接口,对通过SPI 接收的数据和命令进行解析。

6)  MAC(Medium Access Control)模块,实现符合IEEE 802.3 标准的MAC 逻辑。

7)  PHY(物理层)模块,对双绞线上的模拟数据进行编码和译码。

ENC28J60还包括其他支持模块,诸如振荡器、片内稳压器、电平变换器(提供可以接受5V 电压的I/O 引脚)和系统控制逻辑。

      ENC28J60的功能框图如图57.1.1.2所示:

57.1.1.2 ENC28J60功能框图

       ALIENTEK ENC28J60网络模块采用ENC28J60作为主芯片,单芯片即可实现以太网接入,利用该模块,基本上只要是个单片机,就可以实现以太网连接。ALIENTEK ENC28J60网络模块原理图如图57.1.1.3所示:

57.1.1.3 ALIENTEK ENC28J60网络模块原理图

       ALIENTEK ENC28J60网络模块外观图如图57.1.1.4所示:

57.1.1.4 ALIENTEK ENC28J60网络模块外观图

       该模块通过一个8个引脚的排针与外部电路连接,这8个引脚分别是:GNDRSTMISOSCKMOSIINTCSV3.3。其中GNDV3.3用于给模块供电,MISO/MOSI/SCK用于SPI通信,CS是片选信号,INT为中断输出引脚,RST为模块复位信号。

57.1.2 uIP简介

uIP 由瑞典计算机科学学院(网络嵌入式系统小组)Adam Dunkels 开发。其源代码由C 语言编写,并完全公开,uIP的最新版本是1.0 版本,本指南移植和使用的版本正是此版本。

uIP 协议栈去掉了完整的TCP/IP 中不常用的功能,简化了通讯流程,但保留了网络通信必须使用的协议,设计重点放在了IP/TCP/ICMP/UDP/ARP 这些网络层和传输层协议上,保证了其代码的通用性和结构的稳定性。

由于uIP 协议栈专门为嵌入式系统而设计,因此还具有如下优越功能:

1)  代码非常少,其协议栈代码不到6K,很方便阅读和移植。

2)  占用的内存数非常少,RAM 占用仅几百字节。

3)  其硬件处理层、协议栈层和应用层共用一个全局缓存区,不存在数据的拷贝,且发送和接收都是依靠这个缓存区,极大的节省空间和时间。

4)  支持多个主动连接和被动连接并发。

5)  其源代码中提供一套实例程序:web 服务器,web 客户端,电子邮件发送程序(SMTP 客户端)Telnet 服务器, DNS 主机名解析程序等。通用性强,移植起来基本不用修改就可以通过。

6)  对数据的处理采用轮循机制,不需要操作系统的支持。

由于uIP 对资源的需求少和移植容易,大部分的8 位微控制器都使用过uIP协议栈, 而且很多的著名的嵌入式产品和项目(如卫星,Cisco 路由器,无线传感器网络)中都在使用uIP 协议栈。

uIP相当于一个代码库,通过一系列的函数实现与底层硬件和高层应用程序的通讯,对于整个系统来说它内部的协议组是透明的,从而增加了协议的通用性。uIP协议栈与系统底层和高层应用之间的关系如图57.1.2.1所示:

57.1.2.1 uIP在系统中的位置

从上图可以看出,uIP协议栈主要提供2个函数供系统底层调用:uip_inputuip_periodic。另外和应用程序联系主要是通过UIP_APPCALL函数。

当网卡驱动收到一个输入包时,将放入全局缓冲区uip_buf 中,包的大小由全局变量uip_len 约束。同时将调用uip_input()函数,这个函数将会根据包首部的协议处理这个包和需要时调用应用程序。当uip_input()返回时,一个输出包同样放在全局缓冲区uip_buf 里,大小赋给uip_len。如果uip_len 0,则说明没有包要发送。否则调用底层系统的发包函数将包发送到网络上。

uIP 周期计时是用于驱动所有的uIP 内部时钟事件。当周期计时激发,每一个TCP 连接都会调用uIP 函数uip_periodic()。类似于uip_input()函数。uip_periodic()函数返回时,输出的IP 包要放到uip_buf 中,供底层系统查询uip_len 的大小发送。

由于使用TCP/IP 的应用场景很多,因此应用程序作为单独的模块由用户实现。uIP 协议栈提供一系列接口函数供用户程序调用,其中大部分函数是作为C的宏命令实现的,主要是为了速度、代码大小、效率和堆栈的使用。用户需要将应用层入口程序作为接口提供给uIP 协议栈, 并将这个函数定义为宏UIP_APPCALL()。这样,uIP 在接受到底层传来的数据包后,在需要送到上层应用程序处理的地方,调用UIP_APPCALL( )。在不用修改协议栈的情况下可以适配不同的应用程序。

uIP协议栈提供了我们很多接口函数,这些函数在 uip.h 中定义,为了减少函数调用造成的额外支出,大部分接口函数以宏命令实现的,uIP提供的接口函数有:

1,初始化uIP协议栈:uip_init()

2.处理输入包:uip_input()

3.处理周期计时事件:uip_periodic()

4.开始监听端口:uip_listen()

5.连接到远程主机:uip_connect()

6.接收到连接请求:uip_connected()

7.主动关闭连接:uip_close()

8.连接被关闭:uip_closed()

9.发出去的数据被应答:uip_acked()

10.在当前连接发送数据:uip_send()

11.在当前连接上收到新的数据:uip_newdata()

12.告诉对方要停止连接:uip_stop()

13.连接被意外终止:uip_aborted()

接下来,我们看看uIP的移植过程。首先,uIP1.0的源码包里面有如下内容,如图57.1.2.2所示:

57.1.2.2 uIP 1.0源码包内容

其中apps文件夹里面是uip提供的各种参考代码,本章我们主要有用到里面的webserver部分。doc文件夹里面是一些uip的使用及说明文件,是学习uip的官方资料。lib文件夹里面是用于内存管理的一个代码,本章我们没有用到。uip里面就是uip 1.0的源码了,我们全盘照收。unix里面提供的是具体的应用实例,我们移植参考主要是依照这个里面的代码。

移植第一步:实现在unix/tapdev.c里面的三个函数。首先是tapdev_init函数,该函数用于初始化网卡(也就是我们的ENC28J60),通过这个函数实现网卡初始化。其次,是tapdev_read函数,该函数用于从网卡读取一包数据,将读到的数据存放在uip_buf里面,数据长度返回给uip_len。最后,是tapdev_send函数,该函数用于向网卡发送一包数据,将全局缓存区uip_buf里面的数据发送出去(长度为uip_len)。其实这三个函数就是实现最底层的网卡操作。

第二步,因为uIP协议栈需要使用时钟,为TCPARP的定时器服务,因此我们需要STM32提供一个定时器做时钟,提供10ms计时(假设clock-arch.h里面的CLOCK_CONF_SECOND100),通过clock-arch.c里面的clock_time函数返回给uIP使用。

第三步,配置uip-conf.h里面的宏定义选项。主要用于设置TCP最大连接数、TCP监听端口数、CPU大小端模式等,这个大家根据自己需要配置即可。

通过以上3步的修改,我们基本上就完成了uIP的移植。在使用uIP的时候,一般通过如下顺序:

1) 实现接口函数(回调函数)UIP_APPCALL

该函数是我们使用uIP最关键的部分,它是uIP和应用程序的接口,我们必须根据自己的需要,在该函数做各种处理,而做这些处理的触发条件,就是前面提到的uIP提供的那些接口函数,如uip_newdatauip_ackeduip_closed等等。另外,如果是UDP,那么还需要实现UIP_UDP_APPCALL回调函数。

2) 调用tapdev_init函数,先初始化网卡。

此步先初始化网卡,配置MAC地址,为uIP和网络通信做好准备。

3) 调用uip_init函数,初始化uIP协议栈。

此步主要用于uip自身的初始化,我们直接调用就是。

4) 设置IP地址、网关以及掩码

这个和电脑上网差不多,只不过我们这里是通过uip_ipaddruip_sethostaddruip_setdraddruip_setnetmask等函数实现。

5) 设置监听端口

uIP根据你设定的不同监听端口,实现不同的服务,比如我们实现Web Server就监听80端口(浏览器默认的端口是80端口),凡是发现80端口的数据,都通过Web ServerAPPCALL函数处理。根据自己的需要设置不同的监听端口。不过uIP有本地端口(lport)和远程端口(rport)之分,如果是做服务端,我们通过监听本地端口(lport)实现;如果是做客户端,则需要去连接远程端口(rport)。

6) 处理uIP事件

最后,uIP通过uip_polling函数轮询处理uIP事件。该函数必须插入到用户的主循环里面(也就是必须每隔一定时间调用一次)。

 

57.2 硬件设计

本节实验功能简介:开机检测ENC28J60,如果检测不成功,则提示报错。在成功检测到ENC28J60之后,初始化uIP,并设置IP地址(192.168.1.16)等,然后监听80端口和1200端口,并尝试连接远程1400端口,80端口用于实现WEB Server功能,1200端口用于实现TCP Server功能,连接1400端口实现TCP Client功能。此时,我们在电脑浏览器输入http://192.168.1.16 ,就可以登录到一个界面,该界面可以控制开发板上两个LED灯的亮灭,还会显示开发板的当前时间以及开发板STM32芯片的温度(每10秒自动刷新一次)。另外,我们通过网络调试软件(做TCP Server时,设置IP地址为:192.168.1.103,端口为1400;做TCP Client时,设置IP地址为:192.168.1.16,端口为1200)同开发板连接,即可实现开发板与网络调试软件之间的数据互发。按KEY0,由开发板的TCP Server端发送数据到电脑的TCP Client端。按KEY2,则由开发板的TCP Client端发送数据到电脑的TCP Server端。LCD显示当前连接状态。

所要用到的硬件资源如下:

1)  指示灯DS0 DS1

2)  KEY0/KEY2两个按键

3)  串口

4)  TFTLCD模块

5)  ENC28J60网络模块

前面4部分都已经详细介绍过,本章,我们重点看看ALIENTEK ENC28J60网络模块同ALIENTEK 战舰STM32开发板的连接,前面我们介绍了ALIENTEK ENC28J60网络模块的接口,我们通过杜邦线(或排线)连接网络模块和开发板的P12端口,连接关系如表56.2.1所示:

56.2.1 ENC28J60网络模块同战舰STM32开发板连接关系表

上表可以看出,其实网络模块同战舰STM32开发板的线序是一一对应的,所以如果你有一个1*8的排线,就可以直接对插即可。这里需要注意,本来开发板的P12端口是用来连接SD卡,实现SPI读写SD卡的,如果要连接网络模块,我们需要把跳线帽连接到P10P11,这样还是可以通过SDIO访问SD卡。

在开发板连接网络模块以后,我们还需要一根网线(自备),连接网络模块和路由器,这样我们才能实现和电脑的连接。

57.3 软件设计

本章,我们在第二十八章实验 (实验23 )的基础上修改,在该工程源码下面加入uIP-1.0文件夹,存放uIP1.0源码,再新建uIP-APP文件夹,存放应用部分代码,因为uIP自己有一个timer.ctimer.h的文件,所以我们还需要修改HARDWARE里面的timer.ctimer.h为不同的名字,本章我们改为timerx.ctimerx.h,我们还需要实现ENC28J60的驱动代码,存放在HARDWARE文件夹下的ENC28J60文件夹里面。详细的步骤我们就不一一阐述了,全部改好之后,工程如图57.3.1所示:

57.3.1 移植完后,MDK工程图

       图中uIP-1.0文件夹里面的代码全部是uIP提供的协议栈源码,而uIP-APP里面的代码则部分是我们自己实现的,部分是uIP提供的,其中:

       clock-arch.c,属于uIP协议栈,uIP通过该代码里面的clock_time函数获取时钟节拍。

       tapdev.c,同样是uIP提供,用来实现uIP与网卡的接口,该文件实现tapdev_inittapdev_readtapdev_send三个重要函数。

       tcp_demo.c,完成UIP_APPCALL函数的实现,即tcp_demo_appcall函数。该函数根据端口的不同,分别调用不同的appcall函数,实现不同功能。同时该文件还实现了uip_log函数,用于打印日志。

       tcp_client_demo.c,完成一个简单的TCP客户端应用,实现与电脑TCP服务端的数据收发。

       tcp_server_demo.c,完成一个简单的TCP服务端应用,实现与电脑TCP客户端的数据收发。

       httpd.chttpd-cgi.chttpd-fs.chttpd-strings.h,属于uIP提供的WEB服务器参考代码,我们通过修改部分代码,实现一个简单的WEB服务器。

       本章代码很多,我们仅挑一些重点和大家介绍。

首先是tapdev.c里面的三个函数,代码如下:

//MAC地址,必须唯一

//如果你有两个战舰开发板,想连入路由器,则需要修改MAC地址不一样!

const u8 mymac[6]={0x04,0x02,0x35,0x00,0x00,0x01};   //MAC地址

//配置网卡硬件,并设置MAC地址

//返回值:0,正常;1,失败;

u8 tapdev_init(void)

{           

       u8 i,res=0;                                   

       res=ENC28J60_Init((u8*)mymac);      //初始化ENC28J60                                   

       //IP地址和MAC地址写入缓存区

      for (i = 0; i < 6; i++)uip_ethaddr.addr=mymac

    //指示灯状态:0x476 is PHLCON LEDA(绿)=links status, LEDB()=receive/transmit

      //PHLCONPHY 模块LED 控制寄存器     

       ENC28J60_PHY_Write(PHLCON,0x0476);

       return res;      

}

//读取一包数据 

uint16_t tapdev_read(void)

{    

       return  ENC28J60_Packet_Receive(MAX_FRAMELEN,uip_buf);

}

//发送一包数据 

void tapdev_send(void)

{

       ENC28J60_Packet_Send(uip_len,uip_buf);

}

       tapdev_init函数,该函数用于初始化网卡,即初始化我们的ENC28J60,初始化工作主要通过调用ENC28J60_Init函数实现,该函数在enc28j60.c里面实现,同时该函数还用于设置MAC地址,这里请确保MAC地址的唯一性。在初始化enc28j60以后,我们设置enc28j60LED控制器工作方式,即完成对ENC28J60的全部初始化工作。该函数的返回值用于判断网卡初始化是否成功。

tapdev_read函数,该函数调用ENC28J60_Packet_Receive函数,实现从网卡(ENC28J60)读取一包数据,数据被存放在uip_buf里面,同时返回读到的包长度(包长度一般是存放在uip_len里面的)。

       tapdev_send函数,该函数调用ENC28J60_Packet_Send函数,实现从网卡(ENC28J60)发送一包数据到网络,数据内容存放在uip_buf,数据长度为uip_len

       再来看看tcp_demo.c里面的tcp_demo_appcall函数,该函数代码如下:

//TCP应用接口函数(UIP_APPCALL)

//完成TCP服务(包括serverclient)HTTP服务

void tcp_demo_appcall(void)

{    

       switch(uip_conn->lport)//本地监听端口801200

       {

              case HTONS(80):

                     httpd_appcall();

                     break;

              case HTONS(1200):

                  tcp_server_demo_appcall();

                     break;

              default: break;       

       }               

       switch(uip_conn->rport)       //远程连接1400端口

       {

           case HTONS(1400):

                     tcp_client_demo_appcall();

                   break;

           default: break;  

       }  

}

该函数即UIP_APPCALL函数,是uIP同应用程序的接口函数,该函数通过端口号选择不同的appcall函数,实现不同的服务。其中80端口用于实现WEB服务,通过调用httpd_appcall实现;1200端口用于实现TCP服务器,通过调用tcp_server_demo_appcall函数实现;1400是远程端口,用于实现TCP客户端,调用tcp_client_demo_appcall函数实现。

接着,我们来看看这3appcall函数,首先是WEB服务器的appcall函数:httpd_appcall,该函数在httpd.c里面实现,源码如下:

//http服务(WEB)处理

void httpd_appcall(void)

{

       struct httpd_state *s = (struct httpd_state *)&(uip_conn->appstate);//读取连接状态

       if(uip_closed() || uip_aborted() || uip_timedout())//异常处理(这里无任何处理)

       else if(uip_connected())//连接成功

       {

              PSOCK_INIT(&s->sin, s->inputbuf, sizeof(s->inputbuf) - 1);

              PSOCK_INIT(&s->sout, s->inputbuf, sizeof(s->inputbuf) - 1);

              PT_INIT(&s->outputpt);

              s->state = STATE_WAITING;

              /*    timer_set(&s->timer, CLOCK_SECOND * 100);*/

              s->timer = 0;

              handle_connection(s);//处理

       }else if(s!=NULL)

       {

              if(uip_poll())

              {

                     ++s->timer;

                     if(s->timer >= 20)uip_abort();

                    else s->timer = 0;

              }

              handle_connection(s);

       }else uip_abort();//

}

该函数在连接建立的时候,通handle_connection函数处理http数据,handle_connection函数代码如下:

//分析http数据

static void handle_connection(struct httpd_state *s)

{

       handle_input(s);  //处理http输入数据

       if(s->state==STATE_OUTPUT)handle_output(s);//输出状态,处理输出数据

}

该函数调用handle_input处理http输入数据,通过调用handle_output实现http网页输出。对我们来说最重要的是handle_input函数,handle_input函数代码如下:

extern unsigned char data_index_html[];//httpd-fsdata.c里面定义,用于存放html网页源代码

extern void get_temperature(u8 *temp);       //main函数实现,用于获取温度字符串

extern void get_time(u8 *time);           //main函数实现,用于获取时间字符串

const u8 * LED0_ON_PIC_ADDR="/data/attachment/forum/2012/9/27/ad65ee9f478ca

11241933beed5b5dbcc_971.gif";          //LED0,图标地址

const u8 * LED1_ON_PIC_ADDR="/data/attachment/forum/2012/9/27/bab5bef0379dc

50129202157c2739c57_775.gif";         //LED1,图标地址

const u8 * LED_OFF_PIC_ADDR="/data/attachment/forum/2012/9/27/ccecf4ebeb84b

095545b8feb0cecc671_254.gif";          //LED,图标地址

//处理HTTP输入数据

static PT_THREAD(handle_input(struct httpd_state *s))

{                         

       char *strx;

       u8 dbuf[17];

       PSOCK_BEGIN(&s->sin);          

       PSOCK_READTO(&s->sin, ISO_space);   

       if(strncmp(s->inputbuf, http_get, 4)!=0)PSOCK_CLOSE_EXIT(&s->sin);      //比较客户端

//浏览器输入的指令是否是申请WEB指令 GET    

       PSOCK_READTO(&s->sin, ISO_space);                                   //" "

       if(s->inputbuf[0] != ISO_slash)PSOCK_CLOSE_EXIT(&s->sin);     //判断第一个数据

//(去掉IP地址之后),是否是"/"

       if(s->inputbuf[1] == ISO_space||s->inputbuf[1] == '?')      //第二个数据是空格/问号

       {

              if(s->inputbuf[1]=='?'&&s->inputbuf[6]==0x31)//LED1 

              {                  

                     LED0=!LED0;      

                     strx=strstr((const char*)(data_index_html+13),"LED0状态"); 

                     if(strx)//存在"LED0状态"这个字符串

                     {

                            strx=strstr((const char*)strx,"color:#");//找到"color:#"字符串

                            if(LED0)//LED0

                            {

                                   strncpy(strx+7,"5B5B5B",6); //灰色

                                   strncpy(strx+24,"",2);              //

                                   strx=strstr((const char*)strx,"http:");//找到"http:"字符串

                                   strncpy(strx,(const char*)LED_OFF_PIC_ADDR,strlen((const char*)

LED_OFF_PIC_ADDR));//LED0灭图片     

                            }else

                            {

                                   strncpy(strx+7,"FF0000",6);  //红色

                                   strncpy(strx+24,"",2);              //""

                                   strx=strstr((const char*)strx,"http:");//找到"http:"字符串

                                   strncpy(strx,(const char*)LED0_ON_PIC_ADDR,strlen((const char*)

LED0_ON_PIC_ADDR));//LED0亮图片     

                            }    

                     } 

              }else if(s->inputbuf[1]=='?'&&s->inputbuf[6]==0x32)//LED2 

              {                  

                     LED1=!LED1;      

                     strx=strstr((const char*)(data_index_html+13),"LED1状态"); 

                     if(strx)//存在"LED1状态"这个字符串

                     {

                            strx=strstr((const char*)strx,"color:#");//找到"color:#"字符串

                            if(LED1)//LED1

                            {

                                   strncpy(strx+7,"5B5B5B",6); //灰色

                                   strncpy(strx+24,"",2);       //

                                   strx=strstr((const char*)strx,"http:");//找到"http:"字符串

                                   strncpy(strx,(const char*)LED_OFF_PIC_ADDR,strlen((const char*)

LED_OFF_PIC_ADDR));//LED1灭图片     

                            }else

                            {

                                   strncpy(strx+7,"00FF00",6);  //绿色

                                   strncpy(strx+24,"",2);       //""

                                   strx=strstr((const char*)strx,"http:");//找到"http:"字符串

                                   strncpy(strx,(const char*)LED1_ON_PIC_ADDR,strlen((const char*)

LED1_ON_PIC_ADDR));//LED1亮图片     

                            }    

                     }

              }

              strx=strstr((const char*)(data_index_html+13),"");//找到""字符

              if(strx)

              {

                     get_temperature(dbuf);                       //得到温度      

                     strncpy(strx-4,(const char*)dbuf,4);     //更新温度    

              }

              strx=strstr((const char*)strx,"RTC时间:");   //找到"RTC时间:"字符

              if(strx)

              {

                     get_time(dbuf);                                  //得到时间 

                     strncpy(strx+33,(const char*)dbuf,16); //更新时间

              }

              strncpy(s->filename, http_index_html, sizeof(s->filename));

       }else //如果不是' '/'?'

       {

              s->inputbuf[PSOCK_DATALEN(&s->sin)-1] = 0;

              strncpy(s->filename,&s->inputbuf[0],sizeof(s->filename));

       }  

       s->state = STATE_OUTPUT;    

       while(1)

       {

              PSOCK_READTO(&s->sin, ISO_nl);

              if(strncmp(s->inputbuf, http_referer, 8) == 0)

              {

                     s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;             

              }

       }                                                         

       PSOCK_END(&s->sin);

}

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-4-26 22:48:23 | 显示全部楼层
 

这里,我们需要了解uIP是把网页数据(源文件)存放在data_index_html,通过将这里面的数据发送给电脑浏览器,浏览器就会显示出我们所设计的界面了。当用户在网页上面操作的时候,浏览器就会发送消息给WEB服务器,服务器根据收到的消息内容,判断用户所执行的操作,然后发送新的页面到浏览器,这样用户就可以看到操作结果了。本章,我们实现的WEB服界面如图57.3.2所示:

57.3.2 WEB服务器界面

图中两个按键分别控制DS0DS1的亮灭,然后还显示了STM32芯片的温度和RTC时间等信息。

控制DS0DS1亮灭我们是通过发送不同的页面请求来实现的,这里我们采用的是Get方法(科普找百度),将请求参数放到URL里面,然后WEB服务器根据URL的参数来相应内容,这样实际上STM32就是从URL获取控制参数,以控制DS0DS1的亮灭。uIP在得到Get请求后判断URL内容,然后做出相应控制,最后修改data_index_html里面的部分内容(比如指示灯图标的变化,以及提示文字的变化等),再将data_index_html发送给浏览器,显示新的界面。

显示STM32温度和RTC时间是通过刷新实现的,uIP每次得到来自浏览器的请求就会更新data_index_html里面的温度和时间等信息,然后将data_index_html发送给浏览器,这样达到更新温度和时间的目的。但是这样我们需要手动刷新,比较笨,所以我们在网页源码里面加入了自动刷新的控制代码,每10秒钟刷新一次,这样就不需要手动刷新了。

handle_input函数实现了我们所说的这一切功能,另外请注意data_index_html是存放在httpd-fsdata.c(该文件通过include的方式包含进工程里面)里面的一个数组,并且由于该数组的内容需要不停的刷新,所以我们定义它为sram数据,data_index_html里面的数据,则是通过一个工具软件:amo的编程小工具集合V1.2.6.exe,将网页源码转换而来,该软件在光盘有提供,如果想自己做网页的朋友,可以通过该软件转换。

WEB服务器就为大家介绍这么多。

接下来看看TCP服务器appcall函数:tcp_server_demo_appcall,该函数在tcp_server_demo.c里面实现,该函数代码如下:

u8 tcp_server_databuf[200];       //发送数据缓存      

u8 tcp_server_sta;                        //服务端状态

//[7]:0,无连接;1,已经连接;

//[6]:0,无数据;1,收到客户端数据

//[5]:0,无数据;1,有数据需要发送

//这是一个TCP 服务器应用回调函数。

//该函数通过UIP_APPCALL(tcp_demo_appcall)调用,实现Web Server的功能.

//uip事件发生时,UIP_APPCALL函数会被调用,根据所属端口(1200),确定是否执行该函数。

//例如 : 当一个TCP连接被创建时、有新的数据到达、数据已经被应答、数据需要重发等事件

void tcp_server_demo_appcall(void)

{

      struct tcp_demo_appstate *s = (struct tcp_demo_appstate *)&uip_conn->appstate;

       if(uip_aborted())tcp_server_aborted();         //连接终止

      if(uip_timedout())tcp_server_timedout();     //连接超时  

       if(uip_closed())tcp_server_closed();            //连接关闭       

      if(uip_connected())tcp_server_connected();  //连接成功        

       if(uip_acked())tcp_server_acked();                     //发送的数据成功送达

       //接收到一个新的TCP数据包

       if (uip_newdata())//收到客户端发过来的数据

       {

              if((tcp_server_sta&(1<<6))==0)//还未收到数据

              {

                     if(uip_len>199) ((u8*)uip_appdata)[199]=0;  

                  strcpy((char*)tcp_server_databuf,uip_appdata);                  

                     tcp_server_sta|=1<<6;//表示收到客户端数据

              }

       }else if(tcp_server_sta&(1<<5))//有数据需要发送

       {

              s->textptr=tcp_server_databuf;

              s->textlen=strlen((const char*)tcp_server_databuf);

              tcp_server_sta&=~(1<<5);//清除标记

       }  

       //当需要重发、新数据到达、数据包送达、连接建立时,通知uip发送数据

       if(uip_rexmit()||uip_newdata()||uip_acked()||uip_connected()||uip_poll())

       {

              tcp_server_senddata();

       }

}

该函数通过uip_newdata()判断是否接收到客户端发来的数据,如果是,则将数据拷贝到tcp_server_databuf缓存区,并标记收到客户端数据。当有数据要发送(KEY0按下)的时候,将需要发送的数据通过tcp_server_senddata函数发送出去。

最后,我们看看TCP客户端appcall函数:tcp_client_demo_appcall,该函数代码同TCP服务端代码十分相似,该函数在tcp_server_demo.c里面实现,代码如下:

u8 tcp_client_databuf[200];        //发送数据缓存      

u8 tcp_client_sta;                        //客户端状态

//[7]:0,无连接;1,已经连接;

//[6]:0,无数据;1,收到客户端数据

//[5]:0,无数据;1,有数据需要发送

//这是一个TCP 客户端应用回调函数。

//该函数通过UIP_APPCALL(tcp_demo_appcall)调用,实现Web Client的功能.

//uip事件发生时,UIP_APPCALL函数会被调用,根据所属端口(1400),确定是否执行该函数。

//例如 : 当一个TCP连接被创建时、有新的数据到达、数据已经被应答、数据需要重发等事件

void tcp_client_demo_appcall(void)

{             

      struct tcp_demo_appstate *s = (struct tcp_demo_appstate *)&uip_conn->appstate;

       if(uip_aborted())tcp_client_aborted();          //连接终止       

       if(uip_timedout())tcp_client_timedout();      //连接超时  

       if(uip_closed())tcp_client_closed();             //连接关闭       

      if(uip_connected())tcp_client_connected();   //连接成功        

       if(uip_acked())tcp_client_acked();               //发送的数据成功送达

      //接收到一个新的TCP数据包

       if (uip_newdata())

       {

              if((tcp_client_sta&(1<<6))==0)//还未收到数据

              {

                     if(uip_len>199) ((u8*)uip_appdata)[199]=0;          

                  strcpy((char*)tcp_client_databuf,uip_appdata);                                             

                     tcp_client_sta|=1<<6;//表示收到客户端数据

              }                           

       }else if(tcp_client_sta&(1<<5))//有数据需要发送

       {

              s->textptr=tcp_client_databuf;

              s->textlen=strlen((const char*)tcp_client_databuf);

              tcp_client_sta&=~(1<<5);//清除标记

       } 

       //当需要重发、新数据到达、数据包送达、连接建立时,通知uip发送数据

       if(uip_rexmit()||uip_newdata()||uip_acked()||uip_connected()||uip_poll())

       {

              tcp_client_senddata();

       }                                                                             

}

该函数也是通过uip_newdata()判断是否接收到服务端发来的数据,如果是,则将数据拷贝到tcp_client_databuf缓存区,并标记收到服务端数据。当有数据要发送(KEY2按下)的时候,将需要发送的数据通过tcp_client_senddata函数发送出去。

uIP通过clock-arch里面的clock_time获取时间节拍,我们通过在timerx.c里面初始化定时器6,用于提供clock_time时钟节拍,每10ms1,这里代码就不贴出来了,请大家查看光盘源码。

最后在test.c里面,我们要实现好几个函数,但是这里仅贴出main函数以及uip_polling函数,该部分如下:

#define BUF ((struct uip_eth_hdr *)&uip_buf[0])

int main(void)

{         

       u8 key;

       u8 tcnt=0;

       u8 tcp_server_tsta=0XFF;

       u8 tcp_client_tsta=0XFF;

      uip_ipaddr_t ipaddr;     

       Stm32_Clock_Init(9);           //系统时钟设置

       uart_init(72,9600);             //串口初始化为9600

       delay_init(72);                         //延时初始化

       LED_Init();                        //初始化与LED连接的硬件接口

       LCD_Init();                       //初始化LCD

       KEY_Init();                         //初始化按键

       RTC_Init();                          //初始化RTC

       Adc_Init();                          //初始化ADC    

       usmart_dev.init(72);             //初始化USMART       

      POINT_COLOR=RED;         //设置为红色    

       LCD_ShowString(60,10,200,16,16,"WarShip STM32");   

       LCD_ShowString(60,30,200,16,16,"ENC28J60 TEST");  

       LCD_ShowString(60,50,200,16,16,"ATOM@ALIENTEK");

      while(tapdev_init())       //初始化ENC28J60错误

       {                                                        

              LCD_ShowString(60,70,200,16,16,"ENC28J60 Init Error!"); delay_ms(200);      

             LCD_Fill(60,70,240,86,WHITE);//清除之前显示

       };          

       uip_init();                            //uIP初始化    

       LCD_ShowString(60,70,200,16,16,"KEY0:Server Send Msg");

       LCD_ShowString(60,90,200,16,16,"KEY2:Client Send Msg");   

       LCD_ShowString(60,110,200,16,16,"IP:192.168.1.16");          

LCD_ShowString(60,130,200,16,16,"MASK:255.255.255.0");                    

LCD_ShowString(60,150,200,16,16,"GATEWAY:192.168.1.1");                 

      LCD_ShowString(30,200,200,16,16,"TCP RX:");                                                      

       LCD_ShowString(30,220,200,16,16,"TCP TX:");                                                            

       LCD_ShowString(30,270,200,16,16,"TCP RX:");                                                      

       LCD_ShowString(30,290,200,16,16,"TCP TX:");                                                      

       POINT_COLOR=BLUE;         

      uip_ipaddr(ipaddr, 192,168,1,16); //设置本地设置IP地址

       uip_sethostaddr(ipaddr);                                

       uip_ipaddr(ipaddr, 192,168,1,1); //设置网关IP地址(其实就是你路由器的IP地址)

       uip_setdraddr(ipaddr);                                     

       uip_ipaddr(ipaddr, 255,255,255,0);//设置网络掩码

       uip_setnetmask(ipaddr);

       uip_listen(HTONS(1200));                  //监听1200端口,用于TCP Server

       uip_listen(HTONS(80));                            //监听80端口,用于Web Server

      tcp_client_reconnect();                   //尝试连接到TCP Server,用于TCP Client

       while (1)

       {

              uip_polling(); //处理uip事件,必须插入到用户程序的循环体中 

              key=KEY_Scan(0);

              if(tcp_server_tsta!=tcp_server_sta)//TCP Server状态改变

              {                                                                                                      

                     if(tcp_server_sta&(1<<7))LCD_ShowString(30,180,200,16,16,"TCP Server

Connected   ");

                     else LCD_ShowString(30,180,200,16,16,"TCP Server Disconnected");

                    if(tcp_server_sta&(1<<6))     //收到新数据

                     {

                            LCD_Fill(86,200,240,216,WHITE);     //清除之前显示

                            LCD_ShowString(86,200,154,16,16,tcp_server_databuf);

                         printf("TCP Server RX:%s\r\n",tcp_server_databuf);//打印数据

                            tcp_server_sta&=~(1<<6);           //标记数据已经被处理 

                     }

                     tcp_server_tsta=tcp_server_sta;

              }

              if(key==KEY_RIGHT)//TCP Server 请求发送数据

              {

                     if(tcp_server_sta&(1<<7))     //连接还存在

                     {

                            sprintf((char*)tcp_server_databuf,"TCP Server OK %d\r\n",tcnt);    

                            LCD_Fill(86,220,240,236,WHITE);                                 //清除之前显示

                            LCD_ShowString(86,220,154,16,16,tcp_server_databuf);   //显示发送数据

                            tcp_server_sta|=1<<5;//标记有数据需要发送

                            tcnt++;

                     }

              }

              if(tcp_client_tsta!=tcp_client_sta)//TCP Client状态改变

              {                                                                                                      

                     if(tcp_client_sta&(1<<7))LCD_ShowString(30,250,200,16,16,"TCP Client

Connected   ");

                     else LCD_ShowString(30,250,200,16,16,"TCP Client Disconnected");

                    if(tcp_client_sta&(1<<6))     //收到新数据

                     {

                            LCD_Fill(86,270,240,286,WHITE);     //清除之前显示

                            LCD_ShowString(86,270,154,16,16,tcp_client_databuf);

                         printf("TCP Client RX:%s\r\n",tcp_client_databuf);//打印数据

                            tcp_client_sta&=~(1<<6);            //标记数据已经被处理 

                     }

                     tcp_client_tsta=tcp_client_sta;

              }

              if(key==KEY_LEFT)//TCP Client 请求发送数据

              {

                     if(tcp_client_sta&(1<<7))     //连接还存在

                     {

                            sprintf((char*)tcp_client_databuf,"TCP Client OK %d\r\n",tcnt);      

                            LCD_Fill(86,290,240,306,WHITE);            //清除之前显示

                            LCD_ShowString(86,290,154,16,16,tcp_client_databuf);//显示发送数据

                            tcp_client_sta|=1<<5;//标记有数据需要发送

                            tcnt++;

                     }

              }

              delay_ms(1);

       } 

}

//uip事件处理函数

//必须将该函数插入用户主循环,循环调用.

void uip_polling(void)

{

       u8 i;

       static struct timer periodic_timer, arp_timer;

       static u8 timer_ok=0;    

       if(timer_ok==0)//仅初始化一次

       {

              timer_ok = 1;

              timer_set(&periodic_timer,CLOCK_SECOND/2); //创建10.5秒的定时器

              timer_set(&arp_timer,CLOCK_SECOND*10);    //创建110秒的定时器

       }                         

       uip_len=tapdev_read();  //从网络读取一个IP,得到数据长度.uip_lenuip.c中定义

       if(uip_len>0)                      //有数据

       {  

              //处理IP数据包(只有校验通过的IP包才会被接收)

              if(BUF->type == htons(UIP_ETHTYPE_IP))//是否是IP?

              {

                     uip_arp_ipin();       //去除以太网头结构,更新ARP

                     uip_input();         //IP包处理

                     //当上面的函数执行后,如果需要发送数据,则全局变量 uip_len > 0

                     //需要发送的数据在uip_buf, 长度是uip_len  (这是2个全局变量)               

                     if(uip_len>0)//需要回应数据

                     {

                            uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求

                            tapdev_send();//发送数据到以太网

                     }

              }else if (BUF->type==htons(UIP_ETHTYPE_ARP))//处理arp报文,是否是ARP?

              {

                     uip_arp_arpin();

                    //当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0

                     //需要发送的数据在uip_buf, 长度是uip_len(这是2个全局变量)

                    if(uip_len>0)tapdev_send();//需要发送数据,则通过tapdev_send发送    

              }

       }

       else if(timer_expired(&periodic_timer))      //0.5秒定时器超时

       {

              timer_reset(&periodic_timer);             //复位0.5秒定时器

              //轮流处理每个TCP连接, UIP_CONNS缺省是40 

              for(i=0;i<UIP_CONNS;i++)

              {

                     uip_periodic(i);      //处理TCP通信事件 

                    //当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0

                     //需要发送的数据在uip_buf, 长度是uip_len (这是2个全局变量)

                    if(uip_len>0)

                     {

                            uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求

                            tapdev_send();//发送数据到以太网

                     }

              }

#if UIP_UDP  //UIP_UDP

              //轮流处理每个UDP连接, UIP_UDP_CONNS缺省是10

              for(i=0;i<UIP_UDP_CONNS;i++)

              {

                     uip_udp_periodic(i);      //处理UDP通信事件

                    //当上面的函数执行后,如果需要发送数据,则全局变量uip_len>0

                     //需要发送的数据在uip_buf, 长度是uip_len (这是2个全局变量)

                     if(uip_len > 0)

                     {

                            uip_arp_out();//加以太网头结构,在主动连接时可能要构造ARP请求

                            tapdev_send();//发送数据到以太网

                     }

              }

#endif

              //每隔10秒调用1ARP定时器函数 用于定期ARP处理,ARP10秒更新一次,

//旧的条目会被抛弃

              if(timer_expired(&arp_timer))

              {

                     timer_reset(&arp_timer);

                     uip_arp_timer();

              }

       }

}

其中main函数相对比较简单,先初始化网卡(ENC28J60)和uIP等,然后设置IP地址(192.168.1.16)及监听端口(120080),就开始轮询uip_polling函数,实现uIP事件处理,同时扫描按键,实现数据发送处理。当有收到数据的时候,将其显示在LCD上,同时通过串口发送到电脑。注意,这里main函数调用的tcp_client_reconnect函数,用于本地(STM32TCP Client去连接外部服务端,该函数设置服务端IP地址为192.168.1.103(就是你电脑的IP地址),连接端口为1400,只要没有连上,该函数就会不停的尝试连接。

uip_polling函数,第一次调用的时候创建两个定时器,当收到包的时候(uip_len>0),先区分是IP包还是ARP包,针对不同的包做不同处理,对我们来说主要是通过uip_input处理IP包,实现数据处理。当没有收到包的时候(uip_len=0),通过定时器定时处理各个TCP/UDP连接以及ARP表处理。

软件设计部分就为大家介绍到这里。

57.4 下载验证

在代码编译成功之后,我们通过下载代码到战舰STM32开发板上(假设网络模块已经连接上开发板),LCD显示如图57.4.1所示界面:

57.4.1 初始界面

可以看到,此时TCP ServerTCP Client都是没有连接的,我们打开:网络调试助手V3.7.exe这个软件(该软件在光盘有提供),然后选择TCP Server,设置本地IP地址为:192.168.1.103(默认就是),设置本地端口为1400,点击连接按钮,就会收到开发板发过来的消息,此时我们按开发板的KEY2,就会发送数据给网络调试助手,同时也可以通过网络调试助手发送数据到STM32开发板。如图57.4.2所示:

 57.4.2 STM32 TCP Client测试

    在连接成功建立的时候,会在战舰STM32开发板上面显示TCP Client的连接状态,然后如果收到来自电脑TCP Server端的数据,也会在LCD上面显示,并打印到串口。这是我们实现的TCP Client功能。

    如果我们在网络调试助手,选择协议类型为TCP Client,然后设置服务器IP地址为192.168.1.16(就是我们STM32开发板设置的IP地址),然后设置服务器端口为1200,点击连接,同样可以收到开发板发过来的消息,此时我们按开发板的KEY0按键,就可以发送数据到网络调试助手,同时网络调试助手也可以发送数据到我们的开发板。如图57.4.3所示:

57.4.3 STM32 TCP Server测试

在连接成功建立的时候,会在战舰STM32开发板上面显示TCP Server的连接状态,然后如果收到来自电脑TCP Client端的数据,便会在LCD上面显示,并打印到串口。这是我们实现的TCP Server功能。

       最后,我们测试WEB服务器功能。打开浏览器,输入http://192.168.1.16  ,就可以看到如下界面,如图57.4.4所示:


57.4.4 STM32 WEB Server测试

       此时,我们点击网页上的DS0状态反转和DS1状态反转按钮,就可以控制DS0DS1的亮灭了。同时在该界面还显示了STM32的温度和RTC时间,每次刷新的时候,进行数据更新,另外浏览器每10秒钟会自动刷新一次,以更新时间和温度信息。

 

《STM32开发指南》第五十七章 ENC28J60网络实验.rar

1.32 MB, 下载次数: 2217

实验52 ENC28J60网络模块实验.rar

1.12 MB, 下载次数: 2278

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

4

主题

119

帖子

0

精华

初级会员

Rank: 2

积分
169
金钱
169
注册时间
2013-4-6
在线时间
3 小时
发表于 2013-4-26 23:09:24 | 显示全部楼层
请教版大,  这篇文章跟原来的是完全一样吧 ?

我最近试运行了这个范例,  发现稳定度似乎不够,  只用PING , 不出3分钟就无法回应,  重启后马上又恢复正常.

网页也是.    连可靠运行10分钟都好像不行呢.

不知道其它人有没有这种情形?

(使用STM32 Mini上)
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-4-26 23:37:02 | 显示全部楼层
回复【3楼】liuje:
---------------------------------
和原来的一样.
稳定性是需要加强,呵呵.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

头像被屏蔽

88

主题

231

帖子

2

精华

高级会员

Rank: 4

积分
844
金钱
844
注册时间
2013-4-11
在线时间
40 小时
发表于 2013-4-27 09:27:19 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-4-27 12:38:40 | 显示全部楼层
回复【5楼】nonplus:
---------------------------------
1,把同网络模块连接的线捆绑起来试试.
2,电源供电,请测量下V3.3电压是多少.是不是电压不足?
3,uip稳定性不如lwip,可以考虑换Lwip试试.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

头像被屏蔽

88

主题

231

帖子

2

精华

高级会员

Rank: 4

积分
844
金钱
844
注册时间
2013-4-11
在线时间
40 小时
发表于 2013-4-27 12:44:34 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

50

主题

201

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
423
金钱
423
注册时间
2013-4-9
在线时间
1 小时
发表于 2013-5-3 15:51:12 | 显示全部楼层
我想问下 这个可以移植邮件的协议栈 然后发送邮件吗
回复 支持 反对

使用道具 举报

50

主题

201

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
423
金钱
423
注册时间
2013-4-9
在线时间
1 小时
发表于 2013-5-5 11:18:40 | 显示全部楼层
回复【2楼】正点原子:
---------------------------------
u32 uip_timer=0;//
 
void TIM6_IRQHandler(void)
{              
if(TIM6->SR&0X0001)//
{
uip_timer++;//                       
}    
TIM6->SR&=~(1<<0);//      

clock_time(void)
{
return uip_timer; /* 10ms ???? */  
}

我想问下这个时间种子似乎是在TIM6中实现的
每次中断uip_timer会++
但是程序中并没有初始化TIM6
我就不解了
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-5-5 13:15:01 | 显示全部楼层
回复【9楼】charlieholy:
---------------------------------
有初始化的,请仔细阅读代码.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

5

主题

7

帖子

0

精华

新手上路

积分
47
金钱
47
注册时间
2013-3-21
在线时间
0 小时
发表于 2013-5-17 14:54:43 | 显示全部楼层
想问一下,回复【楼主位】正点原子:
---------------------------------
他这个发送接受数据是怎么通过应用接收的,不都是付给全局变量buf,怎么在调试软件显示出来的呢
回复 支持 反对

使用道具 举报

48

主题

167

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
380
金钱
380
注册时间
2013-6-27
在线时间
17 小时
发表于 2013-7-7 13:22:31 | 显示全部楼层
回复【2楼】正点原子:
---------------------------------
想知道关于自动更新的是怎么实现的,是网页更新一次去获取一次呢,还是得到数据一次去更新一次网页。更新部分的代码能贴一下不。还有就是网页源码怎么通过工具转换成数组的,具体说一下几个数组不。
回复 支持 反对

使用道具 举报

4

主题

119

帖子

0

精华

初级会员

Rank: 2

积分
169
金钱
169
注册时间
2013-4-6
在线时间
3 小时
发表于 2013-7-7 14:09:36 | 显示全部楼层
HTML 回复【12楼】can123dao:

回复【2楼】正点原子:
---------------------------------
想知道关于自动更新的是怎么实现的,是网页更新一次去获取一次呢,还是得到数据一次去更新一次网页。更新部分的代码能贴一下不。还有就是网页源码怎么通过工具转换成数组的,具体说一下几个数组不。

---------------------------------


浏览端自动重载网页的语法是  "<meta http-equiv="refresh" content="300; url=target.html">"     
把它夹带在PRINT出网页的开头,  就OK了.       300是代表300秒 ,  如果要让它5秒刷新一次,  请填  5 .
url 是重载时的页面地址,  可以直接省略就写  ...url=">"   (不指定) ,  就是重刷目前这个页面了.

很简单就达到.   但重刷时, 画面老是闪动不停,  很讨厌.    如果在手机/平台浏览, 你把画面放大,  重刷时就又自己变小了.....

唯一的完美解决办法是......."AJAX"  ->    JavaScript异步资料更新技术..        但这个不适合初学者.   要了解的还有很多.   一步一步来吧.
回复 支持 反对

使用道具 举报

48

主题

167

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
380
金钱
380
注册时间
2013-6-27
在线时间
17 小时
发表于 2013-7-7 16:15:41 | 显示全部楼层
回复【13楼】liuje:
---------------------------------
温度和RTC时间都是在变的,两个值的显示是怎么实现的。数据从MCU到PC不知道怎么处理的,移植过LWIP,只实现了从WEB控制MCU,现在就想简单地把显示MCU温度和RTC时间加上去,实现双向通信。不知道怎么弄
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-7-7 21:26:29 | 显示全部楼层
写入到网页数据里面就可以了.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

4

主题

119

帖子

0

精华

初级会员

Rank: 2

积分
169
金钱
169
注册时间
2013-4-6
在线时间
3 小时
发表于 2013-7-8 16:30:27 | 显示全部楼层
/*----------------------------------------室內溫控器狀態值監控-------------------------------------------------*/
plen=fill_tcp_data_p(buf,plen,PSTR("<center><p><i>MODBUS溫控器狀態監控[IP1]</i></p></center>"));
plen=fill_tcp_data_p(buf,plen,PSTR("<center><font size=5> 室內溫度:</font>"));
  plen=fill_tcp_data_p(buf,plen,PSTR("<font size=5>"));
sprintf(tmp1,"%d.%d",IP[1].rt/10,IP[1].rt%10);
plen=fill_tcp_data(buf,plen,tmp1);
plen=fill_tcp_data_p(buf,plen,PSTR("</font>"));
................................
.................................

把要显示的资料转换成字串符,   一起串到要发出的资料中就是了..........
也就是HTML码 + 变量 夹杂处理的方式 ....
回复 支持 反对

使用道具 举报

48

主题

167

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
380
金钱
380
注册时间
2013-6-27
在线时间
17 小时
发表于 2013-7-9 09:52:42 | 显示全部楼层
回复【16楼】liuje:
---------------------------------
sprintf(tmp1,"%d.%d",IP[1].rt/10,IP[1].rt%10);后面的两个参数是什么?你是用过神舟3号开发板么,是的话能给一下你的源码不
回复 支持 反对

使用道具 举报

4

主题

119

帖子

0

精华

初级会员

Rank: 2

积分
169
金钱
169
注册时间
2013-4-6
在线时间
3 小时
发表于 2013-7-9 10:00:22 | 显示全部楼层
回复【17楼】can123dao:

回复【16楼】liuje:
---------------------------------
sprintf(tmp1,"%d.%d",IP[1].rt/10,IP[1].rt%10);后面的两个参数是什么。这个temp1直接在主函数里通过AD转换和计算出直接赋值就可以了么。你是用过神舟3号开发板么,是的话能给一下你的源码不

---------------------------------


我晕....   那是你自定的内存变量啊.    假如你AD转换后的变量名叫 "ADvalue" ,   就放进去 sprintf 转换为字串就行了....

"%d.%d" ,  代表整型为X.Y这样文数字型式(例如 25.5) ,       xx/10 代表 数字/10 ,    XX%10 代表整除10后的馀数 ,  就是要把原来整数的数值, 除以10, 转成带一位小数点的表示方式.

看来你还有很多东西要学的.....
回复 支持 反对

使用道具 举报

48

主题

167

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
380
金钱
380
注册时间
2013-6-27
在线时间
17 小时
发表于 2013-7-12 21:36:51 | 显示全部楼层
回复【4楼】正点原子:
---------------------------------
这个稳定性主要是受什么影响?是因为协议本身的问题么,做了LWIP的移植,运行3个小时也就不行了。
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-7 16:57:50 | 显示全部楼层
ENC28J60作为客户端,连接上服务端时,有何数据下发下来
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-7 17:30:39 | 显示全部楼层
原子哥,如果我那板子做客户端,IP为192.168.1.15,然后PC的服务端的IP为192.168.2.123.为何连接不上呢???是不是服务端与客户端必须是同一个路由的呢???
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-8-7 17:41:05 | 显示全部楼层
回复【21楼】愤怒的工程师:
---------------------------------
必须的啊.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-8 08:47:40 | 显示全部楼层
回复【22楼】正点原子:
---------------------------------
如果我这边有一个服务器,那边你那边如何能够连接上我这边的服务器呢???用TCP虚拟的服务器我们可以设置IP,但如果真实的服务器,IP是固定的,那你那边如何能够连接上呢???原子哥
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-8-8 09:03:27 | 显示全部楼层
有独立IP,就能访问。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-8 09:17:20 | 显示全部楼层
回复【24楼】正点原子:
---------------------------------
独立的IP?我是给我的以太网模块设置了一个IP呀!是不是独立IP呢?还是我理解错了
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-8-8 09:19:53 | 显示全部楼层
独立的公网IP
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-9 08:40:16 | 显示全部楼层
回复【26楼】正点原子:
---------------------------------
原子哥,为什么以太网作为客服端连接到服务器,过一段时间后会自动断开呢,然后检测不到掉线的信息?如果检测到断开的话,能否重连呢?
回复 支持 反对

使用道具 举报

48

主题

167

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
380
金钱
380
注册时间
2013-6-27
在线时间
17 小时
发表于 2013-8-9 08:53:05 | 显示全部楼层
回复【22楼】正点原子:
---------------------------------
原子哥,如何能实现域名访问呢。LWIP有域名解析函数,可以成功地解析。这个怎么去连解析出来的IP呢,也就是怎么去局域网外的IP,和连局域网是一样么。
web例程怎么实现外网可访问呢,就是把板子搞好插到路由上,用不在这个局域网内的PC,或者其它可上网终端实现WEB访问,上网账号绑定在路由上的。
LWIP裸机的稳定性似乎不错,都没出现什么掉线的情况。多种情况的人为断开,稍稍处理下,大部分情况还是可以连上的
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-8-9 10:42:58 | 显示全部楼层
回复【28楼】can123dao:
---------------------------------
这我也没整过哦,呵呵,你试试lwip吧,这个还稳定些.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-14 16:16:10 | 显示全部楼层
回复【29楼】正点原子:
---------------------------------
原子哥,ENC28J60作为客户端连接到服务端,设置本机IP地址时,如何让ENC28J60自动配置IP?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-8-14 16:24:43 | 显示全部楼层
这我们的例程不具备这个功能哦。。。
得你加入DHCP才可以。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-15 11:07:13 | 显示全部楼层
回复【31楼】正点原子:
---------------------------------
谢谢原子哥!!!
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-26 15:37:51 | 显示全部楼层
原子哥,我想请教一下,如果我以太网模块连接上了网络,电脑也连接了网络,同一个路由器,那么我的以太网模块能否与电脑进行通信呢???
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2013-8-26 18:16:36 | 显示全部楼层
可以互相通信。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
55
金钱
55
注册时间
2013-8-7
在线时间
7 小时
发表于 2013-8-27 12:27:21 | 显示全部楼层
回复【34楼】正点原子:
---------------------------------
互相通信是基于什么协议呢?
回复 支持 反对

使用道具 举报

76

主题

355

帖子

0

精华

高级会员

Rank: 4

积分
671
金钱
671
注册时间
2012-9-19
在线时间
5 小时
发表于 2013-9-14 17:10:01 | 显示全部楼层
回复【5楼】nonplus:
---------------------------------
我擦。。你的问题貌似和我现在遇到的一样。你解决了吗
具有资深STM32点灯技术,
回复 支持 反对

使用道具 举报

头像被屏蔽

88

主题

231

帖子

2

精华

高级会员

Rank: 4

积分
844
金钱
844
注册时间
2013-4-11
在线时间
40 小时
发表于 2013-9-16 08:36:14 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

76

主题

355

帖子

0

精华

高级会员

Rank: 4

积分
671
金钱
671
注册时间
2012-9-19
在线时间
5 小时
发表于 2013-9-16 10:37:56 | 显示全部楼层
回复【37楼】nonplus:
---------------------------------
哈。。。。LWIP 有人说也很稳定。但有人说也不如UIP 。于是我发现了一个更牛逼的。。。叫是德州的单片机。可不是扒鸡的那个德州啊。。总之人家德州的一款单片机,里内嵌了MAC模块。就是以太网模块。直接调用库函数就能起到驱动以太网。,只需要芯片+rj45就搞定。连嵌入什么协议都不用。直接人家MAC模块内部寄存器修改 修改就OK。嘎嘎爱国
具有资深STM32点灯技术,
回复 支持 反对

使用道具 举报

51

主题

1455

帖子

3

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2612
金钱
2612
注册时间
2011-1-25
在线时间
176 小时
发表于 2013-9-16 11:12:21 | 显示全部楼层
回复【38楼】敌害:
---------------------------------
TI
一直努力就很好。
回复 支持 反对

使用道具 举报

51

主题

1455

帖子

3

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
2612
金钱
2612
注册时间
2011-1-25
在线时间
176 小时
发表于 2013-9-16 11:14:12 | 显示全部楼层
回复【38楼】敌害:
---------------------------------
能介绍下是什么单片机么??
一直努力就很好。
回复 支持 反对

使用道具 举报

头像被屏蔽

88

主题

231

帖子

2

精华

高级会员

Rank: 4

积分
844
金钱
844
注册时间
2013-4-11
在线时间
40 小时
发表于 2013-9-16 11:30:47 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

76

主题

355

帖子

0

精华

高级会员

Rank: 4

积分
671
金钱
671
注册时间
2012-9-19
在线时间
5 小时
发表于 2013-9-16 11:34:13 | 显示全部楼层
回复【41楼】nonplus:
---------------------------------
o 不过LWIP是作为ST评估板嵌入的协议,应该比瑞典学院的uip好点吧。。所以我现在首选LWIP。
具有资深STM32点灯技术,
回复 支持 反对

使用道具 举报

76

主题

355

帖子

0

精华

高级会员

Rank: 4

积分
671
金钱
671
注册时间
2012-9-19
在线时间
5 小时
发表于 2013-9-16 11:35:21 | 显示全部楼层
回复【40楼】qq942266575:
---------------------------------
不知道。我只是通过介绍知道有这么一款 德州单片机。型号也不知道。只知道内部的模块是MAC。。
具有资深STM32点灯技术,
回复 支持 反对

使用道具 举报

头像被屏蔽

88

主题

231

帖子

2

精华

高级会员

Rank: 4

积分
844
金钱
844
注册时间
2013-4-11
在线时间
40 小时
发表于 2013-9-16 11:46:04 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2014-1-11
在线时间
0 小时
发表于 2014-1-11 17:07:58 | 显示全部楼层

请教一下,这个例程中,webserver与Tcp Server、Tcp Client是可以一起运行吗?

uip.h中结构uip_conn的成员 appstate类型 在 tcp_demo.h中被定义为

struct tcp_demo_appstate
{
 u8_t state;
 u8_t *textptr;
 int textlen;
}; 
typedef struct tcp_demo_appstate uip_tcp_appstate_t;

而在httpd.c中的函数 void httpd_appcall(void) {struct httpd_state *s = (struct httpd_state *)&(uip_conn->appstate);}
又将uip_conn->appstate强制转换成httpd_state类型。这里httpd_state的结构空间超出定义的tcp_demo_appstate空间了,是不是有问题?

struct httpd_state {
  unsigned char timer;
  struct psock sin, sout;
  struct pt outputpt, scriptpt;
  char inputbuf[50];
  char filename[20];
  char state;
  struct httpd_fs_file file;
  int len;
  char *scriptptr;
  int scriptlen;
 
  unsigned short count;
};

 

 

 

struct uip_conn {
  uip_ipaddr_t ripaddr;   /**< The IP address of the remote host. */
 
  u16_t lport;        /**< The local TCP port, in network byte order. */
  u16_t rport;        /**< The local remote TCP port, in network byte
    order. */
 
  u8_t rcv_nxt[4];    /**< The sequence number that we expect to
    receive next. */
  u8_t snd_nxt[4];    /**< The sequence number that was last sent by
                         us. */
  u16_t len;          /**< Length of the data that was previously sent. */
  u16_t mss;          /**< Current maximum segment size for the
    connection. */
  u16_t initialmss;   /**< Initial maximum segment size for the
    connection. */
  u8_t sa;            /**< Retransmission time-out calculation state
    variable. */
  u8_t sv;            /**< Retransmission time-out calculation state
    variable. */
  u8_t rto;           /**< Retransmission time-out. */
  u8_t tcpstateflags; /**< TCP state and flags. */
  u8_t timer;         /**< The retransmission timer. */
  u8_t nrtx;          /**< The number of retransmissions for the last
    segment sent. */

  /** The application state. */
  uip_tcp_appstate_t appstate;
};
 

回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2014-1-11 20:05:19 | 显示全部楼层
是的,一起运行
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

1

主题

215

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
254
金钱
254
注册时间
2013-9-23
在线时间
4 小时
发表于 2014-3-10 16:23:03 | 显示全部楼层
回复【13楼】liuje:
---------------------------------
大神理解得这么透彻了,能不能指导一下啊,我现在加上dht11温湿度显示功能,却更新不了数据!
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2014-5-22
在线时间
6 小时
发表于 2014-5-22 08:20:13 | 显示全部楼层
楼主好,RJ45用的是什么型号的?HR91105可以吗?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2014-5-22 23:14:29 | 显示全部楼层
回复【48楼】zhilu:
---------------------------------
可以
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2014-5-22 23:15:57 | 显示全部楼层
HR911105A
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-24 20:49

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表