eLua_elua - CSDN
精华内容
参与话题
  • 前言   嵌入式开发人员一般情况下都是从C语言开始入手的,然而C语言的学习难度较大,入门门槛也比较高。很多人因为自身C语言的瓶颈导致很难做出来复杂的产品。有的人也仅仅是因为兴趣爱好只是想diy做些小玩意玩玩...

    前言

      嵌入式开发人员一般情况下都是从C语言开始入手的,然而C语言的学习难度较大,入门门槛也比较高。很多人因为自身C语言的瓶颈导致很难做出来复杂的产品。有的人也仅仅是因为兴趣爱好只是想diy做些小玩意玩玩。如果仅仅是因为个人兴趣想去diy玩玩,而去花费大量的时间去学习C语言显然是不合算的,得不偿失。我见过很多人自身本来不从事嵌入式开发的相关工作,但是他们却用Arduino玩diy玩的风生水起,丝毫不比一些市面上所谓的商用产品差,总不能因为别人不从事相关的工作而去抹杀他们的兴趣爱好吧。我们合宙通信深知这个痛点,于是我们花费了大量的时间研发了LUAT开发,以牺牲一部分性能为代价,为用户带来更简单的开发模式,这不仅仅针对个人爱好者,面对企业客户我们的LUAT开发所能提供的功能也是绰绰有余,还能能够大大减少开发时间。

      正所谓鱼和熊掌不可兼得,由于我们的LUAT开发是在嵌入式平台上运行一个LUAT解释器,执行效率上肯定是比不上直接用C语言开发应用的。在社区一直都有人问我们的CAT1有没有CSDK的开发方式。了解到这个情况后我们加班加点赶在五月中旬之前就开源了第一版CSDK的开发包。提供给极客们使用。客户要的我们都能满足!

      那么现在我们就拥有了两种开发方式了。想要开发简单高效,我们提供了LUAT模式,在社区我们还提供了大量的文档教程、视频教程,让用户在极短的时间内就能掌握我们的模块的开发,大大降低了用户的学习成本。那么对于极客们如果想要榨干CPU性能的话那我们也提供了CSDK的开发方式,让你也能在某些地方比别人更快一点。

      正常来讲的话我们的LUAT开发和CSDK开发,已经能满足绝大部分的使用需求。你以为就到此为止了吗,不!当然不仅仅这样。我们合宙通信既然选择了做开源,那么就要把开源进行到底,开源到连裤衩都不剩。做别人不敢做的,做别人不会去做的,做行业的先锋!

      “面对友商的步步紧逼,我们的小伙伴们加班开了好几场会议,研究打法之后”,最后决定将elua解析器代码开源出来。在这里我们不仅仅是开源代码,我们还要为elua解析器配上详细的开源文档,让用户可以自己去选择裁剪哪些功能模块,添加哪些功能模块。让用户自己决定需要在底层执行那些模块,那些放到上层执行。

    目录

    第一章、Lua解析器软件架构

    1.1、什么是lua

      Lua是一个小巧的脚本语言。它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo三人所组成的研究小组于1993年开发的。 其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的JIT项目,提供在特定平台上的即时编译功能。

    1.2、我们为什么要选择lua

      Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,这使得Lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,代替XML,ini等文件格式,并且更容易理解和维护。 Lua由标准C编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。一个完整的Lua解释器不过200k,在所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择。

      就是因为lua解释器的代码足够小,在所有的脚本引擎中是运行最快的,我们才选择lua作为切入点,绝不是因为头难一热乱选了一个。

    1.3、lua解析器架构

      Lua语言的架构图如下图所示,大家可以看看软件架构,主要是通过全局状态机完成各个模块的调用,这里不做深入讲解,有兴趣的可以自行阅读lua源码

    在这里插入图片描述

    1.4、嵌入式平台的lua实现

      我们的elua开源项目是基于Lua5.1之上进行开发的,将其进行了相应的基础库功能裁剪,并且添加了一些用于硬件驱动的库,使其在具备必要的硬件驱动的基础上占用的资源更少,更加适合运行在嵌入式平台上。

    第二章、elua开源模块

      在第一章中我们学习了下lua是什么、为什么要选择lua、lua解析器架构以及elua的设计思路。这些基本上都是一些概念和设计理念,可以说是作为本文档的一个绪论。带着大家走进elua的大门。

      这里只讲概念显然是没有什么太大的用处的,那么本章我们就来点实际的东西,一起看一下elua的代码。

      我们的elua开源模块主要由五大部分组成,他们分别是:newlib、lua、modules、lib、platfom。他们对应的功能如下表所示。

    newlib lua modules lib platfom
    c库代码 解析器代码 功能模块 功能库代码 平台适配代码
    • c库代码:主要是一些c标准库
    • 解析器代码:lua内核
    • 功能库代码:提供一些软件算法
    • 功能模块:硬件驱动模块,和抽象层对接
    • 平台适配代码:是elua的抽象层,与外界代码打交道

      上面所述的五大模块功能各不相同,他们共同组成了我们的elua开源项目,每个大模块内又会细分为几个小模块,一层套一层。各个模块之间又有千丝万缕联系,并不是互相独立的。本开源项目的文件数量达到了656个,仅仅是纯代码就占用了50M的存储空间。随着后续开源文档的进行,我们会拿出几个小节来手把手教大家添加其他的功能模块,让我们自己写的代码也能让lua解释器调用。到那时这个文件数量还会进一步增加。
    在这里插入图片描述

      现阶段我们的elua开源模块的总目录结构如下图所示。里面仅仅是列出了部分重要的具有代表意义的功能以及文件。从这张图我们也能看出来elua开源项目默认就支持了那些功能。

    在这里插入图片描述

    2.1、C库代码

      由于某些原因我们的elua开源项目中不能采用标准的c库函数,而是内置了一套专用的c库代码,这套c库代码与标准c库的命名保持一致。不过其内部的实现却发生了很大的变化。

      有的同学在学习STM32的时候,可能会遇到一个问题那就是串口通信printf重定向。st官方并没有提供printf函数给我们用,这时候如果想要使用printf向串口打印日志的话那就需要对printf进行重定向。

      在标准的c库函数中printf是向控制台打印输出信息。而在嵌入式平台上,是没有控制台的。如果想要用printf函数的话那就只有两个办法。

      第一就是大家在使用stm32时用到的方法,对printf函数进行输出重定向。那么这个办法在裸机编程上倒是看不出来什么弊端。大家在使用操作系统编程比如freertos这些就会发现一个问题。如果任务的堆栈空间分配比较小,同时又在任务中使用到了printf打印数据那么就会出现堆栈溢出导致程序崩溃。这是因为printf函数使用时会在栈空间定义一个大缓冲区存储数据,这个缓冲区只是临时定义的,用完就会被析构掉。在使用printf时临时缓冲区会被算到任务堆栈大小中,只要给任务分配到空间小了,那么就会导致程序堆栈空间溢出程序崩溃。按照我以前踩坑的经验,即使你只是点个灯,只要用到了printf函数,如果此时任务的堆栈空间小于2048程序就会崩溃。这样就不太好了是吧,起步2048哪有这么多内存去搞。

    在这里插入图片描述

      为了解决这个问题,那么我们就需要用到第二个方法,那就是我们即将要讲到的重写c标准库,把它里面的空间缩小,改变实现的方法。

      C库代码这一小节对应的是我们的elua开源项目中的newlib文件夹里面的内容,里面工作是将标准的C库函数重新实现了一下。主要就是一些我们经常使用到的这些,比如#include"string.h"#include"stdio.h"#include"stdlib.h"#include"malloc.h"#include"math.h"。还有一些用的比较少,但是elua解析器里面会用到的,这里就不讲了。

      newlib文件夹的结构如下图所示,涉及到的文件数量也是一大把。有兴趣的自己去看。

    在这里插入图片描述

      有的函数需要和硬件平台打交道,不能直接用标准库。比如stdio库中的一些函数,就拿fopen_ext"函数来看一下,它里面的实现用到了一些和平台相关的代码。

    char *fgets_ext(char *buf, int n, FILE *fp);
    FILE *fopen_ext(const char *file, const char *mode);
    int fclose_ext(FILE *fp);
    int getc_ext(FILE *fp);
    int ungetc_ext(int c, FILE *fp);
    size_t fread_ext(void *buf, size_t size, size_t count, FILE *fp);
    int fseek_ext(FILE *fp, long offset, int whence);
    long ftell_ext(FILE *fp);
    int feof_ext(FILE *fp);
    
    FILE *fopen_ext(const char *file, const char *mode)
    {
        FILE *fp = NULL;
        E_LUA_SCRIPT_TABLE_SECTION section = LUA_SCRIPT_TABLE_MAX_SECTION;
        T_UNCOMPRESS_FILE_TABLE_ITEM *pItem = NULL;
        int fileNameLen = strlen(file);
        
        if((!file) || (strlen(file) == 0))
        {
            OPENAT_print("[fopen_ext]: para error!\n");
            return fp;
        }
    
        if(FindUncompressFileItem(&section, &pItem, file))
        {        
            fp = L_CALLOC(1,sizeof(FILE));
            if(fp)
            {
                fp->_flags = section;
                fp->_cookie = pItem;
            }
            fp->_type = LUA_UNCOMPRESS_FILE;
    
        #ifdef AM_LUA_CRYPTO_SUPPORT
            if(strncmp(&file[fileNameLen - 5],".luac", 5) == 0)
            {
                fp->_type |= ENC_FILE;
            }
        #endif
    
            //printf("[fopen_ext]: %s %d!\n", file, fp->_type);
    
        }
    
        return fp;
    }
    

      当然这其中个例不能说明一切,那我们就再来看一个函数_malloc_r

    void* _malloc_r( size_t size )
    {
    #ifdef MUXUSE_DLMALLOC_MEMORY_AS_LUA_SCRIPT_LOAD
        if(bScriptLoaded)
        {
            return dlmalloc(size);
        }
        else
    #endif
        {
            return CNAME( malloc )( size );
        }
    

      这个_malloc_r经过if判断调用的是CNAME( malloc )( size ),而CNAME( malloc )( size )最终又指向platform_malloc这是我们的elua抽象层的代码,最终它调用的是函数是需要和硬件打交道的,直接采用标准版的c库肯定是不适用的。

    void* platform_malloc( size_t size )
    {
        return OPENAT_malloc( size );
    }
    

      这个其实也算是一个抽象层,只不过它抽象的是标准库函数。使elua解析器能够在嵌入式平台上面正常运行。

      有人可能会讲,那我用其他的开发平台怎么能直接用malloc函数、printf函数,这些标准函数直接用也没有什么问题啊,哪有这么多事情。这里我提醒一下,那是因为开发包底层已经把这部分代码处理好了,有可能是重写,也有可能是重定向。对于用户什么来讲都不用管,直接用。但是这个底层绝对不是直接使用的c库的标准代码的。

      各位同学不信的话可以打开我们的iot_sdk_4g_8910开发包看下components文件夹下是不是也有一个newlib文件夹。
    在这里插入图片描述

      那我们可能又有一个疑问,既然elua开源项目是运行在iot_sdk_4g_8910上面的,为什么不直接用iot_sdk_4g_8910的newlib呢。这里那就说一下,既然我们做开源,那就说明这个elua软件包不仅仅是只能用在iot_sdk_4g_8910上面的,只要各位有能力那就可以把它运行在任何平台上。那就拿stm32来讲,他就没有给用户提供这些c标准库(现在我也不清楚有没有提供,至少几年前是没有的)。它都没有,那用户移植起来岂不是很复杂,甚至有可能移植不成功。

    2.2、lua解析器代码

      在上一节我们讲到了C标准库的重新实现。只要讲到了为什么要重新实现C标准库,既然csdk开发包中既然已经存在了newlib为什么还要在elua软件包中还要重新再写一遍这两个问题。

      那么费这么大劲搞着玩意,那么这个C标准库到底是给谁用的呢?

      它的使用对象,那自然是elua解析器了,那么本小节我们就一起来捋一遍lua解析器的代码。

      lua解析器位于elua/lua文件夹下,其代码结构如下图所示。

    在这里插入图片描述

      lua解析器代码我这里将其分为三个大块,它们分别是虚拟机核心功能部分、源代码解析和预编译部分以及内嵌库部分。

    注意:这是我个人对它源码的结构进行的划分,目的是方便理解,不是官方的定义,官方有没有这个结构的划分我也不清楚。

    2.2.1、Lua内核

      lua内核顾名思义那就是lua的核心代码,负责的是lua虚拟机的运行、调度、内存管理、输入输出设备监视以及全局状态机的管理等功能。这些都是最基本的功能,虚拟机能够运行起来靠的就是内核在后面起作用。没有内核你搞那些乱七八招的东西没有意义。

      lua内核中的各个文件具体是干什么的,在下表也有说明。

    源文件 功能
    lvm.c Lua虚拟机
    ltm.c 元方法
    ldebug.c 调试接口
    lzio.c 通用输入流接口
    lstate.c 全局状态机 管理全局信息
    ldo.c Lua的堆栈和调用结构
    lfunc.c 函数原型及闭包管理
    lobject.c 对象操作函数
    lgc.c 垃圾回收机制
    lapi.c Lua API
    lmem.c 内存管理接口
    lopcodes.c 虚拟机字节码定义
    lua.c lua的可执行入口 main函数
    lstring.c 字符串表(保留Lua处理的所有字符串)
    ltable.c Lua表(哈希)

    2.2.2、Lua解析和预编译

      Lua语言是一个解释性语言,它不是直接就能运行的。运行时需要将文件系统中的lua文件读取出来,然后需要将lua语句翻译成对应的c指令才能运行。那么这个解析功能就需要一个单独的模块来负责,它由内核控制主要负责lua脚本的词法分析、解析、编译等这些功能。

    源文件 功能
    lparser.c Lua的解析器
    luac.c Lua编译器(将字节码保存到文件;也列出字节码)
    ldump.c 保存预编译的Lua块
    lundump.c 加载预编译的Lua块
    llex.c Lua的词法分析器
    lcode.c Lua的代码生成器

    2.2.3、一些库函数

      除了让lua虚拟机能够运行起来,还要给它内置一写操作库函数。让它用起来更方便、更简单。要有化繁为简的本领,让用户使用起来更不易出错,这也是脚本语言的特色。

      下表中列出了lua解析器内部一些内置库的实现文件,主要是基础库、数学运算库、操作系统库、table库以及调试库。

    源文件 功能
    loadlib.c Lua的动态库加载器
    liolib.c 标准I /O(和系统)库
    loslib.c 标准操作系统库(这应该是协程)
    lauxlib.c 用于构建Lua库的辅助功能
    linit.c 初始化lua.c的库
    ltablib.c 用于表操作的库
    ldblib.c 从Lua到其调试API的接口
    lmathlib.c 标准数学运算库
    lbaselib.c 基础库
    lstrlib.c 用于字符串操作和模式匹配的标准库
    print.c 打印字节码

    2.3、硬件功能模块

      上一节我们简单介绍了下lua解析器的代码结构,lua解析器默认自带了一些基础的运算库、操作库,只能实现很简单的功能。那么到现在为止lua脚本简单的运行起来应该是没有什么问题了,但是这个运行的结果我们却没办法看到,因为它里面还没有写任何和硬件打交道的代码。即使是一个简单的串口打印helloworld它也需要使用到串口这个硬件驱动。

      那其他的硬件那就更不必说,都是需要人为加进去的,lua解析器自身并不带这些功能。在elua/modules文件夹内,我们也集成了一部分常用的硬件驱动。

    在这里插入图片描述

      上图中只是列出了一部分常用的驱动库,实际的modules文件夹下的的库文件有很多。接下来拿出adc库进行相应的说明,怎么才能把adc的驱动代码注册到lua虚拟机中。让用户能够在lua脚本中进行adc的采集控制。

    2.3.1、注册adc

      用户如果想要让lua内核能够调用adc的相关库,首先需要在auxmods.h中声明模块名字和adc模块初始化函数。

    #define AUXLIB_ADC      "adc"
    LUALIB_API int ( luaopen_adc )( lua_State *L );
    

      声明之后需要在platform_conf.h文件中完成将模块名和初始化函数注册到lua内核中。

        _ROM( AUXLIB_ADC, luaopen_adc, adc_map )
    

      adc模块注册之后,相当于我们只是告诉了lua内核我们提供了adc的相关驱动,但是这个驱动内有什么函数可以调用lua内核却不知道。这时候我们还需要另外在lua模块内完成adc驱动的相关调用函数的注册。

      下面的代码中我们为adc模块注册了三个可用lua脚本调用的函数,分别是openreadclose。这三条lua函数对应的c语言中的实现函数分别是adc_open()adc_read()adc_close()

    const LUA_REG_TYPE adc_map[] =
    { 
      { LSTRKEY( "open" ),  LFUNCVAL( adc_open ) },
      { LSTRKEY( "read" ),  LFUNCVAL( adc_read ) },
      { LSTRKEY( "close" ),  LFUNCVAL( adc_close ) },
    
      { LNILKEY, LNILVAL }
    };
    
    LUALIB_API int luaopen_adc( lua_State *L )
    {
        luaL_register( L, AUXLIB_ADC, adc_map );
    
        return 1;
    }  
    

      那这样写为什么就能被lua脚本调用呢,之间又有什么关系。下面我把这三条脚本换个写法,openreadclose三个函数对应的lua调用脚本是adc.open()adc.read()adc.close(),这样看起来是不是有点似曾相识的感觉。但一时半会好像又想不起来在哪见过,给你们五秒钟时间想,给我使劲想!!!

    在这里插入图片描述

      对,没错!如果各位同学之前使用过我们模块的adc采集功能,我猜各位应该已经想到了在luat脚本的adc例程中,有这么一段。

    local function ADC0_Task()
        local adcValue, voltValue = 0, 0
        local result = adc.open(0)----------1、这一行是不是很眼熟
        while true do
            adcValue, voltValue = adc.read(0)----------2、这一行好像也有点眼熟
            if adcValue ~= 0xffff then
                log.info("ADC 0的原始测量数据和电压值:", adcValue, voltValue)
            end
            sys.wait(1000)
        end
        adc.close(0)----------3、看到这一行的话,应该就没有什么疑问了吧
    end
    

      上面的Luat脚本中看起来有很多东西,实际上和adc有关的就用到了三行,而这三行正好就是我们的adc注册函数中注册的三条语句。我们底层中只要把这三条注册的语句写出来了,就算这三个函数的里面没有任何实现(函数还是要写的哈),是一个空函数。你也能写lua脚本给lua底层去解析,lua底层就能找到这个函数的位置然后去执行。你可以在这个函数内写很多功能,做非常复杂的事。这对于lua脚本而言,就只需要一行。

    2.3.2、adc调用函数的实现

      虽然现在我们所做的工作已经能够让lua内核来运行我们的自定义模块了,当然这个模块现在还是空的,啥也没有。lua解析器跑到这里转一圈又回去了,什么事情都没干。

    // adc.open(id)
    static int adc_open(lua_State *L) {
        return 1; 
    }
    
    // adc.read(id)
    static int adc_read(lua_State *L) {    
        return 2; 
    }
    
    static int adc_close(lua_State *L) {
        return 1; 
    }
    

      我们还需要按照特定的规则,完成调用函数内部具体的实现方法。来大家注意看了哈,我变。

    在这里插入图片描述

      咳咳,变出来了。

    // adc.open(id)
    static int adc_open(lua_State *L) {
        int id = luaL_checkinteger(L, 1);
        int ret;
    
        MOD_CHECK_ID(adc, id);
    
        ret = platform_adc_open(id,0);
    
        lua_pushinteger(L, ret);
    
        return 1; 
    }
    
    // adc.read(id)
    static int adc_read(lua_State *L) {    
        int id = luaL_checkinteger(L,1);
        int adc, volt;
    
        MOD_CHECK_ID(adc, id);
    
        platform_adc_read(id, &adc, &volt);
    
        lua_pushinteger(L, adc);
        lua_pushinteger(L, volt);
       
        return 2; 
    }
    
    static int adc_close(lua_State *L) {
        int id = luaL_checkinteger(L, 1);
        int ret;
    
        MOD_CHECK_ID(adc, id);
    
        ret = platform_adc_close(id);
    
        lua_pushinteger(L, ret);
    
        return 1; 
    }
    

    2.3.3、几个无关紧要的函数

      上面的三个代码块中我们分别为adc的打开,查询和关闭功能进行了相应的实现。我们可以找一下规律。上面三个函数的实现结构很相似,可以用下面的伪代码表示。

    // adc.open(id)
    static int adc_open(lua_State *L) {
        int id = luaL_checkinteger(L, 1);
        MOD_CHECK_ID(adc, id);
       	/*用户代码*/
        ...........
        /*用户代码*/
        lua_pushinteger(L, ret1);
        lua_pushinteger(L, ret2);
        lua_pushinteger(L, ret3);
        return 1; 
    }
    

      luaL_checkinteger作用是检查函数的第 arg 个参数是否是一个字符串,并返回该字符串。

      MOD_CHECK_ID是一个宏,对应的代码如下,目的是判断传入的操作数是不是允许的允许的。打个比方,我们的设备假如只有两个adc,那我们可操作的adc编号就是0和1,你给我来个2,那程序就终止运行了并且打印adc 2 does not exist

    // Helper macros
    #define MOD_CHECK_ID( mod, id )\
      if( !platform_ ## mod ## _exists( id ) )\
        return luaL_error( L, #mod" %d does not exist", ( unsigned )id )
    

      在lua的语法中允许函数一次返回多个值,lua_pushinteger作用就是返回lua的运行结果的。

      在上面说到的lua脚本中的读取adc这一句。等号左边的两个返回值。就是由c代码中的两句lua_pushinteger代码推送的。

     adcValue, voltValue = adc.read(0)
    
        lua_pushinteger(L, adc);
        lua_pushinteger(L, volt);
    

    2.4、功能库代码

      经过上一节的学习,大家应该也都清楚了怎么才能去添加一些驱动库。一般情况下2.3小节提供的驱动库也够用了。有时候我们不仅仅是驱动设备,还需要运行一些软件算法,比如sha1、md5这些信息摘要算法,还有一些图片的解码算法。json数据处理等等。

      这些算法都是比较常见的,也经常会使用到。我们也在elua代码包中内置了一些纯软算法,具体见下图。

    在这里插入图片描述

      上面图片中的命名有的人可能不知道这些库有什么作用,这里做一下简要说明。

    代码包 功能
    crypto 加密/摘要功能(sha1、sha2、md5、base64、xxtea、aes)
    iconv 字体编码转化(gb2312、ucs2、utf8、)
    json JSON内容或JSON文件进行格式化解析
    lpng 图片解码
    lzma 压缩文件接口
    pbc 基于双线性对的密码学库
    qr_encode 二维码生成
    zlib 通用的压缩库
    zziplib 轻量级的用来从ZIP文件抽读取文件的C语言包

      还是和上节一样,库太多了,这些库里面又有一大堆文件。本来准备选个高大上的库讲讲,就是那个啥----基于双线性对的密码学库。这时候没想到json这么积极,举手要求上台,那我就勉为其难的答应她好了。接下来我们讲讲json,举一反三都一样哈。

    在这里插入图片描述

    2.4.1、json注册

      和硬件驱动一样,即使json是纯软运算库也没有什么特殊的地方,该有的东西都得有。

      如果想json能在lua脚本中被调用,首先那就必须得在auxmods.h中增加声明json模块的模块名和注册函数。

    #ifndef AM_JSON_NOT_SUPPORT
    #define AUXLIB_JSON     "json"
    LUALIB_API int ( luaopen_cjson)( lua_State *L );
    #endif
    

      声明之后需要在platform_conf.h文件中完成将模块名和初始化函数注册到lua内核中。

    _ROM( AUXLIB_JSON, luaopen_cjson, json_map )
    

      紧接着就在模块初始化函数函数内进行模块函数注册。这一段json库的函数注册代码就要比adc库的函数注册代码要复杂得多。

    /* Return cjson module table */
    static int lua_cjson_new(lua_State *l)
    {
        luaL_Reg reg[] = {
            { "encode", json_encode },
            { "decode", json_decode },
            { "encode_sparse_array", json_cfg_encode_sparse_array },
            { "encode_max_depth", json_cfg_encode_max_depth },
            { "decode_max_depth", json_cfg_decode_max_depth },
            { "encode_number_precision", json_cfg_encode_number_precision },
            { "encode_keep_buffer", json_cfg_encode_keep_buffer },
            { "encode_invalid_numbers", json_cfg_encode_invalid_numbers },
            { "decode_invalid_numbers", json_cfg_decode_invalid_numbers },
            { "new", lua_cjson_new },
            { NULL, NULL }
        };
    
        /* Initialise number conversions */
        fpconv_init();
    
        /* cjson module table */
        lua_newtable(l);
    
        /* Register functions with config data as upvalue */
        json_create_config(l);
        luaL_setfuncs(l, reg, 1);
    
        /* Set cjson.null */
        lua_pushlightuserdata(l, NULL);
        lua_setfield(l, -2, "null");
    
        /* Set module name / version fields */
        lua_pushliteral(l, CJSON_MODNAME);
        lua_setfield(l, -2, "_NAME");
        lua_pushliteral(l, CJSON_VERSION);
        lua_setfield(l, -2, "_VERSION");
    
        return 1;
    }
    int luaopen_cjson(lua_State *l)
    {
        lua_cjson_new(l);
    
    #ifdef ENABLE_CJSON_GLOBAL
        /* Register a global "cjson" table. */
        lua_pushvalue(l, -1);
        lua_setglobal(l, CJSON_MODNAME);
    #endif
    
        /* Return cjson table */
        return 1;
    }
    

    2.4.2、json调用函数的实现

      纯软模块和硬件驱动不同的地方现在开始体现出来了。像json这一类纯软模块不依赖外部环境,实现用的都是一些标准库。基本上就是拿来就能用,都不需要改的。如果json模块只是给elua用,不考虑提供给外界使用的话。那我们就可以直接在上述lua调用的实现中直接去调用json库中的函数。而无需去单独在抽象一层。

      看这个adc模块中的adc_open函数实现,去掉一些必须得写的,实际上和adc有关的就一行。这一行platform开头的代码就是我们下一章节要讲的平台适配代码。它可以看作是elua开源项目的一个特殊的抽象层,负责和外界的环境进行交互,这里不做细讲,了解即可。

    // adc.open(id)
    static int adc_open(lua_State *L) {
        int id = luaL_checkinteger(L, 1);
        int ret;
    
        MOD_CHECK_ID(adc, id);
    
        ret = platform_adc_open(id,0);
    
        lua_pushinteger(L, ret);
    
        return 1; 
    }
    

      再来看看json模块中的json_decode函数的实现,这就不一样了是吧,这么一大坨看着都晕。这还是我挑选的一个比较简单的,其他的那就更长了。

    static int json_decode(lua_State *l)
    {
        json_parse_t json;
        json_token_t token;
        size_t json_len;
    
        luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
    
        json.cfg = json_fetch_config(l);
        json.data = luaL_checklstring(l, 1, &json_len);
        json.current_depth = 0;
        json.ptr = json.data;
    
        /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3)
         *
         * CJSON can support any simple data type, hence only the first
         * character is guaranteed to be ASCII (at worst: '"'). This is
         * still enough to detect whether the wrong encoding is in use. */
        if (json_len >= 2 && (!json.data[0] || !json.data[1]))
            luaL_error(l, "JSON parser does not support UTF-16 or UTF-32");
    
        /* Ensure the temporary buffer can hold the entire string.
         * This means we no longer need to do length checks since the decoded
         * string must be smaller than the entire json string */
        json.tmp = strbuf_new(json_len);
    
        json_next_token(&json, &token);
        json_process_value(l, &json, &token);
    
        /* Ensure there is no more input left */
        json_next_token(&json, &token);
    
        if (token.type != T_END)
            json_throw_parse_error(l, &json, "the end", &token);
    
        strbuf_free(json.tmp);
    
        return 1;
    }
    

      给大家20秒扫一眼,看看上面的代码有没有platform开头的函数。

    在这里插入图片描述

      哎,好像没有是吧!这就说明这个json模块是给elua开源项目特供的,不需要给外界使用。也不需要把它拎到项目外面,再另外做一个抽象层与elua对接。

    2.5、平台适配代码

      上面两节讲到了硬件驱动的注册和软件算法的注册,软件算法可以选择独占还是共有。其中硬件驱动在lua函数中的实现需要通过一个抽象层完成与外部的对接。软件算法如果选择独占那可以直接在lua函数实现进行调用,如果选择共享那它也得和硬件驱动一样另外做一个抽象层与外部进行对接。

      那么大家有没有想过↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    2.5.1、为什么要有平台适配代码

      一个成功的软件系统,往往需要根据需求在不同的系统平台上运行,为了解决系统在多个平台的移植带来的风险,业务架构往往会设计相应的平台适配层来隔离不同平台的差异,如何设计一个易于扩展的平台适配层,是软件设计人员需要考虑的问题。

      为了让大家更方便地移植我们的elua开源模块,我们进行了相应的平台适配代码的编写工作,其目录位于elua/platform。我们由于时间有限,在该目录下只提供了opanat平台的适配代码以及win32平台的适配代码。其中openat平台的适配代码支持的功能较多,可以直接用于我们的csdk开发环境,该提供的功能基本都提供了。若有其他需求,也可自行添加其他的程序模块。

    2.5.2、平台适配代码adc与openat对接

      拿上面硬件驱动中的adc模块讲下,它的平台适配代码极其简单,看看下面的代码块其中platform_adc_open的对接只需要一行。platform_adc_close的对接还是一行。
    platform_adc_read的对接看起来有好几行,仔细看看读取部分,实际上还是只有一行,只不过为了给elua提供两个结果的返回值,进行了一点点运算。

    int platform_adc_open(unsigned id, unsigned mode)
    {
        //return PLATFORM_OK;
        /*+\BUG\wangyuan\2020.06.30\lua版本编译不过*/
        return IVTBL(InitADC)(id, mode) ? PLATFORM_OK : PLATFORM_ERR;
        /*-\BUG\wangyuan\2020.06.30\lua版本编译不过*/
    }
    
    int platform_adc_close(unsigned id)
    {
        return IVTBL(CloseADC)(id) ? PLATFORM_OK : PLATFORM_ERR;
    }
    
    int platform_adc_read(unsigned id, int *adc, int *volt)
    {
        u16 adcVal = 0xFFFF;
        u16 voltage = 0xffff;
        BOOL ret;
    
        ret = IVTBL(ReadADC)(id, &adcVal, &voltage);
    
        *adc = voltage / 3;
        *volt = voltage;
        return ret ? PLATFORM_OK : PLATFORM_ERR;
    }
    

      那么到现在为止,我们已经将elua的内核部分、模块的注册部分、平台的对接部分,都捋了一遍。东西就这么多,流程就是这样。只不过有的模块复杂点,有的地方的代码它就多一点,化繁为简就那么几行重要的。那么我们的elua开源项目的基础部分也就到处为止了。说那么多有什么用,你甚至可以不看,是骡子是马拉出来溜溜。

    第三章、Lua解析器代码编译

      第二章我们讲了下lua内核和模块注册示例,那么第三章那我们就把它拿出来跑跑。

      有的同学会讲,我这第二章也没怎么看懂啊,第三章还能继续学吗,要是不行的话那我就溜了,删库跑路走起。

    在这里插入图片描述

      少年不要怕,我以人格担保。这第二章别说你没看懂,就算你没看。第三章你也能跑起来,你也能学会。So ,Believe in Yourself !!!

    在这里插入图片描述

    3.1、CSDK分区相关知识

      接下来的内容就和设备密切相关,在进入代码编译之前,我们需要知道一些基本的分区信息。我们这里以rda8910平台为例。

    3.1.1、默认CSDK分区

    rda8910(8M) app预留空间 文件系统空间
    起始地址 0x60160000 0x60340000
    长度 0x1E0000 0x160000
    结束地址 0x60340000 0x604a0000

      正常的csdk底层存在两块用户可操作的空间,第一是app的预留空间,其大小为1.875MB。理论上用户的app编译出来的结果最大可为1.875MB。
    0x1E0000=1966080=x10241024 0x1E0000=1966080=x*1024*1024

    x=1.875(MB) x=1.875(MB)

      但是实际上后面需要预留一点,留少了程序就运行不了,具体要预留多少我也没测试,所以…嘿嘿嘿,你懂的。

      第二就是文件系统的预留空间,其大小为1.375MB。这个空间好像是有点小了哈,也别指望存储什么大文件。就这空间你连一首mp3都存不下。但是用来存些什么其他的小文本文件还是不错的。
    0x160000=1441792=x10241024 0x160000=1441792=x*1024*1024

    x=1.375(MB) x=1.375(MB)

    3.1.2、CSDK—elua项目分区

    rda8910(8M) app预留空间 elua脚本区域 文件系统空间
    起始地址 0x60160000 0x602D8000 0x60340000
    长度 0x178000 0x68000 0x160000
    结束地址 0x602D8000 0x60340000 0x604a0000

      是不是感觉又有点不太一样了,app可用空间又小了点。也就是说你能写的东西更少了。
    0x178000=1540096=x10241024 0x178000=1540096=x*1024*1024

    x=1.46875(MB) x=1.46875(MB)

      我们又把app的空间裁剪出来一部分存储lua脚本。这个空间只有416KB。
    0x68000=425984=x1024 0x68000=425984=x*1024
    x=416(KB) x=416(KB)

      这样的话就发现app空间好像有点不太够,lua脚本空间好像也有点不太够。这也没办法,空间就只有这么大,少就少点吧,省着点用。

    3.2、自定义elua脚本空间

      有的同学讲这不行哎,我贼能写,这400KB脚本空间太小了不够我用的,赶快给我改大点。

    在这里插入图片描述

      emmm,改好了,各位老板们请查收。打开iot_sdk_4g_8910/config.cmake文件看一下。有没有看到你想要的东西。

    
    #/*+\NEW\chenzhimin\2020.07.21\ elua工程专用*/
    if(CONFIG_BUILD_LUA)
        set(CONFIG_LUA_FLASH_OFFSET 0x2D8000)
        set(CONFIG_LUA_FLASH_SIZE 0x68000)
    else()
        set(CONFIG_BUILD_LUA OFF)
    endif(CONFIG_BUILD_LUA)
    
    message("BUILD_LUA:" ${CONFIG_BUILD_LUA})
    #/*-\NEW\chenzhimin\2020.07.21\ elua工程专用*/
    
    
    

      CONFIG_LUA_FLASH_OFFSET参数就是设置lua脚本的起始地址的,默认是起始地址就是0x2D8000。

    在这里插入图片描述

      别打了,别打了…0x602D8000是物理地址,实际上每个地址前面都带有60,这个底层已经处理好了,我们只需要填写逻辑地址即可。

      CONFIG_LUA_FLASH_SIZE参数设置的是lua脚本空间大小,你可以修改这两个值。但是起始地址加上空间大小,也就说lua脚本结束的地址却不能超过0x340000。一定要小心,不能计算错了。虽然我们将修改的接口开放出来了,但是我个人强烈建议不要随便修改!!!!

    3.2、开始编译

      cmd窗口进入iot_sdk_4g_8910/project目录,然后执行app_elua.bat。进入编译状态,这需要一点时间。

    在这里插入图片描述

      。。。。。。

      好了。编译完成

    在这里插入图片描述

    3.3、下载固件及脚本

      接下来就和大家使用LUAT版本开发一样了,打开luatools工具。尽量使用高于2.0.68的版本,低版本会出现CSDK日志信息打印不正确的问题。

    在这里插入图片描述

      下载固件和脚本。注意啦,这里选自己编译的固件!!!!在这个地方~\iot_sdk_4g_8910\hex\Air720U_CSDK_app_elua.pac,脚本的话就和luat开发脚本一样,通用的没有区别。

    在这里插入图片描述

      下载结束就是这个样子了。

    在这里插入图片描述

    在这里插入图片描述

      接下来可以去和其他人吹牛了,我也能编译luat源码了,想让它干什么就让它干什么。自己的模块自己做主!

    第四章、为eLua添加自定义模块

    未完待续。。。。。

    展开全文
  • eLua学习笔记1——为STM32编译elua

    千次阅读 2016-04-09 12:39:37
    准备dir工作: 1. 由于我是在CentOS7上编译的,所以在此之前需要为CentOS7安装32位运行库,方法是从网上搜索来的,如果你是64bit的Linux发行版,也需要安装32bit运行库,ubuntu与CentOS7需要安装的库有所不同,以下...

    步骤:

    1. 由于我是在CentOS7上编译的,所以在此之前需要为CentOS7安装32位运行库,方法是从网上搜索来的,如果你是64bit的Linux发行版,也需要安装32bit运行库,ubuntu与CentOS7需要安装的库有所不同,以下是CentOS7的安装指令:

    sudo yum install xulrunner.i686
    sudo yum install libXtst.i686



    2. toolchain : gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2

    http://pan.baidu.com/s/1dE2BMJv



    3. elua源码,从github上下载

    在命令行输入,可从elua的git仓库clone代码到本地

    git clone git://github.com/elua/elua.git



    4. lua-5.1.4源码,可从官网上下载,这个源码主要是编译安装luarocks时会用到lua的头文件,CentOS7虽然自带了lua-5.1.4,但是并没有带lua的头文件,所以需要下载源码



    5. 下载luarocks源码

    编译并安装luarocks

    tar -xvf luarocks-2.3.0.tar.gz

    cd luarocks-2.3.0

    ./configure --prefix=/usr/local/luarocks

    make

    sudo make install



    6. 使用luarocks安装luafilesystem, MD5, lpack,并为他们建立软链接到lua的lib和share目录下

    luarocks install luafilesystem

    luarocks install md5

    luarocks install lpack



    7. 以上的安装命令会将那三个组件安装到/usr/local/luarocks目录下,所以需要对以上安装的lua组件,建立一个链接到本地lua的lib或者share中,否则编译elua时还是会提示说未找到这三个组件



    8. 编译

    lua build_elua.lua board=stm32f4discovery prog

    注意需要加上"prog", 否则不会生成.hex或者.bin文件,我们需要烧写的是这两个文件之一



    9. 烧写,我是将编译出来的.hex文件拷贝到Windows电脑上烧写的,因为我的CentOS7服务器放在别处。

    Windows本地安装STLink软件后就可以直接烧写STM32DISCOVERY板子了,在上文的百度网盘链接里也有这个软件。

    烧写完成后,将STM32DISCO通电,并使用microUSB线连接板子另一边的microUSB口到PC或者Linux主机。elua在板子中中实现了USB虚拟串口,所以我们不再另外需要USB转串口来连接板子的串口,只需连接板子的microUSB口,然后PC机装上ST的虚拟串口驱动(linux系统不需要安装),就可以和板子通讯了。我将板子接到树莓派上,所以不需要安装虚拟串口驱动,在/dev目录下的ttyACM0节点就是STM32的虚拟串口,使用minicom打开该节点,串口配置为115200, 8N1, 无硬件流:



    就可看见如下信息:




    展开全文
  • 学习elua(三)--定制elua的编译选项

    千次阅读 2014-09-04 13:24:18
    介绍如何定制elua的编译选项
    
    

    第二篇关于elua的文章讲了,elua的编译和烧写。不过编译选项比较简单,全按照默认的配置,在这篇文章中主要讲怎么定制编译选项。当然还是以stm32f4discovery平台为例子。


    关于参考文献

    这里首先提参考文献,是因为以下的内容出处比较混乱,提前说一下,省的读者混淆。

    elua为开源项目,文档还算齐全,但是文档更新的比较混乱:存在多个版本,且有的地方代码和文档不一致。

    例如:

    http://www.eluaproject.net/doc/v0.9/en_index.html

    http://www.eluaproject.net/doc/v0.8/en_index.html

    elua的安装目录下/Doc/En


    我采取的原则是:离代码越近的文档可信度越高。所以首选/Doc/En,之后是v0.9的版本,之后才是0.8的版本。(编译elua时就因为文档更新不及时“codecourcery问题”,导致走了很多弯路)。

    以下以elua/Doc/En/Building.txt为准

    还可以参考:

    自动编译器的变化

    Building.txt

    IMPORTANT:Starting with eLua 0.10, a new Lua-based build system replaces theprevious Python based (scons) build system. You'll need to use thenew build system to build eLua

    现在(0.9)的自动编译工具不同于elua0.7版本scons(例如:sconscpu=at91sam7x256

    而是已经采用基于lua的自动编译器,请注意这里的文档有误导性:

    http://www.eluaproject.net/doc/v0.9/en_building.html(没有改过来)

    http://www.eluaproject.net/doc/master/en_building.html(新的)

    定制eluaimage大致分为两步(两者是有先后顺序的):

    Configuringthe build image。(配置)

    Invokethe build system (build_elua) with the right arguments。(编译选项)


    [[configuring]]

    参考elua/Doc/En/configurator.txt

    Theconfigurator works by reading a Lua based board configuration fileand generating the corresponding C header file (which for the mostpart has the same role as the platform_conf.h file that was usedbefore the configurator). The board name is given by the _board_argument to build_elua (see link:building.html#buildoptions[thislink] for more details about building your eLua image). Theconfigurator looks for a file named _<board>.lua_ in twolocations:

    **boards/custom* is searched first. The files under this directory arenot part of the eLua source tree, so this is the place where you canadd configuration files for your custom board or customize theconfiguration for one of the standard boards (seelink:#config_customize[here] for more details about this).

    **boards/known* is searched if _<board>.lua_ is not found in_boards/custom_. It contains the configuration of the boards on whicheLua is known to run properly. The files under this directory arepart of the eLua source tree.

    Afterfinding _<board>.lua_ in one of these locations, theconfigurator runs and generates the corresponding header file in__boards/headers/board_<board>.h__. The files under_boards/headers_ are also not part of the eLua source tree, so theycan be edited manually if needed (see link:#manualedit[here] for moredetails). After this, the configurator is done and the build processcontinues with the usual steps (compiling and linking the sourcefiles).

    三个目录

    均在elua/board/

    其中配置程序(configurator)首先搜索custom目录,需找<board>.lua文件,其中<board通过buildoptions中的[board=<board>]给出。开始时这个目录为空。

    如果configuratorcustom目录搜索不到配置信息后(如果找到了就不会访问known目录的文件了),转向known目录,同样搜索<board>.lua。如果buildoptions中的[board=<board>]填写正确,就可以找到对应的<board>.lua。例如:stm32f4discovery.lua稍后我们详细解释该文件

    最后,生成board_stm32f4discovery.h文件,该文件类似于以前自动编译器的platform_conf.h。这个文件可以手动修改。稍后我们将解释该文件。

    这里需要说明的是,尽量不要修改known目录下的文件,可以在custom目录进行变更。

    详解known/stm32f4discovery.lua

    -- STM32F4DISCOVERY build configuration


    return {

    cpu = 'stm32f407vg',

    components = {

    sercon = { uart = "cdc",speed = 115200 },

    romfs = true,

    cdc = { buf_size = 128 },

    advanced_shell = true,

    term = { lines = 25, cols = 80},

    linenoise = { shell_lines = 10,lua_lines = 50 },

    stm32f4_enc = true,

    rpc = { uart = 0, speed =115200 },

    adc = { buf_size = 2 },

    xmodem = true,

    cints = true,

    luaints = true

    },

    config = {

    egc = { mode = "alloc"},

    vtmr = { num = 4, freq = 10 },

    ram = { internal_rams = 2 },

    clocks = { external = 8000000,cpu = 168000000 }

    },

    modules = {

    generic = { 'all', "-i2c","-net" },

    platform = 'all',

    },

    }

    首先看一下分为几部分:

    [[config_cpu]]

    The CPU is given by the *cpu* key in the configuration table. The CPUmust be already known to the build system. A list of the known CPUscan be found in the *build_data.lua*file in the __platform_list__ table.

    cpu = 'stm32f407vg',

    build_data.lua

    local platform_list =

    {

    at91sam7x = { cpus = { 'AT91SAM7X256', 'AT91SAM7X512' }, arch ='arm' },

    lm3s = { cpus = { 'LM3S1968', 'LM3S8962', 'LM3S6965', 'LM3S6918','LM3S9B92', 'LM3S9D92' }, arch = 'cortexm' },

    str9 = { cpus = { 'STR912FAW44' }, arch = 'arm' },

    i386 = { cpus = { 'I386' }, arch = 'i386' },

    sim = { cpus = { 'LINUX' }, arch = 'i386' },

    lpc288x = { cpus = { 'LPC2888' }, arch = 'arm' },

    str7 = { cpus = { 'STR711FR2' }, arch = 'arm' },

    stm32f2 = { cpus = { 'STM32F205RF' }, arch = 'cortexm' },

    stm32 = { cpus = { 'STM32F103ZE', 'STM32F103RE' }, arch = 'cortexm'},

    stm32f4 = { cpus = { 'STM32F401RE','STM32F407VG', 'STM32F407ZG' }, arch = 'cortexm' },

    avr32 = { cpus = { 'AT32UC3A0128', 'AT32UC3A0256', 'AT32UC3A0512','AT32UC3B0256' }, arch = 'avr32' },

    lpc24xx = { cpus = { 'LPC2468' }, arch = 'arm' },

    lpc17xx = { cpus = { 'LPC1768' }, arch = 'cortexm' }

    }

    如果你不作porting,那么cpu为以上其中一个。(porting详见elua/doc/en/arch_newport.txt

    [[config_components]]

    涉及文件系统(romfswofs,mmcfs,rfsshell,adnvance_shell(sercon,xmoden,term),中断(luaints,cints,dns,dhcp,tcpip,rpc等参数配置

    详见elua/Doc/En/configurator.txt

    [[config_config]]

    The *config* section containsvarious build time configuration data.

    虚拟时钟vtmr,垃圾收集egc,内存大小起始地址ram,系统时钟clock

    [[config_modules]]

    The configurator has supportfor fine-grained selections of the Lua modules that are going to bepart of the eLua firmware.

    The module chooser knows how todifferentiate between 3 categories of modules:

    1. *Lua modules*: the standardLua modules that are compiled in eLua (_mlmath, _mlio, _mlstring,_mltable, _mldebug, _mlpackage, _mlco). These can be referenced as agroup under the name *all_lua*.

    2. *Generic eLua modules*:these are _madc, _mbit, _mcan, _mcpu, _melua, _mi2c, _mpack, _mrpc,_mnet, _mpd, _mpio, _mpwm, _mspi, _mterm, _mtmr, _muart. These can bereferenced as a group under the name *all_elua*.

    3. *Platform specific eLuamodules*: these are added by each platform as needed.

    *ifused in *generic*, *all* is equivalent with *all_lua* + *all_elua*(all the standard Lua modules and the generic eLua modules)

    * if used in *platform*, *all*is a list of all the platform specific modules.

    A module name can be prefixedwitha dash (*-*) if thatmodule must be _excluded_ from the image instead of being included.Generally, this makes sense only when a group name (*all*, *all_lua*or*all_elua*) is also used in the list of modules.

    注意:allall_luaall_elua,"-i2c"表示不包括I2c组件


    [[config_headers]]

    the configurator compiles theLua board description file into a C header file (*board_<board>.h*)that is later used to compile the eLua firmware. If additionalheaders must be included in the generated header file, they can bespecified in the *headers* section:

    headers = { "specific1.h","specific2.h" }

    [[config_macros]]

    Besides the macros generated bythe configurator, it is sometimes useful to add other macros to the*board_<board>.h* file. These can be specified in the *macros*section. If present, *macros* must be a table with two kinds of keys:

    * strings: these define simplemacros (macros without a value)

    * arrays with two entries:these define macros with values.

    For example, this definition:

    [source, lua]

    macros = { 'MACRO1', {'MACRO2', 0 } }

    [source, c]

    #define MACRO1

    #define MACRO2 0

    定制编译属性时,一般不修改/known目录下的文件,而是采用在/custom目录下添加文件。注意文件名要与对应的平台相同,例如custom/stm32f4discovery.lua

    [source,lua]


    -- Fileboards/custom/stm32f4discovery .lua

    local t =dofile( "boards/known/stm32f4discovery .lua" )

    t.components.wofs=true

    return t

    例如:增加wofs文件系统的支持,(stm32f4discovery平台默认只支持romfs文件系统,只读,不能动态添加文件,限制lua的应用,advanced_shell中的recv命令也不能用,所以添加可读写的文件系统wofs)



    [[buildoptions]]

    参考elua/Doc/En/Building.txt

    luabuild_elua.lua[board=<boardname>](支持的平台详见boards/known文件夹)

    [target=lua | lualong | lualonglong]

    [allocator=newlib | multiple | simple]

    [toolchain=<toolchain name>]

    [optram=true | false]

    [boot=standard | luarpc]

    [romfs=verbatim | compress | compile]

    [cpumode=arm | thumb]

    [bootloader=none | emblod]

    [output_dir=<directory>]

    [romfs_dir=<directory>]

    [board_config_file=<file>]

    [skip_conf=true | false]

    [config_only=true | false]

    [build_mode=keep_dir | build_dir_linearized]

    [build_dir=<directory>]

    [disp_mode=all | summary | minimal]

    [-E | -S]

    [-h]

    [prog]


    target=lua| lualong | lualonglong: specify if you want to build"regular" Lua (with floating point support). 32 bit integeronly Lua (lualong) or 64 bit integer only Lua (lualonglong, startingwith version 0.9). The default is "lua". "lualong"and "lualonglong" run faster on targets that don’t have afloating point co-processor, but they completely lack support forfloating point operations, they can only handle integers. Also,"lualonglong" doesn’t support cross-compilation of Luasource files to bytecode

    默认lua,为float类型,在stm32f4discovery平台下发现,如果编译选项为-mfloat-abi=softfp时,选择target=luaelua中的lua解释器无法正常工作,只能便以为lualong

    optram=true| false: enables of disables the LTR patch, see theLTRdocumentation for more details. The default istrue, which enables the LTR patch. Keep LTR enabled unless you have avery good reason to do otherwise, eLua might notfunction properly with LTR disabled.


    默认打开,目的是节约RAM,详见elua/doc/en/arch_ltr.html


    prog:by default, the abovebuild_eluacommand will build only theelf(executable) file. Specify "prog" to build also theplatform-specific programming file where appropriate (for example, ona AT91SAM7X256 this results in a .bin file that can be programmed inthe CPU).

    生成bin文件


    缺少参数:-c: clean target

    其它参数详见http://www.eluaproject.net/doc/master/en_building.html


    本文介绍了如何定制elua的编译选项,主要是configuratorbuildoptions两部分。下一篇文章讲解如何使用elua,主要讲解elua的链接和shell




    展开全文
  • eLua 体系结构概述

    2019-06-15 18:22:52
    2019独角兽企业重金招聘Python工程师标准>>> ...

    eLua体系结构

    下图整体概述了eLua的体系结构


    eLua使用平台的概念表示一组具有相同内核结构的CPU,尽管这些CPU针对不同的具体外设,内存和其他一些属性分别有特定的实现。一个eLua实现给定平台的一个或多个CPU。比如,eLua的lm3s移植可以运行LMSS8962,LM3S6965,LM3S6918处理器,它们都是lm3s平台下的一部分。从上图可以看出,eLua试着尽可能在不同平台之间使用一些简单的设计规则。

    • 所有代码跟具体平台无关的是通用代码,而且它应该尽可能使用标准C语言书写。这样就可以使它在不同的体系结构和编译器间有高度的可移植性,就像Lua本身一样。
    • 所有那些不可能通用的代码(几乎都是外设和具体CPU相关)应该尽可能的使之可移植,通过使用通用接口,这种通用接口要能让eLua能在所有的平台上都能运行。这种接口称为平台接口。
    • 所有平台(和它们的外设)不能同等的创建,因为在功能上有巨大的变化。如前所述,平台接口试着组合不同平台的共同特性。如果想在一个给定平台上实现具体的一个功能的话,可以通过使用一个平台模块。这些平台模块是数据特定平台的,它们的目的就是填补平台接口和一个具体平台所提供的所有具体特性之间的缺口。

    通用代码

    下面给出了不完整的一些项可以被归类于通用代码。

    • Lua代码(很明显是)加上LTR补丁
    • 所有在eLua的组件(比如ROM文件系统,XMODEM接收代码,eLua shell,TCP/IP栈等等)
    • 所有通用模块,这些模块是lua的模块,展示了Lua平台的不同功能。
    • 通用外设支持代码,比如ADC支持代码(src/common/elua_adc.c),此代码独立于真实的ADC硬件。
    • libc代码(比如allocators和Newlib)

    上述的描述应该会给你一个好的关于通用代码的印象。注意通用代码所占据的层应该尽可能的多,也就是说,应该尽可能的有更多的通用代码。比如如下例子:

    • 如果你想给eLua添加一个新的文件系统,这很清楚应该是通用代码。这就好像代码产生的文件系统与具体依赖相关的物理媒介的处理。如果你幸运的话,你可以使用在平台接口的一些函数解决这种依赖关系。比如通过SPI控制SD卡,因为这个平台拥有SPI层。如果不是那样的话,你必须在一个独立的接口中组合与特定的平台相关的函数,然后实现它让其在所有平台上都能使用这种文件系统。这给了代码最大的移植性。
    • 如果你想为特定的ADC外设(此ADC通过SPI工作)添加驱动,那么可以使用如前所述的同样理论,尽你可能的把它写成通用代码,并且使用平台接口中的函数实现你需要的特定的SPI。
    当设计和实现一个新的组件时,记住另外一个eLua的设计目标:灵活性。用户应该可以选择在二进制文件里面包含哪些组件,实现的时候就要把这些考虑进去。同样的理论也适用于通用模块:用户能根据需要选择相应的模块。
    为了移植性的最大化,尽可能让你的代码工作在各种可能遇到的情景中。这样的例子就如在stdio/stdout/stderr中代码的处理。
    代码(src/Newlib/genstd.c),此中代码就承认了这样一个事实,终端可以通过可以多种我物理传输接口实现(RS232,SPI控制的液晶/键盘等等)。所以代码使用指针来处理收和发的函数。这样在速度和资源的消耗上是最小的,但是它关系到重要的移植部分。

    平台接口

    运用得当的话,平台接口能允许在不同的平台上书写具有可移植性的代码,包括C和Lua.平台接口的一个重要特性就是它试着组合不同平台下共同特性。比如,一个eLua所支持的平台的串口能工作在回路模式,但是其它平台不能,那么关于回路模式的支持就不能被包括进平台接口里面。
    关于平台接口使用的特别的一点是:记住它不但可以用C,还可以用Lua.平台接口主要通过通用模块允许Lua控制平台的外设,但这不是它唯一的用法。如果可能的话也可以用C语言实现一个通用模块而且也能用C控制外设。这个例子就是上面所述的打造一个新的文件系统。
    平台接口的定义总是在inc/platform.h头文件里。

    平台和移植

    所有运行eLua的平台(和i平台接口的实现)都是在它的概念层上实现的。移植就是eLua能在一个给定的平台下运行。
    移植应当包括特定的外设驱动,很多时候这都是来自CPU所在平台的支持包。这些驱动用于实现平台接口。注意下面几点:
    • 移植不要实现平台下的所有功能,只是它需要的就够了。也就是说,通过所构建的eLua二进制文件用户能够取得相应的需要控制的部分。如果你不需要SPI模块,你就不需要实现关于它的平台接口。
    • 平台接口中的一部分可以在一个文件中实现,让其够工作在所有平台上(src/common.c),这简化了一些模块的设计(比如定时器模块),这样也实现了所绑定平台的通用特性,但在所有平台上也有共同的表现(比如虚拟定时器)。如果你正在写特定平台下代码的话,尽可能不要去修改它,但是最好记住它是干什么的。
    一个平台的也许包括一个或多个独立平台的模块。正如已经解释的那样,这样做的目的是让Lua能发挥所有平台相关外设的潜能,而不只是被平台接口覆盖到的外设功能。当然外设具有特定的功能时也不会被归类到平台接口。按照惯例,所有平台的独立模块应该被组合进一个单独的模块,此模块的名字与它本身的平台是一样的。如果一个平台的独立模块增加的功能是平台接口所拥有的,它应该也是相同的名字。否则,应该给它一个不一样的名字。比如:

    • 如果在LM3S平台实现了一个新的功能关于UART模块,那么响应的模块应该被称作lms3s.uart。
    • 如果实现了一个特定外设驱动在LPC2888平台下,比如双通道DAC,给它一个特别的名字,比如lpc288x.audiodac.

    移植相关结构

    所有关于平台名字的代码(包括外设驱动)必须集中在一个叫做src/platform/<name>的文件里(比如src/platform/lm3s就是针对lm3s平台的)。每一个这样的特定于平台的子目录必须包含至少这些文件:
    type.h:其中定义的特定的数据类型。下图是关于i386平台下一些数据类型。
    typedef unsigned char u8;
    typedef signed char s8;
    typedef unsigned short u16;
    typedef signed short s16;
    typedef unsigned long u32;
    typedef signed long s32;
    typedef unsigned long long u64;
    typedef signed long long s64;

    conf.py:这是特定平台的构建文件。构建系统会使用此文件,此文件有以下目的:
    获得特定平台下将被编译成eLua二进制文件的文件列表。它们通过specific_files字符串 加载,空格隔开。而且必须给出相关文件的路径。下面的例子是关于i386平台的:
    specific_files = "boot.s common.c descriptor_tables.c gdt.s interrupt.s isr.c kb.c  monitor.c timer.c platform.c"
    # Prepend with path
    specific_files = " ".join( [ "src/platform/%s/%s" % ( platform, f ) for f in specific_files.split() ] )

    为了获得不同工具链单元的所有需要的命令来编译eLua。它们需要被声明在tools变量里。每个独立的字典里的关键字是相应平台的名字,linkcom代码linker,ascom代表assember,比如下面就是关于i386平台工具变量的设置:
    # Toolset data
     tools[ 'i386' ] = {}
     tools[ 'i386' ][ 'cccom' ] = "%s %s %s -march=i386 -mfpmath=387 -m32 -ffunction-sections -fdata-sections -fno-builtin -fno-stack-protector %s -Wall -c $SOURCE -o $TARGET" % ( toolset[ 'compile' ], opt, local_include, cdefs )
     tools[ 'i386' ][ 'linkcom' ] = "%s -nostartfiles -nostdlib -march=i386 -mfpmath=387 -m32 -T %s -Wl,--gc-sections -Wl,-e,start -Wl,--allow-multiple-definition -o $TARGET $SOURCES -lc -lgcc -lm %s" % ( toolset[ 'compile' ], ldscript, local_libs )

    注意到tools的定义使用到了toolset的定义,toolset是一个包含当前所使用工具名的字典。这也是eLua构建系统的一部分。

    为了实现一个可以接收eLua可执行文件的函数(构建步骤的结果),并且产生一个在相应平台上合适的编程文件,这个函数的名字应该被设置进tools字典中,如下所示(关于str7平台的例子代码):
    # Programming function for STR7
     def progfunc_str7( target, source, env ):
       outname = output + ".elf"
       os.system( "%s %s" % ( toolset[ 'size' ], outname ) )
       print "Generating binary image..."
       os.system( "%s -O binary %s %s.bin" % ( toolset[ 'bin' ], outname, output ) )
               
       tools[ 'str7' ][ 'progfunc' ] = progfunc_str7

    再一次提醒这个函数使用相同的toolset变量跟上面的一样。
    stacks.h:按照惯例,系统所使用的栈空间被声明在这里。下面给出的例子是来自at91sam7x平台下的一段代码:
    #define  STACK_SIZE_USR   2048
    #define  STACK_SIZE_IRQ   64
    #define  STACK_SIZE_TOTAL ( STACK_SIZE_USR + STACK_SIZE_IRQ )

    platform.c:按照惯例,平台接口的实现就在这个文件里。它也包含一些与平台无关的初始化函数。
    platform_conf.h:这是关于平台配置的头文件,其中给出了平台本身和构建相关平台的配置信息。从这个文件中,你将看到下列内容:
    • 将要被构建的组件列表
    • 将要被构建的模块列表
    • 静态配置数据
    • CPU的各种外设的数目,下面的例子代码展示了来自同一平台(lm3s)下不同CPU的外设情况。宏FORXXX被定义在conf.py文件中
    // Number of resources (0 if not available/not implemented)
    #define NUM_PIO               7
    #define NUM_SPI               1
    #ifdef FORLM3S6965
      #define NUM_UART            3
    #else
      #define NUM_UART            2
    #endif
    #define NUM_TIMER             4
    #ifndef FORLM3S6918
      #define NUM_PWM             6
    #else
      #define NUM_PWM             0
    #endif  
    #define NUM_ADC               4

    • 特定的外设配置:包括UART使能缓冲,使能和开启虚拟定时器,设置PIO配置等等。
    • 内存配置:描述了内存分布区域。此区域将被标准系统分配。两个宏MEM_START_ADDRESS 和MEM_END_ADDRESS定义了空闲RAM的起始地址和结束地址。如果你的板子有外部RAM空间,你也应该在这里定义。如果没有的话,你将只能使用内部内存,并且一般你需要使用链接定义标志end找出你空闲内存从哪里开始,下面的例子来自ATEVK1100(AVR32)开发板,它拥有内部和外部RAM
    // Allocator data: define your free memory zones here in two arrays
    // (start address and end address)
    #define MEM_START_ADDRESS     { ( void* )end, ( void* )SDRAM }
    #define MEM_END_ADDRESS       { ( void* )( 0x10000 - STACK_SIZE_TOTAL - 1 ), ( void* )( SDRAM + SDRAM_SIZE - 1 ) }

    • 网络配置:如果需要TCP/IP在你的开发板上,你需要向eLua添加网络支持。你也需要另一个叫做uip_conf.h文件用来配置uip以用于你特定的处理器架构。

    除了上述那些被要求的文件外,常见的情况是在移植中需要添加跟特定平台相关的文件:
    • 启动文件:一般用汇编书写,做一些低档次的初始化,设置堆栈指针,归零BSS区域,将ROM中DATA区域移动到RAM中,然后跳转到main函数。
    • 链接命令文件
    • CPU支持包:一般来自设备制造商,包括的代码有控制外设,配置内核,开启中断等等。

    水平有限,如有错误,给出指正。

    转载于:https://my.oschina.net/u/1587304/blog/399812

    展开全文
  • eLua学习第一课: ---和Lua脚本语言的第一次亲密接触 (作者:LL 出处:http://blog.csdn.net/tcpipstack , 欢迎转载,也请保留这段声明。谢谢!) 引子 本人有一块TI的EK-LM3S8962开发板,虽然功能还算...
  • 1、安装Ubuntu 2、安装需要的软件 sudo apt-get install git sudo apt-get install 5.1 sudo apt-get install luarocks sudo luarocks install luafilesystem sudo luarocks install lpack ...
  • 学习elua(四)--使用elua

    千次阅读 2014-09-04 11:58:55
    elua的使用,以及elua的文件系统
  • elua跑起来了

    千次阅读 2012-11-28 20:58:03
    哈哈,这几天没有开发板,但又想玩eLua,只有翻译下eLua官方文档解解燃眉之急。今天果断找同学借了一块LPC1768,512KFlash,64K的RAM,很满足eLua的需要。最重要的是,elua0.8版本已经支持LPC1768,为了速度体验一下...
  • 学习elua(一)——简介

    千次阅读 2014-09-04 10:48:09
    介绍lua编程语言以及lua在嵌入式方面的应用--elua
  • 为什么要学习elua

    千次阅读 2015-12-15 15:22:21
    我问了群大老是不是参考elua,他说是的。 因此我重新审视了一下我之前一直忽略的elua。发现elua很值得学习,具体体现在两个方面: (1)elua是如何处理软件架构的 因为elua能用在不同的arm处理器,而不仅仅是某一arm...
  • 【记录】eLua初体验

    2019-04-05 11:24:07
    认识eLuaeLua官网:http://www.eluaproject.net/eLua支持的硬件平台 http://www.eluaproject.net/overview/status生成并下载eLua固件详细步骤看这里...使用STM32 ST-LINK Utility将eLua固件(.bin文
  • 在SD/MMC卡中可读写的FAT文件系统

    千次阅读 2012-11-27 21:43:58
    FAT文件系统 关于eLua中FAT文件系统的实现是使用了来自Elm Chan的一个很好的FatFS文件包。它可以在读写模式中处理FAT12,FAT16和FAT32文件系统。...eLua在FatFS之上增加了一个平台抽象层使之可以容易的在不同的eLua
  • 学习elua(二)--编译和烧写

    千次阅读 2014-09-03 10:46:52
    在stm32f4discovery平台上, elua的编译和烧写
  • 比如,在PC机上编译eLua二进制镜像文件,但是使用此二进制文件在eLua开发板上的过程就是交叉编译。Lua也能交叉编译。通过在PC机上交叉编译Lua生成最终的二进制文件运行在你的eLua开发板上你将获得下面两个重要的好处...
  • 遇雪长安的博客目录

    千次阅读 2020-09-04 21:54:14
    一、物联网系列教程 (一)、合宙4G模块Air720SL入门教程 1、Luat二次开发教程
  • eLua学习第二课: ---在Ubuntu OS下的Lua源码安装方法 (作者:LL 出处:http://blog.csdn.net/tcpipstack , 欢迎转载,也请保留这段声明。谢谢!) 在上一课 eLua学习第一课:和Lua脚本语言的第一次亲密...
  • elua

    千次阅读 2013-03-11 14:27:04
    eLua学习第一课 Building eLua Building eLua If you decide to build your own binary image instead of <a href="http://translate.googleusercontent.com/translate_c?anno=2&depth=1&hl
  • Building eLua in Linux

    2013-03-11 14:30:48
    Building eLua in Linux Building eLua in Linux Building eLua in Linux is fairly easy. This tutorial assumes that Ubuntu is used for building, however any distro should be fine, you just need to...
  • 第二章、elua开源模块   在第一章中我们学习了下lua是什么、为什么要选择lua、lua解析器架构以及elua的设计思路。这些基本上都是一些概念和设计理念,可以说是作为本文档的一个绪论。带着大家走进elua的大门。  ...
  • 在arm上运行elua

    2014-10-24 02:28:12
    1 .在https://github.com/elua/elua网址Download Zip下载一个源代码 2
1 2 3 4 5 ... 8
收藏数 157
精华内容 62
热门标签
关键字:

eLua