luatos手把手移植教程
目录名称
- 前言
- 架构分析
- 移植思路
- msgbus(消息队列)移植
- timer(定时器)移植
- uart(打印)移植
- 文件系统移植
- 潘多拉移植示例
- 编译环境的集成
- 核心功能的适配
- 外设的适配
- 网络接口的适配
前言
四月时候就说准备出一个教程,但是很抱歉手里一直有事加上状态不好拖到现在。为什么使用潘多拉作为教程呢?因为想做个STM32的但是不能没有通讯呀,那就选iot开发版,潘多拉显然没什么短板,很适合入门使用(当然其他stm32也可以按照本教程做,我也会说一些其他平台的移植思路,所以想移植luatos都可以看一看)。移植思路我们以freertos为例,stm32我们以RT-Thread为例进行移植,尽量多说一些。
架构分析
移植之前首先看一下luatos的总体构架:
可以看到,luatos做了一套适配层去对接平台,所以移植只需要做适配层就可以了(别跑,看着很多,其实移植不用做很多,许多已经做了)
接下来我们看一下luatos目录:
bsp:bsp里存放着各种已经适配了的芯片,目前有air001,air100st(stm32f4),air302(nb),air640w(wifi),air724(cat1),win32,只有这些么?当然不是,W800(WiFi+bt)本人也在做,目前做了基础外设和lvgl,esp32梦程在做,外设做完大部分,相信不久也会和大家见面,还有一些其他的也已经计划适配了
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库(自由选择软件包)
移植思路
介绍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_ALLOCATIONxQueue = xQueueCreateStatic( QUEUE_LENGTH,ITEM_SIZE,ucQueueStorageArea,&xStaticQueue );#elsexQueue = 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);
我们需要针对我们的板子实现这几个串口基本的函数就OK拉
文件系统移植
剩下一个文件系统,如果我们的板子支持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);int (*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];struct luat_vfs_filesystem_opts opts;struct luat_vfs_file_opts fopts;
};
typedef struct luat_vfs_mount {struct luat_vfs_filesystem *fs;void *userdata;char prefix[16];int ok;
} luat_vfs_mount_t;
typedef struct luat_vfs_fd{FILE* fd;luat_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];luat_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为例
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)
{bpool(ptr, size); // lua vm需要一块内存用于内部分配, 给出首地址及大小.luat_main();// luat_main是LuatOS的主入口, 该方法通常不会返回.
}
接下来我们就是编译,根据报错修改,调试
这样luatos基础移植就实现了,随后就是外设的适配,和之前一样,查看对应的.h文件,去对接需要实现的函数,可以参考已经实现的做移植。
可以看到,luatos移植的依赖并不多,甚至没有RTOS也可以实现移植
潘多拉移植示例
移植顺序按照wendal在luatos上的bsp移植顺序:
编译环境的集成
首先我们需要一个潘多拉的rtt工程,clone rtt的最新仓库,进去潘多拉的bsp使用scons --dist命令提取一个工程
将
- lua # Lua虚拟机- luat/module # lua库实现
放进去编译
确保编译没问题
核心功能的适配
我们使用的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
#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); // 故意延后100msluat_log_set_uart_port(1);luat_main();while (1){rt_thread_delay(10000000);}
}
此时编译测试正常,下载测试
可以看到虚拟机正常跑起来了,因为没找到main.lua所以重启
我们把sys.lua和main.lua通过ymodem下载进去重启
脚本运行成功,至此luatos移植成功
外设的适配
基于RTT的大部分外设已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可
网络接口的适配
基于RTT的网络接口也已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可
luatos手把手移植教程相关推荐
- phpcmsV9视频模块插件 - 手把手开发教程
phpcmsV9官方的视频模块,一点也不完整不系统. 官方给出的文档说明.文件下载.等等都太缺了. 这里给出个人开发过程后,总结出的经验精华,请及时汲取. 1. 准备工作 安装完整地 wampserv ...
- 嵌入式数据库Sqlite移植教程-转
嵌入式数据库Sqlite移植教程 sqlite-3.3.6编译安装与交叉编译全过程详细记录 本文介绍的内容都是基于 Linux RedHat 9.0 平台的. 一.PC机编译安装 请阅读在安装包里的 ...
- 基于STM32F4的CANOpen移植教程(超级详细)
CANopen移植到STM32F4平台 前言 1 物品准备 2 相关软件安装 2.1 CAN上位机 2.2 对象字典生成工具objdictedit环境配置 3 将CANopen移植到STM32F407 ...
- 安卓android+rom定制,移植,安卓Android ROM定制移植教程.doc
安卓Android ROM定制移植教程 CyanogenMOD移植教程 转自秋叶随风Ivan.系统选择和安装建议使用 ubuntu LTS 10.04 X64 新手朋友推荐Deepin Linux 基 ...
- 安卓android+rom定制,移植,安卓Android ROM定制移植教程。.doc
安卓Android ROM定制移植教程. CyanogenMOD移植教程 转自秋叶随风Ivan.系统选择和安装建议使用 ubuntu LTS 10.04 X64 新手朋友推荐Deepin Linux ...
- rtl8723bu linux wifi驱动移植教程
rtl8723bu linux 驱动移植教程 将文件 rtl8723BU_WiFi_linux_v4.3.9.3_13200.20150106_BTCOEX20140929-5443.tar.gz 拷 ...
- 百度云ROM移植教程
这是基于MTK平头的 ,应该高通的移植起来差不多 百度云ROM移植教程(基于MTK6577) 百度云已经有了MTK6577平台的ROM A789,基于A789,我们可以将百度云ROM移植到其他的MT ...
- Python for Android最简单详细的,最手把手的教程 之第一节安装
Python for Android最简单详细的,最手把手的教程 之第一节安装 本文转自:http://bbs.python520.com/thread-179-1-1.html Python for ...
- php 108张牌洗牌,完美洗牌原理大揭秘 手把手图文教程!呕心沥血的超级干货...
原标题:完美洗牌原理大揭秘 & 手把手图文教程!呕心沥血的超级干货 先感慨一下,昨晚写到半夜,今天又修改了很多遍,我终于把这篇文章写完了. 然后自夸一下,我敢保证,你从来没有看过像这样的完美洗 ...
最新文章
- 汇编语言中寻址方式[bx + idata]
- sql server 表索引碎片处理
- 2015年要学习和巩固的知识点
- SAP Cloud for Customer创建Account也会生成对应的Business partner实例
- H264基本概念之 宏块、片和片组
- ActiveMQ反序列化漏洞(CVE-2015-5254)复现
- 某社交 App 涉黄被下架,真相令人窒息
- 寻找Linux单机负载瓶颈
- terminal登录mysql_转载-MySQL之终端(Terminal)管理MySQL
- python网页前端和react有什么区别_Vue 和 React 的优点分别是什么?
- selenium元素等待及滚动条滚动
- 数据赋能变现时代,应用有哪些有效的变现方式?
- python爬虫的国内外现状_Python3.5多线程爬虫越爬越慢的解决方法
- linux网络管理员认证考试,红帽认证系统管理员 (RHCSA) 考试
- 微信聊天记录删除了怎么恢复
- 《信任的速度》读后感
- 开心网注册详解和邀请码发放
- 树莓派4b 3.5inch显示屏+远程+FTP+建站
- Armbian搭建本地Gitea服务器
- Django 2.1.7 项目技巧 - 创建apps应用目录归纳所有应用