将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函数)。然后我写了一个简单的函数来实现解释器:

/* 测试的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的勾打上了,如下图:将Lua解释器移植到STM32_第1张图片

编译之后报错说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解释器移植到STM32_第2张图片将Lua解释器移植到STM32_第3张图片

这段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做一个可编程的设备(就像编程计算器),这因该会相当好玩。现在记下移植过程也方便以后用。第一次写博客比较啰嗦,还请看官见谅哈。

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


你可能感兴趣的:(c语言,移植,stm32,单片机,lua)