μC/OS-II兼容层——让基于μC/OS-II开发的应用层无感地迁移到RT-Thread操作系统
1 概述
这是一个针对RT-Thread国产操作系统的μCOS-II操作系统兼容层,可以让基于美国Micriμm公司的μCOS-II操作系统的项目快速、无感地迁移到RT-Thread操作系统上。在兼容层的设计、编写上尊重原版μC/OS-II,保证原版μC/OS-II的原汁原味。
uCOS-II兼容层的设计和编写融合了uCOS-III兼容层的编写经验,同时与uCOS-III兼容层进行交叉检查,确保两个兼容层的可靠性。
支持版本:μC/OS-II 2.00-2.93全部版本
代码仓库地址:
https://github.com/mysterywolf/RT-Thread-wrapper-of-uCOS-II
欢迎star!
1.1 RT-Thread的其他RTOS兼容层
RT-Thread操作系统的μCOS-III兼容层:https://github.com/mysterywolf/RT-Thread-wrapper-of-uCOS-III
1.2 本兼容层适合于
之前学习过μCOS-II操作系统,意图转向学习RT-Thread国产操作系统。本兼容层可以帮您用已有的μCOS-II编程经验和习惯快速将项目跑起来,日后在应用过程中深入熟悉RT-Thread的API函数,逐步向RT-Thread过度,降低您的学习门槛和时间成本。有了本兼容层,对RT-Thread API以及编程风格的不熟悉再也不是您学习RT-Thread的阻力!
现有任务(线程)模块采用μCOS-II编写,想要用在基于RT-Thread的工程上
老项目需要从μCOS-II操作系统向RT-Thread操作系统迁移
当需要快速基于RT-Thread开发产品,但是工程师之前均采用μC/OS开发,从未用过RT-Thread的开发经验。本兼容层可以帮助让工程师快速基于μC/OS-II开发经验开发产品,简化软件的重用、缩短微控制器新开发人员的学习过程,并缩短新设备的上市时间。
避免在从μCOS-II迁移到RT-Thread时,由于μCOS-II的编程经验导致的思维定式引发的错误,这种错误一般很难被发现
例如:
软件定时器参数的不同
任务堆栈的数据类型不同
1.3 版本详细信息
1.4 官网
RT-Thread:
https://www.rt-thread.org/
文档中心:
https://www.rt-thread.org/document/site/tutorial/nano/an0038-nano-introduction/
μCOS-II:
https://www.micrium.com/
文档中心:
https://doc.micrium.com/pages/viewpage.action?pageId=10753158
2 使用
2.1 Keil-MDK仿真工程
本仿真工程是基于STM32F103平台。
Keil工程路径:rt-thread-3.1.3/bsp/stm32f103/Project.uvprojx
需要提前安装好RT-Thread Nano-3.1.3 Keil支持包.
注意:调试串口使用的是USART2,不是USART1
2.2 迁移步骤
(如果使用的是RT-Thread Nano版请参见以下步骤;若使用RT-Thread完整版可以直接跳转至Env工具自动化配置到工程中章节)
将uCOS-II文件夹内的所有文件都加入到你的工程中,最好保持原有文件夹的结构。相较于原版μCOS-II增加了
os_rtwrap.c
文件,负责对RT-Thread和μCOS-II的转换提供支持。配置
os_cfg.h
每个选项的配置说明和原版μCOS-II一致,若有不同,我已经在注释中有所解释。
原版μCOS-II配置说明可参见:
a)《嵌入式实时操作系统μC/OS-II(第二版)》北京航空航天大学出版社 邵贝贝等译
b) Micriμm公司μCOS-II在线文档μCOS-II原版定时器回调函数是在定时器线程中调用的,而非在中断中调用,因此要使用μCOS-II兼容层的软件定时器,需要将rtconfig.h中的宏定义
RT_USING_TIMER_SOFT
置1。由于兼容层采用rt-thread内核自带的堆内存分配方式,因此免去了原版uCOS-II中配置任务以及各内核对象内存池大小的步骤,遂需要在rtconfig.h中定义
RT_USING_MEMHEAP
2.3 os_cfg.h配置文件
1#define OS_TMR_CFG_TICKS_PER_SEC 10u /* Rate at which timer management task runs (Hz) */
在原版μCOS-II中,该宏定义定义了软件定时器的时基信号,这与RT-Thread的软件定时器有本质的不同,在RT-Thread中,软件定时器的时基信号就等于OS Ticks。因此为了能够将μCOS-II软件定时器时间参数转为RT-Thread软件定时器的时间参数,需要用到该宏定义。请使该宏定义与原工程使用μCOS-II时的该宏定义参数一致。需要注意的是,虽然在兼容层中定义了软件定时器的时基频率,但是在兼容层内部使用的RT-Thread软件定时器的时基频率等同于OS Ticks,因此OS_TMR
结构体的.OSTmrMatch
成员变量其保存的数值是以OS Ticks频率来计算的。
由于兼容层采用rt-thread内核自带的堆内存分配方式,因此免去了原版uCOS-II中配置任务以及各内核对象内存池大小的步骤,遂相关宏定义在兼容层中均被删除。
2.4 运行
2.4.1 手动初始化流程
本兼容层完全兼容官方给出的标准初始化流程,如果您兼容老项目,μCOS-II初始化部分无需做任何修改。
2.4.2 自动初始化流程
如果您在应用层中不想手动初始化本兼容层,可以在rtconfig.h
文件中定义PKG_USING_UCOSII_WRAPPER_AUTOINIT
宏定义。请参见 [4.2.1章节](#4.2.1 Enable uCOS-II wrapper automatically init)(如无特殊要求,建议采用该种方式)。
2.5 注意
1、μCOS-II的任务堆栈大小单位是sizeof(CPU_STK)
,而RT-Thread的线程堆栈大小单位是sizeof(rt_uint8_t)
,虽然在兼容层已经做了转换,但是在填写时一定要注意,所有涉及到μCOS-II的API、宏定义全部是按照μCOS-II的标准,即堆栈大小为sizeof(CPU_STK)
,切勿混搭!这种错误极其隐晦,一定要注意!下面是混搭的错误示例:
1ALIGN(RT_ALIGN_SIZE)2static rt_uint8_t thread2_stack[1024];//错误:混搭RT-Thread的数据类型定义线程堆栈34OSTaskCreateExt(task,5 0,6 &task_stack[TASK_SIZE-1],7 TASK_PRIO,8 0,9 &task_stack[0],
10 sizeof(thread2_stack),//任务堆栈大小(错误:这个参数的单位是sizeof(CPU_STK))
11 0,
12 OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
下面是正确写法:
1#define THREAD_STACK_SIZE 256 //正确,要通过宏定义单独定义堆栈大小,单位为sizeof(CPU_STK)2ALIGN(RT_ALIGN_SIZE)3 static CPU_STK thread2_stack[THREAD_STACK_SIZE];//正确,使用uCOS-II自己的数据类型定义任务堆栈45OSTaskCreateExt(task,6 0,7 &task_stack[TASK_SIZE-1],8 TASK_PRIO,9 0,
10 &task_stack[0],
11 THREAD_STACK_SIZE,//任务堆栈大小(正确)
12 0,
13 OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
2、本兼容层文件内含有中文注释,编码格式ANSI - GB2312,并非UTF-8编码。
3 接口
3.1 新增的API
额外实现OSMutexCreateEx()
函数,该函数并不在uCOS-II原版的函数中,OSMutexCreate()
函数中第一个参数prio
在兼容层中没有任何意义,因此该函数将OSMutexCreate()
函数中的第一个参数略去,以方便用户使用。原因是由于uCOS-II的实现方式过于落后,不支持相同任务在同一优先级。推荐用户使用这个API:
1OS_EVENT *OSMutexCreateEx (INT8U *perr);
额外实现OSQCreateEx()
函数,该函数并不在uCOS-II原版的函数中,OSQCreateEx()
函数中第一个参数size
在本兼容层中没有意义,因此该函数将OSQCreateEx()
函数中的第一个参数略去,以方便用户使用。推荐用户使用这个API:
1OS_EVENT *OSQCreateEx (INT16U size);
3.2 没有实现兼容的API (仅2个)
1INT8U OSTaskCreate (void (*task)(void *p_arg),2 void *p_arg,3 OS_STK *ptos,4 INT8U prio);56INT16U OSEventPendMulti (OS_EVENT **pevents_pend,7 OS_EVENT **pevents_rdy,8 void **pmsgs_rdy,9 INT32U timeout,
10 INT8U *perr);
3.3 钩子函数
μCOS-II的钩子函数仅对μCOS-II兼容层负责。 即如果你注册了OSTaskDelHook
函数,他仅会在调用OSTaskDel函数时被调用,不会在调用rt_thread_detach
函数时被调用(这个由RTT的钩子函数负责)。这样做是为了层次分明,防止μCOS-II兼容层插手RT-Thread内部事务。
μCOS-II的钩子函数在两个文件中实现:os_cpu_c.c
和app_hooks.c
。按照μCOS-II的思想,os_cpu_c.c
提供原始的钩子函数(即这些钩子函数被相应的函数直接调用),该文件以及其内部的钩子函数是移植工程师编写的内容,应用工程师不应该操作这个文件的内容,os_cpu_c.c
文件的钩子函数提供相应的函数指针供app_hooks.c
文件内的钩子函数注册和使用,这个文件内的钩子函数应用工程师是可以操作的。换句话说,我们有什么需要在钩子函数中调用的函数,应该放在app_hooks.c
文件中。
以下原版μCOS-II钩子函数将予以取消,由RT-Thread接管相关钩子函数接管:
1void OSTaskReturnHook (OS_TCB *p_tcb);
2void OSTaskSwHook (void);
3void OSTimeTickHook (void);
同时,上述钩子函数对应的应用级钩子函数也被取消:
1void App_TaskReturnHook (OS_TCB *p_tcb);
2void App_TaskSwHook (void);
3void App_TimeTickHook (void);
3.4 统计任务(OS_TaskStat())
在μCOS-II中,统计任务是一个系统任务,通过OS_TASK_STAT_EN
宏决定是否开启,可以在系统运行时做一些统计工作。CPU的利用率用一个0-100之间的整数表示(对应0% - 100%)。
但是RT-Thread并没有统计任务,因此需要创建一个任务来兼容原版μCOS-II的统计任务,完成上述功能。该统计任务会在兼容层初始化时自动创建,用户无需干预。用户仅需调用OSCPUUsage
全局变量即可获取当前的CPU使用率,CPU使用率的计算策略和原版μCOS-II完全一致。
3.5 任务控制块、内核对象控制块(结构体)
本兼容层尽可能的兼容任务、内核对象控制块(结构体)的每个成员变量,确保迁移过来的老程序如果直接访问这些结构体的成员变量也是可以直接运行,无需做修改的(尽管直接访问结构体的成员变量μCOS-II官方并不建议甚至十分反对)。
例如,OS_TCB
结构体的各个成员变量如下,可以看到,其包含了原版绝大多数成员变量。如果不用兼容原版成员变量,可以定义宏PKG_USING_UCOSII_WRAPPER_TINY
,可以看到OS_TCB
结构体大幅度缩减。也可以将OS_TASK_PROFILE_EN
、 OS_TASK_NAME_EN
、OS_CFG_TASK_REG_TBL_SIZE
关闭以进一步裁剪。
1typedef struct os_tcb {2 struct rt_thread OSTask;3 OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */45#if OS_TASK_CREATE_EXT_EN > 0u6 void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */7 OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */8 INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */9 INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
10 INT16U OSTCBId; /* Task ID (0..65535) */
11#endif
12
13 struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
14 struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
15
16#if OS_TASK_CREATE_EXT_EN > 0u
17#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
18 OS_TLS OSTCBTLSTbl[OS_TLS_TBL_SIZE];
19#endif
20#endif
21
22#ifndef PKG_USING_UCOSII_WRAPPER_TINY
23#if (OS_EVENT_EN)
24 OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
25#endif
26#if (OS_FLAG_EN > 0u)
27 OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
28#endif
29 INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
30#endif
31 INT8U OSTCBStat; /* Task status */
32 INT8U OSTCBStatPend; /* Task PEND status */
33 INT8U OSTCBPrio; /* Task priority (0 == highest) */
34
35#if OS_TASK_DEL_EN > 0u
36 INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */
37#endif
38
39#if OS_TASK_PROFILE_EN > 0u
40 OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */
41 INT32U OSTCBStkUsed; /* Number of bytes used from the stack */
42#endif
43
44#if OS_TASK_NAME_EN > 0u
45 INT8U *OSTCBTaskName;
46#endif
47
48#if OS_TASK_REG_TBL_SIZE > 0u
49 INT32U OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
50#endif
51} OS_TCB;
3.6 全局变量
目前,本兼容层可以使用以下μCOS-II原版全局变量(位于ucos_ii.h
)。这些全局变量的具体含义请参见[2.2节](#2.2 迁移步骤)中所列举出的参考资料。
1#if OS_TASK_STAT_EN > 0u2OS_EXT INT8U OSCPUUsage; /* Percentage of CPU used */3OS_EXT INT32U OSIdleCtrMax; /* Max. value that idle ctr can take in 1 sec. */4OS_EXT INT32U OSIdleCtrRun; /* Val. reached by idle ctr at run time in 1 sec. */5OS_EXT BOOLEAN OSStatRdy; /* Flag indicating that the statistic task is rdy */6OS_EXT OS_STK OSTaskStatStk[OS_TASK_STAT_STK_SIZE]; /* Statistics task stack */7#endif89#define OSIntNesting rt_interrupt_get_nest() /* Interrupt nesting level */
10
11#define OSLockNesting rt_critical_level() /* Multitasking lock nesting level */
12
13#define OSPrioCur rt_thread_self()->current_priority /* Priority of current task */
14
15OS_EXT BOOLEAN OSRunning; /* Flag indicating that kernel is running */
16
17OS_EXT INT8U OSTaskCtr; /* Number of tasks created */
18
19#if OS_TASK_STAT_EN > 0u
20OS_EXT volatile INT32U OSIdleCtr; /* Idle counter */
21#endif
22
23#ifdef OS_SAFETY_CRITICAL_IEC61508
24OS_EXT BOOLEAN OSSafetyCriticalStartFlag;
25#endif
26
27#define OSTCBCur ((OS_TCB*)rt_thread_self()) /* Pointer to currently running TCB */
28OS_EXT OS_TCB *OSTCBFreeList; /* Pointer to list of free TCBs */
29OS_EXT OS_TCB *OSTCBList; /* Pointer to doubly linked list of TCBs */
30OS_EXT OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1u]; /* Table of pointers to created TCBs */
31OS_EXT OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]; /* Table of TCBs */
32
33#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
34OS_EXT OS_MEM *OSMemFreeList; /* Pointer to free list of memory partitions */
35OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART];/* Storage for memory partition manager */
36#endif
37
38#if OS_TASK_REG_TBL_SIZE > 0u
39OS_EXT INT8U OSTaskRegNextAvailID; /* Next available Task register ID */
40#endif
41
42#if OS_TIME_GET_SET_EN > 0u
43#define OSTime rt_tick_get() /* Current value of system time (in ticks) */
44#endif
45
46#if OS_TMR_EN > 0u
47#define OSTmrTime rt_tick_get() /* Current timer time */
48#endif
4 Env工具自动化配置到工程中
4.1 配置方法
uCOS-II兼容层在RT-Thread Nano版中需要手动添加到工程中,但如果使用RT-Thread完整版,则可以通过Env工具进行自动化添加到工程中。方法如下:
1RT-Thread online packages
2 system packages --->
3 [*] Micrium: Micrium software products porting for RT-Thread --->
4 [*] uCOS-II Wrapper --->
5 [*] Enable uCOS-II wrapper automatically init
6 [*] Enable uCOS-II wrapper tiny mode
7 Version (latest) --->
8
4.2 可选功能说明
4.2.1 Enable uCOS-II wrapper automatically init
uCOS-II兼容层支持按照uCOS-II原版的初始化步骤进行初始化,但是在有些情况,用户不想手动初始化uCOS-II兼容层,想要直接运行应用层任务或模块,则可以使用该宏定义。在rtconfig.h
中定义本宏定义后,在RT-Thread初始化完成并进入到main线程之前会自动将uCOS-II兼容层初始化完毕,用户仅需要专注于uCOS-II的应用级任务即可。
若将该功能开启,则会在rtconfig.h
文件中中定义PKG_USING_UCOSII_WRAPPER_AUTOINIT
宏。在os_rtwrap.c
文件中的以下函数将被使能并在RT-Thread初始化时自动执行。
若没有使用完整版(即nano版)也想使用本功能,可以在rtconfig.h
中手动添加定义宏定义PKG_USING_UCOSII_WRAPPER_AUTOINIT
。
1/**2 * 自动初始化3 * uCOS-II兼容层支持按照uCOS-II原版的初始化步骤进行初始化,但是在有些情况,4 * 用户不想手动初始化uCOS-II兼容层,想要直接运行应用层任务或模块,则可以使用该5 * 宏定义。在rtconfig.h中定义本宏定义后,在RT-Thread初始化完成并进入到main线程之前6 * 会自动将uCOS-II兼容层初始化完毕,用户仅需要专注于uCOS-II的应用级任务即可。7 * The wrapper supports uCOS-II standard startup procedure. Alternatively,8 * if you want to run uCOS-II apps directly and ignore the startup procedure, 9 * you can choose this option.
10 */
11#ifdef PKG_USING_UCOSII_WRAPPER_AUTOINIT
12static int rt_ucosii_autoinit(void)
13{
14 OSInit(); /*uCOS-II操作系统初始化*/
15 OSStart(); /*开始运行uCOS-II操作系统*/
16
17#if OS_TASK_STAT_EN > 0u
18 OSStatInit();
19#endif
20 return 0;
21}
22INIT_PREV_EXPORT(rt_ucosii_autoinit);
23#endif
4.2.2 Enable uCOS-II wrapper tiny mode
如果你在使用过程中不需要兼容任务/内核对象结构体的成员变量,可使能该选项。ENV将自动在rtconfig.h
文件中定义PKG_USING_UCOSII_WRAPPER_TINY
宏定义。该模式可满足所有API的基本兼容需求,建议勾选该选项。
5 其他
5.1 联系方式
维护:Meco Man
联系方式:jiantingman@foxmail.com
5.1.2 个人GitHub主页
https://github.com/mysterywolf/RT-Thread-wrapper-of-uCOS-II
RT-Thread
让物联网终端的开发变得简单、快速,芯片的价值得到最大化发挥。Apache2.0协议,可免费在商业产品中使用,不需要公布源码,无潜在商业风险。
长按二维码,关注我们
???????????? 点击阅读原文进入RT-Thread官网
μC/OS-II兼容层——让基于μC/OS-II开发的应用层无感地迁移到RT-Thread操作系统相关推荐
- 【精彩推荐】RT-Thread操作系统μC/OS-III兼容层
RT-Thread社区作者:满鉴霆 1 概述 这是一个针对RT-Thread操作系统的μCOS-III操作系统兼容层,可以让基于美国Micriμm公司的μCOS-III操作系统的项目快速迁移到RT-T ...
- 日志平台(网关层) - 基于Openresty+ELKF+Kafka
背景介绍 1.问题现状与尝试 没有做日志记录的线上系统,绝对是给系统运维人员留下的坑.尤其是前后端分离的项目,后端的接口日志可以解决对接.测试和运维时的很多问题.之前项目上发布的接口都是通过Oracl ...
- DCOS实践分享(4):如何基于DC/OS整合SMACK(Spark, Mesos, Akka, Cassandra, Kafka)
这篇文章入选CSDN极客头条 http://geek.csdn.net/news/detail/71572 当前,要保证业务的市场竞争力,仅靠设计一个可用并且好看的产品,已经完全不能满足要求.全球消费 ...
- Qomolangma实现篇(三):兼容层设计
================================================================================ Qomolangma OpenProj ...
- egret与php相连,JavaScript_Mac OS X 系统下安装和部署Egret引擎开发环境, 概述
Egret基于TypeScript开 - phpStudy...
Mac OS X 系统下安装和部署Egret引擎开发环境 概述 Egret基于TypeScript开发的,而TypeScript编译工具tsc是基于Node.js开发的.所以在安装过程中,我们先需要对 ...
- google android win10 ios,谷歌自研 Fuchsia OS 将兼容安卓,欲和 iOS、Win10 一较高下?...
早前,有消息称谷歌准备自研一套连 Linux 内核都不需要的新 OS,其或被命名为 Fuchsia.据 XDA 报道,开发者在 AOPS 的开源库中发现了 Fuchsia OS 现身,不过,仅仅是在 ...
- 鸿蒙系统是基于微内核的全场景分布式OS,华为鸿蒙系统:基于微内核的全场景分布式OS...
原标题:华为鸿蒙系统:基于微内核的全场景分布式OS 8月9日下午华为在东莞松山湖基地举行的2019华为开发者大会上,正式推出Harmony(鸿蒙) OS. 华为发布鸿蒙系统:基于微内核的全场景分布式O ...
- android源码下载方法 批量下载 基于windows os
安装win版的Gitbash, 在这里 http://msysgit.googlecode.com/files/Git-1.6.0.2-preview20080923.exe. 选择默认安装路径(否则 ...
- Wine 4.3 发布,Windows 应用的兼容层
开发四年只会写业务代码,分布式高并发都不会还做程序员? Wine 4.3 发布了,Wine(Wine Is Not an Emulator)是一个能够在多种兼容 POSIX 接口的操作系统(诸如 ...
- opencl 加速 c语言程序_Win10应用获得面向OpenCL和OpenGL的兼容层
今年早些时候,微软宣布正在努力在Windows 10的Direct3D 12(D3D12)中启用对OpenCL和OpenGL映射层的支持.为了启用映射层,解决设备上没有OpenCL和OpenGL硬件驱 ...
最新文章
- Web的26项基本概念和技术
- python中cmd全称_【转】Python中执行cmd的三种方式
- 动态css语言less,less让css具有动态语言的特性
- python网络爬虫与信息提取视频_Python网络爬虫与信息提取入门5
- 深度学习——初识TensorFlow
- 字符串时间格式转化-java Unit
- JavaScript多线程 html5 Worker, SharedWorker
- 模块电源(一):DC-DCLDO
- 细聊智能家居开发中必备的通信协议
- html 自动打开超链接,JS、html打开超链接的几种形式
- 网站安全检测漏洞扫描系统邮件安全
- linpack测试软件,服务器性能测试(linpack,stream,netperf,iometer)
- 飞秋2013设置后无法保存配置问题
- 高通平台8953 Linux DTS(Device Tree Source)设备树详解之三(高通MSM8953 android7.1实例分析篇)
- BAT批处理如何去写Windows防火墙规则
- uc 浏览器不能打开网页
- Integer a=1与Integer a=new Integer(1)的区别
- Calendars and DateFormats should not be static
- rayfire免费中文版3D插件下载 V2020
- spring源码分析11
热门文章
- 计算机隐藏功能表格行,Excel如何一键隐藏、显示某些行(excel表格)
- 在J.U.C多线程中,AQS维护这一个CLH同步队列,这个队列遵循着FIFO原则
- python 计算字符串表达式_python计算数学表达式
- python生成的词云没有图案_python词云(词云图生成器)
- 原生小说APP源码出售,可二次开发,小说阅读app源码
- 离职时如何拒绝领导的挽留
- 【图像加密】基于LSB算法实现图像隐写含Matlab源码
- 为什么我每天都会使用坚果云
- 自定义阿里云OSS图片大小
- QT学习 实时显示时间