luatos手把手移植教程

本文详细介绍了如何将Luatos操作系统移植到STM32微控制器上,使用RT-Thread实时操作系统作为基础。教程涵盖了移植流程、架构分析、消息队列、定时器、UART、文件系统等核心功能的适配,并提供了具体的代码示例。通过潘多拉开发板的移植实例,展示了从编译环境集成到外设驱动的全过程。
摘要由CSDN通过智能技术生成

前言

四月时候就说准备出一个教程,但是很抱歉手里一直有事加上状态不好拖到现在。为什么使用潘多拉作为教程呢?因为想做个STM32的但是不能没有通讯呀,那就选iot开发版,潘多拉显然没什么短板,很适合入门使用(当然其他stm32也可以按照本教程做,我也会说一些其他平台的移植思路,所以想移植luatos都可以看一看)。移植思路我们以freertos为例,stm32我们以RT-Thread为例进行移植,尽量多说一些。

架构分析

移植之前首先看一下luatos的总体构架:

0

可以看到,luatos做了一套适配层去对接平台,所以移植只需要做适配层就可以了(别跑,看着很多,其实移植不用做很多,许多已经做了)

接下来我们看一下luatos目录:
0

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_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);

我们需要针对我们的板子实现这几个串口基本的函数就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库实现

放进去编译
image.png
确保编译没问题

核心功能的适配

我们使用的RTT,这部分移植已经做好了,只需要把RTT目录放进去,首次移植编译我们只加入一些核心基础的就可以,不需要加入rtt目录中全部代码
image.png
可以看到,核心的移植已经都做好了。
编译之前需要配置一下RTT
menuconfig进入开启文件系统
image.png
开启nor flash(我们使用了板载的nor flash)
image.png
修改主线程heap
image.png
开启libc库
image.png
开启ymodem为了后面下载脚本
image.png
外设开启QSPI FLASH驱动
image.png
开启timer等其他驱动(按自己实际需要)
image.png
软件包开启FAL
image.png
软件包开启littlefs
image.png
随后将luat_rtt_base.c中未使用的库注释掉
image.png
编译看看,会报错
image.png
我们的bsp已经做了FAL配置,所以进入FAL软件包,把sample去掉
image.png
然后我们初始化文件系统,新建一个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();
    while (1)
    {
        rt_thread_delay(10000000);
    }
}

此时编译测试正常,下载测试
image.png
可以看到虚拟机正常跑起来了,因为没找到main.lua所以重启
我们把sys.lua和main.lua通过ymodem下载进去重启
image.png
脚本运行成功,至此luatos移植成功

外设的适配

基于RTT的大部分外设已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可

网络接口的适配

基于RTT的网络接口也已经适配了,直接添加我们之前删除的RTT目录下的文件编译测试即可

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ISE 14.7是Xilinx公司的一款FPGA设计软件,这里是ISE 14.7手把手使用教程: 1. 下载并安装ISE 14.7软件 首先需要在Xilinx公司的官网上下载ISE 14.7软件,并按照提示进行安装。安装完成后,可以在安装目录下找到ISE软件。 2. 创建一个新工程 打开ISE软件后,选择“File”菜单中的“New Project”选项,弹出“New Project Wizard”向导,按照提示进行设置,包括工程名称、项目目录、FPGA型号等。 3. 添加源文件 在新建工程后,需要添加设计文件。在ISE软件中,设计文件包括VHDL文件、Verilog文件、UCF文件等。可以使用ISE自带的编辑器进行编写,或者使用其他编辑器编写后再添加到ISE中。添加文件后,需要进行约束(Constraints)设置,包括时钟频率、时序等。 4. 进行综合、实现和下载 完成设计文件和约束设置后,需要进行综合和实现。综合是将设计文件转换成逻辑门电路,实现是将逻辑门电路映射到FPGA器件中。完成综合和实现后,可以生成比特流文件(Bitstream),用于下载到FPGA器件中。下载可以使用Xilinx公司提供的下载工具或者第三方下载工具。 5. 调试和优化 在下载完成后,需要进行调试和优化。可以使用ISE提供的仿真工具进行仿真,或者使用外部的仿真工具进行仿真。调试和优化包括时序约束调整、时钟域划分、资源占用优化等。 以上就是ISE 14.7手把手使用教程的基本步骤,希望能够帮助到你。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值