来了!STM32移植LuatOS,潘多拉示例全新教程

Posted 合宙Luat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了来了!STM32移植LuatOS,潘多拉示例全新教程相关的知识,希望对你有一定的参考价值。

进击的五月,继上期教程之后,@打盹的消防车 又为大家带来基于STM32的潘多拉LuatOS移植全新教程:

为什么使用潘多拉作为教程呢?

STM32不能没有通讯,那就选IoT开发板——潘多拉显然没什么短板,很适合入门使用。当然,其他STM32也可以参照本教程来做。

文中同样涉及一些其他平台的移植思路,所以想移植LuatOS都可以看一看。

本教程主体分为:LuatOS架构分析+LuatOS移植思路+潘多拉移植示例三个部分。





1


LuatOS架构分析





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

LuatOS架构图





来了!STM32移植LuatOS,潘多拉示例全新教程


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

接下来我们看一下LuatOS目录:

LuatOS文件目录





来了!STM32移植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]; 

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为例:

- 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


潘多拉移植示例





来了!STM32移植LuatOS,潘多拉示例全新教程

移植顺序按照wendal在LuatOS上的bsp移植顺序,依次为:编译环境的集成、核心功能的适配、外设的适配以及网络接口的适配。

01
  编译环境的集成
  LuatOS移植



首先我们需要一个潘多拉的rtt工程,clone rtt的最新仓库,进去潘多拉的bsp使用scons --dist命令提取一个工程。

- lua                    # Lua虚拟机    

- luat/module       # lua库实现



放进去编译,确保编译没问题。

来了!STM32移植LuatOS,潘多拉示例全新教程

02
  核心功能的适配
  LuatOS移植



我们使用的RTT,这部分移植已经做好了,只需要把RTT目录放进去,首次移植编译我们只加入一些核心基础的就可以,不需要加入RTT目录中全部代码。

来了!STM32移植LuatOS,潘多拉示例全新教程

可以看到,核心的移植已经都做好了。编译之前需要配置一下RTT:

- RTT基础配置操作 -


▼上下滚动,查看全部▼

手机浏览建议横屏查看


• menuconfig进入开启文件系统

来了!STM32移植LuatOS,潘多拉示例全新教程

• 开启nor flash(我们使用了板载的nor flash)

来了!STM32移植LuatOS,潘多拉示例全新教程

• 修改主线程heap

来了!STM32移植LuatOS,潘多拉示例全新教程

• 开启libc库

来了!STM32移植LuatOS,潘多拉示例全新教程

• 开启ymodem为了后面下载脚本

来了!STM32移植LuatOS,潘多拉示例全新教程

• 外设开启QSPI FLASH驱动

来了!STM32移植LuatOS,潘多拉示例全新教程

• 开启timer等其他驱动(按自己实际需要)

来了!STM32移植LuatOS,潘多拉示例全新教程

• 软件包开启FAL

来了!STM32移植LuatOS,潘多拉示例全新教程

• 软件包开启littlefs

来了!STM32移植LuatOS,潘多拉示例全新教程




随后,将luat_rtt_base.c中未使用的库注释掉:

来了!STM32移植LuatOS,潘多拉示例全新教程

编译看看,会报错:

来了!STM32移植LuatOS,潘多拉示例全新教程

我们的bsp已经做了FAL配置,所以进入FAL软件包,把sample去掉:

来了!STM32移植LuatOS,潘多拉示例全新教程

然后我们初始化文件系统,新建一个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();    

   while (1)

   {
       rt_thread_delay(10000000);
   }
}

 
               

此时编译测试正常,下载测试:

来了!STM32移植LuatOS,潘多拉示例全新教程

可以看到虚拟机正常跑起来了,因为没找到main.lua所以重启。

我们把sys.lua和main.lua,通过ymodem下载进去重启:

来了!STM32移植LuatOS,潘多拉示例全新教程脚本运行成功,至此LuatOS基础移植成功。

03
  外设的适配
  LuatOS移植



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

04
  网络接口的适配
  LuatOS移植

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


本期移植教程就讲到这里了,文末【阅读原文】获取全部源码。大家可以多尝试不同板子和平台,欢迎加入技术群共同探讨交流。

来了!STM32移植LuatOS,潘多拉示例全新教程

来了!STM32移植LuatOS,潘多拉示例全新教程

合宙Luat交流群18(LuatOS):1061642968

合宙Luat交流群03:1092305811

合宙Luat交流群04:877164555  

合宙Luat交流群17(iRTU):1027923658


每个建议都值得关注

每个技能都值得分享

来了!STM32移植LuatOS,潘多拉示例全新教程

- 更多精彩等你参与 -

了解更多相关内容






来了!STM32移植LuatOS,潘多拉示例全新教程


以上是关于来了!STM32移植LuatOS,潘多拉示例全新教程的主要内容,如果未能解决你的问题,请参考以下文章

必看!LuatOS自定义C库全新教程,一文极速上手

LuatOS | 全新在线模拟器,随时随地发挥创意

干货 | LuatOS BSP移植教程,简单到复制粘贴!!!

BSP视频教程STM32H7视频教程第11期:STM32H7的GPIO实战,深化非阻塞编程思想,移植驱动到全新器件上,开启Event Recorder狂暴模式

STM32CubeMX开发01——全新的编程框架

STM32CubeMX开发01——全新的编程框架