我用的KEIL 5.17,开发板是STM32F429I Discovery,刚好之前写了一个GUI库,就直接拿这个做显示了。移植的过程没有想象中的顺利,开始我按网上说的不使用MicroLib,编译是过了,但是单片机上根本不运行(实际上是缺少一些C标准库函数),折腾了大概一天才把这事解决。废话不多说,就直接上移植方法吧。
首先是吧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;
- }
这段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))
最后说下题外话,其实移植脚本语言解释器并非我一时兴起,很久前就想做了,只是移植没找到合适的语言,以前用TI的nspire计算器听说过lua语言,lua虽然没去研究,却也听说过了lua的效率,后来我也做了些东西了,C语言有了点积累,造轮子的冲动慢慢在心里萌芽,去年暑假我开始写一个GUI,不过后来似乎有在抄uC/GUI之嫌了,这两天无意间想起lua,于是我就花了点时间移植到了STM32,移植成功那一刻的心情当然是非常非常激动的。接下来我要接着完善我的GUI,Lua暂时就不折腾了,等到一切做妥当我可能会用STM32做一个可编程的设备(就像编程计算器),这因该会相当好玩。现在记下移植过程也方便以后用。第一次写博客比较啰嗦,还请看官见谅哈。
卧槽,第一次写博客保存时还断网。。。人品真是爆了!