单片机移植Lua(STM32H743移植Lua-5.4.6)

文章目录

目的

通常单片机都是使用C/C++来开发的,任何修改都需要重新编译固件然后下载运行。在一些需要灵活性更强的场合中可以内嵌Lua解释器实现动态更新应用程序的功能。这篇文章将对相关内容做个简单说明。

移植演示

Lua本身就是纯C实现的,不管是移植到上位机程序还是单片机程序中本质上没有多大区别,单就移植以及Lua脚本和C代码交互等参考我之前的文章即可:
《Lua和C语言交互入门》https://blog.csdn.net/Naisu_kun/article/details/134000058

这里进行简单演示,使用NUCLEO-H743ZI2开发板,启用默认使能的串口,用来打印输出信息:

修改最小堆栈大小,生成初始化项目代码。

然后参考上面文章,直接引入Lua源码(排除 lua.cluac.c),接着改写 main.c 代码如下:

c 复制代码
#include "main.h"

#include <stdio.h> // 引入该库为了可以使用 printf 函数

#include "src/lua.h"     // Lua数据类型与函数接口
#include "src/lauxlib.h" // Lua与C交互辅助函数接口
#include "src/lualib.h"  // Lua标准库打开接口

UART_HandleTypeDef huart3;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART3_UART_Init(void);

/* 以下代码用来实现putchar操作,以此可实现printf功能 */
/* With GCC, small printf (option LD Linker->Libraries->Small printf set to 'Yes') calls __io_putchar() */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) // 实现__io_putchar函数
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE
{
	// 实际发送操作,这里可以改成自己真实需要操作的端口
    HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}
/* 以上代码用来实现putchar操作,以此可实现printf功能 */

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART3_UART_Init();

  lua_State* L = luaL_newstate(); // 创建Lua线程
  luaL_openlibs(L); // 打开标准库

  while (1)
  {
	 HAL_Delay(1000);
	luaL_dostring(L, "print('Naisu, Lua!')"); // 解析并执行Lua脚本字符串
  }

  lua_close(L); // 关闭Lua线程(虽然代码并不会运行到这里)
}

void SystemClock_Config(void)
{
  // some code ...
}

static void MX_USART3_UART_Init(void)
{
  // some code ...
}

static void MX_GPIO_Init(void)
{
  // some code ...
}

void Error_Handler(void)
{
  // some code ...
}

上面代码主要就两方面内容:

  • 实现了 putchar 操作,这样在 Lua 中就可以使用 print 函数了;
  • 创建Lua虚拟机初始化标准库,然后循环执行Lua脚本;

将上述代码编译后下载到开发板可以看到相关输出:

示例链接

仓库地址: https://github.com/NaisuXu/STM32_MCU_Examples

本文中的示例位于仓库中 Lua_H743

更多说明

从上面演示可以看到,移植本身很简单,不过相比在上位机上移植,在单片机中移植还有些需要注意的地方。

合理设置内存大小

单片机相比上位机一个比较大的问题是内存比较小,所以需要合理设计内存使用。

单片机本身设置合适的堆栈空间:

Lua源码 luaconf.h 文件中 #define LUAI_MAXSTACK 宏定义用于设置Lua使用的最大栈大小:

c 复制代码
/*
@@ LUAI_MAXSTACK limits the size of the Lua stack.
** CHANGE it if you need a different limit. This limit is arbitrary;
** its only purpose is to stop Lua from consuming unlimited stack
** space (and to reserve some numbers for pseudo-indices).
** (It must fit into max(size_t)/32 and max(int)/2.)
*/
#if LUAI_IS32INT
#define LUAI_MAXSTACK		1000000
#else
#define LUAI_MAXSTACK		15000
#endif

按需加载标准库

上面演示中使用 luaL_openlibs(L); 方式加载了所有的Lua标准库,实际使用中完全可以按需加载。

在Lua源码 linit.c 中注释掉不需要的库即可:

c 复制代码
/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
*/
static const luaL_Reg loadedlibs[] = {
  {LUA_GNAME, 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},
  {NULL, NULL}
};

实现系统和IO接口

Lua中很多操作都是调用了操作系统相关的接口,这些接口单片机是不带的,如果需要用到相关功能的话要不就去实现相关接口(比如演示中的print函数就需要实现相关接口才可以使用),要不就直接改写Lua标准库相关函数。

特别的,在创建Lua虚拟机时会需要动态申请内存,这个内存申请函数可以自己指定,也可以使用默认的。默认会使用 lauxlib.cl_alloc 函数,该函数中调用了系统的 freerealloc 函数。如果对内存操作有更加细致的需求,那就需要注意处理这些方面。

设置引用路径

如果单片机上跑了文件系统,将Lua脚本以文件的形式放到文件系统中,那么在执行这些文件的时候可能需要合理设置Lua相关路径。比如在Lua源码 luaconf.h 文件中 LUA_PATH_DEFAULT 宏定义就可以设置默认的路径。

总结

总体来说在单片机上移植Lua并不复杂,更多的是对内存的限制以及没有操作系统自带的各种接口问题上花时间去调试处理。

相关推荐
PegasusYu2 小时前
STM32CUBEIDE FreeRTOS操作教程(九):eventgroup事件标志组
stm32·教程·rtos·stm32cubeide·free-rtos·eventgroup·时间标志组
lantiandianzi6 小时前
基于单片机的多功能跑步机控制系统
单片机·嵌入式硬件
文弱书生6566 小时前
输出比较简介
stm32
哔哥哔特商务网6 小时前
高集成的MCU方案已成电机应用趋势?
单片机·嵌入式硬件
跟着杰哥学嵌入式6 小时前
单片机进阶硬件部分_day2_项目实践
单片机·嵌入式硬件
东芝、铠侠总代136100683938 小时前
浅谈TLP184小型平面光耦
单片机·嵌入式硬件·物联网·平面
lantiandianzi8 小时前
基于单片机中医药柜管理系统的设计
单片机·嵌入式硬件
嵌入式知识大讲堂8 小时前
HDMI数据传输三种使用场景
单片机
黑客呀9 小时前
[系统安全]Rootkit基础
stm32·单片机·系统安全
小A1599 小时前
STM32完全学习——使用SysTick精确延时(阻塞式)
stm32·嵌入式硬件·学习