来了!STM32移植LuatOS,潘多拉示例全新教程
Posted 合宙Luat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了来了!STM32移植LuatOS,潘多拉示例全新教程相关的知识,希望对你有一定的参考价值。
进击的五月,继上期《》教程之后,@打盹的消防车 又为大家带来基于STM32的潘多拉LuatOS移植全新教程:
为什么使用潘多拉作为教程呢?
STM32不能没有通讯,那就选IoT开发板——潘多拉显然没什么短板,很适合入门使用。当然,其他STM32也可以参照本教程来做。
文中同样涉及一些其他平台的移植思路,所以想移植LuatOS都可以看一看。
本教程主体分为:LuatOS架构分析+LuatOS移植思路+潘多拉移植示例三个部分。
1
LuatOS架构分析
移植之前首先看一下LuatOS的总体构架:
LuatOS架构图
可以看到,LuatOS做了一套适配层去对接平台,所以移植只需要做适配层就可以了(别跑,看着很多,其实移植不用做很多,许多已经做了)。
接下来我们看一下LuatOS目录:
LuatOS文件目录
bsp:
bsp文件里存放着各种已经适配了的芯片。目前有:
- Air001
- Air100ST(STM32F4)
- Air302(NB-IoT)
- Air640W(Wi-Fi)
- Air724UG(4G Cat.1)
- Win32
只有这些么?当然不是。W800(Wi-Fi+bt)本人也在做,目前做了基础外设和LVGL;,外设做完大部分,相信不久也会和大家见面;还有一些其他的,也已经计划适配了。
components:一些中间层,本次移植不需要。
docs:一些说明
lua:Lua虚拟机,重要
luat:luat层,重要
mind:思维导图
script:脚本,本次移植不需要。
tools:工具
可以看到,我们主要做的就是移植lua、luat两个文件夹,其中lua层为Lua虚拟机与平台无关,几乎不用改什么,通常放进去可以直接编译。
我们主要看luat:
- luat/cmsis_os2
# cmsis_os2库移植对接层,如果库支持可以直接对接
- luat/freertos
# freertos库移植对接层,如果使用freertos可以直接对接
- luat/rtt
# RT-Thread库移植对接层,如果使用RT-Thread可以直接对接
- luat/include # 头文件
- luat/module # lua库实现,几乎无需改动
- luat/packages/lua-cjson
# 平台无关的json库(自由选择软件包)
考。
2
LuatOS移植思路
介绍Luatos构架之后,我们说一下移植思路。需要移植的核心功能有:
- lua虚拟机
- msgbus(消息队列)
- timer(定时)
- uart(打印)
- fs(文件系统)
- 外设
lua虚拟机我们直接把lua文件夹放进去编译即可;
msgbus(消息队列)、timer(定时)如果使用FreeRTOS、RT-Thread或者cmsis_os2,可以直接使用现成的,无需移植(可能不同平台需要微调);
uart(打印)和fs(文件系统)以及外设,我们需要针对自己的芯片进行对接。
Msgbus(消息队列)移植
首先看msgbus(消息队列),我们要实现luat_msgbus.h中的函数:
// 定义接口方法
void luat_msgbus_init(void);
uint32_t luat_msgbus_put(rtos_msg_t* msg, size_t timeout);
uint32_t luat_msgbus_get(rtos_msg_t* msg, size_t timeout);
uint32_t luat_msgbus_freesize(void);
可以看到我们只需要实现四个函数就可以:
luat_msgbus_init(消息队列初始化)
luat_msgbus_put(消息队发送)
luat_msgbus_get(消息获取)
luat_msgbus_freesize(消息队列剩余空闲位置)
这里我们以FreeRTOS为例:
▼上下滚动,查看全部▼
手机浏览建议横屏查看
void
luat_msgbus_init(
void
)
{
if (!xQueue) {
#if configSUPPORT_STATIC_ALLOCATION xQueue =
xQueueCreateStatic( QUEUE_LENGTH,
ITEM_SIZE,ucQueueStorageArea,&xStaticQueue );
#else
xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);
#endif
}
}
uint32_t
luat_msgbus_put
(
rtos_msg_t*
msg,
size_t
timeout)
{
if (xQueue == NULL)
return
1
;
return xQueueSendFromISR(xQueue, msg, NULL) == pdTRUE ?
0 : 1
;
}
uint32_t
luat_msgbus_get
(
rtos_msg_t*
msg,
size_t
timeout)
{
if (xQueue == NULL)
return
1
;
return xQueueReceive(xQueue, msg, timeout) == pdTRUE ?
0 : 1
;
}
uint32_t
luat_msgbus_freesize
(
void
)
{
if (xQueue == NULL)
return
1
;
return
1
;
}
可以看到,我们做的就是将LuatOS的消息队列对接到RTOS。
Timer(定时器)移植
接下来移植timer(定时器),我们要实现luat_timer.h中的函数:
int luat_timer_start(luat_timer_t* timer);
int luat_timer_stop(luat_timer_t* timer);
luat_timer_t* luat_timer_get(size_t timer_id);
int luat_timer_mdelay(size_t ms);
也很简单,我们只有需要实现:
luat_timer_start(定时器开启)
luat_timer_stop(定时器停止)
luat_timer_get(定时器获取)
luat_timer_mdelay(延迟)
同样我们以FreeRTOS为例:
▼上下滚动,查看全部▼
手机浏览建议横屏查看
int
luat_timer_start
(luat_timer_t*
timer)
{
TimerHandle_t os_timer;
int
timerIndex;
timerIndex = nextTimerSlot();
if
(timerIndex < 0
)
{
return 1;
// too many timer!!
}
os_timer = xTimerCreate("luat_timer", timer->timeout / portTICK_RATE_MS, timer->repeat, timer, luat_timer_callback);
if
(!os_timer)
{
return -1
;
}
timers[timerIndex] = timer;
timer->os_timer = os_timer;
int
re = xTimerStart(os_timer, 0
);
if
(re != pdPASS)
{
xTimerDelete(os_timer, 0
);
timers[timerIndex] = 0
;
}
return re == pdPASS ? 0 : -1
;
}
int
luat_timer_stop
(luat_timer_t*
timer)
{
if
(!timer)
return 1
;
for
(size_t
i = 0; i < FREERTOS_TIMER_COUNT; i++) {
if
(timers[i] == timer)
{
timers[i] = NULL
;
break;
}
}
xTimerStop((TimerHandle_t)timer->os_timer, 10
);
xTimerDelete((TimerHandle_t)timer->os_timer, 10
);
return 0
;
};
luat_timer_t
* luat_timer_get
(size_t
timer_id)
{
for
(size_t
i = 0
; i < FREERTOS_TIMER_COUNT; i++){ if
(timers[i] && timers[i]->id == timer_id)
{
return
timers[i];
}
}
return
NULL
;
}
int
luat_timer_mdelay
(size_t
ms)
{
if
(ms > 0
)
{
vTaskDelay(ms / portTICK_RATE_MS);
}
return 0
;
}
可以看到,和消息队列一样,只要将LuatOS的定时函数对接RTOS的定时函数就OK啦,很简单是不是。
Uart(打印)移植
接下来uart(打印),我们需要实现luat_uart.h,针对使用的板子实现以下几个串口基本的函数即可:
int l_uart_handler(lua_State *L, void* ptr);
int luat_uart_setup(luat_uart_t* uart);
int luat_uart_write(int uartid, void* data, size_t length);
int luat_uart_read(int uartid, void* buffer, size_t length);
int luat_uart_close(int uartid);
int luat_uart_exist(int uartid);
int luat_setup_cb(int uartid, int received, int sent);
文件系统移植
剩下一个文件系统,如果我们的板子支持posix风格,那么恭喜,可以直接对接,否则我们需要实现luat_fs.h。
▼上下滚动,查看全部▼
手机浏览建议横屏查看
int luat_fs_init(void);
int luat_fs_mkfs(luat_fs_conf_t *conf);
int luat_fs_mount(luat_fs_conf_t *conf);
int luat_fs_umount(luat_fs_conf_t *conf);
int luat_fs_info(const char* path, luat_fs_info_t *conf);
FILE* luat_fs_fopen(const char *filename, const char *mode);
int luat_fs_getc(FILE* stream);
int luat_fs_fseek(FILE* stream, long int offset, int origin);
int luat_fs_ftell(FILE* stream);
int luat_fs_fclose(FILE* stream);
int luat_fs_feof(FILE* stream);
int luat_fs_ferror(FILE *stream);
size_t luat_fs_fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t luat_fs_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int luat_fs_remove(const char *filename);
int luat_fs_rename(const char *old_filename, const char *new_filename);
size_t luat_fs_fsize(const char *filename);
int luat_fs_fexist(const char *filename);
int luat_fs_mkdir(char const* _DirName);
int luat_fs_rmdir(char const* _DirName);
#ifdef LUAT_USE_FS_VFS
struct
luat_vfs_file_opts
{
FILE* (*fopen)(void* fsdata, const char *filename, const char *mode); i
nt (*getc)(void* fsdata, FILE* stream);
int (*fseek)(void* fsdata, FILE* stream, long int offset, int origin);
int (*ftell)(void* fsdata, FILE* stream);
int (*fclose)(void* fsdata, FILE* stream);
int (*feof)(void* fsdata, FILE* stream);
int (*ferror)(void* fsdata, FILE *stream);
size_t (*fread)(void* fsdata, void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t (*fwrite)(void* fsdata, const void *ptr, size_t size, size_t nmemb, FILE *stream);
};
struct luat_vfs_filesystem_opts
{
int (*remove)(void* fsdata, const char *filename); int
(*rename)(void* fsdata, const char *old_filename, const char *new_filename);size_t (*fsize)(void* fsdata, const char *filename); int (*fexist)(void* fsdata, const char *filename);
int (*mkfs)(void* fsdata, luat_fs_conf_t *conf);
int (*mount)(void** fsdata, luat_fs_conf_t *conf);
int (*umount)(void* fsdata, luat_fs_conf_t *conf); int (*info)(void* fsdata, const char* path, luat_fs_info_t *conf);
int (*mkdir)(void* fsdata, char const* _DirName); int (*rmdir)(void* fsdata, char const* _DirName);
};
struct luat_vfs_filesystem
{
char name[16];
s
truct luat_vfs_filesystem_opts opts;
struct luat_vfs_file_opts fopts;
};
t
ypedef struct luat_vfs_mount{
struct luat_vfs_filesystem *fs;
void *userdata; char prefix[16];
i
nt ok;
}
luat_vfs_mount_t;
typedef struct luat_vfs_fd
{
FILE* fd;
l
uat_vfs_mount_t *fsMount;
}
luat_vfs_fd_t;
typedef struct luat_vfs
{
struct luat_vfs_filesystem* fsList[LUAT_VFS_FILESYSTEM_MAX];
luat_vfs_mount_t mounted[LUAT_VFS_FILESYSTEM_MOUNT_MAX];
l
uat_vfs_fd_t fds[LUAT_VFS_FILESYSTEM_FD_MAX+1];
}
luat_vfs_t;
int luat_vfs_init(void* params);
int luat_vfs_reg(const struct luat_vfs_filesystem* fs);
FILE* luat_vfs_add_fd(FILE* fd, luat_vfs_mount_t * mount);
int luat_vfs_rm_fd(FILE* fd);
#endif
这部分需要针对各自的平台实现对接,各位需要针对自己的去实现。
随后我们需要创建一个luat_base_xxx.c去管理我们移植的库以及自己的板卡信息,这里我们以Air302为例:
- Air302移植示例 -
▼上下滚动,查看全部▼
手机浏览建议横屏查看
static const luaL_Reg loadedlibs[] = {
{"_G", luaopen_base}, // _G
{LUA_LOADLIBNAME, luaopen_package}, // require
{LUA_COLIBNAME, luaopen_coroutine}, // coroutine协程库
{LUA_TABLIBNAME, luaopen_table}, // table库,操作table类型的数据结构
{LUA_IOLIBNAME, luaopen_io}, // io库,操作文件
{LUA_OSLIBNAME, luaopen_os}, // os库,已精简
{LUA_STRLIBNAME, luaopen_string}, // string库,字符串操作
{LUA_MATHLIBNAME, luaopen_math}, // math 数值计算
{LUA_DBLIBNAME, luaopen_debug}, // debug库,已精简
// 往下是LuatOS定制的库, 如需精简请仔细测试
//------------------------------------------------------------
// 核心支撑库, 不可禁用!!
{"rtos", luaopen_rtos}, // rtos底层库, 核心功能是队列和定时器
{"log", luaopen_log}, // 日志库
{"timer", luaopen_timer}, // 延时库
//-------------------------------------------------------------
// 设备驱动类, 可按实际情况删减. 即使最精简的固件, 也强烈建议保留uart库
{"uart", luaopen_uart}, // 串口操作
{"gpio", luaopen_gpio}, // GPIO脚的操作
{"i2c", luaopen_i2c}, // I2C操作
{"spi", luaopen_spi}, // SPI操作
{"adc", luaopen_adc}, // ADC模块
{"pwm", luaopen_pwm}, // PWM模块
//-------------------------------------------------------------
// 工具库, 按需选用
{"json", luaopen_cjson}, // json的序列化和反序列化
{"pack", luaopen_pack}, // pack.pack/pack.unpack
{"mqttcore",luaopen_mqttcore}, // MQTT 协议封装
{"libcoap", luaopen_libcoap}, // 处理COAP消息
{"libgnss", luaopen_libgnss}, // 处理GNSS定位数据
{"fs", luaopen_fs}, // 文件系统库,在io库之外再提供一些方法
{"sensor", luaopen_sensor}, // 传感器库,支持DS18B20
{"disp", luaopen_disp}, // OLED显示模块,支持SSD1306
{"u8g2", luaopen_u8g2}, // u8g2
{"crypto",luaopen_crypto}, // 加密和hash模块
// {"eink", luaopen_eink},
// 电子墨水屏,试验阶段
//{"iconv", luaopen_iconv},
// 编码转换,暂不可用
//----------------------------------------------------------------
// 联网及NBIOT特有的库
{"socket", luaopen_socket}, // 套接字操作
{"lpmem", luaopen_lpmem}, // 低功耗时仍工作的内存块
{"nbiot", luaopen_nbiot}, // NBIOT专属模块
{"pm", luaopen_pm}, // 低功耗模式
{"http", luaopen_http}, // http库
{"ctiot", luaopen_ctiot}, // ctiot库,中国电信ctwing平台
{NULL, NULL}
};
// 按不同的rtconfig加载不同的库函数
void luat_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 */
}
}
const char* luat_os_bsp(void) {
return "ec616";
}
我们可以将未实现的或者不想编译的注释掉,修改bsp名等,随后在我们的主程序中启用lua虚拟机。
#include "bget.h
"#include "luat_base.h"
void app_main(void)
{
luat_main();// luat_main是LuatOS的主入口, 该方法通常不会返回
}
接下来我们就是编译,根据报错修改、调试。
这样LuatOS基础移植就实现了,随后就是外设的适配。和之前一样,查看对应的.h文件,去对接需要实现的函数,可以参考已经实现的做移植。
可以看到——LuatOS移植的依赖并不多,甚至没有RTOS也可以实现移植。
3
潘多拉移植示例
移植顺序按照wendal在LuatOS上的bsp移植顺序,依次为:编译环境的集成、核心功能的适配、外设的适配以及网络接口的适配。
首先我们需要一个潘多拉的rtt工程,clone rtt的最新仓库,进去潘多拉的bsp使用scons --dist命令提取一个工程。
- lua # Lua虚拟机
- luat/module # lua库实现
放进去编译,确保编译没问题。
我们使用的RTT,这部分移植已经做好了,只需要把RTT目录放进去,首次移植编译我们只加入一些核心基础的就可以,不需要加入RTT目录中全部代码。
可以看到,核心的移植已经都做好了。编译之前需要配置一下RTT:
- RTT基础配置操作 -
▼上下滚动,查看全部▼
手机浏览建议横屏查看
• menuconfig进入开启文件系统
• 开启nor flash(我们使用了板载的nor flash)
• 修改主线程heap
• 开启libc库
• 开启ymodem为了后面下载脚本
• 外设开启QSPI FLASH驱动
• 开启timer等其他驱动(按自己实际需要)
• 软件包开启FAL
• 软件包开启littlefs
随后,将luat_rtt_base.c中未使用的库注释掉:
编译看看,会报错:
我们的bsp已经做了FAL配置,所以进入FAL软件包,把sample去掉:
然后我们初始化文件系统,新建一个luat_fs_init.c:
- 新建luat_fs_init.c文件 -
▼上下滚动,查看全部▼
手机浏览建议横屏查看
#include "luat_base.h"
#include "luat_malloc.h"
#include "luat_msgbus.h"
#include "luat_timer.h"
#include "luat_gpio.h"
#include "rtthread.h"
#include <rtdevice.h>
/* 添加 fal 头文件 */
#include <fal.h>
/* 添加文件系统头文件 */
#include <dfs_fs.h>
#define DBG_TAG "port.fs"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "drv_flash.h"
#include "lfs.h"
/* 定义要使用的分区名字 */
#define FS_PARTITION_NAME "filesystem"
static uint8_t fs_ok = 0;
extern char luadb_inline[];
int luat_fs_init(void){
if (fs_ok) return 0;
fs_ok = 1;
struct rt_device *mtd_dev= RT_NULL;
/* 初始化 fal */ fal_init(); /* 生成 mtd 设备 */ mtd_dev = fal_mtd_nor_device_create(FS_PARTITION_NAME);
if (!mtd_dev) { LOG_E("Can't create a mtd device on '%s' partition.", FS_PARTITION_NAME); }
else {
/* 挂载 littlefs */ if (dfs_mount(FS_PARTITION_NAME, "/", "lfs", 0, 0) == 0) { LOG_I("Filesystem initialized!"); }
else {
/* 格式化文件系统 */ dfs_mkfs("lfs", FS_PARTITION_NAME);
/* 挂载 littlefs */ if (dfs_mount("filesystem", "/", "lfs", 0, 0) == 0) { LOG_I("Filesystem initialized!"); }
else { LOG_E("Failed to initialize filesystem!"); } }
}
// 尝试挂载luadb区域 mkdir("/lua", 0);
return 0;} INIT_ENV_EXPORT(luat_fs_init);
- 修改main文件 -
▼上下滚动,查看全部▼
手机浏览建议横屏查看
#include
<rtthread.h>
#include
<rtdevice.h>
#include
<board.h>
#define DBG_ENABLE#define DBG_SECTION_NAME
"main"
#define DBG_LEVEL DBG_LOG
#define DBG_COLOR
#include
<rtdbg.h>
#include
"luat_base.h"
int
main(void)
{
rt_thread_mdelay(100); // 故意延后100ms
luat_log_set_uart_port(1);
luat_main();
w
hile (1){
rt_thread_delay(10000000);
}
}
此时编译测试正常,下载测试:
可以看到虚拟机正常跑起来了,因为没找到main.lua所以重启。
我们把sys.lua和main.lua,通过ymodem下载进去重启:
脚本运行成功,至此LuatOS基础移植成功。
基于RTT的大部分外设已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可。
基于RTT的网络接口也已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可。
合宙Luat交流群18(LuatOS):1061642968
合宙Luat交流群03:1092305811
合宙Luat交流群04:877164555
合宙Luat交流群17(iRTU):1027923658
每个建议都值得关注
每个技能都值得分享
- 更多精彩等你参与 -
了解更多相关内容
以上是关于来了!STM32移植LuatOS,潘多拉示例全新教程的主要内容,如果未能解决你的问题,请参考以下文章
干货 | LuatOS BSP移植教程,简单到复制粘贴!!!
BSP视频教程STM32H7视频教程第11期:STM32H7的GPIO实战,深化非阻塞编程思想,移植驱动到全新器件上,开启Event Recorder狂暴模式