stm32应用:lua在Keil上的移植和使用

stm32应用:lua在Keil上的移植和使用

    • Lua的简介
    • Lua的移植
    • 关于Lua的应用举例

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。本文主要说明一下关于lua移植到STM32上的过程以及简单的举两个例子来说明lua的应用。

Lua的简介

关于Lua的官方介绍,此处不在说明。有需要了解的看官可以在百度或者lua教程了解。关于其应用有:
游戏开发
独立应用脚本
Web 应用脚本
扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
安全系统,如入侵检测系统
但这些对此文章来时并不是重点,而重点在于将Lua移植到单片机上到底有什么用处。经过这些天的移植和测试,个人理解是这样:将通过某个方式将一段字符串输送给STM32。该段字符串就是一段lua脚本程序,而单片机就会根据这段脚本程序实现脚本的功能。你可以将这段脚本做成文件,用文件系统进行读取,或者通过串口输入,又后者用flash存储。那么它在单片机里具体有什么用呢,在我学习的过程中,在一些讨论群中很多网友都说这是个鸡肋,压根没什么用途。而本人则觉得如果对于一些懂得lua脚本编程的开发者来说未必会单片机开发能力,如果设备拥有脚本编程能力,对这类有设备需要的开发者来说还是挺好的。下面简单举个例子说明一下lua在单片机中如果灵活的使用。
假设当前有个主机设备,设备提供有串口3个(编号A,B,C),网口1个(net),继电器控制口8个(Q1-Q8)。485通信口2个(M1,M2)
现在有四个设备接在M1口上,分别为设备(A1-A4);这四个设备都可以通过命令进行查寻,而查询的结果可以让主机设备根据脚本进行不同的动作,比如,可以让查询A1设备的结果发送到网络,也可以查询A1的结果发送到串口,这样作为设备开发商对于这种不定性的要求是无法掌握的。如果主机能够支持脚本语言,虽然接口有限,但是却可以根据用户要求进行不同的转换或者处理。只要客户自行编写自己得脚本既可以。或者有厂家帮客户提供脚本服务。而不需要去编写底层代码,这类应有在一些PLC中控器是比较常见的。
这是目前个人对Lua的一点理解。如果说的不好也请见谅。这个并非本文的重点。接下来开始文章点重点,移植和使用。

Lua的移植

准备:开发板一块,lua程序包(当前版本为Lua-5.3.5版本)
说明:由于当前版本Lua的内存开销如下
l RAM >= 7.5Kb,建议16KB以上
l ROM >= 65kb,建议128kb以上
因此RAM和ROM少于最低要求的芯片是无法移植的
先创建一个简单的工程项目,保存项目可以正常运行。
Lua的程序文件都在包中的scr文件夹下面,因此将整个文件夹复制到项目文件夹中,并添加到工程项目里,然后在include paths中添加Lua的路径。注意添加时有两个文件不需要添加,lua.c 和 luac.c (分别为PC的解释器和编译器,这个跟java类型,如果有java基础的应该会有一定的理解)。既然是面对PC机的,所以我们不需要,如果加到项目里,因为其各自有个mian函数就会有错误。
stm32应用:lua在Keil上的移植和使用_第1张图片
由于lua底层还是有一些标准库函数,因此还需要为其添加一些标准库函数,具体有下面三个:
本机时间函数:

time_t time(time_t * time){
	return 0;
}

进程退出函数,用于退出进程(单片机系统本是功能单一的应用,一般都不会用到,Lua程序要求因此添加一个空函数)

void exit(int status){}

此处添加后会有警告,这是因为在stilib.h文件定义中,该函数是有返回值的,个人使用直接在头文件中修改了定义。

extern _ARMABI_NORETURN void exit(int /*status*/);

修改为

extern void exit(int status);

将命令名称或程序名称传给要被命令处理器执行环境,同样设置为空

int system(const char * string){
	return 0;
}

然后全编译后,成功如下图
stm32应用:lua在Keil上的移植和使用_第2张图片
个人还是喜欢无错误无警告的程序,(^ __ ^)

关于Lua的应用举例

主要用两个例子来说明Lua的应用
1、利用Lua脚本语言做个2的11次方运行,并通过串口打印出来。
2、添加自定义的接口函数实现LED灯的闪烁。其闪烁频率可以自己通过延时来设置。
第二个例子还是比较贴合实际应用,主要是了解如何将自定义点函数添加到Lua脚本解释器中,还有关于带有参数的接口程序如何实现。

串口打印运算结果:(此处不多少,直接上关键代码)

/*
 *	库函数发送调用功能函数
 *	形式参数:
 *	返 回 值:
 */
int fputc(int ch, FILE *f)
{
	USART_SendData(USART1 , (uint8_t)ch);										
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
	USART_ClearITPendingBit(USART1,USART_FLAG_TC);
	return (ch);
}
/*
 *	lua串口输出运算的脚本程序
 */
const char lua_test[] = {
    "print(\"Hello,I am lua!\\n--this is newline printf\")\n"
    "function foo()\n"
    "  local i = 0\n"
    "  local sum = 1\n"
    "  while i <= 10 do\n"
    "    sum = sum * 2\n"
    "    i = i + 1\n"
    "  end\n"
    "return sum\n"
    "end\n"
    "print(\"sum =\", foo())\n"
    "print(\"and sum = 2^11 =\", 2 ^ 11)\n"
};
/*
 *	编译器测试
 */
static int do_file_script(void){
	lua_State *L;
	L = luaL_newstate(); 			//	创建Lua编译器
	luaopen_base(L);				//	注册基本函数
	luaL_dostring(L, lua_test); 	//	执行脚本语句
	return 0;
}
/*
 *	主函数
 */
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	usart1_initialization();
	do_file_script();
	while(1){
	}
}

其中lua代码中的print就是输出功能函数。

例子二,利用串口传入脚本,然后执行脚本语句,思路比较简单,就是通过串口接收一串数据然后直接送到脚本解释器解析就行。几个关键点函数如下:

/*
 *	利用死循环在做得延时函数,为了方面
 */
static int delay_ms(lua_State *L){
	int num;
	int count = lua_tointeger(L,1);			//	读取脚本中送入的参数,1表示第一个参数
	for(int i = 0 ; i < count ; i++){
		num = 20000;
		while(num--);
	}
	return 1;
}
/*
 *	Led控制函数
 */
static int Lua_LED(lua_State *L){
	bool flag = lua_toboolean(L,1);					//	读取传入函数的参数
	if(flag)GPIO_ResetBits(GPIOF, GPIO_Pin_6);		//	LED灯亮
	else GPIO_SetBits(GPIOF, GPIO_Pin_6);			//	LED灯灭
	return 1;
}
/*
 *	自定义函数注册结构体数组
 */
static const struct luaL_Reg mylib[] ={
	{"delay_ms",delay_ms},
	{"Lua_LED",Lua_LED},
	{NULL,NULL}
};
lua_State *L;					//	解析器指针
static int do_file_script(void){
	L = luaL_newstate(); 		//	创建解析器
	luaopen_base(L);			//	注册基础函数
	luaL_setfuncs(L, mylib, 0);	//	注册自定义函数
	return 0;
}
/*
 *	主函数
 */
int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	usart1_initialization();	//	串口初始
	led_initialization();		//	LED的初始化
	printf("\r\n\r\n");
	do_file_script();			
	while(1){
		if(Usart1_Receive(150000)){				//	读取串口数据
			luaL_dostring(L, (char*)usartBuf);	//	读取到的数据直接送到解析器解析
			memset(usartBuf,0,1024);			//	清楚缓冲
		}
	}
}
//	下面为串口输的数据
/*
	count = 10 \
	while (count > 0) do \
		Lua_LED(false) \
		delay_ms(50) \
		Lua_LED(true) \
		delay_ms(50) \
		count = count - 1 \
	end
*/

最后实验结果如下:
(本以为能够上传视频,结果上传不了,只能截取个图片)
stm32应用:lua在Keil上的移植和使用_第3张图片
下面附上自己调试的源码,如果有需求朋友可以下载看看,不过我不知道在哪里设置分数,本来想设置成1分的就是没找到设置的位置。
https://download.csdn.net/download/weixin_41558887/11826346

你可能感兴趣的:(STM32,Lua)