必看!LuatOS自定义C库全新教程,一文极速上手
今天继续讲LuatOS的开发,上一期简单说了一下如何移植LuatOS,相信很多朋友已经看过了。那么今天,我就开始讲C和Lua调用的部分教程。
LuatOS相关资料及Lua语言的官方定义,详见以下链接:
LuatOS仓库:
https://gitee.com/openLuat/LuatOS
Lua 5.3 中文参考手册:
https://www.runoob.com/manual/lua53doc/contents.html
闲谈C与Lua的调用
LuatOS自定义C库可以实现用户的自定义功能,比如一些对延时要求很高的需求,通过C进行使用会更方便快捷:
Lua使用一个虚拟栈来和C互传值,也就是说,C和Lua的数据交互是在栈上进行的。
栈的基本概念及原理
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。
它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。
如果你没接触过栈,就当它是个先入后出的大袋子,你要做的就是往里面放东西和拿东西。
无论何时Lua调用C,被调用的函数都得到一个新的栈,这个栈独立于C函数本身的栈,也独立于之前的Lua栈。它里面包含了Lua传递给C函数的所有参数,而C函数则把要返回的结果放入这个栈以返回给调用者 。
所以,C和Lua的调用说大白话就是——在栈上操作。
常用函数简介
在写函数的时候,首先使用lua_State:Lua虚拟机中的环境表、注册表、运行堆栈、虚拟机的上下文等数据。
常用函数还涉及判断函数、检查函数、取出函数、返回函数等等,如下所示:
常用判断函数
lua_isboolean :
lua_isboolean (lua_State *L, int index); 当给定索引的值是一个布尔量时,返回 1 ,否则返回 0 。
lua_isinteger :
lua_isinteger (lua_State *L, int index); 当给定索引的值是一个整数时,返回 1 ,否则返回 0 。
lua_isnil :
lua_isnil (lua_State *L, int index); 当给定索引的值是 nil 时,返回 1 ,否则返回 0 。
lua_isnumber :
lua_isnumber (lua_State *L, int index); 当给定索引的值是一个数字,或是一个可转换为数字的字符串时,返回 1 ,否则返回 0 。
lua_isstring :
lua_isstring (lua_State *L, int index); 当给定索引的值是一个字符串或是一个数字( 数字总能转换成字符串)时,返回 1 ,否则返回 0 。
常用检查函数
luaL_checkinteger :
luaL_checkinteger (lua_State *L, int arg); 检查函数的第 arg 个参数是否是一个 整数(或是可以被转换为一个整数) 并以 lua_Integer 类型返回这个整数值。
luaL_checknumber :
luaL_checknumber (lua_State *L, int arg); 检查函数的第 arg 个参数是否是一个 数字,并返回这个数字。
luaL_checkstring :
luaL_checkstring (lua_State *L, int arg); 检查函数的第 arg 个参数是否是一个 字符串并返回这个字符串。
luaL_checklstring :
luaL_checklstring (lua_State *L, int arg, size_t *l); 检查函数的第 arg 个参数是否是一个 字符串,并返回该字符串;如果 l 不为 NULL , 将字符串的长度填入 *l。字符串内可以是任意二进制数据,包括零字符。
常用取出函数
luaL_optinteger :
luaL_optinteger (lua_State *L , int arg , lua_Integer d);
如果函数的第 arg 个参数是一个 整数(或可以转换为一个整数), 返回该整数。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。
luaL_optnumber :
luaL_optnumber (lua_State *L, int arg, lua_Number d);
如果函数的第 arg 个参数是一个 数字,返回该数字。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。
luaL_optstring :
luaL_optstring (lua_State *L, int arg, const char *d);
如果函数的第 arg 个参数是一个 字符串,返回该字符串。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。
luaL_optlstring :
luaL_optlstring (lua_State *L , int arg , const char *d , size_t *l);
如果函数的第 arg 个参数是一个 字符串,返回该字符串。若该参数不存在或是 nil, 返回 d。除此之外的情况,抛出错误。若 l 不为 NULL, 将结果的长度填入 *l 。字符串内可以是任意二进制数据,包括零字符。
常用返回函数
lua_pushboolean :
void lua_pushboolean (lua_State *L, int b); 把 b 作为一个布尔量压栈。
lua_pushinteger:
lua_pushinteger (lua_State *L, lua_Integer n); 把值为 n 的整数压栈。
lua_pushnumber :
lua_pushnumber (lua_State *L, lua_Number n); 把一个值为 n 的浮点数压栈。
lua_pushstring :
lua_pushstring (lua_State *L, const char *s); 将指针 s 指向的零结尾的字符串压栈。如果 s 为 NULL,将 nil 压栈并返回 NULL。
lua_pushlstring :
lua_pushlstring (lua_State *L, const char *s, size_t len); 把指针 s 指向的长度为 len 的字符串压栈。字符串内可以是任意二进制数据,包括零字符。
注意:
xxxlstring与xxxstring的区别,前者传二进制数据是必须的!!!
第一个函数
创建自定义模块框架:
在LuatOS源码目录下找到luat/mouldes文件夹,新建一个名为luat_lib_xxx.c的文件;
在C文件中先引入"luat_base.h",这个头文件里面包含了我们需要用到的函数。
创建自定义函数:
static int l\_jiaFa(lua\_State \*L)
//函数名一般写l\_xxx ,
这样可以直观的知道这是一个和lua交互的函数
{
int a = luaL\_checkinteger(L, 1);
//取出输入的第一个值(int类型)
int b = luaL\_optinteger(L, 2, 0);
//取出输入的第二个值(int类型)
lua\_pushinteger(L, a + b); //返回ab之和
return 1; //返回一个值
}
分析一下:
函数输入(lua_State *L),前面提到了,C和Lua的调用是在栈里进行的;
a的值是通过luaL_checkinteger(L, 1);获得的,这是一个检查函数,输入的第一个值是int类型的时候,函数会把值返回,在这里就是赋值给了a。
b的值是通过luaL_optinteger(L, 2, 0);获得的,只是一个取出函数,取出的对象是lua函数输入的第二个值,如果第二个值不是int类型,则会返回0。
lua_pushinteger(L, a + b); 是向lua函数返回值(int类型)
最后的return 1; 代表这个函数的返回值只有一个。
注册函数:
#include "rotable.h"
static const rotable\_Reg exa\[\] =
//exa\[\]就是你的自定义库名字
{
{"jiaFa", l\_jiaFa, 0},
//第一个是lua要调用的函数名字,
第二个是对应的c函数,
第三个是lua\_Integer值,一般写0
{NULL, NULL, 0}};
//结尾要有这一行,代表没有其他的函数了
LUAMOD\_API int luaopen\_exa(lua\_State \*L)
//注册一个LUAMOD\_API,之后要写入luat\_base.h
{
luat\_newlib(L, exa);
//创建一张新的表,并把列表L中的函数注册进去
return 1;
}
这里重点是rotable_Reg:
typedef struct rotable\_Reg
{
char const\* name;
lua\_CFunction func;
lua\_Integer value;
}
rotable\_Reg;
你需要写几个函数,就在这个结构体里写,最后以 {NULL, NULL, 0}结尾,代表结束。
写完上面这一段之后,在luat_base.h里面加上LUAMOD_API int luaopen_exa(lua_State *L);
启用自定义模块
打开芯片/模组对应的base文件,例如air302的base文件是:
https://gitee.com/openLuat/LuatOS/blob/master/bsp/air302/src/luat_air302_base.c
根据上一期的移植教程,我们在loadedlibs[]里加入 {"exa", luaopen_exa}, 代表启用该模块。
编译新固件
这里不细说了,在LuatOS仓库里都有编译说明,air302我之前也做过编译教程,详见:
https://doc.openluat.com/article/2047
编写Lua脚本
\-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "exademo"
VERSION = "1.0.0"
\-- sys库是标配
\_G.sys = require("sys")
local function exatext()
log.info("jiafa", exa.jiaFa(2,10))
end
sys.timerLoopStart(exatext, 1000)
\-- 用户代码已结束---------------------------------
\-- 结尾总是这一句
sys.run()
\-- sys.run()之后后面不要加任何语句!!!!!
进阶函数
当前面的步骤做完,确保正确运行后,再往下看进阶部分。
返回字符串
static int l\_reString(lua\_State \*L)
{
if (!lua\_isnumber(L, 1))
//如果输入的是字符串(不是number自然就是字符串咯)
{
const char \*c = luaL\_optstring(L, 1, "");
lua\_pushstring(L, c);
//如果第一个输入值是string,直接返回去
return 1;
}
else
{
lua\_pushstring(L,"nostring");
//到这里就代表输入的第一个值不是字符串,返回nostring
return 1;
}
}
这一部分较基础部分多了判断函数,根据注释了解一下即可,不难的。
多输入多返回
static int l\_reMore(lua\_State \*L)
{
if (lua\_isinteger(L, 1))
//判断输入的第一个值是不是int
{
lua\_pushboolean(L, 1); //返回true
const char \*st = luaL\_optstring(L, 2, "nostring");
lua\_pushstring(L, st); //返回字符串
return 2;
}
else
{
lua\_pushboolean(L, 0); //返回flase
return 1;
}
}
这里边看代码边说明:
1)函数首先判断lua输入的第一个值类型,如果是int类型,那么返回第一个bool类型值;
2)现在已经确定第一个值是int类型了,那么取出lua输入的第二个值,如果是string就赋值给st,如果是nil那就把“nostring”赋值给st;
3)返回st,使用函数为lua_pushstring(L, st);
4)分析返回值的定义——我们可以看到在if为真的大括号里,我们先返回了bool类型的值,然后返回了string类型的值,一共返回了两个值,那么return的值我们写为2;在else的大括号里,我们只返回了一个bool类型的值,那么return的值即为1。
必看!LuatOS自定义C库全新教程,一文极速上手的更多相关文章
- 一些必看的jQuery导航插件和教程
导航是一个网站最重要的元素之一,您必须遵循统一的风格来设计您的网站,特别是导航的风格,让用户可以轻松地找到他们想要的内容.这里最大的部分是开发一个菜单,即要直观易用,又要符合你网站的设计风格.本文列举 ...
- 【Pycharm使用者必看】自定义【光标快速定位到行尾】的按键
1.问题描述 使用Pycharm写代码时,有很多比较方便的快捷键,比如:Shift+Enter快速切换到下一行, 但每次切换到多个括号外或者想移动到行尾,就必须按 End 键或者鼠标点击, 这样操作幅 ...
- 【程序】必看干货:Photon多人游戏开发教程
PUN介绍 http://vibrantlink.com/ 入门 Photon Unity Networking(首字母缩写PUN)是一个Unity多人游戏插件包.它提供了身份验证选项.匹配,以及快速 ...
- (转载)Android之有效防止按钮多次重复点击的方法(必看篇)
为了防止测试妹子或者用户频繁点击某个按钮,导致程序在短时间内进行多次数据提交or数据处理,那到时候就比较坑了~ 那么如何有效避免这种情况的发生呢? 我的想法是,判断用户点击按钮间隔时间,如果间隔时间太 ...
- 来了!STM32移植LuatOS,潘多拉示例全新教程
进击的五月,继上期<使用Air724UG制作简易贪吃蛇>教程之后,@打盹的消防车 又为大家带来基于STM32的潘多拉LuatOS移植全新教程: 为什么使用潘多拉作为教程呢? STM32不能 ...
- 新手必看,史上最全的iOS开发教程集锦,没有之一!
最近大火的iPhone XS Max和iPhone XS,不知道有没有同学已经下手了呢?一万三的价位确实让很多人望而却步啊.据说为了赢得中国的用户,专门出了双卡双待的,可想而知中国市场这块“肥肉”人人 ...
- 小白必看Python视频基础教程
Python的排名从去年开始就借助人工智能持续上升,现在它已经成为了第一名.Python的火热,也带动了工程师们的就业热.可能你也想通过学习加入这个炙手可热的行业,可以看看Python视频基础教程,小 ...
- 8个设计师必看的免费UI图标设计资源站
图标是我们日常APP及网页设计过程中必不可少的元素之一,通过小小的图标,可以快速方便的实现视觉引导和功能划分.在创作时,我们需要寻找各种各样的图标来满足自己的设计需求,非常浪费时间和精力.今天,小编给 ...
- 高级Java必看的10本书
1.深入理解Java虚拟机:JVM高级特性与最佳实践 本书共分为五大部分,围绕内存管理.执行子系统.程序编译与优化.高效并发等核心主题对JVM进行了全面而深入的分析,深刻揭示了JVM的工作原理. 2. ...
随机推荐
- Django(5)django配置信息
前言 Django的配置文件settings.py用于配置整个网站的环境和功能,核心配置必须有项目路径.密钥配置.域名访问权限.App列表.中间件.资源文件.模板配置.数据库的连接方式 基本配置信息 ...
- 10.qml-组件、Loader、Component介绍
1.组件介绍 一个组件通常由一个qml文件定义(单独文件定义组件), 实际也可以在qml里面通过Component对象来嵌入式定义组件 (4小节讲解). Component对象封装的内容默认不会显示, ...
- 用户对象/GDI对象/内核对象
对象的分类 Windows的对象可以分为三种,分别是用户对象,GDI对象和内核对象.系统使用用户对象支持窗口管理,使用GDI对象支持图形,并使用内核对象支持内存管理,进程执行和进程间通信(IPC) . ...
- 浅谈CRM系统的选型和实施
CRM的本质是最大化利用企业的现有资源来提供客户所需的产品,保证提供给客户最好的服务,帮助销售人员提高客户转化率,储存所有重要的客户信息,帮助企业深入挖掘潜在客户等等. 对于企业来说,即使处于同一行业 ...
- 自定义元类 __call__,__init__,__new__总结
只要对象能被调用 产生对象的类里必然有__call__方法 在调用类时,必定先触发type里的__call__ __call__下有: 1.产生对象的object.__new__ 2..被调用的类自己 ...
- (五)Jira Api对接:修改任务状态
项目迭代结束后我们需要把sprint下面的story.task任务状态修改到结束状态,如果手动修改会花费不少时间,本文就介绍如何通过jira api自动修改任务状态,提高工作效率. 一.查看任务工作流 ...
- QTableWidget - 基础讲解(1)
转载:http://www.cnblogs.com/fuqia/p/8904196.html QTableWidget是QT程序中常用的显示数据表格的空间,很类似于VC.C#中的DataGrid.说到 ...
- [Linux-网络性能测试] -- netperf测试
[Linux-网络性能测试] -- netperf测试 2017.01.17 14:15:54字数 1599阅读 4400 简述 Netperf是一种网络性能的测量工具(由惠普公司开发的,测试网络栈. ...
- property - 必应词典 美['prɑpərti]英['prɒpə(r)ti] n.属性;财产;财产权;【戏】道具
英语 (已检测) 自动检测 阿拉伯语 自动检测 爱尔兰语 自动检测 爱沙尼亚语 自动检测 保加利亚语 自动检测 冰岛语 自动检测 波兰语 自动检测 波斯尼亚语(拉丁语) 自动检测 波斯语 自动检测 丹 ...
- python3 使用random函数批量产生注册邮箱
'''你是一个高级测试工程师,现在要做性能测试,需要你写一个函数,批量生成一些注册使用的账号. 1.产生的账号是以@163.com结尾,长度由用户输,产生多少条也由用户输入,2.用户名不能重复,用户名 ...