首先是吧Lua源码解压会得到一个src文件夹,把src下的源码添加到STM32的工程里,设置好包含路径,注意lua.c 和 luac.c 这两个文件是不包含的(它们包含PC上Lua解释器和编译器的main函数)。然后我写了一个简单的函数来实现解释器:
/* 测试的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"
"print(\"exp(200) =\", math.exp(200))\n"
};
/* 运行Lua */
void lua_main(void)
{
lua_State *L;
L = luaL_newstate(); /* 建立Lua运行环境 */
luaL_openlibs(L);
luaopen_base(L);
luaL_dostring(L, lua_test); /* 运行Lua脚本 */
lua_close(L);
}
我还没有做文件方面的功能,直接把Lua脚本保存到字符串lua_test[]里。Lua脚本的运行就靠lua_main()函数来完成。由于单片机的IO系统不同于PC,我们需要重新实现print函数的底层,Lua的print函数的C语言实现部分是lbaselib.c文件的luaB_print()函数,我把底层的输出打印到了一个字符串LuaBuffer[](比较简单嘛),代码如下:
char LuaBuffer[500] = ""; /* 输出缓存数组 */
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
char *Str = LuaBuffer + strlen(LuaBuffer); /* LuaBuffer直接接着上次的打印 */
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
size_t l;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tolstring(L, -1, &l); /* get result */
if (s == NULL)
return luaL_error(L, "'tostring' must return a string to 'print'");
if (i>1) { /* 这个是连续输出元素之间的空格,我用的1个空格 */
sprintf(Str, " ");
Str += 1; /* 一个空格 */
}
/* 我自己定义的输出(输出到字符串) */
sprintf(Str, "%s", s);
Str += l; /* 跳过字符串的长度 */
lua_pop(L, 1); /* pop result */
}
sprintf(Str, "\n"); /* 输出一个换行符 */
return 0;
}
此外我还修改了Lua内存管理的底层函数(在lauxlib.c中):
/* 修改了free()和realloc()函数 */
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud; (void)osize; /* not used */
if (nsize == 0) {
myfree(SDRAMEX, ptr); /* 修改 */
return NULL;
}
else
return myrealloc(SDRAMEX, ptr, nsize); /* 修改 */
}
myfree()和myrealloc()函数是从正点原子的动态内存分配程序里来的,l_alloc()函数也可以不改,在使用MicroLib的时候是可以使用free()和realloc()函数的,这时就得在启动文件里把堆(Heap_Size)设置的足够大,我试过0x00008000(32KB)是没问题的,其实要不了这么多。建议把栈设置的大一点(1KB足够)。
到这里应该就可以编译了,下载到板子里会发现不能运行,程序根本没有进main()函数,联想起以前的一次经历知道这是因为Lua源码里调用了一些C标准库的输入输出(例如printf())函数导致的——STM32平台是不提供这些函数的,一开始我尝试修改源码,把system(),fwrite(),free()等函数统统改掉,无奈半天过后还是不能运行,然后我终于缴械,把MicroLib的勾打上了,如下图:
编译之后报错说time(), exit(), system()这三个标准库函数没有定义,这个好说,直接写三个不就完了。。。
/* 定义MicroLib没有的函数 */
time_t time(time_t * time)
{
return 0;
}
void exit(int status)
{
}
int system(const char * string)
{
return 0;
}
好,现在终于可以运行了,拍张照片,顺便演示一下我的GUI:
这段Lua源码也贴出来:
print("Hello,I am lua!\n--this is newline printf")
function foo()
local i = 0
local sum = 1
while i <= 10 do
sum = sum * 2
i = i + 1
end
return sum
end
print("sum =", foo())
print("and sum = 2^11 =", 2 ^ 11)
print("exp(200) =", math.exp(200))
照片上的运行结果跟PC上是一致的。
最后说下题外话,其实移植脚本语言解释器并非我一时兴起,很久前就想做了,只是移植没找到合适的语言,以前用TI的nspire计算器听说过lua语言,lua虽然没去研究,却也听说过了lua的效率,后来我也做了些东西了,C语言有了点积累,造轮子的冲动慢慢在心里萌芽,去年暑假我开始写一个GUI,不过后来似乎有在抄uC/GUI之嫌了,这两天无意间想起lua,于是我就花了点时间移植到了STM32,移植成功那一刻的心情当然是非常非常激动的。接下来我要接着完善我的GUI,Lua暂时就不折腾了,等到一切做妥当我可能会用STM32做一个可编程的设备(就像编程计算器),这因该会相当好玩。现在记下移植过程也方便以后用。第一次写博客比较啰嗦,还请看官见谅哈。
卧槽,第一次写博客保存时还断网。。。人品真是爆了!