μC/OS-II 通过 uCOS_II.H 中定义的 OS_EVENT 数据结构来维护一个事件控制块的所有信息
[程序清单 L6.1],也就是本章开篇讲到的事件控制块 ECB。该结构中除了包含了事件本身的定
义,如用于信号量的计数器,用于指向邮箱的指针,以及指向消息队列的指针数组等,还定义
了等待该事件的所有任务的列表。
typedef struct {
void *OSEventPtr; /* 指向消息或者消息队列的指针 */
INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待任务列表 */
INT16U OSEventCnt; /* 计数器(当事件是信号量时) */
INT8U OSEventType; /* 时间类型 */
INT8U OSEventGrp; /* 等待任务所在的组 */
} OS_EVENT;
.OSEventPtr 指针,只有在所定义的事件是邮箱或者消息队列时才使用。 当所定义的事件是
邮箱时,它指向一个消息,而当所定义的事件是消息队列时,它指向一个数据结构,详见 6.06
节消息邮箱和 6.07 节消息队列。
.OSEventTbl[] 和  .OSEventGrp 很像前面讲到的 OSRdyTbl[]和 OSRdyGrp,只不过前两者
包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。(见 3.04 节 就
绪表)
.OSEventCnt 当事件是一个信号量时,.OSEventCnt 是用于信号量的计数器,(见 6.05 节
信号量)。
e .OSEventType 定义了事件的具体类型。它可以是信号量(OS_EVENT_SEM)、邮箱
(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。用户要根据该域的具体值
来调用相应的系统函数,以保证对其进行的操作的正确性。

下面的代码将一个任务放到事件的等待任务列表中。
程序清单  L6.2 ——将一个任务插入到事件的等待任务列表中
pevent->OSEventGrp |= OSMapTbl[prio >> 3];
pevent->OSEventTbl[prio >> 3] |= OSMapTbl[prio & 0x07];

程序清单  L6.3  从等待任务列表中删除一个任务
if ((pevent->OSEventTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0) {
pevent->OSEventGrp &= ~OSMapTbl[prio >> 3];
}

程序清单  L6.4  在等待任务列表中查找最高优先级的任务
y = OSUnMapTbl[pevent->OSEventGrp];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
prio = (y << 3) + x;

void OSEventWaitListInit (OS_EVENT *pevent)
{
INT8U i;
pevent->OSEventGrp = 0x00;
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
pevent->OSEventTbl[i] = 0x00;
}
}

程序清单  L6.6  使一个任务进入就绪状态
void OSEventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
{
OS_TCB *ptcb;
INT8U x;
INT8U y;
INT8U bitx;
INT8U bity;
INT8U prio;
y = OSUnMapTbl[pevent->OSEventGrp];  (1)
bity = OSMapTbl[y];  (2)
x = OSUnMapTbl[pevent->OSEventTbl[y]];  (3)
bitx = OSMapTbl[x];  (4)
prio = (INT8U)((y << 3) + x);  (5)
if ((pevent->OSEventTbl[y] &= ~bitx) == 0) {  (6)
pevent->OSEventGrp &= ~bity;
}
ptcb = OSTCBPrioTbl[prio];  (7)
ptcb->OSTCBDly = 0;  (8)
ptcb->OSTCBEventPtr = (OS_EVENT *)0;  (9)
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
ptcb->OSTCBMsg = msg;  (10)
#else
msg = msg;
#endif
ptcb->OSTCBStat &= ~msk;  (11)
if (ptcb->OSTCBStat == OS_STAT_RDY) {  (12)
OSRdyGrp |= bity;  (13)
OSRdyTbl[y] |= bitx;
}
}

程序清单  L6.7  使一个任务进入等待状态
void OSEventTaskWait (OS_EVENT *pevent)
{
OSTCBCur->OSTCBEventPtr = pevent; (1)
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0) { (2)
OSRdyGrp &= ~OSTCBCur->OSTCBBitY;
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; (3)
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}

程序清单  L6.8  因为等待超时将任务置为就绪状态
void OSEventTO (OS_EVENT *pevent)
{
if ((pevent->OSEventTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0)
{  (1)
pevent->OSEventGrp &= ~OSTCBCur->OSTCBBitY;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY;  (2)
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;  (3)
}

信号量
μC/OS-II 中的信号量由两部分组成:一个是信号量的计数值,它是一个 16 位的无符号整
数 (0 到 65,535 之间) ; 另一个是由等待该信号量的任务组成的等待任务表。 用户要在 OS_CFG.H
中将 OS_SEM_EN 开关量常数置成 1,这样μC/OS-II 才能支持信号量。信号量
μC/OS-II 中的信号量由两部分组成:一个是信号量的计数值,它是一个 16 位的无符号整
数 (0 到 65,535 之间) ; 另一个是由等待该信号量的任务组成的等待任务表。 用户要在 OS_CFG.H
中将 OS_SEM_EN 开关量常数置成 1,这样μC/OS-II 才能支持信号量。

程序清单  L6.9  建立一个信号量
OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent;
OS_ENTER_CRITICAL();
pevent = OSEventFreeList;  (1)
if (OSEventFreeList != (OS_EVENT *)0) {  (2)
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {  (3)
pevent->OSEventType = OS_EVENT_TYPE_SEM;  (4)
pevent->OSEventCnt = cnt;  (5)
OSEventWaitListInit(pevent);  (6)
}
return (pevent);  (7)
}

程序清单  L6.10  等待一个信号量
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {  (1)
OS_EXIT_CRITICAL();
*err = OS_ERR_EVENT_TYPE;
}
if (pevent->OSEventCnt > 0) {  (2)
pevent->OSEventCnt--;  (3)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
6-15
} else if (OSIntNesting > 0) {  (4)
OS_EXIT_CRITICAL();
*err = OS_ERR_PEND_ISR;
} else {
OSTCBCur->OSTCBStat |= OS_STAT_SEM;  (5)
OSTCBCur->OSTCBDly = timeout;  (6)
OSEventTaskWait(pevent);  (7)
OS_EXIT_CRITICAL();
OSSched();  (8)
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStat & OS_STAT_SEM) {  (9)
OSEventTO(pevent);  (10)
OS_EXIT_CRITICAL();
*err = OS_TIMEOUT;
} else {
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;  (11)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
}
}

程序清单  L6.11  发出一个信号量
INT8U OSSemPost (OS_EVENT *pevent)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {  (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
if (pevent->OSEventGrp) {  (2)
OSEventTaskRdy(pevent, (void *)0, OS_STAT_SEM);  (3)
OS_EXIT_CRITICAL();
OSSched();  (4)
return (OS_NO_ERR);
} else {
if (pevent->OSEventCnt < 65535) {
pevent->OSEventCnt++;  (5)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
} else {
OS_EXIT_CRITICAL();
return (OS_SEM_OVF);
}
}
}

程序清单  L6.12  无等待地请求一个信号量
INT16U OSSemAccept (OS_EVENT *pevent)
{
INT16U cnt;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {  (1)
OS_EXIT_CRITICAL();
return (0);
}
cnt = pevent->OSEventCnt;  (2)
if (cnt > 0) {  (3)
pevent->OSEventCnt--;  (4)
}
OS_EXIT_CRITICAL();
return (cnt);  (5)
}

程序清单  L6.13  查询一个信号量的状态
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *pdata)
{
INT8U i;
INT8U *psrc;
INT8U *pdest;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {  (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
pdata->OSEventGrp = pevent->OSEventGrp;  (2)
psrc = &pevent->OSEventTbl[0];
pdest = &pdata->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*pdest++ = *psrc++;
}
pdata->OSCnt = pevent->OSEventCnt;  (3)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}

程序清单  L6.14  建立一个邮箱
OS_EVENT *OSMboxCreate (void *msg)
6-20
{
OS_EVENT *pevent;
OS_ENTER_CRITICAL();
pevent = OSEventFreeList;
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {
pevent->OSEventType = OS_EVENT_TYPE_MBOX; (1)
pevent->OSEventPtr = msg; (2)
OSEventWaitListInit(pevent);
}
return (pevent); (3)
}

程序清单  L6.15  等待一个邮箱中的消息
void *OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
void *msg;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  (1)
OS_EXIT_CRITICAL();
*err = OS_ERR_EVENT_TYPE;
return ((void *)0);
}
msg = pevent->OSEventPtr;
if (msg != (void *)0) {  (2)
pevent->OSEventPtr = (void *)0;  (3)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else if (OSIntNesting > 0) {  (4)
OS_EXIT_CRITICAL();
*err = OS_ERR_PEND_ISR;
} else {
OSTCBCur->OSTCBStat |= OS_STAT_MBOX;  (5)
OSTCBCur->OSTCBDly = timeout;
OSEventTaskWait(pevent);
OS_EXIT_CRITICAL();
OSSched();
OS_ENTER_CRITICAL();
if ((msg = OSTCBCur->OSTCBMsg) != (void *)0) {  (6)
OSTCBCur->OSTCBMsg = (void *)0;
OSTCBCur->OSTCBStat = OS_STAT_RDY;
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
} else if (OSTCBCur->OSTCBStat & OS_STAT_MBOX) {  (7)
OSEventTO(pevent);  (8)
OS_EXIT_CRITICAL();
msg = (void *)0;  (9)
*err = OS_TIMEOUT;
} else {
6-22
msg = pevent->OSEventPtr;  (10)
pevent->OSEventPtr = (void *)0;
(11)
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;  (12)
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
}
return (msg);
}

程序清单  L6.16  向邮箱中发送一条消息
INT8U OSMboxPost (OS_EVENT *pevent, void *msg)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
if (pevent->OSEventGrp) {  (2)
OSEventTaskRdy(pevent, msg, OS_STAT_MBOX);  (3)
OS_EXIT_CRITICAL();
OSSched();  (4)
return (OS_NO_ERR);
} else {
if (pevent->OSEventPtr != (void *)0) {  (5)
OS_EXIT_CRITICAL();
return (OS_MBOX_FULL);
} else {
pevent->OSEventPtr = msg;  (6)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
}
}

程序清单  L6.16  向邮箱中发送一条消息
INT8U OSMboxPost (OS_EVENT *pevent, void *msg)
{
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
if (pevent->OSEventGrp) {  (2)
OSEventTaskRdy(pevent, msg, OS_STAT_MBOX);  (3)
OS_EXIT_CRITICAL();
OSSched();  (4)
return (OS_NO_ERR);
} else {
if (pevent->OSEventPtr != (void *)0) {  (5)
OS_EXIT_CRITICAL();
return (OS_MBOX_FULL);
} else {
pevent->OSEventPtr = msg;  (6)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}
}
}

程序清单  L6.17  无等待地从邮箱中得到消息
void *OSMboxAccept (OS_EVENT *pevent)
{
void *msg;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  (1)
OS_EXIT_CRITICAL();
return ((void *)0);
}
msg = pevent->OSEventPtr;  (2)
if (msg != (void *)0) {  (3)
pevent->OSEventPtr = (void *)0;  (4)
}
OS_EXIT_CRITICAL();
return (msg);  (5)
}

程序清单  L6.18  查询邮箱的状态
INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *pdata)
{
INT8U i;
6-25
INT8U *psrc;
INT8U *pdest;
OS_ENTER_CRITICAL();
if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) {  (1)
OS_EXIT_CRITICAL();
return (OS_ERR_EVENT_TYPE);
}
pdata->OSEventGrp = pevent->OSEventGrp;  (2)
psrc = &pevent->OSEventTbl[0];
pdest = &pdata->OSEventTbl[0];
for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
*pdest++ = *psrc++;
}
pdata->OSMsg = pevent->OSEventPtr;  (3)
OS_EXIT_CRITICAL();
return (OS_NO_ERR);
}

程序清单  L6.19  使用邮箱作为二值信号量
OS_EVENT *MboxSem;
void Task1 (void *pdata)
{
INT8U err;
for (;;) {
OSMboxPend(MboxSem, 0, &err); /* 获得对资源的访问权 */
6-26
.
. /* 任务获得信号量,对资源进行访问 */
.
OSMboxPost(MboxSem, (void*)1); /* 释放对资源的访问权 */
}
}

程序清单  L6.21  建立一个消息队列
OS_EVENT *OSQCreate (void **start, INT16U size)
{
OS_EVENT *pevent;
OS_Q *pq;
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; (1)
if (OSEventFreeList != (OS_EVENT *)0) {
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; (2)
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) {
OS_ENTER_CRITICAL();
pq = OSQFreeList; (3)
if (OSQFreeList != (OS_Q *)0) {
OSQFreeList = OSQFreeList->OSQPtr;
}
OS_EXIT_CRITICAL();
if (pq != (OS_Q *)0) {
pq->OSQStart = start; (4)
pq->OSQEnd = &start[size];
pq->OSQIn = start;
pq->OSQOut = start;
pq->OSQSize = size;
pq->OSQEntries = 0;
pevent->OSEventType = OS_EVENT_TYPE_Q; (5)
pevent->OSEventPtr = pq; (6)
OSEventWaitListInit(pevent); (7)
} else {
OS_ENTER_CRITICAL();
pevent->OSEventPtr = (void *)OSEventFreeList; (8)
6-33
OSEventFreeList = pevent;
OS_EXIT_CRITICAL();
pevent = (OS_EVENT *)0;
}
}
return (pevent); (9)
}

9.03 OS_CPU.H 文件
OS_CPU.H 文件中包含与处理器相关的常量,宏和结构体的定义。程序清单L9.2是为80x86编
写的OS_CPU.H文件的内容。

9.04 OS_CPU_A.ASM
μC/OS-II 的移植需要用户改写OS_CPU_A.ASM中的四个函数:
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
9.04.01  OSStartHighRdy()
该函数由SStart()函数调用,功能是运行优先级最高的就绪任务,在调用OSStart()之前,
用户必须先调用OSInit(),并且已经至少创建了一个任务(请参考OSTaskCreate()和
OSTaskCreateExt()函数)。OSStartHighRdy()默认指针OSTCBHighRdy指向优先级最高就绪任务
的任务控制块(OS_TCB)(在这之前OSTCBHighRdy已由OSStart()设置好了)。图F9.3给出了由
函 数 OSTaskCreate() 或 OSTaskCreateExt() 创 建 的 任 务 的 堆 栈 结 构 。 很 明 显 ,
OSTCBHighRdy->OSTCBStkPtr指向的是任务堆栈的顶端。

程序清单L 9.3 OSStartHighRdy().
_OSStartHighRdy PROC FAR
MOV AX, SEG _OSTCBHighRdy ; 载入 DS
MOV DS, AX ;
LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr
(1)
MOV SS, ES:[BX+2] ;
MOV SP, ES:[BX+0] ;
;
POP DS ; 恢复任务环境  (2)
POP ES ;  (3)
POPA ;  (4)
;
IRET ; 运行任务  (5)
_OSStartHighRdy ENDP

9.05 OS_CPU_C.C
μC/OS-II 的移植需要用户改写OS_CPU_C.C中的六个函数:
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
OSTaskResume ()的返回值为下述之一:
?  OS_NO_ERR:函数调用成功。
?  OS_TASK_RESUME_PRIO:要唤醒的任务不存在。
?  OS_TASK_NOT_SUSPENDED:要唤醒的任务不在挂起状态。
?  OS_PRIO_INVALID:参数指定的优先级大于或等于 OS_LOWEST_PRIO。

OS_MAX_EVENTS 定义系统中最大的事件控制块的数量。系统中的每一个消息邮箱,消息队
列,信号量都需要一个事件控制块。例如,系统中有 10 个消息邮箱,5 个消息队列,3 个信号
量,则 OS_MAX_EVENTS 最小应该为 18。只要程序中用到了消息邮箱,消息队列或是信号量,
则 OS_MAX_EVENTS 最小应该设置为 2。

μC/OS-II邵贝贝节选相关推荐

  1. μC/OS-III_与II的对比(邵贝贝)

    查看全文 http://www.taodudu.cc/news/show-3188338.html 相关文章: 嵌入式实时操作系统μC/OS II(一) μCOS-II中的任务就绪表及任务调度 μC/ ...

  2. 再读 ucosII源码(邵贝贝):内核结构

    本章给出μC/OS-Ⅱ的主要结构概貌: - μC/OS-Ⅱ是怎样处理临界段代码的; - 什么是任务,怎样把用户的任务交给μC/OS-Ⅱ; - 任务是怎样调度的; - 应用程序CPU的利用率是多少,μC ...

  3. UART0串口编程(四):UART0串口编程之在UC/OS—II中遭遇的危机

    UART0串口编程之在UC/OS-II中遭遇的危机 一.潜在的危机 1.在uc/os操作系统中设计串口编程时,由于ISR和多个任务并发执行,情况比较复杂.尤其是接收状态为被动状态时,只能靠串行口中断来 ...

  4. Lab 6:uC/OS II

    为什么80%的码农都做不了架构师?>>>    目标: 移植uC/OS II到RPi上,实现两个任务的调度.这两个任务能轮流点亮LED,并通过串口发送消息表明自己正在运行 具体步骤: ...

  5. 嵌入式实时操作系统μC/OS II(一)

    嵌入式实时操作系统μC/OS II 为什么要学习μC/OS-II ㈠.凡从事嵌入式系统开发工作的人,必须对嵌入式操作系统有足够的了解. ㈡.对于初学者,从μC/OS-II开始是个明智的选择. 1.μC ...

  6. uc/OS II——多任务设计

    uc/OS II--多任务设计 (1)设计 开始任务 [1]/声明 开始任务 任务块 static OS_STK App_TaskStartStk[APP_TASK_START_STK_SIZE]; ...

  7. ucos-ii中文书(邵贝贝)--用borland c4.5编译ucosii全过程

    ucos-ii中文书(邵贝贝)--用borland c4.5编译ucosii全过程 一.IDE是:borland c++ v3.1 还是borland c++ v4.5? ucos-ii中文书(邵贝贝 ...

  8. linux串口互斥,UART0串口编程之在UC/OS—II中遭遇的危机

    一.潜在的危机 1.在uc/os操作系统中设计串口编程时,由于ISR和多个任务并发执行,情况比较复杂.尤其是接收状态为被动状态时,只能靠串行口中断来接收数据. 2.在进行串行通信时,双方遵循相同的通信 ...

  9. 移植µC/OS-Ⅱ原理(邵贝贝)

    这一章介绍如何将µC/OS-Ⅱ移植到不同的处理器上.所谓移植,就是使一个实时内核能在某个微处理器或微控制器上运行.为了方便移植,大部分的µC/OS-Ⅱ代码是用C语言写的:但仍需要用C和汇编语言写一些与 ...

最新文章

  1. 图像分割2020总结:结构,损失函数,数据集和框架
  2. linux进入文件系统命令,卸载正在被访问的文件系统 - fuser命令
  3. 准备写一个Ibatisnet开发指南
  4. hdu 6168 Numbers
  5. CentOS7 最小化安装后的必备操作
  6. 【elasticsearch】 document 查询原理
  7. Spring-发送QQ邮件
  8. linux命令行用户登录,Linux终端以及用户登录相关命令
  9. 微型计算机的 CPU主要由两部分构成,微机是由哪两部分组成
  10. 【深度学习计算机视觉实战】给深度学习计算机视觉初学者的学习和求职建议,这个行业还缺人
  11. STM32CubeMX的使用教程
  12. 三菱GX Developer+GX Simulator进行仿真调试
  13. zabbix agent安装配置
  14. 三大中值定理及简单例题
  15. Jenkins 更新网站静态文件
  16. XR,VR,AR虚拟服务器,虚拟演播室
  17. 十年再出发,Dubbo 3.0 Preview 即将在 3 月发布
  18. SRS 简单高效的实时视频服务器
  19. 在linux系统命令行模式下如何输入中文
  20. CLRS第四章思考题

热门文章

  1. selenium driver.find_element 报错 invalid argument: invalid locator
  2. php农历代码,PHP阳历转农历实现代码
  3. LVDS SerDes 设计
  4. win下使用fvm实现多个Flutter版本的切换
  5. HTML+CSS+JavaScript仿京东购物网站制作 html静态网页设计制作 dw静态网页成品模板素材网页 web前端网页设计与制作 div静态网页设计
  6. Zabbix学习(五)之Zabbix Agent Active 主动模式监控
  7. 不谋全局者,不足谋一域-预布局-PCB系列教程1-11
  8. 客官请留步!年销千万台的智能香薰加湿器震撼来袭
  9. Win10如何新建用户怎么添加新账户
  10. 【今日头条】二阶魔方