将Lua解释器移植到STM32

这是我第一次写博客,当然在这之前已经学了会儿单片机和C语言,这两天突然有一个新想法:把Lua移植到STM32上去,网上搜了一下,好像非常简单,所以我就立马开始尝试。到 Lua官网 下载了 Lua 5.3.2 的代码,先是用GCC编译了一个Lua解释器试试手,熟悉下Lua语法,然后又在VS2015下建了工程(个人比较喜欢用VS的界面。。。),看看Lua源码的大概组织编排,折腾了一会儿后我就琢磨着移植到STM32了。 
       我用的KEIL 5.17,开发板是STM32F429I Discovery,刚好之前写了一个GUI库,就直接拿这个做显示了。移植的过程没有想象中的顺利,开始我按网上说的不使用MicroLib,编译是过了,但是单片机上根本不运行(实际上是缺少一些C标准库函数),折腾了大概一天才把这事解决。废话不多说,就直接上移植方法吧。 

       首先是吧Lua源码解压会得到一个src文件夹,把src下的源码添加到STM32的工程里,设置好包含路径,注意lua.c 和 luac.c 这两个文件是不包含的(它们包含PC上Lua解释器和编译器的main函数)。然后我写了一个简单的函数来实现解释器:

[cpp]  view plain  copy
  1. /* 测试的Lua代码字符串 */  
  2. const char lua_test[] = {   
  3.     "print(\"Hello,I am lua!\\n--this is newline printf\")\n"  
  4.     "function foo()\n"  
  5.     "  local i = 0\n"  
  6.     "  local sum = 1\n"  
  7.     "  while i <= 10 do\n"  
  8.     "    sum = sum * 2\n"  
  9.     "    i = i + 1\n"  
  10.     "  end\n"  
  11.     "return sum\n"  
  12.     "end\n"  
  13.     "print(\"sum =\", foo())\n"  
  14.     "print(\"and sum = 2^11 =\", 2 ^ 11)\n"  
  15.     "print(\"exp(200) =\", math.exp(200))\n"  
  16. };  
  17.   
  18. /* 运行Lua */  
  19. void lua_main(void)  
  20. {  
  21.     lua_State *L;  
  22.       
  23.     L = luaL_newstate(); /* 建立Lua运行环境 */  
  24.     luaL_openlibs(L);  
  25.     luaopen_base(L);  
  26.     luaL_dostring(L, lua_test); /* 运行Lua脚本 */  
  27.     lua_close(L);  
  28. }  

我还没有做文件方面的功能,直接把Lua脚本保存到字符串lua_test[]里。Lua脚本的运行就靠lua_main()函数来完成。由于单片机的IO系统不同于PC,我们需要重新实现print函数的底层,Lua的print函数的C语言实现部分是lbaselib.c文件的luaB_print()函数,我把底层的输出打印到了一个字符串LuaBuffer[](比较简单嘛大笑),代码如下:

[cpp]  view plain  copy
  1. char LuaBuffer[500] = ""/* 输出缓存数组 */  
  2. static int luaB_print (lua_State *L) {  
  3.   int n = lua_gettop(L);  /* number of arguments */  
  4.   int i;  
  5.   char *Str = LuaBuffer + strlen(LuaBuffer); /* LuaBuffer直接接着上次的打印 */  
  6.   lua_getglobal(L, "tostring");  
  7.   for (i=1; i<=n; i++) {  
  8.     const char *s;  
  9.     size_t l;  
  10.     lua_pushvalue(L, -1);  /* function to be called */  
  11.     lua_pushvalue(L, i);   /* value to print */  
  12.     lua_call(L, 1, 1);  
  13.     s = lua_tolstring(L, -1, &l);  /* get result */  
  14.     if (s == NULL)  
  15.       return luaL_error(L, "'tostring' must return a string to 'print'");  
  16.     if (i>1) {    /* 这个是连续输出元素之间的空格,我用的1个空格 */  
  17.       sprintf(Str, " ");  
  18.         Str += 1; /* 一个空格 */  
  19.     }  
  20.     /* 我自己定义的输出(输出到字符串) */  
  21.     sprintf(Str, "%s", s);  
  22.     Str += l; /* 跳过字符串的长度 */  
  23.     lua_pop(L, 1);  /* pop result */  
  24.   }  
  25.   sprintf(Str, "\n"); /* 输出一个换行符 */  
  26.   return 0;  
  27. }  

此外我还修改了Lua内存管理的底层函数(在lauxlib.c中):

[cpp]  view plain  copy
  1. /* 修改了free()和realloc()函数 */  
  2. static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {  
  3.   (void)ud; (void)osize;  /* not used */  
  4.   if (nsize == 0) {  
  5.     myfree(SDRAMEX, ptr); /* 修改 */  
  6.     return NULL;  
  7.   }  
  8.   else  
  9.     return myrealloc(SDRAMEX, ptr, nsize); /* 修改 */  
  10. }  

myfree()和myrealloc()函数是从正点原子的动态内存分配程序里来的,l_alloc()函数也可以不改,在使用MicroLib的时候是可以使用free()和realloc()函数的,这时就得在启动文件里把堆(Heap_Size)设置的足够大,我试过0x00008000(32KB)是没问题的,其实要不了这么多。建议把栈设置的大一点(1KB足够)。

到这里应该可以编译了,下载到板子里会发现不能运行,程序根本没有进main()函数,联想起以前的一次经历知道这是因为Lua源码里调用了一些C标准库的输入输出(例如printf())函数导致的——STM32平台是不提供这些函数的,一开始我尝试修改源码,把system(),fwrite(),free()等函数统统改掉,无奈半天过后还是不能运行,然后我终于缴械,把MicroLib的勾打上了,如下图:

编译之后报错说time(), exit(), system()这三个标准库函数没有定义,这个好说,直接写三个不就完了。。。

[cpp]  view plain  copy
  1. /* 定义MicroLib没有的函数 */  
  2. time_t time(time_t * time)  
  3. {  
  4.     return 0;  
  5. }  
  6.   
  7. void exit(int status)  
  8. {  
  9.       
  10. }  
  11.   
  12. int system(const char * string)  
  13. {  
  14.     return 0;  
  15. }  
好,现在终于可以运行了,拍张照片,顺便演示一下我的GUI:


这段Lua源码也贴出来:

[plain]  view plain  copy
  1. print("Hello,I am lua!\n--this is newline printf")  
  2. function foo()  
  3.   local i = 0  
  4.   local sum = 1  
  5.   while i <= 10 do  
  6.     sum = sum * 2  
  7.     i = i + 1  
  8.   end  
  9. return sum  
  10. end  
  11. print("sum =", foo())  
  12. print("and sum = 2^11 =", 2 ^ 11)  
  13. print("exp(200) =", math.exp(200))  
照片上的运行结果跟PC上是一致的。

最后说下题外话,其实移植脚本语言解释器并非我一时兴起,很久前就想做了,只是移植没找到合适的语言,以前用TI的nspire计算器听说过lua语言,lua虽然没去研究,却也听说过了lua的效率,后来我也做了些东西了,C语言有了点积累,造轮子的冲动慢慢在心里萌芽,去年暑假我开始写一个GUI,不过后来似乎有在抄uC/GUI之嫌了尴尬,这两天无意间想起lua,于是我就花了点时间移植到了STM32,移植成功那一刻的心情当然是非常非常激动的。接下来我要接着完善我的GUI,Lua暂时就不折腾了,等到一切做妥当我可能会用STM32做一个可编程的设备(就像编程计算器),这因该会相当好玩。现在记下移植过程也方便以后用。第一次写博客比较啰嗦,还请看官见谅哈微笑

卧槽,第一次写博客保存时还断网。。。人品真是爆了抓狂

猜你喜欢

转载自blog.csdn.net/taohaiwu/article/details/79384490