嵌入式技术论坛
直播中

lee_st

11年用户 45163经验值
擅长:可编程逻辑 嵌入式技术 处理器/DSP RF/无线
私信 关注
[经验]

第54章 RL-TCPnet实现emWin VNC远程桌面简单说明

本帖最后由 lee_st 于 2018-1-23 05:29 编辑

转帖
本章节主要为大家讲解使用RL-TCPnet实现emWin VNC服务器。通过开发板上面建立的VNC服务器,用户就可以在电脑端登陆VNC客户端软件来访问开发板上显示屏的界面,支持远程控制。
      本章教程含STM32F407开发板和STM32F429开发板。
54.1 初学者重要提示
54.2 通过VNC客户端输入NetBIOS Name访问
54.3 通过VNC客户端输入IP地址访问
54.4 emWin VNC接口函数
54.5 emWin VNC的初始化
54.6      总结

回帖(24)

lee_st

2018-1-22 09:34:43
54.1  初学者重要提示
1、本章节仅进行了简单说明,更多emWin VNC的相关知识,看emWin官方手册的VNC章节即可。
2、关于VNC客户端软件,务必要使用SEGGER提供的,其他第三方软件的效果不够好。
3、由于SEGGER没有提供手机端的VNC客户端,所以从网上下载并测试好了几十款Android版本的,效果都不好,不够流畅。
4、实际测试发现uCOS-III不能使用第21章讲解的事件触发方式,会导致VNC服务器无法访问。而RTX和FreeRTOS版本的例子无此问题。
5、emWin VNC的高性能展示 。
6、本章节配套如下六个例子,F407和F429开发板各三个:
       V5-1075_RL-TCPnet实验_emWinVNC远程桌面(RTX)
       V5-1076_RL-TCPnet实验_emWinVNC远程桌面(uCOS-III)
       V5-1077_RL-TCPnet实验_emWinVNC远程桌面(FreeRTOS)
       V6-1075_RL-TCPnet实验_emWinVNC远程桌面(RTX)
       V6-1076_RL-TCPnet实验_emWinVNC远程桌面(uCOS-III)
       V6-1077_RL-TCPnet实验_emWinVNC远程桌面(FreeRTOS)
举报

lee_st

2018-1-22 09:35:19
54.2  通过VNC客户端输入NetBIOS Name访问
第1步:下载VNC客户端小软件。
      下载并解压后就是一个exe小软件,打开后效果如下:
                              

举报

lee_st

2018-1-22 09:35:32
第2步:输入NetBIOS Name访问。

举报

lee_st

2018-1-22 09:35:50
第3步:弹出的窗口中输入密码123456,点击“OK”按钮。

举报

lee_st

2018-1-22 09:36:03
第4步:进入到emWin的远程控制界面,跟显示屏上面显示的效果是一样的。

举报

lee_st

2018-1-22 09:36:17
此时,大家就可以在VNC客户端控制开发板的界面了。反过来,控制开发板上的界面,也会反馈到VNC客户端上。
举报

lee_st

2018-1-22 09:36:30
54.3  通过VNC客户端输入IP地址访问
      由于已经使能了DHCP,板子可以自动获取IP地址,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获取板子的IP地址,获取方法如下:
第1步:WIN+R组合键打开“运行”窗口,输入cmd。

举报

lee_st

2018-1-22 09:36:42
第2步:弹出的命令窗口中,输入ping armfly。

举报

lee_st

2018-1-22 09:37:18
第3步:输入ping armfly后,回车。

举报

lee_st

2018-1-22 09:37:40
获得IP地址是192.168.1.8。
第4步:下载VNC客户端小软件。
      下载并解压后就是一个exe小软件,打开后效果如下:

举报

lee_st

2018-1-22 09:37:55
第5步:输入IP地址192.168.1.8,点击按钮“Connect”。

举报

lee_st

2018-1-22 09:38:16
第6步:弹出的窗口中输入密码123456,点击“OK”按钮。

举报

lee_st

2018-1-22 09:38:33
第7步:进入到emWin的远程控制界面,跟显示屏上面显示的效果是一样的。
进入后,大家就可以在VNC客户端控制开发板的界面了。反过来,控制开发板上的界面,也会反馈到VNC客户端上。

举报

lee_st

2018-1-22 09:38:56
54.4 emWin VNC接口函数
      针对emWinVNC服务器的实现,emWin专门提供了一个接口文件:GUI_VNC_X_StartServer.c,用户在这个文件里面实现VNC的具体功能即可。STemWin软件包中没有此文件,大家可以在MDK安装目录里面找。
1、以MDK4.74为例,是在路径:C:Keil_v474ARMSeggeremWinSampleGUI_X。
2、以MDK5.24a为例,是在路径:C:Keil_v524aARMPACKKeilMDK-Middleware7.4.1emWinSampleGUI_X
举报

lee_st

2018-1-22 09:39:21
对于这个文件,原始内容是基于SEEGGER的embOS实现的:
#include



#include "IP.h"       // BSD socket interface

#include "RTOS.H"     // embOS header

#include "GUI.h"

#include "GUI_VNC.h"



/*********************************************************************

*

*       Static data

*

**********************************************************************

*/

static GUI_VNC_CONTEXT    _Context;

static struct sockaddr_in _Addr;



//

// embOS Stack area of the server

//

static OS_STACKPTR int _StackVNCServer[1000];



//

// embOS Task-control-block of the server

//

static OS_TASK         _VNCServer_TCB;



/*********************************************************************

*

*       Static functions

*

**********************************************************************

*/

/*********************************************************************

*

*       _Send

*

* Function description

*   This function is called indirectly by the server; it's address is passed to the actual

*   server code as function pointer. It is needed because the server is independent

*   of the TCP/IP stack implementation, so details for the TCP/IP stack can be placed here.

*/

static int _Send(const U8 * buf, int len, void * pConnectionInfo) {

  int r;



  r = send((long)pConnectionInfo, (const char *)buf, len, 0);

  return r;

}



/*********************************************************************

*

*       _Recv

*

* Function description

*   This function is called indirectly by the server; it's address is passed to the actual

*   server code as function pointer. It is needed because the server is independent

*   of the TCP/IP stack implementation, so details for the TCP/IP stack can be placed here.

*/

static int _Recv(U8 * buf, int len, void * pConnectionInfo) {

  return recv((long)pConnectionInfo, (char *)buf, len, 0);

}



/*********************************************************************

*

*       _ListenAtTcpAddr

*

* Starts listening at the given TCP port.

*/

static int _ListenAtTcpAddr(U16 Port) {

  int sock;

  struct sockaddr_in addr;



  sock = socket(AF_INET, SOCK_STREAM, 0);

  memset(&addr, 0, sizeof(addr));

  addr.sin_family      = AF_INET;

  addr.sin_port        = htons(Port);

  addr.sin_addr.s_addr = INADDR_ANY;

  bind(sock, (struct sockaddr *)&addr, sizeof(addr));

  listen(sock, 1);

  return sock;

}



/*********************************************************************

*

*       _ServerTask

*

* Function description

*   This routine is the actual server task.

*   It executes some one-time init code, then runs in an ednless loop.

*   It therefor does not terminate.

*   In the endless loop it

*     - Waits for a conection from a client

*     - Runs the server code

*     - Closes the connection

*/

static void _ServerTask(void) {

  int s, Sock, AddrLen;

  U16 Port;



  //

  // Prepare socket (one time setup)

  //

  Port = 5900 + _Context.ServerIndex; // Default port for VNC is is 590x, where x is the 0-based layer index

  //

  // Loop until we get a socket into listening state

  //

  do {

    s = _ListenAtTcpAddr(Port);

    if (s != -1) {

      break;

    }

    OS_Delay(100); // Try again

  } while (1);

  //

  // Loop once per client and create a thread for the actual server

  //

  while (1) {

    //

    // Wait for an incoming connection

    //

    AddrLen = sizeof(_Addr);

    if ((Sock = accept(s, (struct sockaddr*)&_Addr, &AddrLen)) == SOCKET_ERROR) {

      continue; // Error

    }

    //

    // Run the actual server

    //

    GUI_VNC_Process(&_Context, _Send, _Recv, (void *)Sock);

    //

    // Close the connection

    //

    closesocket(Sock);

    memset(&_Addr, 0, sizeof(struct sockaddr_in));

  }

}



/*********************************************************************

*

*       Public code

*

**********************************************************************

*/

/*********************************************************************

*

*       GUI_VNC_X_StartServer

*

* Function description

*   To start the server, the following steps are executed

*   - Make sure the TCP-IP stack is up and running

*   - Init the server context and attach it to the layer

*   - Start the thread (task) which runs the VNC server

* Notes:

*   (1) The first part of the code initializes the TCP/IP stack. In a typical

*       application, this is not required, since the stack should have already been

*       initialized some other place.

*       This could be done in a different module. (TCPIP_AssertInit() ?)

*/

int GUI_VNC_X_StartServer(int LayerIndex, int ServerIndex) {

  //

  // Init VNC context and attach to layer (so context is updated if the display-layer-contents change

  //

  GUI_VNC_AttachToLayer(&_Context, LayerIndex);

  _Context.ServerIndex = ServerIndex;

  //

  // Create task for VNC Server

  //

  OS_CREATETASK(&_VNCServer_TCB, "VNC Server", _ServerTask, 230, _StackVNCServer);

  //

  // O.k., server has been started

  //

  return 0;

}



/*********************************************************************

*

*       GUI_VNC_X_StartServerFT

*

* Function description

*   In addition to GUI_VNC_X_StartServer() it enables the RFB-protocoll extensions

*   and sets an API-structure containing function pointers required for file access.

*/

int GUI_VNC_X_StartServerFT(int LayerIndex, int ServerIndex) {

  //

  // Enable file transfer extensions in RFB-protocoll

  //

  GUI_VNC_EnableFileTransfer(1);

  //

  // Set API-functions used for file access

  //

  GUI_VNC_SetFS_API(&IP_FS_FS);

  //

  // Call GUI_VNC_X_StartServer()

  //

  return GUI_VNC_X_StartServer(LayerIndex, ServerIndex);

}



/*********************************************************************

*

*       GUI_VNC_X_getpeername

*

* Function description

*   Retrieves the IP addr. of the currently connected VNC client.

*

*   Return values

*     IP addr. if VNC client connected

*     0 if no client connected

*/

void GUI_VNC_X_getpeername(U32 * Addr) {

  *Addr = _Addr.sin_addr.s_addr;

}



/*************************** End of file ****************************
举报

lee_st

2018-1-22 09:39:35
如果大家要将此文件在其它RTOS下实现,仅需要修改上面代码中置红的部分即可,主要是创建一个VNC服务器任务。而网络通信的代码是采用的bsd socket,如果大家使用的网络协议栈支持bsd socket,这部分代码是不需要做任何修改的。我们使用的RL-TCPnet网络协议栈是支持bsd scoket的,所以这部分代码无需做修改。
举报

lee_st

2018-1-22 09:40:02
下面的代码是采用RTX操作系统实现的,代码中置红的部分是修改后的。uCOS-III和FreeRTOS也都有实现,参看本章节配套的例子即可:
#include

#include

#include



#include "rtl.h"

#include "GUI.h"

#include "GUI_VNC.h"



/*********************************************************************

*

*       Static data

*

**********************************************************************

*/

static GUI_VNC_CONTEXT    _Context;

static struct sockaddr_in _Addr;



static U64 AppTaskVNCStk[4096/8];   /* 任务栈 */





/*********************************************************************

*

*       Static functions

*

**********************************************************************

*/

/*********************************************************************

*

*       _Send

*

* Function description

*   This function is called indirectly by the server; it's address is passed to the actual

*   server code as function pointer. It is needed because the server is independent

*   of the TCP/IP stack implementation, so details for the TCP/IP stack can be placed here.

*/

static int _Send(const U8 * buf, int len, void * pConnectionInfo) {

  int r;



  r = send((long)pConnectionInfo, (const char *)buf, len, 0);

  return r;

}



/*********************************************************************

*

*       _Recv

*

* Function description

*   This function is called indirectly by the server; it's address is passed to the actual

*   server code as function pointer. It is needed because the server is independent

*   of the TCP/IP stack implementation, so details for the TCP/IP stack can be placed here.

*/

static int _Recv(U8 * buf, int len, void * pConnectionInfo) {

  return recv((long)pConnectionInfo, (char *)buf, len, 0);

}



/*********************************************************************

*

*       _ListenAtTcpAddr

*

* Starts listening at the given TCP port.

*/

static int _ListenAtTcpAddr(U16 Port) {

  int sock;

  struct sockaddr_in addr = {0};



  sock = socket(AF_INET, SOCK_STREAM, 0);

  memset(&addr, 0, sizeof(addr));

  addr.sin_family      = AF_INET;

  addr.sin_port        = htons(Port);

  addr.sin_addr.s_addr = INADDR_ANY;

  bind(sock, (struct sockaddr *)&addr, sizeof(addr));

  listen(sock, 1);

  return sock;

}



/*********************************************************************

*

*       _ServerTask

*

* Function description

*   This routine is the actual server task.

*   It executes some one-time init code, then runs in an ednless loop.

*   It therefor does not terminate.

*   In the endless loop it

*     - Waits for a conection from a client

*     - Runs the server code

*     - Closes the connection

*/

__task void _ServerTask(void) {

  int s, Sock, AddrLen;

  U16 Port;

  //

  // Prepare socket (one time setup)

  //

  Port = 5900 + _Context.ServerIndex; // Default port for VNC is is 590x, where x is the 0-based layer index

  //

  // Loop until we get a socket into listening state

  //

  do {

    s = _ListenAtTcpAddr(Port);

    if (s != -1) {

      break;

    }

    os_dly_wait(100); // Try again

  } while (1);

  //

  // Loop once per client and create a thread for the actual server

  //

  while (1) {

    //

    // Wait for an incoming connection

    //

    AddrLen = sizeof(_Addr);

    if ((Sock = accept(s, (struct sockaddr*)&_Addr, &AddrLen)) < 0) {

      continue; // Error

    }

    //

    // Run the actual server

    //

    GUI_VNC_Process(&_Context, _Send, _Recv, (void *)Sock);

    //

    // Close the connection

    //

    closesocket(Sock);

    memset(&_Addr, 0, sizeof(struct sockaddr_in));

  }

}



/*********************************************************************

*

*       Public code

*

**********************************************************************

*/

/*********************************************************************

*

*       GUI_VNC_X_StartServer

*

* Function description

*   To start the server, the following steps are executed

*   - Make sure the TCP-IP stack is up and running

*   - Init the server context and attach it to the layer

*   - Start the thread (task) which runs the VNC server

* Notes:

*   (1) The first part of the code initializes the TCP/IP stack. In a typical

*       application, this is not required, since the stack should have already been

*       initialized some other place.

*       This could be done in a different module. (TCPIP_AssertInit() ?)

*/

int GUI_VNC_X_StartServer(int LayerIndex, int ServerIndex) {

  //

  // Init VNC context and attach to layer (so context is updated if the display-layer-contents change

  //

  GUI_VNC_AttachToLayer(&_Context, LayerIndex);

  _Context.ServerIndex = ServerIndex;

   

  //

  // Create task for VNC Server

  //

     os_tsk_create_user(_ServerTask,            /* 任务函数 */

                       4,                      /* 任务优先级 */

                       &AppTaskVNCStk,         /* 任务栈 */

                       sizeof(AppTaskVNCStk)); /* 任务栈大小,单位字节数 */

  //

  // O.k., server has been started

  //

  return 0;

}



/*********************************************************************

*

*       GUI_VNC_X_getpeername

*

* Function description

*   Retrieves the IP addr. of the currently connected VNC client.

*

*   Return values

*     IP addr. if VNC client connected

*     0 if no client connected

*/

void GUI_VNC_X_getpeername(U32 * Addr) {

  *Addr = _Addr.sin_addr.s_addr;

}



/*************************** End of file ****************************/
举报

lee_st

2018-1-22 09:40:22
从上面的代码可以看出,主要是创建一个VNC服务器任务并提供一个延迟函数即可。
举报

lee_st

2018-1-22 09:41:12
54.5 emWin VNC的初始化
      (注:更多emWin VNC的相关知识,大家直接看emWin官方手册中的VNC章节即可)
      emWin VNC的初始化比较容易,调用函数GUI_VNC_X_StartServer即可,这个函数是在GUI_VNC_X_StartServer.c文件中实现。调用前要先保证大家的网络协议栈和emWin的初始化函数GUI_Init函数已经调用。
/* 使能VNC服务器 */

GUI_VNC_X_StartServer(0, 0);

GUI_VNC_SetPassword("123456");

GUI_VNC_SetProgName("Designed by Eric2013");

GUI_VNC_RingBell();
举报

更多回帖

发帖
×
20
完善资料,
赚取积分