你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

查看: 5772|回复: 7

【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验

[复制链接]

24

主题

12

回帖

2

蝴蝶豆

初级会员

最后登录
2020-2-27
发表于 2013-4-26 23:30:56 | 显示全部楼层 |阅读模式
 
<a name="_Toc342394401">57.1.1 ENC28J60简介

ENC28J60 是带有行业标准串行外设接口(Serial Peripheral Interface,SPI)的独立以太网控制器。它可作为任何配备有SPI 的控制器的以太网接口。ENC28J60 符合IEEE 802.3 的全部规范,采用了一系列包过滤机制以对传入数据包进行限制。 它还提供了一个内部DMA 模块,以实现快速数据吞吐和硬件支持的IP校验和计算。 与主控制器的通信通过两个中断引脚和
SPI 实现,数据传输速率高达10 Mb/s。两个专用的引脚用于连接LED,进行网络活动状态指示。
       ENC28J60的主要特点如下:
l 兼容IEEE802.3协议的以太网控制器
l 集成MAC和10 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所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image001.jpg

图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所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image002.jpg
图57.1.1.2 ENC28J60功能框图

       ALIENTEK ENC28J60网络模块采用ENC28J60作为主芯片,单芯片即可实现以太网接入,利用该模块,基本上只要是个单片机,就可以实现以太网连接。ALIENTEK ENC28J60网络模块原理图如图57.1.1.3所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image003.jpg
图57.1.1.3 ALIENTEK ENC28J60网络模块原理图

       ALIENTEK ENC28J60网络模块外观图如图57.1.1.4所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image004.png

图57.1.1.4 ALIENTEK ENC28J60网络模块外观图

       该模块通过一个8个引脚的排针与外部电路连接,这8个引脚分别是:GND、RST、MISO、SCK、MOSI、INT、CS和V3.3。其中GND和V3.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所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image005.png

图57.1.2.1 uIP在系统中的位置

从上图可以看出,uIP协议栈主要提供2个函数供系统底层调用:uip_input和uip_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所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image006.jpg
图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协议栈需要使用时钟,为TCP和ARP的定时器服务,因此我们需要STM32提供一个定时器做时钟,提供10ms计时(假设clock-arch.h里面的CLOCK_CONF_SECOND为100),通过clock-arch.c里面的clock_time函数返回给uIP使用。
第三步,配置uip-conf.h里面的宏定义选项。主要用于设置TCP最大连接数、TCP监听端口数、CPU大小端模式等,这个大家根据自己需要配置即可。
通过以上3步的修改,我们基本上就完成了uIP的移植。在使用uIP的时候,一般通过如下顺序:
1)实现接口函数(回调函数)UIP_APPCALL
该函数是我们使用uIP最关键的部分,它是uIP和应用程序的接口,我们必须根据自己的需要,在该函数做各种处理,而做这些处理的触发条件,就是前面提到的uIP提供的那些接口函数,如uip_newdata、uip_acked、uip_closed等等。另外,如果是UDP,那么还需要实现UIP_UDP_APPCALL回调函数。
2)调用tapdev_init函数,先初始化网卡。
此步先初始化网卡,配置MAC地址,为uIP和网络通信做好准备。
3)调用uip_init函数,初始化uIP协议栈。
此步主要用于uip自身的初始化,我们直接调用就是。
4)设置IP地址、网关以及掩码
这个和电脑上网差不多,只不过我们这里是通过uip_ipaddr、uip_sethostaddr、uip_setdraddr和uip_setnetmask等函数实现。
5)设置监听端口
uIP根据你设定的不同监听端口,实现不同的服务,比如我们实现Web Server就监听80端口(浏览器默认的端口是80端口),凡是发现80端口的数据,都通过Web Server的APPCALL函数处理。根据自己的需要设置不同的监听端口。不过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所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image007.jpg
表56.2.1 ENC28J60网络模块同战舰STM32开发板连接关系表

上表可以看出,其实网络模块同战舰STM32开发板的线序是一一对应的,所以如果你有一个1*8的排线,就可以直接对插即可。这里需要注意,本来开发板的P12端口是用来连接SD卡,实现SPI读写SD卡的,如果要连接网络模块,我们需要把跳线帽连接到P10和P11,这样还是可以通过SDIO访问SD卡。
在开发板连接网络模块以后,我们还需要一根网线(自备),连接网络模块和路由器,这样我们才能实现和电脑的连接。
57.3软件设计
本章,我们在第二十八章实验 (实验23 )的基础上修改,在该工程源码下面加入uIP-1.0文件夹,存放uIP1.0源码,再新建uIP-APP文件夹,存放应用部分代码,因为uIP自己有一个timer.c和timer.h的文件,所以我们还需要修改HARDWARE里面的timer.c和timer.h为不同的名字,本章我们改为timerx.c和timerx.h,我们还需要实现ENC28J60的驱动代码,存放在HARDWARE文件夹下的ENC28J60文件夹里面。详细的步骤我们就不一一阐述了,全部改好之后,工程如图57.3.1所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image008.jpg
图57.3.1 移植完后,MDK工程图

       图中uIP-1.0文件夹里面的代码全部是uIP提供的协议栈源码,而uIP-APP里面的代码则部分是我们自己实现的,部分是uIP提供的,其中:
       clock-arch.c,属于uIP协议栈,uIP通过该代码里面的clock_time函数获取时钟节拍。
       tapdev.c,同样是uIP提供,用来实现uIP与网卡的接口,该文件实现tapdev_init、tapdev_read和tapdev_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.c、httpd-cgi.c、httpd-fs.c和httpd-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
      //PHLCON:PHY 模块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以后,我们设置enc28j60的LED控制器工作方式,即完成对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服务(包括server和client)和HTTP服务
void tcp_demo_appcall(void)
{    
       switch(uip_conn->lport)//本地监听端口80和1200
       {
              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函数实现。
接着,我们来看看这3个appcall函数,首先是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="http://www.openedv.com/upload/2012/9/27/ad65ee9f478ca
11241933beed5b5dbcc_971.gif";          //LED0亮,图标地址
const u8 * LED1_ON_PIC_ADDR="http://www.openedv.com/upload/2012/9/27/bab5bef0379dc
50129202157c2739c57_775.gif";         //LED1亮,图标地址
const u8 * LED_OFF_PIC_ADDR="http://www.openedv.com/upload/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);
}
 

 
这里,我们需要了解uIP是把网页数据(源文件)存放在data_index_html,通过将这里面的数据发送给电脑浏览器,浏览器就会显示出我们所设计的界面了。当用户在网页上面操作的时候,浏览器就会发送消息给WEB服务器,服务器根据收到的消息内容,判断用户所执行的操作,然后发送新的页面到浏览器,这样用户就可以看到操作结果了。本章,我们实现的WEB服界面如图57.3.2所示:
https://www.stmcu.org.cn/file:///C:/Documents%20and%20Settings/Administrator/桌面/论坛推广/第57章/新建%20Microsoft%20Word%20文档.files/image009.png

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服务器就为大家介绍这么多。

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

下载

1.32 MB, 下载次数: 139, 下载积分: ST金币 -1

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

下载

1.12 MB, 下载次数: 124, 下载积分: ST金币 -1

<
回复

使用道具 举报

24

主题

12

回帖

2

蝴蝶豆

初级会员

最后登录
2020-2-27
 楼主| 发表于 2013-4-26 23:31:47 | 显示全部楼层

回复:【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验

接下来看看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 *)&amp;uip_conn-&gt;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())//收到客户端发过来的数据
       {

&lt;span lang=&quot;EN-US&quot;&gt;              if((tcp_server_sta&amp;(1
回复 支持 反对

使用道具 举报

1

主题

49

回帖

0

蝴蝶豆

初级会员

最后登录
1970-1-1
发表于 2013-12-15 10:32:14 | 显示全部楼层

RE:【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验

神舟的网络这个东西资料我看更实用
回复 支持 反对

使用道具 举报

0

主题

3

回帖

0

蝴蝶豆

新手上路

最后登录
1970-1-1
发表于 2014-3-12 08:26:30 | 显示全部楼层

RE:【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第五十七章 ENC28J60网络实验

看不到图片呀!顶顶顶顶
回复 支持 反对

使用道具 举报

1

主题

23

回帖

0

蝴蝶豆

新手上路

最后登录
2016-1-22
发表于 2015-10-23 19:09:00 | 显示全部楼层
楼主做过RS232转以太网的吗 基于stm32 ENC28j60的
回复 支持 反对

使用道具 举报

0

主题

3

回帖

0

蝴蝶豆

新手上路

最后登录
2019-1-3
发表于 2017-7-17 17:04:47 | 显示全部楼层
非常感谢,非常好的资料,正需要用。谢谢
回复 支持 反对

使用道具 举报

5

主题

850

回帖

58

蝴蝶豆

论坛元老

最后登录
2020-12-9
发表于 2018-9-3 11:59:00 | 显示全部楼层
謝謝大神分享
回复 支持 反对

使用道具 举报

6

主题

764

回帖

90

蝴蝶豆

金牌会员

最后登录
2020-12-9
发表于 2018-9-3 13:02:42 | 显示全部楼层
谢谢分享 学习一下
回复 支持 反对

使用道具 举报

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版