进击的五月,继上期《使用Air724UG制作简易贪吃蛇》教程之后,@打盹的消防车 又为大家带来基于STM32的潘多拉LuatOS移植全新教程:

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

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

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

@

Luat OS架构分析

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

LuatOS架构图


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

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

LuatOS文件目录


  • bsp:

    bsp文件里存放着各种已经适配了的芯片。目前有:

    - Air001

    - Air100ST(STM32F4)

    - Air302(NB-IoT)

    - Air640W(Wi-Fi)

    - Air724UG(4G Cat.1)

    - Win32

    只有这些么?当然不是。W800(Wi-Fi+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移植思路


介绍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) { 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:

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:

新建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); }}

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

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

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

脚本运行成功,至此LuatOS基础移植成功。

外设的适配


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

网络接口的适配


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

本期移植教程就讲到这里了.

源码下载

上海合宙通信模块 - 合宙Luat,让万物互联更简单

来了!STM32移植LuatOS,潘多拉示例全新教程的更多相关文章

  1. STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085和串口只发送数据不能接收数据问题

    STM32移植RT-Thread后的串口在调试助手上出现:(mq != RT_NULL) assert failed at rt_mq_recv:2085的问题讨论:http://www.rt-thr ...

  2. 图论(Tarjan缩点):BZOJ 1194: [HNOI2006]潘多拉的盒子

    1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 344  Solved: 181[Submit][Stat ...

  3. Hello China操作系统STM32移植指南(一)

    Hello China操作系统移植指南 首先说明一下,为了适应更多的文化背景,对Hello China操作系统的名字做了修改,修改为"Hello X",或者连接在一起,写为&quo ...

  4. BZOJ 1194: [HNOI2006]潘多拉的盒子( BFS + tarjan + dp )

    O(S²)枚举2个诅咒机, 然后O(n²)BFS去判断. 构成一个有向图, tarjan缩点, 然后就是求DAG的最长路.. ------------------------------------- ...

  5. 阿里安全潘多拉实验室首先完美越狱苹果iOS 11.2

    苹果官方对iOS 11的评价是"为iPhone带来巨大进步,让iPad实现里程碑式飞跃."但为了不断修复Bug,苹果于12月2日推出最新的iOS 11.2,修复了Google安全人 ...

  6. 独家探寻阿里安全潘多拉实验室,完美越狱苹果iOS11.2.1

    知道如何从攻击的视角去发现漏洞,才能建立更安全的体系,促进了整个生态的良性发展.以阿里安全潘多拉实验室为例,在对移动系统安全研究的过程中,把研究过程中发现的问题上报给厂商,促进系统安全性的提升. 小编 ...

  7. 【BZOJ-1194】潘多拉的盒子 拓扑排序 + DP

    1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 456  Solved: 215[Submit][Stat ...

  8. 【转】hurry_liu 大神STM32移植contiki入门之一:系统介绍和开发环境搭建

    前言: 由于项目的原因,需要在LPC1788(STM32 cortex-M3)上面跑contiki. 之前没有涉及到contiki,不知其为何物.不过这个不是难事,做IT的,每每遇到新事物,都不会处理 ...

  9. [BZOJ1194][HNOI2006][强连通分量Tarjan+dfs]潘多拉的盒子

    [BZOJ1194][HNOI2006]潘多拉的盒子 Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语 ...

随机推荐

  1. dispatcherServlet-servlet.xml(SSM maven 项目)

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  2. 【转】风控中的特征评价指标(三)——KS值

    转自:https://zhuanlan.zhihu.com/p/79934510 风控业务背景 在风控中,我们常用KS指标来评估模型的区分度(discrimination).这也是风控模型同学最为追求 ...

  3. [PowerShell] 快速入门, 基本语法, 常用类型, 函数, .NET 互操作

    PowerShell 快速入门 开始之前, 我们认定你已经有一定的编程基础, 熟悉 .NET 中的类型与对象. 此文章对于 .NET 开发者来说更简单哦! 在 PowerShell 中, 几乎一切都是 ...

  4. JavaI/O流汇总

    Java中常见流学习汇总 流的含义 流在Java中是指计算中流动的缓冲区. 从外部设备流向中央处理器的数据流成为"输入流",反之成为"输出流". 字符流和字节流 ...

  5. 33.2.NIO

    4.1概述[理解] BIO Blocking IO,阻塞型IO NIO No Blocking IO,非阻塞型IO 阻塞IO的弊端 在等待的过程中,什么事也做不了 非阻塞IO的好处 不需要一直等待,当 ...

  6. gdb调试coredump(使用篇)

    gdb调试coredump(使用篇) 看到一个非常好的介绍coredump的文章,做个记录, 参考链接: https://blog.csdn.net/sunxiaopengsun/article/de ...

  7. 解决 Ubuntu 无法使用 root 用户进行 ssh 远程登陆

    解决 Ubuntu 无法使用 root 用户进行 ssh 远程登陆 操作系统 Ubuntu 20.04.2 LTS 一.修改sshd配置文件 //打开 /etc/ssh/sshd_config 配置文 ...

  8. Linux_网络基础管理

    一.网卡的命名 1.传统网卡命名 eth0.eth1.eth2.eth3......... wlan0.wlan1.waln2.wlan3......... 2.RHEL7命名机制 systemd对网 ...

  9. unity texture 占用内存大小对比

    打包多种类型的项目,空项目和10张放在Resources文件夹中的图为比较案例.以下是比较数据. IPHONE: 1.空项目----空间占用量42.3MB----IPA大小10MB 2.10张1200 ...

  10. Navigation DialogFragment展示dialog

    如果按照一般fragment的写法: 在nav_config中 <fragment android:id="@+id/fragment_crime_detail" andro ...