硬汉嵌入式论坛

 找回密码
 立即注册
查看: 2868|回复: 5
收起左侧

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

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2018-1-19 16:49:39 | 显示全部楼层 |阅读模式
第54章     RL-TCPnet实现emWin VNC远程桌面简单说明

      本章节主要为大家讲解使用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      总结



54.1  初学者重要提示
1、本章节仅进行了简单说明,更多emWin VNC的相关知识,看emWin官方手册的VNC章节即可。
2、关于VNC客户端软件,务必要使用SEGGER提供的,其他第三方软件的效果不够好。
       下载地址:http://bbs.armfly.com/read.php?tid=36976
3、由于SEGGER没有提供手机端的VNC客户端,所以从网上下载并测试好了几十款Android版本的,效果都不好,不够流畅。
4、实际测试发现uCOS-III不能使用第21章讲解的事件触发方式,会导致VNC服务器无法访问。而RTX和FreeRTOS版本的例子无此问题。
5、emWin VNC的高性能展示,看此贴http://bbs.armfly.com/read.php?tid=30543
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)

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2018-1-19 16:54:28 | 显示全部楼层
54.2  通过VNC客户端输入NetBIOS Name访问
第1步:下载VNC客户端小软件。
      下载地址:http://bbs.armfly.com/read.php?tid=36976 。下载并解压后就是一个exe小软件,打开后效果如下:
54.1.png
                              
第2步:输入NetBIOS Name访问。
54.2.png
第3步:弹出的窗口中输入密码123456,点击“OK”按钮。
54.3.png
第4步:进入到emWin的远程控制界面,跟显示屏上面显示的效果是一样的。
54.4.png
此时,大家就可以在VNC客户端控制开发板的界面了。反过来,控制开发板上的界面,也会反馈到VNC客户端上。

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2018-1-19 16:56:47 | 显示全部楼层
54.3  通过VNC客户端输入IP地址访问
      由于已经使能了DHCP,板子可以自动获取IP地址,而且使能了局域网域名NetBIOS,用户只需在电脑端ping armfly就可以获取板子的IP地址,获取方法如下:
第1步:WIN+R组合键打开“运行”窗口,输入cmd。
54.5.png
                              
第2步:弹出的命令窗口中,输入ping armfly。
54.6.png
第3步:输入ping armfly后,回车。
54.7.png
      获得IP地址是192.168.1.8。
第4步:下载VNC客户端小软件。
      下载地址:http://bbs.armfly.com/read.php?tid=36976 。下载并解压后就是一个exe小软件,打开后效果如下:
54.8.png
第5步:输入IP地址192.168.1.8,点击按钮“Connect”。
54.9.png
第6步:弹出的窗口中输入密码123456,点击“OK”按钮。
54.10.png
第7步:进入到emWin的远程控制界面,跟显示屏上面显示的效果是一样的。
54.11.png
进入后,大家就可以在VNC客户端控制开发板的界面了。反过来,控制开发板上的界面,也会反馈到VNC客户端上。

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2018-1-19 16:58:48 | 显示全部楼层
54.4 emWin VNC接口函数
      针对emWinVNC服务器的实现,emWin专门提供了一个接口文件:GUI_VNC_X_StartServer.c,用户在这个文件里面实现VNC的具体功能即可。STemWin软件包中没有此文件,大家可以在MDK安装目录里面找。
1、以MDK4.74为例,是在路径:C:\Keil_v474\ARM\Segger\emWin\Sample\GUI_X。
2、以MDK5.24a为例,是在路径:C:\Keil_v524a\ARM\PACK\Keil\MDK-Middleware\7.4.1\emWin\Sample\GUI_X
      对于这个文件,原始内容是基于SEEGGER的embOS实现的:
  1. <font size="3">#include <stdlib.h></font>



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

  3. #include "RTOS.H"     // embOS header

  4. #include "GUI.h"

  5. #include "GUI_VNC.h"



  6. /*********************************************************************

  7. *

  8. *       Static data

  9. *

  10. **********************************************************************

  11. */

  12. static GUI_VNC_CONTEXT    _Context;

  13. static struct sockaddr_in _Addr;



  14. //

  15. // embOS Stack area of the server

  16. //

  17. static OS_STACKPTR int _StackVNCServer[1000];



  18. //

  19. // embOS Task-control-block of the server

  20. //

  21. static OS_TASK         _VNCServer_TCB;



  22. /*********************************************************************

  23. *

  24. *       Static functions

  25. *

  26. **********************************************************************

  27. */

  28. /*********************************************************************

  29. *

  30. *       _Send

  31. *

  32. * Function description

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

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

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

  36. */

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

  38.   int r;



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

  40.   return r;

  41. }



  42. /*********************************************************************

  43. *

  44. *       _Recv

  45. *

  46. * Function description

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

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

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

  50. */

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

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

  53. }



  54. /*********************************************************************

  55. *

  56. *       _ListenAtTcpAddr

  57. *

  58. * Starts listening at the given TCP port.

  59. */

  60. static int _ListenAtTcpAddr(U16 Port) {

  61.   int sock;

  62.   struct sockaddr_in addr;



  63.   sock = socket(AF_INET, SOCK_STREAM, 0);

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

  65.   addr.sin_family      = AF_INET;

  66.   addr.sin_port        = htons(Port);

  67.   addr.sin_addr.s_addr = INADDR_ANY;

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

  69.   listen(sock, 1);

  70.   return sock;

  71. }



  72. /*********************************************************************

  73. *

  74. *       _ServerTask

  75. *

  76. * Function description

  77. *   This routine is the actual server task.

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

  79. *   It therefor does not terminate.

  80. *   In the endless loop it

  81. *     - Waits for a conection from a client

  82. *     - Runs the server code

  83. *     - Closes the connection

  84. */

  85. static void _ServerTask(void) {

  86.   int s, Sock, AddrLen;

  87.   U16 Port;



  88.   //

  89.   // Prepare socket (one time setup)

  90.   //

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

  92.   //

  93.   // Loop until we get a socket into listening state

  94.   //

  95.   do {

  96.     s = _ListenAtTcpAddr(Port);

  97.     if (s != -1) {

  98.       break;

  99.     }

  100.     OS_Delay(100); // Try again

  101.   } while (1);

  102.   //

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

  104.   //

  105.   while (1) {

  106.     //

  107.     // Wait for an incoming connection

  108.     //

  109.     AddrLen = sizeof(_Addr);

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

  111.       continue; // Error

  112.     }

  113.     //

  114.     // Run the actual server

  115.     //

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

  117.     //

  118.     // Close the connection

  119.     //

  120.     closesocket(Sock);

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

  122.   }

  123. }



  124. /*********************************************************************

  125. *

  126. *       Public code

  127. *

  128. **********************************************************************

  129. */

  130. /*********************************************************************

  131. *

  132. *       GUI_VNC_X_StartServer

  133. *

  134. * Function description

  135. *   To start the server, the following steps are executed

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

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

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

  139. * Notes:

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

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

  142. *       initialized some other place.

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

  144. */

  145. int GUI_VNC_X_StartServer(int LayerIndex, int ServerIndex) {

  146.   //

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

  148.   //

  149.   GUI_VNC_AttachToLayer(&_Context, LayerIndex);

  150.   _Context.ServerIndex = ServerIndex;

  151.   //

  152.   // Create task for VNC Server

  153.   //

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

  155.   //

  156.   // O.k., server has been started

  157.   //

  158.   return 0;

  159. }



  160. /*********************************************************************

  161. *

  162. *       GUI_VNC_X_StartServerFT

  163. *

  164. * Function description

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

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

  167. */

  168. int GUI_VNC_X_StartServerFT(int LayerIndex, int ServerIndex) {

  169.   //

  170.   // Enable file transfer extensions in RFB-protocoll

  171.   //

  172.   GUI_VNC_EnableFileTransfer(1);

  173.   //

  174.   // Set API-functions used for file access

  175.   //

  176.   GUI_VNC_SetFS_API(&IP_FS_FS);

  177.   //

  178.   // Call GUI_VNC_X_StartServer()

  179.   //

  180.   return GUI_VNC_X_StartServer(LayerIndex, ServerIndex);

  181. }



  182. /*********************************************************************

  183. *

  184. *       GUI_VNC_X_getpeername

  185. *

  186. * Function description

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

  188. *

  189. *   Return values

  190. *     IP addr. if VNC client connected

  191. *     0 if no client connected

  192. */

  193. void GUI_VNC_X_getpeername(U32 * Addr) {

  194.   *Addr = _Addr.sin_addr.s_addr;

  195. }



  196. /*************************** End of file ****************************
复制代码
      如果大家要将此文件在其它RTOS下实现,仅需要修改上面代码中置红的部分即可,主要是创建一个VNC服务器任务。而网络通信的代码是采用的bsd socket,如果大家使用的网络协议栈支持bsd socket,这部分代码是不需要做任何修改的。我们使用的RL-TCPnet网络协议栈是支持bsd scoket的,所以这部分代码无需做修改。
      下面的代码是采用RTX操作系统实现的,代码中置红的部分是修改后的。uCOS-III和FreeRTOS也都有实现,参看本章节配套的例子即可:
  1. #include <stdlib.h>

  2. #include <string.h>

  3. #include <stdio.h>



  4. #include "rtl.h"

  5. #include "GUI.h"

  6. #include "GUI_VNC.h"



  7. /*********************************************************************

  8. *

  9. *       Static data

  10. *

  11. **********************************************************************

  12. */

  13. static GUI_VNC_CONTEXT    _Context;

  14. static struct sockaddr_in _Addr;



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





  16. /*********************************************************************

  17. *

  18. *       Static functions

  19. *

  20. **********************************************************************

  21. */

  22. /*********************************************************************

  23. *

  24. *       _Send

  25. *

  26. * Function description

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

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

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

  30. */

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

  32.   int r;



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

  34.   return r;

  35. }



  36. /*********************************************************************

  37. *

  38. *       _Recv

  39. *

  40. * Function description

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

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

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

  44. */

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

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

  47. }



  48. /*********************************************************************

  49. *

  50. *       _ListenAtTcpAddr

  51. *

  52. * Starts listening at the given TCP port.

  53. */

  54. static int _ListenAtTcpAddr(U16 Port) {

  55.   int sock;

  56.   struct sockaddr_in addr = {0};



  57.   sock = socket(AF_INET, SOCK_STREAM, 0);

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

  59.   addr.sin_family      = AF_INET;

  60.   addr.sin_port        = htons(Port);

  61.   addr.sin_addr.s_addr = INADDR_ANY;

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

  63.   listen(sock, 1);

  64.   return sock;

  65. }



  66. /*********************************************************************

  67. *

  68. *       _ServerTask

  69. *

  70. * Function description

  71. *   This routine is the actual server task.

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

  73. *   It therefor does not terminate.

  74. *   In the endless loop it

  75. *     - Waits for a conection from a client

  76. *     - Runs the server code

  77. *     - Closes the connection

  78. */

  79. __task void _ServerTask(void) {

  80.   int s, Sock, AddrLen;

  81.   U16 Port;

  82.   //

  83.   // Prepare socket (one time setup)

  84.   //

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

  86.   //

  87.   // Loop until we get a socket into listening state

  88.   //

  89.   do {

  90.     s = _ListenAtTcpAddr(Port);

  91.     if (s != -1) {

  92.       break;

  93.     }

  94.     os_dly_wait(100); // Try again

  95.   } while (1);

  96.   //

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

  98.   //

  99.   while (1) {

  100.     //

  101.     // Wait for an incoming connection

  102.     //

  103.     AddrLen = sizeof(_Addr);

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

  105.       continue; // Error

  106.     }

  107.     //

  108.     // Run the actual server

  109.     //

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

  111.     //

  112.     // Close the connection

  113.     //

  114.     closesocket(Sock);

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

  116.   }

  117. }



  118. /*********************************************************************

  119. *

  120. *       Public code

  121. *

  122. **********************************************************************

  123. */

  124. /*********************************************************************

  125. *

  126. *       GUI_VNC_X_StartServer

  127. *

  128. * Function description

  129. *   To start the server, the following steps are executed

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

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

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

  133. * Notes:

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

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

  136. *       initialized some other place.

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

  138. */

  139. int GUI_VNC_X_StartServer(int LayerIndex, int ServerIndex) {

  140.   //

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

  142.   //

  143.   GUI_VNC_AttachToLayer(&_Context, LayerIndex);

  144.   _Context.ServerIndex = ServerIndex;

  145.    

  146.   //

  147.   // Create task for VNC Server

  148.   //

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

  150.                        4,                      /* 任务优先级 */

  151.                        &AppTaskVNCStk,         /* 任务栈 */

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

  153.   //

  154.   // O.k., server has been started

  155.   //

  156.   return 0;

  157. }



  158. /*********************************************************************

  159. *

  160. *       GUI_VNC_X_getpeername

  161. *

  162. * Function description

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

  164. *

  165. *   Return values

  166. *     IP addr. if VNC client connected

  167. *     0 if no client connected

  168. */

  169. void GUI_VNC_X_getpeername(U32 * Addr) {

  170.   *Addr = _Addr.sin_addr.s_addr;

  171. }



  172. /*************************** End of file ****************************/
复制代码
从上面的代码可以看出,主要是创建一个VNC服务器任务并提供一个延迟函数即可。

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2018-1-19 17:00:01 | 显示全部楼层
54.5 emWin VNC的初始化
      (注:更多emWin VNC的相关知识,大家直接看emWin官方手册中的VNC章节即可)
      emWin VNC的初始化比较容易,调用函数GUI_VNC_X_StartServer即可,这个函数是在GUI_VNC_X_StartServer.c文件中实现。调用前要先保证大家的网络协议栈和emWin的初始化函数GUI_Init函数已经调用。
  1. /* 使能VNC服务器 */

  2. GUI_VNC_X_StartServer(0, 0);

  3. GUI_VNC_SetPassword("123456");

  4. GUI_VNC_SetProgName("Designed by Eric2013");

  5. GUI_VNC_RingBell();
复制代码
函数GUI_VNC_X_StartServer的两个参数分别是使用的图层序号和服务器序号,我们这里都填写0即可。并且设置了密码是“123456”,程序名是“Designed by Eric2013”,这个程序名会显示在VNC客户端软件上面。
54.12.png
                              
由于SEGGER提供的这个VNC小软件不支持RingBell,所以函数GUI_VNC_RingBell的调用可以注释掉。

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2018-1-19 17:00:25 | 显示全部楼层
54.6 总结
      关于本章节就为大家讲解这么多。有兴趣的话,大家可以自己创建一个新的emWin界面,远程控制下。

努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

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

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-29 23:10 , Processed in 0.182588 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

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