0


LUA移植到STM32F4,移植REPL,通过RTT Viewer交互

概述

站内移植LUA多数是使用C函数调用LUA,并没有移植REPL交互端口
本文将REPL也移植进去,做了简单的适配

LUA源码使用标准C库函数,如fgets,fwrite等,在嵌入式环境中要使用fgets,fwrite等C库函数,需要做的工作就是重定向。

本文重定向了STDIN和STDOUT数据流到J-Link RTT Viewer,可以通过 RTT Viewer向LUA虚拟机进行交互。
在这里插入图片描述

环境

MCU:STM32F407, 192KB RAM, 1MFLASH。建议运行平台至少有256KBRAM,256KB的FLASH,否则加载lib的时候会爆内存或者FLASH。
KEIL:527
编译器:AC6

准备工程

https://www.lua.org/download.html网站下载LUA源码,在KEIL中新建一个LUA文件夹,将所有文件添加到里面。luac.c不要添加进去,这个文件是用来编译lua脚本的,我们不需要。

准备SEGGER RTT打印相关文件

新建一个空文件syscall.c,后面的C库系统调用函数我们会写入到此文件中。

处理好所有文件的头文件,包含路径问题。
在这里插入图片描述

对接C库系统调用函数

以下函数的编写参考了如下资源

  • <rt_sys.h>文件定义了函数头文件,里面还有函数功能和返回值的描述
  • Arm® C and C++ Libraries and Floating-Point Support User Guide:一些函数描述

主要实现了

  • 关闭半主机模式
  • _sys_open:打开文件,返回STDIN,STDOUT,STDERR的文件描述符,,普通文件流不处理
  • _sys_write:写文件,向STDOUT写入的数据流流向SEGGER RTT,普通文件流不处理
  • _sys_read:读文件,SEGGER RTT读取的数据流向STDIN,普通文件流不处理、
  • _sys_istty:判断文件描述符是否为终端
  • time:事件相关的函数,对接的是hal_gettick,返回系统上电运行了多少ms
#include<rt_sys.h>#include<stdio.h>#include<string.h>#include<time.h>#include"SEGGER_RTT.h"#include"main.h"//关闭半主机模式/********************************************************************************/#ifdefined(__clang__)__asm(".global __use_no_semihosting\n\t");#elifdefined(__CC_ARM)#pragmaimport(__use_no_semihosting)#endif#defineSTDOUT0x00000001#defineSTDIN0x00000002#defineSTDERR0x00000003constchar __stdin_name[]="STDIN";constchar __stdout_name[]="STDOUT";constchar __stderr_name[]="STDERR";

FILEHANDLE _sys_open(constchar*pcFile,int openmode){if(0==strncmp(pcFile, __stdin_name,strlen(__stdin_name)))return STDIN;if(0==strncmp(pcFile, __stdout_name,strlen(__stdout_name)))return STDOUT;if(0==strncmp(pcFile, __stderr_name,strlen(__stderr_name)))return STDERR;//pcFile    :文件路径//openmode  :文件打开模式//返回值    :文件描述符return0;}int_sys_close(FILEHANDLE fh){return0;}int_sys_write(FILEHANDLE fh,constunsignedchar* buf,unsigned len,int mode){if(fh == STDOUT){SEGGER_RTT_Write(0,(constchar*)buf, len);return0;}return0;}int_sys_read(FILEHANDLE fh,unsignedchar* buf,unsigned len,int mode){//读取一行数据,回车结束。读取完毕之后在字符串末尾添加结束符staticint count_p =0;if(fh == STDIN){
    count_p =0;
    buf[count_p]=SEGGER_RTT_WaitKey();while(buf[count_p]!='\n'){
      count_p++;
      buf[count_p]=SEGGER_RTT_WaitKey();}
    buf[count_p +1]='\0';return0;}return0;//EOF}void_ttywrch(int ch){fputc(ch,stdout);// stdoutfflush(stdout);}int_sys_istty(FILEHANDLE fh){return(fh==STDIN || fh==STDOUT || fh==STDERR);}int_sys_seek(FILEHANDLE fh,long pos){return0;}int_sys_ensure(FILEHANDLE fh){return0;}long_sys_flen(FILEHANDLE fh){return0;}int_sys_tmpnam(char* name,int sig,unsigned maxlen){return0;}void_sys_exit(int returncode)/* never returns */{}char*_sys_command_string(char* cmd,int len){return0;}intremove(constchar*filename){return0;}intsystem(constchar*string){return0;}intrename(constchar*old,constchar*new){return0;}time_ttime(time_t*timer){returnHAL_GetTick();}clock_tclock(void){return0;}

修改LUA源码

LUA源码中操作行数据使用fgets和puts,这个函数我的对接始终有问题,这里更改为fread和fwrite函数
在luaconf.h末尾添加如下代码

/* =================================================================== *//*
** Local configuration. You can use this space to add your redefinitions
** without modifying the main part of the file.
*/#defineLUA_MAXINPUT128#definelua_readline(L,b,p)(fread(b,1, LUA_MAXINPUT,stdin)!=0)#definelua_initreadline(L)((void)L              )#definelua_saveline(L,line){(void)L;(void)line;}#definelua_freeline(L,b){(void)L;(void)b;}#definelua_writestring(s,l)fwrite(s,1, l,stdout)#definelua_writeline()fwrite("\n",1,1,stdout)#definelua_writestringerror(...)printf(__VA_ARGS__)

lua.c中已经有一个main函数,我们需要将这个main函数改名为lua_main,在keil中的main函数调用lua_main来启动LUA

intlua_main(int argc,char**argv){//修改函数名int status, result;
  lua_State *L =luaL_newstate();/* create state */if(L ==NULL){l_message(argv[0],"cannot create state: not enough memory");return EXIT_FAILURE;}lua_gc(L, LUA_GCSTOP);/* stop GC while building state */lua_pushcfunction(L,&pmain);/* to call 'pmain' in protected mode */lua_pushinteger(L, argc);/* 1st argument */lua_pushlightuserdata(L, argv);/* 2nd argument */
  status =lua_pcall(L,2,1,0);/* do the call */
  result =lua_toboolean(L,-1);/* get result */report(L, status);lua_close(L);return(result && status == LUA_OK)? EXIT_SUCCESS : EXIT_FAILURE;}

lua .h中增加lua_main的函数声明

intlua_main(int argc,char**argv);

启动LUA虚拟机

main函数中,增加如下代码
这里我们要给lua_main 传递两个假参数,如下

int   fake_argc =1;char*fake_argv =NULL;lua_main(fake_argc,&fake_argv);

启动

启动前要先配置好RTT VIEWER,复位启动即可.
测试指令如下

< _VERSION
  <print("hello world")<print("abc".."666")<print("system run "..os.time().." msec")

在这里插入图片描述
错误指令和提示如下

<print("system run "..XXX.time().." msec")

在这里插入图片描述

自建C库

流程

自建C语言库,实现延时和LED控制

在linit.c中,lua定义了顶层lib库, 并且在后面的函数中加载了所有lib。
库中第一个参数是库名称,第二个参数是对应的库打开函数。

staticconst 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}};

我们打开一个函数看一下,可以看到它使用luaL_newlib创建了子lib pk_funcs, pk_funcs中定义了lua中的函数名称和函数声明。

LUAMOD_API intluaopen_package(lua_State *L){createclibstable(L);luaL_newlib(L, pk_funcs);/* create 'package' table */createsearcherstable(L);
  ···
staticconst luaL_Reg pk_funcs[]={{"loadlib", ll_loadlib},{"searchpath", ll_searchpath},/* placeholders */{"preload",NULL},{"cpath",NULL},{"path",NULL},{"searchers",NULL},{"loaded",NULL},{NULL,NULL}};

我们需要做的就是先在顶层lib中增加我们自己的lib名称”HAL“,增加自己的lib打开函数luaopen_hal,之后再创建一个子lib HAL_lib,在子lib中定义函数名称和声明即可。
在这里插入图片描述

//增加自定义顶层libexternintluaopen_hal(lua_State *L);/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
*/staticconst 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},{"HAL", luaopen_hal},{NULL,NULL}};
//增加自己的lib打开函数luaopen_hal,之后再创建一个子lib HAL_lib,在子lib中定义函数名称和声明staticintLUA_HAL_GPIO_Write(lua_State *L){int r =(luaL_checkinteger(L,1)==0);HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, r ? GPIO_PIN_SET:GPIO_PIN_RESET);return0;}staticintLUA_HAL_delay(lua_State *L){HAL_Delay(luaL_checkinteger(L,1));return0;}staticconststructluaL_Reg HAL_lib[]={{"delay",     LUA_HAL_delay},{"led",       LUA_HAL_GPIO_Write},{NULL,NULL}};intluaopen_hal(lua_State *L){luaL_newlib(L, HAL_lib);return1;}

测试

 HAL
 HAL.delay
 HAL.led
 while(1)do  HAL.delay(1000) HAL.led(1) HAL.delay(1000) HAL.led(0)end

在这里插入图片描述
最后一个测试中,LED应该开始闪烁

TODO&其他

工程参考:https://gitee.com/nwwhhh/stm32f407
TODO:对接文件函数,调用本地文件

在这里插入图片描述

标签: lua stm32 交互

本文转载自: https://blog.csdn.net/qq_42039294/article/details/139806522
版权归原作者 菠萝地亚狂想曲 所有, 如有侵权,请联系我们删除。

“LUA移植到STM32F4,移植REPL,通过RTT Viewer交互”的评论:

还没有评论