轻松实现Lua脚本控制W5500

栏目: Lua · 发布时间: 5年前

内容简介:※已刊登在“无线电”12月刊上轻松实现Lua脚本控制W5500作者:孔东明,张博Lua是巴西里约热内卢天主教大学里的一个研究小组于1993年基于标准C开发的一个轻量级的嵌入式脚本语言,其设计目的是为了将传统嵌入式程序“编写→编译→链接→运行”的复杂过程简化为“编写→运行”两个环节,从而为嵌入应用程序提供灵活的扩展和定制功能。

※已刊登在“无线电”12月刊上轻松实现 Lua 脚本控制W5500

作者:孔东明,张博

1、引言

Lua是巴西里约热内卢天主教大学里的一个研究小组于1993年基于标准C开发的一个轻量级的嵌入式脚本语言,其设计目的是为了将传统嵌入式程序“编写→编译→链接→运行”的复杂过程简化为“编写→运行”两个环节,从而为嵌入应用程序提供灵活的扩展和定制功能。

Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。一个完整的Lua解释器不过200K,在目前所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择。

2、项目背景

随着物联网的快速发展,传统的工控、电力、银行机、闸机甚至家电等设备也纷纷加入了连接互联网大军。工厂的车床需要把运行数据实时上传至PLC,水表、电表、燃气表实现了远程抄录,点验钞机可以实时将RMB的冠字号上传至银行数据库,停车场无人值守,家里的窗帘用某猫精灵很方便的进行语音控制……

小编在一家做网络通信设备的公司上班,领导要求基于现有的串口转以太网模块开发出一款支持用户使用Lua语言进行二次开发的串口转以太网模块,项目工期1个月。小编刚刚毕业4个月,没有多少项目经验,只是在学校玩过ARM M3的开发板,C语言自我感觉勉强及格,以太网技术基本小白一枚,对如何实现用户使用Lua语言“二次开发”更是一窍不通。但是任务时间紧迫,再难也要搞定,要不然没有奖金就要勒紧裤腰带了。

接到项目当晚就去找度娘商讨对策。经过一番搜索,方才大致了解了什么是Lua,什么是脚本语言,为什么客户要二次开发。用户在使用串口转以太网模块时,由于应用场景的不同及嵌入式产品资源的限制,需要灵活的调用模块的各项功能去实现差异化应用,而传统的模块只能实现既定的功能,因此支持二次开发的产品应用范围将大为拓展。而用户二次开发输入的代码肯定是无法执行传统的“编写→编译→链接→运行”这整个过程,脚本语言将这个过程简化为“编写→运行”就可以完美的解决了这个问题,Lua便是一款最佳的嵌入式脚本语言。

原理理顺了,如何落实便成了当务之急,我需要先做一个Demo来模拟整个过程。我找来了之前开发串口转以太网模块用到的以太网开发板W5500EVB,如下图。W5500EVB是由ST的STM32F103RC+W5500网络芯片构成,STM32F103内部256K的Flash足以容纳最大200K的Lua驱动。W5500是一颗以太网接口芯片,它用全硬件逻辑门电路搭建了一整套全硬件TCP/IP协议栈,发送数据时单片机只需将用户数据通过SPI发送至W5500,W5500内部会自动完成数据TCP/IP封包,并发送至网口,接收数据时W5500内部自动完成解包,仅将MCU关心的用户数据提交。W5500内含8路完全独立的硬件Socket,这意味着W5500可以同时运行8个上层应用程序,而且传输速率互不影响,不会像软件协议栈那样线程增加,速度明显降下来。W5500内部还集成了MAC和PHY,符合了接入以太网的所有条件,对于刚刚接触以太网的攻城狮来说,是一款简单易上手的网络接口芯片。

轻松实现Lua脚本控制W5500 图 1 W5500EVB

我想象中的Demo是这样的:用户通过Web网页向W5500EVB提交一段能让W5500EVB连接到TCP服务器的Lua脚本代码,W5500EVB解析出来这段代码后通过已经运行的Lua虚拟机中的Lua接口函数来解释用户代码要实现的功能,最后 W5500EVB按照用户代码中的参数连接到一个指定的TCP服务器实现以太网数据通信。这个过程可以参考W5500官网提供的HTTP Server和TCP Client的例程。

轻松实现Lua脚本控制W5500 图 2 实施方案原理图

3、准备工作

(1)安装编译环境:Keil V5.11

(2)硬件:W5500EVB、Jlink调试器

(3)驱动:Lua最新驱动V5.3.2

4、宿主C部分

4.1 加载驱动

驱动包括STM32F103RC的单片机驱动、W5500以太网部分驱动以及Lua驱动。STM32F103RC驱动不必多说,W5500驱动和Lua驱动如下图所示,均可以在对应官网下载到。

轻松实现Lua脚本控制W5500 轻松实现Lua脚本控制W5500

图 3 W5500驱动                                                                                    图 4 Lua驱动-V5.3.2

4.2 初始化部分

初始化部分包括STM32初始化及W5500初始化,Lua在用的时候才需要初始化。

01 /******* STM32 初始化 ********/

02 Systick_Init(72);

03 RCC_Configuration();

04 GPIO_Configuration();

05 Timer_Configuration();

06 NVIC_Configuration();

07 USART1_Init();

08 at24c16_init();

09

10 /******* W5500 初始化 ********/

11 printf(“W5500 Config….\r\n”);

12 Reset_W5500(); // 重启 W5500

13 WIZ_SPI_Init(); //SPI 初始化

14 set_default(); // 配置默认信息

15 set_network(); // 用默认信息初始化 W5500

4.3 Socket分配

W5500内含8个socket(0~7),可以同时进行8路独立的数据通信或上次应用服务。小编这里用Socket 7作为HTTP Server服务的端口,这样用户可以在后续的Lua脚本中任意使用socket 0~6来进行数据收发。

01 //Socket 0~6 can be used by user for data communication

02 #define SOCK_HTTP             7

4.4 HTTP Server部分

该部分主要是往W5500EVB内部嵌入一个HTTP Server服务和一个网页,以实现浏览器访问并接收用户Lua脚本的功能。HTTP Server服务是基于TCP Server的短连接实现的,第一个过程,用户在浏览器中输入HTTP Server的IP地址后,浏览器默认会向服务器请求index.html这个页面,W5500EVB会相应的返回一个如下的示例网页并关闭这个连接,等待下一次请求。

轻松实现Lua脚本控制W5500

图 5 HTTP Server网页

以下是该嵌入式网页的源码:

轻松实现Lua脚本控制W5500 轻松实现Lua脚本控制W5500

第二个过程也差不多,用户在浏览器中输入Lua脚本并提交,HTTP Server收到Lua脚本后将其原原本本的解析出来,保存在C的缓存中,等待Lua虚拟机的调用。下面是HTTP的实现过程。

01 void do_http(void) {

02     uint8 ch = SOCK_HTTP; // 使用 Socket 7 来处理 HTTP Server 服务

03     uint16 len;

04

05     st_http_request *http_request;

06     memset(rx_buf,0x00,MAX_URI_SIZE);

07     http_request = (st_http_request*)rx_buf;

08 /* socket 轮询状态机 */

09 switch (getSn_SR(ch)) {

10 case SOCK_INIT:              //socket 已经初始化

11         listen(ch); // 建立监听,等待 TCP Client 连接

12break;

13 case SOCK_LISTEN:

14break;

15 case SOCK_ESTABLISHED:                                 // 已连接

16 if (getSn_IR(ch) & Sn_IR_CON) {

17             setSn_IR(ch, Sn_IR_CON);

18         }

19 if ((len = getSn_RX_RSR(ch)) > 0) {

20             len = recv(ch, (uint8*)http_request, len); // 接收 HTTP 请求

21             *(((uint8*)http_request)+len) = 0;

22             proc_http(ch, (uint8*)http_request); // 发送 HTTP 应答

23             disconnect(ch); // 关闭连接

24         }

25break;

26 case SOCK_CLOSE_WAIT:        // 等待连接关闭过程中也要处理 HTTP

27 if ((len = getSn_RX_RSR(ch)) > 0) {

28             len = recv(ch, (uint8*)http_request, len);

29             *(((uint8*)http_request)+len) = 0;

30             proc_http(ch, (uint8*)http_request);

31         }

32         disconnect(ch);

33break;

34 case SOCK_CLOSED:                    //socket 处于关闭状态

35         socket(ch,Sn_MR_TCP,80,0×00); // 初始化并打开该 socket

36break;

37default:

38break;

39     }

5、C与Lua的交互

5.1 C与Lua的交互过程

下面是C与Lua的交互过程。第2行,Lua_State是Lua语言中的一种基本类型,用来初始化一个Lua虚拟机的运行环境。第3行,luaL_newstate是为了初始化并启动Lua虚拟机,包括创建Lua栈空间等(Lua与C交互主要通过栈来进行,因此需要足够的栈空间,建议设为800byte)。第4行,声明Lua基础库,Lua有丰富的库供调用,这里基础库主要用来处理字符串,声明后的Lua基础库会注册在Lua全局表(Global Table)中,形成了Lua全局表的一部分。第5、6行,声明Lua接口函数及运行用户Lua脚本。第7行,关闭Lua。

01 void do_lua(void) {

02     lua_State* L;

03     L= luaL_newstate(); // 初始化并启动 Lua 虚拟机

04     luaopen_base(L); // 声明 Lua 基础库

05     luaL_setfuncs(L, mylib, 0); // 声明 Lua 接口函数

06     luaL_dostring(L, LUA_SCRIPT_GLOBAL); // 运行解析出来的用户 Lua 脚本

07     lua_close(L); // 关闭 Lua 虚拟机

08 }

以下是声明Lua接口函数,声明后的Lua接口函数会注册在Lua全局表中,形成了Lua全局表的另一部分。这些接口函数其实是在C中定义的,在运行Lua脚本时,Lua虚拟机会在Lua全局表中查询已经注册的C接口函数,查到后就将Lua脚本中的参数经过栈传递给C,C通过对应的接口函数计算出结果,再通过栈传递给Lua虚拟机。

01 static const struct luaL_Reg mylib[]= { // 以下是声明 Lua 接口函数

02     {“Get_rxbuf”,get_rx_buf},

03     {“get_state”,get_state},

04     {“socket_config”,socket_config},

05     {“connet_server”,connet_server},

06     {“recv_server”,recv_server},

07     {“close_socket”,close_socket},

08     {NULL,NULL}

09 };

以下是运行用户Lua脚本,这是运行用户Lua脚本的入口。当执行Get_rxbuf()时,Lua虚拟机根据Lua全局表找到get_rx_buf这个C函数接口,然后通过栈操作执行之。

01 const char LUA_SCRIPT_GLOBAL[] =”\

02   local array \

03       array=Get_rxbuf() \

04   fun=load(array)\

05   fun()\

06 “;

以下是C执行get_rx_buf的过程。C将存放input_temp缓存中的HTTP传递给W5500EVB的Lua脚本字符串,通过栈压入给Lua虚拟机,Lua虚拟机会依照上述过程逐句解析该Lua脚本,再结合其他的C接口函数的计算,最终实现需要的功能。

01 static int get_rx_buf(lua_State *L) {

02     uint8 size=0;

03

04     size=strlen(input_temp);

05 if (size) {

06         lua_pushstring(L,input_temp);

07 return 1;

08     }

09 return 0;

10 }

5.2 解释Lua脚本

以下是用户通过网页上传的Lua示例脚本,Lua虚拟机通过解释该脚本,就可以连接到TCP Server,并实现数据通信。

01 lua_mode=1 // 进入轮询模式

02 local state=nil

03 local s=2 // 使用 socket 2 来进行数据 TCP 通信

04 local lport=5000 // 配置 TCP Client 端口号

05 local rport=6000 // 配置 TCP Server 端口号

06 local rip=”192.168.1.100″ // 配置 TCP Client IP 地址

07

08 state=get_state(s) // 查询该 socket 的状态

09 if state==”SOCK_CLOSED” then             // socket 处于关闭状态

10       socket_config(s,lport) // 初始化并打开该 socket

11 elseif state==”SOCK_INIT” then // socket 处于已初始化状态

12       connet_server(s,rip,rport) // 向服务器发起 TCP 连接请求

13 elseif state==”SOCK_ESTABLISHED” then // socket 处于连接建立状态

14       recv_server(s) // 接收 TCP Server 发来的数据再回给 TCP Server

15 else

16       close_socket(s) // socket 处于其他状态时,关闭该 socket

17 end

6、测试过程

第一步 在浏览器中输入HTTP Server的IP地址(192.168.1.111)登录网页并提交 Lua脚本,W5500EVB解析到Lua脚本后向TCP Server发起连接。

轻松实现Lua脚本控制W5500

图 6 登录网页并提交 Lua脚本

第二步 PC建立TCP Server监听、建立连接、数据收发。

轻松实现Lua脚本控制W5500 图 7 建立TCP连接并实现通信

至此,整个Demo就实现了,其实核心部分就是要搞清楚Lua程序与C是如何交互的,以及用户的Lua脚本字符串是如何输入并一步步传递至Lua环境。当然,站在产品的角度上,这样操作还有一些问题,如果模块重新上电,Lua脚本就会消失。我这里提供两种解决思路:第一,可以在单片机中运行文件系统,将网页提交的Lua脚本以.lua文件的形式存储起来;第二,实际的用户拿到模块后一般是需要一个MCU去控制的,可以将Lua脚本放在用户MCU中,这样就不需要用网页输入Lua脚本了。不知小编的理解是否合理,还请诸位看客指正,谢过!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML5秘籍

HTML5秘籍

Matthew MacDonald / 李松峰、朱巍 / 人民邮电出版社 / 2012-8 / 79.00元

《HTML5秘籍》共包括四个部分,共12章。第一部分介绍了html5的发展历程,利用html5重新构造网页,以及html5的语义元素。第二部分介绍了html5对传统web表单的翻新、html5中的音频与视频、canvas绘图技术、css3等内容。第三部分介绍了数据存储、离线应用、与web服务器通信,以及html5与javascript技术的强大结合等内容。第四部分为附录,简单介绍了css和java......一起来看看 《HTML5秘籍》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具