如何用W7100A实现串口转以太网(二)

Home / 博客 / 如何用W7100A实现串口转以太网(二)

4    串口转以太网代码

在这一节,我们将会检查‘串口转以太网’中已载入的代码。代码的执行有三种方式:TCP服务器,TCP客户端,UDP。所有的代码按照下面章节中的讲述。

4.1  服务器模式

TCP服务器中的监听(LISTEN)状态表示服务器等待客户端的连接请求。客户端将会请求在连接(CONNECT)状态下与TCP服务器进行连接。如果服务器和客户端建立连接,SOCKET就会进入建立(ESTABLISH)状态(SOCK_ESTABLISHED;由WIZnet定义的)。在此状态下,可以进行数据发送/接收直到SOCKET关闭为止。TCP服务器模式下SOCKET的循环周期由打开(OPEN)、监听(LISTEN)、发送(SEND)、接收(RECEIVE)、断开(DISCONNECT)、关闭(CLOSE)这六种状态组成。

    每一个状态下所对应的程序将会在后面的章节介绍。W7100A驱动支持socket()、listen()、recv()、send()、disconnect()、close()这些函数。

4.1.1     打开(OPEN)

 s = 0;      // 设置SOCKET 0 (由0 to 7)

socket(s, Sn_MR_TCP, port, mode);   //打开SOCKET 0

while(getSn_SR(s) != SOCK_INIT);

程序4.1 打开SOCKET

    打开(OPEN)状态是表示SOCKET已创建了,而SOCKET由socket()函数创建。SOCKET号、协议、端口号以及标志位这些参数是需要在socket()函数中输入的;SOCKET号取0~7之间的值,而协议需要输入Sn_MR_TCP(0x01),端口号则取将要用来与客户端进行通信的端口号,标志位设置为0表示‘无延时Ack标志’。

    在所有的设置完成后,调用socket()函数。检测Sn_SR()寄存器是否已经变成SOCK_INIT(0x13),该寄存器可以指示socket的状态。Sn_SR的值可以利用getSn_SR()函数直接读出。如果SOCKET “n”没有变成SOCK_INIT(0x13),则表示创建SOCKET失败。这种情况下,需要再次调用socket()函数重新创建SOCKET.

4.1.2    监听(LISTEN)

s = 0;      // 设置SOCKET 0

listen(s);  // 将SOCKET 0的状态改成监听(LISTEN)状态

程序4.2 监听(LISTEN)状态

    SOCKET被创建后,为了能够与客户端建立连接,SOCKET需要处于监听(LISTEN)状态下。通常有两种方法将SOCKET的状态从SOCK_INIT(0x13)变为SOCK_LISTEN(0x14):一种方法是对用户来说的,可以执行Sn_CR()寄存器中的Sn_CR_LISTEN(0x02);另一种方法是调用listen()函数。通过这两种方法都可以直接改变SOCKET的状态。在监听(LISTEN)状态下,TCP服务器等待客户端的连接请求。当服务器与客户端建立连接时,SOCKET的状态将变成SOCK_ESTABLISHED(0x17)。这时,TCP服务器就可以与客户端进行数据交换。

4.1.3 RS232参数初始化

/* 配置定时器(TIMER)来产生波待率 */

ET1 = 0;          /* 禁止TIMER1中断 */

TMOD = 0x20;         /* 选择TIMER2 */

PCON |= 0x80;         /* SMOD = 1 */

TH1 = 0xFC;            /*在88.4736MHZ频率下 X2 115200(SMOD=1) */

TR1 = 1;                  /*启动TIMER1 */

SCON = 0x52;          /*串行模式1, REN=1, TI=1, RI=0 */

/* 中断设置 */

RI  = 0;          /* 清除UART的接收中断标志位*/

TI  = 0;          /* 清除UART的传输中断标志位*/

ES   = 1;        /* 允许串行中断 */

程序4.3 RS232初始化

    TCP服务器初始化完成后,用户应该将在W7100A串行通信中的定时器(TIMER)相关寄存器也进行初始化设置,如上4.3代码段中所示。

4.1.4  串行中断处理

if(RI)                       //检测接收中断标志位

{                                  //s_buf :串行缓存器

s_buf[s_write_ptr] = SBUF;       /从串行缓存器向uart接收缓存器中写入数据

s_write_ptr++;         //写指针加1

//检测写指针是否大于串行缓存器的最大存储空间

if(s_write_ptr >= MAX_SBUF_SIZE){

         s_write_ptr = 0;

        if(s_write_ptr == s_read_ptr) overflow = 1;  //缓存器溢出检测

}

else{

        if(s_write_ptr == s_read_ptr) overflow = 1;  //缓存器溢出检测

}

RI = 0;      //禁止接收中断标志位

}

程序4.4 串行中断处理

    在‘串行初始化代码’中允许串行中断时,当发送或者接收信息时串行中断就会发生。因此,需要输入处理串行中断的代码程序。如代码段4.4所示保存串行缓存数据的一段程序。如果缓存器溢出,就需要对串行缓存器进行控制。

4.1.5   TCP转串口

if((e_len = getSn_RX_RSR(s)) > 0)      //检测接收数据{                       // e_len 为接收到的以太网数据的长度, e_buf为以太网接收缓存器

if(e_len > MAX_EBUF_SIZE) e_len = MAX_EBUF_SIZE; //检测以太网缓存器的大小

s_len = recv(s, e_buf, e_len);     //接收以太网数据

for(i=0; i < s_len; i++){

putchar(*(e_buf + i));              //利用putchar()函数向串口发送数据

}

}

程序4.5接收和发送数据

    当接收数据时,Sn_RX_RSR()的值为接收到的数据的长度(参考W7100A用户手册)。因此,需要检测Sn_RX_RSR()寄存器的值是否大于0,如果是,利用getSn_RX_RSR()函数读取Sn_RX_RSR()的值。Recv()函数是用来接收数据的,recv()函数中的所有参数中s表示socket号,e_buf表示保存接收数据的缓存器,e_len表示接收数据的长度。当接收数据后,使用putchar()函数将接收到的数据发送给RS232串行端口。

4.1.6    串口转TCP

EA = 0;                           //禁止所有中断

tmp_read = s_read_ptr;           // 获取当前读指针的值

tmp_write = s_write_ptr;          //获取当前写指针的值

EA = 1;                           //允许所有中断

If(overflow){

length = (MAX_SBUF_SIZE – tmp_read) + tmp_write;  //计算读取数据的长度

send(s, &s_buf[tmp_read], MAX_SBUF_SIZE – tmp_read));

send(s, &s_buf[0], tmp_write);

     overflow = 0;                  //清除溢出标志位

}

if(tmp_write == tmp_read) return 0; //读指针和写指针不能相同

if(tmp_write > tmp_read){

length = tmp_write – tmp_read;  //计算要读取的数据长度

send(s, &s_buf[tmp_read], length);

}

else{

length = (MAX_SBUF_SIZE – tmp_read) + tmp_write;  //计算要读取的数据的长度

send(s, &s_buf[tmp_read], MAX_SBUF_SIZE – tmp_read));

send(s, &s_buf[0], tmp_write);

}

s_read_ptr += length;       //更新读指针

//检测读指针是否比串行缓存器的空间大

if(s_read_ptr >= MAX_SBUF_SIZE) s_read_ptr -= MAX_SBUF_SIZE;

return length;         //返回读数据的长度

程序4.6串行中断处理

    ‘RS232到TCP的转换’的基本概念与上面提到的‘TCP到RS232’ 的概念大致相同。当串行数据接收时,根据‘中断服务路径’将数据保存到临时缓存中,并且利用SEND()函数将数据发送到TCP服务器。在此过程中,需要注意中断服务路径缓存器溢出的时间。根据代码段2.2.3,当中断溢出发生时,将立即发送所有接收到的数据。 一旦转换完成,即没有数据需要发送/接收,TCP连接就会被终止。可以利用disconnect()函数或者close()函数来结束连接过程。

4.1.7  断开连接(DISCONNECT)

s = 0; // 设置SOCKET 0

disconnect(s);

程序4.7 断开连接(DISCONNECT)

    disconnect()函数发送断开请求(FIN 数据包),并且等待断开回复(FIN/ACK 数据包)。当使用disconnect()函数时,SOCKET的状态变成SOCK_CLOSED(0x00),然后关闭SOCKET。当断开请求到达时,W7100A创建FIN/ACK数据包,以便其他用户可以关闭SOCKET。如果发送断开请求后,在一段时间内没有回复,将会发生TCP超时,SOCKET的状态将会变为SOCK_CLOSED(0x00)。

4.1.8  关闭(CLOSE)

s = 0; // 设置SOCKET 0

close(s);

程序4.8 关闭(CLOSE)

    和disconnect()函数不同,close()函数将socket的状态立即变成SOCK_CLOSED(0x00)。用户也能够马上断开和其他用户的连接。如果RST数据包被其他用户接收到,socket的状态将会变成SOCK_CLOSED(0x00)。想了解更多RST数据包的详细信息,请参考W7100A用户手册。一旦socket的状态已经改变为SOCK_CLOSED(0X00),如果要重新使用需要再次打开。

4.2  TCP客户端模式

4.2.1   连接(CONNECT)

 

      在TCP客户端模式下实现‘串口转以太网’的程序和TCP服务器模式下基本相同,除了监听(LISTEN)这一步。因为客户端需要与服务器连接,将会使用socket号,目标IP,目的端口号是必须提供的。如果利用connect()函数完成设置,就能成功建立与服务器的连接,socket的状态将会变成SOCK_ESTABLISHED(0x17),此时允许数据的传输。

s = 0; //设置SOCKET 0

serverip[4] = {192, 168, 1, 2}; //设置服务器(目的) IP

serverport = 5000;           //设置服务器(目的)端口

connect(s, serverip, serverport);

程序4.9 连接(CONNECT)

    除了连接过程外所有的程序都与TCP客户端这一章节的程序相同,具体的程序请参照4.1节。

4.3     UDP模式

因为UDP不需要连接,所以监听,连接,断开连接,关闭这些过程就不再需要。当使用UDP执行‘串口转以太网’时,只需要下面的过程:打开socket,UDP到RS232的转换,RS2323到UDP的转换。

4.3.1    打开(OPEN)

s = 0;      //设置SOCKET 0 (从 0到7)

socket(s, Sn_MR_UDP, port, mode);   //打开SOCKET 0

while(getSn_SR(s) != SOCK_INIT);

程序4.10打开SOCKET

    打开SOCKET的基本程序与程序4.1相同。唯一的区别就是在协议参数中要输入Sn_MR_UDP,而不再是Sn_MR_TCP。更多的信息,请参考4.1节。

4.3.2    UDP转串口

if((e_len = getSn_RX_RSR(s)) > 0)      //检测接收数据

{                       // e_len为接收到的以太网数据的长度; e_buf为以太网接收缓存器

if(e_len > MAX_EBUF_SIZE) e_len = MAX_EBUF_SIZE; //检测最大以太网缓存器的空间大小

s_len = recvfrom(s, e_buf, e_len, (uint8 *)destIP, &destport);   //接收以太网数据

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

{

    putchar(*(e_buf + i));              //利用putchar()函数向串口发送数据

   }

}

程序4.11 接收(RECEIVE)和发送(SEND)数据

    ‘UDP到RS232’的基本步骤和4.1.5节中的相同。唯一的区别就是使用recvfrom()函数而不是recv()函数。Recvfrom()函数比recv()还多两个输入参数;destIP表示对端IP数据,destport表示对端端口数据。利用这两个参数,数据可以通过sendto()函数发送到对端而不需要建立连接。当接收数据时,向RS2323串行端口发送数据。具体的详细信息,请参考4.1.5节。

4.3.3     串口转UDP

EA = 0;                           //禁止所有中断

tmp_read = s_read_ptr;           // 获取当前读指针的值

tmp_write = s_write_ptr;          // 获取当前写指针的值

EA = 1;                           //允许所有的中断

If(overflow){

length = (MAX_SBUF_SIZE – tmp_read) + tmp_write;  /计算要读取的数据的长度

sendto(s, (uint8*)&s_buf[tmp_read], MAX_SBUF_SIZE – tmp_read, (uint8*)destIP, destport);

sendto(s, (uint8*)&s_buf[0], tmp_write);

     overflow = 0;                  //清除溢出中断标志位

}

if(tmp_write == tmp_read) return 0; //读指针和写指针不能相同

if(tmp_write > tmp_read){

length = tmp_write – tmp_read;  //计算要读取的数据的长度

sendto(s, (uint8*)&s_buf[tmp_read], length, (uint8*)destIP, destport);

}

Else{

length = (MAX_SBUF_SIZE – tmp_read) + tmp_write;  //计算要读取的数据的长度

sendto(s, (uint8*)&s_buf[tmp_read], MAX_SBUF_SIZE – tmp_read, (uint8*)destIP, destport);

sendto(s, (uint8*)&s_buf[0], tmp_write, (uint8*)destIP, destport);

}

s_read_ptr += length;       //更新读指针

//检测读指针的值是否大于串行缓存器的最大空间大小

if(s_read_ptr >= MAX_SBUF_SIZE) s_read_ptr -= MAX_SBUF_SIZE;

return length;         //返回读到的数据的长度

程序4.12 串行中断处理

    基本的步骤和4.1.6章节的相同,唯一的区别就是使用sendto()函数向以太网发送串行缓存器中接收到的数据,而不是send()函数。Sendto()函数比send()函数多两个输入参数;destIP表示对端IP数据,destport表示对端端口数据。更多的详细信息,请参考4.16节。