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"//关闭半主机模式
/********************************************************************************/
#if defined(__clang__)__asm(".global __use_no_semihosting\n\t");
#elif defined(__CC_ARM)#pragma import(__use_no_semihosting)
#endif#define STDOUT 0x00000001
#define STDIN 0x00000002
#define STDERR 0x00000003 const char __stdin_name[] = "STDIN";
const char __stdout_name[] = "STDOUT";
const char __stderr_name[] = "STDERR";FILEHANDLE _sys_open(const char *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 :文件打开模式//返回值 :文件描述符return 0;
}int _sys_close(FILEHANDLE fh)
{return 0;
}int _sys_write(FILEHANDLE fh, const unsigned char * buf, unsigned len, int mode)
{if (fh == STDOUT){SEGGER_RTT_Write(0, (const char*)buf, len); return 0;}return 0;
}int _sys_read(FILEHANDLE fh, unsigned char * buf, unsigned len, int mode)
{
//读取一行数据,回车结束。读取完毕之后在字符串末尾添加结束符static int 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';return 0;}return 0; //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)
{return 0;
}int _sys_ensure(FILEHANDLE fh)
{return 0;
}long _sys_flen(FILEHANDLE fh)
{return 0;
}int _sys_tmpnam(char * name, int sig, unsigned maxlen)
{return 0;
}void _sys_exit(int returncode) /* never returns */
{
}char *_sys_command_string(char * cmd, int len)
{return 0;
}
int remove(const char *filename)
{return 0;
}
int system(const char *string)
{return 0;
}
int rename(const char *old, const char *new)
{return 0;
}
time_t time(time_t *timer)
{return HAL_GetTick();
}
clock_t clock(void)
{return 0;
}
修改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.
*/
#define LUA_MAXINPUT 128
#define lua_readline(L,b,p) ( fread(b, 1, LUA_MAXINPUT, stdin) != 0)
#define lua_initreadline(L) ( (void)L )
#define lua_saveline(L,line) { (void)L; (void)line; }
#define lua_freeline(L,b) { (void)L; (void)b; }#define lua_writestring(s,l) fwrite(s, 1, l, stdout)
#define lua_writeline() fwrite("\n", 1, 1, stdout)
#define lua_writestringerror(...) printf(__VA_ARGS__)
lua.c中已经有一个main函数,我们需要将这个main函数改名为lua_main,在keil中的main函数调用lua_main来启动LUA
int lua_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的函数声明
int lua_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")

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

相关文章:
LUA移植到STM32F4,移植REPL,通过RTT Viewer交互
概述 站内移植LUA多数是使用C函数调用LUA,并没有移植REPL交互端口 本文将REPL也移植进去,做了简单的适配 LUA源码使用标准C库函数,如fgets,fwrite等,在嵌入式环境中要使用fgets,fwrite等C库函数ÿ…...
【GD32F303红枫派使用手册】第十九节
19.1 实验内容 通过本实验主要学习以下内容: SPI简介 GD32F303 SPI简介 SPI NOR FLASH——GD25Q32ESIGR简介 使用GD32F303 SPI接口实现对GD25Q32ESIGR的读写操作 19.2 实验原理 19.2.1 SPI简介 SPI(Serial Peripheral interface)&…...
【C语言】扫雷游戏
Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 💥💥个人主页:奋斗的小羊 💥💥所属专栏:C语言 🚀本系列文章为个人学习…...
逻辑蕴含、函数依赖集的闭包、Armstrong公理、属性集闭包
一、引言 Armstrong公理-从给定的函数依赖集得到关系模式的完整依赖集 二、逻辑蕴含 1、定义 设F是关系模式R上的函数依赖集,X、Y是R的属性子集,对于R的每个满足F的关系实例r,若函数 依赖都成立,则称F逻辑蕴含。 记为&#…...
macOS聚集搜索功能开启与关闭
按下command空格弹出 使用搜索 关闭搜索 sudo mdutil -a -i off 启用搜索 sudo mdutil -a -i on...
大模型“诸神之战”,落地才是赛点
ChatGPT 诞生已经快一年,你还在与它对话吗? 有的人用来写报告、改代码,让它成为得力帮手;有的人却只是“调戏”个两三回,让它创作诗歌或故事,便不再“宠幸”。 根据网站分析工具 SimilarWeb 的数据&#…...
接口重放攻击
如何保证接口安全,做到防篡改防重放?_接口防止串改-CSDN博客 接口安全设计之防篡改和防重放_接口防篡改机制-CSDN博客 基于nonce timestamp 的方案 nonce的意思是仅一次有效的随机字符串,要求每次请求时该参数要保证不同。实际使用用户信息…...
MySQL学习笔记-进阶篇-SQL优化
SQL优化 插入数据 insert优化 1)批量插入 insert into tb_user values(1,Tom),(2,Cat),(3,Jerry); 2)手动提交事务 mysql 默认是自动提交事务,这样会导致频繁的开启和提交事务,影响性能 start transaction insert into tb_us…...
【机器学习】第2章 线性回归及最大熵模型
一、概念 1.回归就是用一条曲线对数据点进行拟合,该曲线称为最佳拟合曲线,这个拟合过程称为回归。 2.一个自变量 叫 一元线性回归,大于一个自变量 叫 多元线性回归。 (1)多元回归:两个x,一个…...
科技创新对农业发展的影响
科技创新对农业发展的影响深远且广泛,主要体现在以下几个方面: 一、提高农业生产效率 引入先进的农业机械设备:新型农业机械设备如无人机、机器人等的应用,显著减轻了农民的劳动强度,提高了农作物的种植效率。利用精…...
Linux 常用命令 - rm 【删除文件或目录】
简介 rm 命令源于英文单词 “remove”,它用于删除文件或目录。rm 是 Linux 系统中最基本的命令之一,用于删除指定的文件或目录。默认情况下,rm 不会删除目录,但可以通过递归删除选项来实现。 使用方式 rm [选项]... [文件]...常…...
一血c++
题目描述 每一个竞赛选手都无法拒绝拿一血 "一血"其实就是同学们在榜单上看到的深绿色的标记,代表着某道题目,他是第一个通过的。 叶苡朋老师是一名资深信奥选手,在大学多次获奖,也是一个资深抢一血爱好者࿰…...
无问芯穹Qllm-Eval:制作多模型、多参数、多维度的量化方案
前言 近年来,大语言模型(Large Models, LLMs)受到学术界和工业界的广泛关注,得益于其在各种语言生成任务上的出色表现,大语言模型推动了各种人工智能应用(例如ChatGPT、Copilot等)的发展。然而…...
2024-05-31T08:36:09.000+00:00 转换 YYYY-MM-DD HH-MM-SS
function formatDate(date) {// 处理ISO 8601字符串if (typeof date string) {date new Date(date);}// 处理时间戳else if (typeof date number) {date new Date(date * 1000); // 假设后端时间戳为秒,需要乘以1000转换为毫秒}// 自定义格式化,例如…...
reason: the Java file contained parse errors
今天用Maven打包项目时发生一个错误: file: D:\workspace\echoo2.0-xxx-xxx-portal\src\main\java\com\echoo\service\impl\DecDataServiceImpl.java; reason: the Java file contained parse errors 打包报错显示这个类解析错误 在IDEA中没有任何错误提示 问题所…...
使用密钥对登录服务器
目录 1、使用密钥文件登录服务器 2、登录成功画面: 3、如若出现以下状况,则说明密钥文件登录失败 1、使用密钥文件登录服务器 首先需要上传pem文件 2、登录成功画面: 3、如若出现以下状况,则说明密钥文件登录失败 解决方法&…...
面试_多线程
线程池 线程池的参数有哪些 线程池七大参数分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler corePoolSize:线程池中常驻核心线程数maximumPoolSize:线程池能够容纳同时执行的最大线程数keepAliveTime&…...
跨境电商必备?揭秘原生IP的作用
一、什么是原生IP? 原生IP(Native IP)是指由互联网服务提供商(ISP)或服务器提供商直接分配给用户的IP地址,这种IP地址直接与用户设备或网络相连,也就是指这个IP的注册地址和服务器机房所在的国…...
mysql竖表变横表不含聚合
文章目录 前言一、vertical_table二、转换1.要将其转换为横表形式,例如:2.sql 总结 前言 在MySQL中将竖表转换为横表(也称为行转列操作),不涉及聚合函数,通常可以通过使用条件判断和自连接来实现。假设有一…...
application/x-www-form-urlencoded和json的区别
application/x-www-form-urlencoded 和 application/json 是两种不同的数据格式,常用于HTTP请求中传递数据。 它们各自的特点和使用场景如下: 1. application/x-www-form-urlencoded •特点:这是一种传统的表单提交时采用的编码类型&#x…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
