risc-v中文社区

 找回密码
 立即注册
查看: 1414|回复: 0

LUA移植至STM32全记录

[复制链接]

347

主题

564

帖子

2237

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2237
发表于 2022-6-20 15:01:39 | 显示全部楼层 |阅读模式
之前使用nodemcu时萌发了一直Lua至STM32的想法。后来项目需要,便花了2天移植。
    Lua移植大约需要67K的rom,所以STM32F103C8无法使用,至少得103CB才行。
1. 官网下载源文件
http://www.lua.org/download.html
我下载的 是当前最新版本--5.3.5。
2. 解压,源代码位于src文件夹,去掉lua.c 和 luac.c 。
3. lauxlib.c:把l_alloc 函数调用的 free 和 realloc替换为自定义的内存管理函数。也可以不修改,但后面heap size需要改大。
4. linit.c:可以利用数组loadedlibs,把一些不要的功能去掉(注释),比如luaopen_os,luaopen_io,luaopen_debug。也可以添加自己的功能库(搜索:在LUA库中添加自己的函数库)。
5. luaconf.h:看文件开头的说明,使能 #define LUA_32BITS ,文件中有文件操作的目录定义LUA_ROOT,LUA_PATH_DEFAULT等,可以不管。
6. 修改单片机启动文件里的堆栈大小。 stack size设为4K; heap size暂时不修改。
7. 在需要使用Lua的的文件里加入头文件

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
编译,如出现未定义函数,自己添加定义,如time();如果出现如stdio_XXX错误之类,应该是串口映射相关问题。这时,选择使用microLib。并加入以下代码。
//USART1为映射到串口1.可以自己修改其他串口

int fputc(int ch, FILE *f)
{
    USART_SendData(USART1, (uint8_t) ch);

    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}   

    return ch;
}

int GetKey (void)  {

    while (!(USART1->SR & USART_FLAG_RXNE));

    return ((int)(USART1->DR & 0x1FF));
}
//end
尝试不使用microLib,结果各种问题,所以还是使用microLib。 8.开始使用:

lua_State *L = NULL;

L =luaL_newstate();  //创建Lua状态机   
if(L == NULL)
{
    printf("LUA ERR!");
    while(1){};//死循环
}
printf("LUA INIT OK!");
luaL_openlibs(L);//注册各种库函数
//end
其中luaL_openlibs()是逐个调用linit.c中数组loadedlibs记录的库。
linit.c文件如下:

/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
*/
static const luaL_Reg loadedlibs[] =
{
  {"_G", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_COLIBNAME, luaopen_coroutine},
  {LUA_TABLIBNAME, luaopen_table},
  //{LUA_IOLIBNAME, luaopen_io},
  //{LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_UTF8LIBNAME, luaopen_utf8},
  {LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
  {LUA_BITLIBNAME, luaopen_bit32},
#endif
  {NULL, NULL}
};

LUALIB_API void luaL_openlibs (lua_State *L)
{
  const luaL_Reg *lib;
  /* "require" functions from 'loadedlibs' and set results to global table */
  for (lib = loadedlibs; lib->func; lib++)
  {
    luaL_requiref(L, lib->name, lib->func, 1);
    lua_pop(L, 1);  /* remove lib */
  }
}

9.运行lua脚本
/*文件形式加载运行*/
luaL_dofile(L,"文件路径");
如果移植的时候没有将使用文件IO的库加入,也可以使用自己的方式,读取脚本数据,然后写入数组buf中,然后:
/*字符变量形式*/
luaL_dostring(L,buf);
/*关闭lua*/
lua_close(L);

这时已经成功把Lua解析器移植进STM32,可以运行Lua脚本了。不过单纯的Lua解析器在项目中没什么意义。

下面讲讲Lua与C函数的交互使用。一般给项目做功能扩展,都是Lua调用C函数。譬如想通过Lua脚本去控制单片机的某个外设,可将专门编写相关控制函数,并注册到Lua,需要的时候便可以在脚本中调用。


10.lua脚本调用自定义C函数
每个版本的使用方法好像都有些不一样。我使用的是当前最新的版本(5.3.5)
可以使用函数(其实是宏)lua_register(L,name,func)注册自定义的C函数,在lua在调用name(参数列表);func为实际的C函数。
并且,func函数形式必须类似为

static int func1(lua_State *L)
{
    //luaL_checknumber检测参数是否为数字
    float  val=sin(luaL_checknumber(L, 1));
    //压入结果
    lua_pushnumber(L,val );

    return 1; //结果的个数,这里为1
}
//如无需返回值,则更加简单
static int func2(lua_State *L)
{
    //luaL_checkstring检测参数是否为字符串并返回地址
      printf(luaL_checkstring(L,1));
      LCD_ShowString(30,60+20,200,16,16,"LUA:show");

    return 1; //结果的个数,这里为1
}
这种逐个函数注册的方式,虽然方便,但是比较乱。可以仿照库的方式,实现注册。
(1).新建一个C文件,以库名命名文件,这是必须的。
(2).添加需要自定义的功能C函数。
(3).定义一个luaL_Reg结构数组,把上面的C函数列入,并以字符串的方式命名,比如下面:

static const luaL_Reg mylib[] =
{
    {"my_cos",   mymath_cos},
    {"my_sin",   mymath_sin},
    {NULL, NULL}
};
(4).添加open函数luaopen_XXX,XXX    为库名。

LUALIB_API int luaopen_mylib (lua_State *L)
{
  luaL_newlib(L, mylib);
  return 1;
}
(5).这时,有两种选择。可以按以下注册函数

luaL_requiref(L,"mylib",luaopen_mylib,1);
lua_pop(L,1);
也可以在linit.c的loadedlibs数组中添加{"mylib",luaopen_mylib}。记得要保持数组后面的{NULL, NULL};如编译不过,可在lualib.h中添加函数声明。
至此,完成移植与基本使用。
————————————————
版权声明:本文为CSDN博主「林子xxx」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangzibigan/article/details/87945779

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



Archiver|手机版|小黑屋|risc-v中文社区

GMT+8, 2024-4-26 22:29 , Processed in 0.015415 second(s), 17 queries .

risc-v中文社区论坛 官方网站

Copyright © 2018-2021, risc-v open source

快速回复 返回顶部 返回列表