文章目录

  • 本篇目的
  • 开发环境
  • 简介
  • 接口定义
  • select事件处理
  • 接口用于任务间通讯
  • 接口用于任务间通讯的程序语法要求
  • 任务间多个多个接口通讯
  • 服务端发起事务通知(Notifications)
  • 接口数组
  • 扩展接口的客户端API

本篇目的

继续XMOS的程序开发-xc语言的任务间的通讯机制:使用接口的方式,以及xC的事件处理。

开发环境

  • 硬件平台使用官方评估板"xCORE VOCAL FUSION XP-VF3100-BASE"
  • IDE开发环境win10 下的 xTIMEcomposer

简介

C语言可以处理这样的事件:按键按下,定时器溢出,串口接收到数据。C语言一般通过中断/异常处理这样的事件。xC则是通过select的事件处理模式来处理这样的事件。

接口则是用于一个任务向另一个任务传递事务的方式,使用接口通讯的这两个任务,一个叫接口客户端,一个叫做接口服务端。比如接口客户端可以发起一个事务:键值为1的按键按下了。服务端通过接口可以获取到这个事务,并通过select事件处理方式对这个事务执行处理。

通过接口传递的事务是有类型定义的,这需要从接口说起。

接口定义

接口使用关键字interface定义,定义如下:

interface T {transaction1(...);transaction2(...);...
};

interface是关键字,T是接口类型名称,interface体内的transaction1和transaction2等表示接口传递的事务,事务按照C函数那样定义,即函数名称和函数参数。比如:

interface my_interface {float add(float a, float b);  // 加法事务void inc(int& a);       // 自加1事务
};

定义了接口my_interface,事务可以传c语言支持的参数,可以传引用参数,可以带返回值,因此同一个接口可以用于任务之间的双向传递。

类似C语言定义了结构一样,还需要定义结构的实例才能使用;xC的接口也一样,使用时要定义接口实例,比如:

interface my_interface mi;  // 定义接口实例mi

select事件处理

语法如下:

select {case event1:  // 有event1事件// 处理event1事件break;case event2:  // 有event2事件// 处理event2事件break;...
}

有点类似于C语言的没有default分支的switch case语句,但是只是写法有点像而已,实际上完全不一样。select处理事件,是阻塞等待事件发生的,如果没有任何一个case的事件发生的话,是阻塞不往下执行的。

event的写法和写接口实例的函数原型是一样的,比如接口interface my_interface定义了一个实例mi,那么

select {case mi.add(float a, float b) -> float ret:// 处理add事件,也需要给返回值ret赋值ret = 2;  // 给返回值赋值break;case mi.inc(int& a):// 处理inc事件break;
}

注意语法case mi.inc(int& a):mi.inc(int& a)实例.函数(参数列表)这样的格式,是没有返回值的事件处理的写法。

case mi.add(float a, float b) -> float ret:这个是带有返回值的写法,即实例.函数(参数列表) -> 返回值类型 返回值变量。处理语句中给返回值变量赋值。

接口用于任务间通讯

上面简单的说明了接口和select事件处理之后,可以开始说明接口在任务间通讯的用法了。

前面简介中说:“接口则是用于一个任务向另一个任务传递事务的方式,使用接口通讯的这两个任务,一个叫接口客户端,一个叫做接口服务端。”

就是说接口用于任务间通讯包含3部分:

  • 定义的接口
  • 接口客户端
  • 接口服务端

下面以一个例子说明,还是用上面定义的my_interface接口,说明接口如何在两个任务间双向通讯,如何通过接口传递事务,select事件处理机制如何处理事务。

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {float add(float a, float b);  // 加法事务void inc(int& a);       // 自加1事务
};/* 接口客户端 */
void task1(client interface my_interface i) {printf("task1: transaction add\n");float sum = i.add(1.2, 3.4);printf("task1: sum=%f\n", sum);int x = 5;printf("task1: transaction inc\n");i.inc(x);printf("task1: x=%d\n", x);
}/* 接口服务端 */
void task2(server interface my_interface i) {while(1) {select {case i.add(float a, float b) -> float ret:printf("task2: handle event: add(%f, %f)\n", a, b);ret = a + b;break;case i.inc(int& a):printf("task2: handle event: inc(%d)\n", a);a++;break;}}
}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端par {task1(i);task2(i);}return 0;
}

执行结果

task1: transaction add
task2: handle event: add(1.200000, 3.400000)
task1: sum=4.600000
task1: transaction inc
task2: handle event: inc(5)
task1: x=6

首先看3个部分,定义了接口my_interface,之后interface my_interface就是一个接口类型,main函数给这个接口类型定义了一个实例 i,并把接口实例传递给并发的两个任务task1和task2,task1传递参数类型写成client interface my_interface,client表明是当客户端,task2传递参数写成server interface my_interface,server表明是服务端。

客户端发起事务传输,方式就跟调用c语言de 结构函数一样,i.add(1.2, 3.4)i.inc(x)都是发起事务,客户端发起事务,服务端就能接收到事务。

服务端处理接收到的事务,通过select事件处理机制处理。当客户端发出add事务之后,服务端收到这个事务,会执行对应的case的代码,这个语法i.add(float a, float b) -> float ret就是事件处理结果返回值传回去的方法,表示传回给ret这个float类型,case语句给ret赋值就可以了。select case语法中没有返回值的事件,则不需要写->的部分

从执行结果看正确的处理了1.2+3.4的事务,以及5自加1的事务。

接口用于任务间通讯的程序语法要求

不遵循要求就会产生编译时错误。

要求:

  • 传递给并行任务的接口必须有且只有一个客户端和一个服务端

    客户端必须在参数列表中用client interface表明;服务端必须在参数列表中用server interface表明。

  • 普通的事务只能从客户端发起(notification机制可以由服务端发起,后续再说)

  • select中的case要包含所有的事务处理

下面举几个错误的例子,假设上面的例子编程错误

  • task1漏写了client修饰符
void task1(interface my_interface i)

编译错误

…/src/mytest.xc:86:23: error: interface parameter must have server' orclient’ modifier

没有client修饰符,会认为没有客户端。

  • task2没写server修饰符,即
void task2(interface my_interface i)

编译错误和没有client修饰符一样。没有server认为是没有服务端。

  • 客户端多于一个或者服务端多于一个

增加一个task3做客户端或者服务端,编译时错误:

error: interface used in two tasks as client
或者
error: interface used in two tasks as server

  • task2服务端发起事务

即在task2中调用float sum = i.add(1.2, 3.4);发起事务,比如

void task2(server interface my_interface i) {float sum = i.add(1.2, 3.4);   // server端发起事务,错误

编译错误

error: trying to call interface function from a server interface

  • task2的select case中少处理一项事务,即
void task2(server interface my_interface i) {while(1) {select {case i.add(float a, float b) -> float ret:printf("task2: handle event: add(%f, %f)\n", a, b);ret = a + b;break;}}
}

编译错误

error: missing case for interface function inc' of interfacemy_interface’

只要写了select case处理了某个接口事件,就要把改接口要处理的所有事件case语句写全。要不就不要用select case处理该。比如删除task2中的select case块,编译是不会错误的。

void task2(server interface my_interface i) {while(1) {}
}
// 这样修改不会产生编译时错误
  • main函数只把接口实例传递给一个任务

比如以下的代码,main函数只把接口实例传递给task1或者task2,虽然不会产生编译时错误,只会警告,但是这样使用是没有任何意义的。

int main () {interface my_interface i;  // 定义一个接口实例,只传给一个任务par {task2(i);}return 0;
}

或者

int main () {interface my_interface i;  // 定义一个接口实例,只传给一个任务par {task1(i);}return 0;
}

编译结果都是警告

warning: `i’ not used in two parallel statements

但是这样的用法没什么意义。

任务间多个多个接口通讯

用于任务间通讯的接口,对于某单个的接口,遵循上面的原则就没问题;多个接口也是由多个遵循这些原则的接口组成的,任务可以是仅仅是某个接口的客户端,或者仅仅是某个接口的服务端,也可以是某个的接口的客户端,同时做另一个接口的服务端,只要遵循对于某个接口,有且只有一个客户端,有且只有一个服务端就可以了。

为了说明任务间使用多个接口通讯的工作方式,把例子简化点,使用两个接口,任务不对接口做任何操作,不发起事务,也不处理事务,便于理解多个接口做任务间通讯的工作方式。

  • 例子1

如下代码:

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {float add(float a, float b);  // 加法事务void inc(int& a);       // 自加1事务
};/* 定义接口 */
interface you_interface {float mul(float a, float b);  // 乘法事务void dec(int& a);       // 自减1事务
};/* 接口客户端 */
void task1(client interface my_interface i, client interface you_interface yi) {}/* 接口服务端 */
void task2(server interface my_interface i, server interface you_interface yi) {}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端interface you_interface yi;  // 定义一个接口实例,传给客户端和服务端par {task1(i, yi);task2(i, yi);}return 0;
}

在前一个代码的基础上多定义一个you_interface接口,task1和task2任务都是使用两个接口实例,task1做两个接口的客户端,task2做两个实例的服务端。

接口i有且只有一个客户端task1,有且只有一个服务端task2;接口yi有且只有一个客户端task1,有且只有一个服务端task2。

  • 例子2

如下代码:

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {float add(float a, float b);  // 加法事务void inc(int& a);       // 自加1事务
};/* 定义接口 */
interface you_interface {float mul(float a, float b);  // 乘法事务void dec(int& a);       // 自减1事务
};/* 接口客户端 */
void task1(client interface my_interface i, server interface you_interface yi) {}/* 接口服务端 */
void task2(server interface my_interface i, client interface you_interface yi) {}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端interface you_interface yi;  // 定义一个接口实例,传给客户端和服务端par {task1(i, yi);task2(i, yi);}return 0;
}

task1和task2任务都是使用两个接口实例,task1做i的客户端,yi的服务端,task2做i的服务端,yi的客户端。

接口i有且只有一个客户端task1,有且只有一个服务端task2;接口yi有且只有一个客户端task2,有且只有一个服务端task1。

  • 例子3

如下代码:

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {float add(float a, float b);  // 加法事务void inc(int& a);       // 自加1事务
};/* 定义接口 */
interface you_interface {float mul(float a, float b);  // 乘法事务void dec(int& a);       // 自减1事务
};/* 接口客户端 */
void task1(client interface my_interface i) {}void task3(client interface you_interface yi) {}/* 接口服务端 */
void task2(server interface my_interface i, server interface you_interface yi) {}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端interface you_interface yi;  // 定义一个接口实例,传给客户端和服务端par {task1(i);task3(yi);task2(i, yi);}return 0;
}

task1做i的客户端,task3做yi的客户端,task2做i和yi的服务端。task1和task2通过接口i通讯,task3和task2通过yi通讯。

接口i有且只有一个客户端task1,有且只有一个服务端task2;接口yi有且只有一个客户端task3,有且只有一个服务端task2。

通过这几个例子应该能理解多个通道用于任务间通讯的用法了。

然后实际使用的时候,需要注意,任务发出事务后,会阻塞等到另一个任务处理事务之后才能往下执行,使用select case来处理事务的任务也会阻塞等待直到事务到来才往下执行,一不小心就会造成相互阻塞。

最后举一个2个接口用于任务间通讯的完整例子,task1做接口i的客户端,task3做yi的客户端,task2做2个接口的服务端,这样用,不会造成相互阻塞。

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {float add(float a, float b);  // 加法事务void inc(int& a);       // 自加1事务
};/* 定义接口 */
interface you_interface {float mul(float a, float b);  // 乘法事务void dec(int& a);       // 自减1事务
};/* 接口客户端 */
void task1(client interface my_interface i) {printf("task1: transaction add\n");float sum = i.add(1.2, 3.4);printf("task1: sum=%f\n", sum);int x = 5;printf("task1: transaction inc\n");i.inc(x);printf("task1: x=%d\n", x);
}void task3(client interface you_interface yi) {printf("task3: transaction mul\n");float mul = yi.mul(1.2, 3.4);printf("task3: mul=%f\n", mul);int x = 5;printf("task3: transaction dec\n");yi.dec(x);printf("task3: x=%d\n", x);
}/* 接口服务端 */
void task2(server interface my_interface i, server interface you_interface yi) {while(1) {select {case i.add(float a, float b) -> float ret:printf("task2: handle event: add(%f, %f)\n", a, b);ret = a + b;break;case i.inc(int& a):printf("task2: handle event: inc(%d)\n", a);a++;break;case yi.mul(float a, float b) -> float ret:printf("task2: handle event: mul(%f, %f)\n", a, b);ret = a * b;break;case yi.dec(int& a):printf("task2: handle event: dec(%d)\n", a);a--;break;}}
}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端interface you_interface yi;  // 定义一个接口实例,传给客户端和服务端par {task1(i);task3(yi);task2(i, yi);}return 0;
}

运行结果正确

task3: transaction mul
task1: transaction add
task2: handle event: mul(1.200000, 3.400000)
task3: mul=4.080000
task2: handle event: add(1.200000, 3.400000)
task3: transaction dec
task1: sum=4.600000
task2: handle event: dec(5)
task1: transaction inc
task3: x=4
task2: handle event: inc(5)
task1: x=6

服务端发起事务通知(Notifications)

以上的方式,都是客户端发起事务,按以上的描述,如果服务端发起事务,会引起编译时错误。再者,客户端发起事务后,如果没有服务端处理事务,客户端会引起阻塞。

这节就说由服务端发起事务的方法:Notifications方法。

接口定义中定义一种事务 - notification事务,notification事务的特点:

  • [[notification]] slave指定是notification事务
  • 不能带参数和返回值
  • 由服务端发起
  • 不阻塞
  • 接口定义[[clears_notification]]的函数用于客户端清除通知,同时发起事务
  • 客户端没清除通知前,重复发起无效,客户端清除通知之后,可再次发起通知事务

举个例子:客户端要从硬件输入读取两个数据输入,用于加法计算,硬件输入准备好之后,服务端发起通知,客户端清除这个通知同时发起读取事务,服务端处理读取事务,并把读取的结果传给客户端,如此就完成了一次读取。读取了2次数据后,客户端发起加法的事务,服务端处理传回结果。

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {int add(int a, int b);  // 加法事务[[clears_notification]] int read_input();[[notification]] slave void input_ready();   // 无参数,没返回值
};/* 模拟从硬件读取到数据回来 */
int read_dat_from_hardware() {const int s_hwdat[] = {12, 34, 56, 78};static int hwdat_index = 0;int dat = s_hwdat[hwdat_index];hwdat_index = (hwdat_index + 1) % (sizeof(s_hwdat) / sizeof(s_hwdat[0]));return dat;
}/* 接口客户端 */
void task1(client interface my_interface i) {int a[2];int recv_count = 0;while(recv_count < 2) {select {case i.input_ready():printf("task1: 清除通知同时发起读输入的事务\n");a[recv_count] = i.read_input();  // 清除 notification ,并引发服务端处理 read_input 事务printf("task1: 读到输入数据为%d\n", a[recv_count]);recv_count++;break;}}printf("task1: transaction add\n");int sum = i.add(a[0], a[1]);printf("task1: sum=%d\n", sum);
}/* 接口服务端 */
void task2(server interface my_interface i) {while(1) {printf("task2: 通知客户端输入已准备\n");i.input_ready();select {case i.add(int a, int b) -> int ret:printf("task2: handle event: add(%d, %d)\n", a, b);ret = a + b;break;case i.read_input() -> int dat:printf("task2: 处理读输入事务,从硬件读输入并传回给客户端\n");dat = read_dat_from_hardware();break;}}
}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端par {task1(i);task2(i);}return 0;
}

执行结果为

task2: 通知客户端输入已准备
task1: 清除通知同时发起读输入的事务
task2: 处理读输入事务,从硬件读输入并传回给客户端
task1: 读到输入数据为12
task2: 通知客户端输入已准备
task1: 清除通知同时发起读输入的事务
task2: 处理读输入事务,从硬件读输入并传回给客户端
task1: 读到输入数据为34
task2: 通知客户端输入已准备
task1: transaction add
task2: handle event: add(12, 34)
task1: sum=46
task2: 通知客户端输入已准备

用时序图表示更清除

服务端客户端notification input_readyclears_notificationread_inputtransaction read_inputread_dat_from_hardware读取硬件输入dat服务端客户端notification和clears_notification的时序图

接口数组

任务可以使用接口数组连接多个任务。

a[0]
a[1]
a[m]
task2
task1
task3
taskn

服务端使用interface_array[int index].接口事务原型这样的方式处理接口数组的事务,这样客户端发起事务的时候,下标会传给index,接口事务原型的参数传输和前面所述的事务传输一样。

举例

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {void f(int x);
};/* 接口服务端 */
void task1(server interface my_interface a[n], unsigned int n) {while(1) {select {case a[int i].f(int x):  // 接口数组处理事务的语法printf("task1: 处理从接口%d发起的事务,值是%d\n", i, x);break;}}
}/* 接口客户端 */
void task2(client interface my_interface i) {i.f(12);
}/* 接口客户端 */
void task3(client interface my_interface i) {i.f(34);
}int main () {interface my_interface a[2];  // 定义一个接口实例数组,传给客户端和服务端par {task1(a, 2);task2(a[0]);task3(a[1]);}return 0;
}

执行结果

task1: 处理从接口0发起的事务,值是12
task1: 处理从接口1发起的事务,值是34

扩展接口的客户端API

接口定义后,可以扩展定义客户端可以使用的API,即是说,可以扩展客户端可以发起的事务。比如原来定义了接口

interface 接口名 {// 事务函数
};

要扩展一个事务,使用如下语法:

extends client interface 接口名: {扩展的事务函数定义(client interface 接口名 实例, 参数列表) {// 定义好函数功能 }
};

扩展客户端API有以下特点:

  • 第一个参数必须是客户端接口类型实例
  • 客户端使用事务函数时,函数参数只写参数列表就行了,那个接口实例不需要
  • 不能访问全局变量
  • 可以调用接口原有的事务函数
  • 扩展事务不是接口的成员,不可以在服务端的select case中处理

举例,比如原来定义了接口my_interface。

interface my_interface {int mul(int a, int b);
};

原来只有乘法事务,现在要扩展一个阶乘(factorial)功能,如下:

extends client interface my_interface: {int factorial(client interface my_interface self, int n) {int ret = 1;for(int i=n; i>=1; ++i) {ret *= i;}return ret;}
}

完整的程序例子

#include <stdio.h>
#include <platform.h>/* 定义接口 */
interface my_interface {int mul(int a, int b);
};extends client interface my_interface: {unsigned int factorial(client interface my_interface self, unsigned int n) {unsigned int ret = 1;for(int i=n; i>=1; --i) {ret *= i;}return ret;}
}/* 接口客户端 */
void task1(client interface my_interface i) {int ret = i.mul(2, 3);printf("task1: 乘法结果是%d\n", ret);ret = i.factorial(4);printf("task1: 4的阶乘是: %d\n", ret);
}/* 接口服务端 */
void task2(server interface my_interface i) {while(1) {select {case i.mul(int a, int b) -> int ret:ret = a * b;printf("task2: 处理mul(%d, %d)事务\n", a, b);break;}}
}int main () {interface my_interface i;  // 定义一个接口实例,传给客户端和服务端par {task1(i);task2(i);}return 0;
}

执行结果为

task2: 处理mul(2, 3)事务
task1: 乘法结果是6
task1: 4的阶乘是: 24

XMOS软件开发入门(6) - xc语言(4)之事件处理以及任务间使用接口通讯相关推荐

  1. XMOS软件开发入门(1) - 开发板硬件资源介绍

    文章目录 文档目的 XMOS简介 评估板"xCORE VOCAL FUSION XP-VF3100-BASE"简介 主处理器资源 GPIO介绍 GPIO简介 连接FLASH的GPI ...

  2. XMOS软件开发入门(3) - xc语言(1)之hello world

    文章目录 本篇目的 开发环境 新建工程 XVF3100评估板新建工程遇到的麻烦 建立自己的新工程 Hello World 程序 本篇目的 后面开始说XMOS的程序开发-xc语言. 开发环境 硬件平台使 ...

  3. XMOS软件开发入门(4) - xc语言(2)之并发机制

    文章目录 本篇目的 开发环境 XC和C的区别简单说明 并发执行机制 本篇目的 继续XMOS的程序开发-xc语言的并发机制. 开发环境 硬件平台使用官方评估板"xCORE VOCAL FUSI ...

  4. XMOS软件开发入门(2) - 烧写、编译、调试开发板

    文章目录 本篇目的 开发环境 主要内容 程序烧写 编译程序 运行程序 详细过程 获取资源 binary文件下载 SDK包下载 开发环境xTIMEcomposer下载 安装注册开发环境 烧写程序 编译程 ...

  5. 第一章 软件开发入门引导及概述

    第一章 软件开发入门引导及概述 前言 计算机系统是由硬件和软件组成,所谓硬件是指组成计算机系统的物理设备,包括电子的.机械的.磁的.光的设备的总和. 我们的电脑.手机.电子设备都算一个小的计算机. 而 ...

  6. 海思软件开发入门篇 (一)

    标题 海思软件开发入门篇 (一)   (第一次写博客,有错别字与写的不好的地方敬请谅解.)   加过很多群,也逛过很多论坛,很多人在问,第一次接触海思不知道从而入手,的确,现在一个SDK动不动上G,还 ...

  7. 【软件开发底层知识修炼】二十六 ABI-应用程序二进制接口 学习总结文章目录

    前面学习了ABI的知识,感觉受益良多.对底层与编译器有更加深刻的认识,为此这里将前面写过的关于ABI 的文章给列出来,方便学习与翻阅. [软件开发底层知识修炼]二十一 ABI-应用程序二进制接口一 [ ...

  8. 智能机器人软件开发入门教程:带你从0到1快速入门

    机器人,现在遍布我们的生活,怎样才能让机器人运作?你了解吗?今天就给大家分享一套智能机器人软件开发教程,教你如何让机器人运作! 对微机原理和硬件控制原理深入理解,使学员更扎实的掌握软硬件开发的核心能力 ...

  9. IOS开发入门之一——Swift语言基础

    需要iOS视频资料可以加我微信: 1914532832  验证信息请注明:IOS开发 很多新人对IOS开发很迷茫,不知道从何下手?看完本系列,你将会觉得IOS入门其实很简单.要学习IOS开发,当然是先 ...

最新文章

  1. crypto-RSA-常用解密代码块
  2. mysql的varchar最大值是多少_MySQL varchar计算:求列的数额和计算N的最大值
  3. Vue学习(常用实例、脚手架搭建)-学习笔记
  4. 光端机的作用有哪些?
  5. 构建第一个fabric网络
  6. Bailian2804 词典【map+字典树】
  7. app具体介绍界面-01
  8. javaweb JAVA JSP学生信息管理系统源码(JSP学生成绩管理系统 学生管理 JSP学生管理系统)
  9. pmp考试中变更的处理流程
  10. h5 如何录音保存上传_html5 网页录音、试听以及上传
  11. 如何利用魔棒工具抠图_PS怎么抠图?魔棒工具抠图详细步骤教程-Photoshop教程
  12. 《Kotin 极简教程》第7章 面向对象编程(OOP)
  13. 【Flink】Flink SQL 读取 CSV 文件
  14. 华为的鸿蒙os,鸿蒙OS明天正式发布,十大特性,能拯救暴跌80%的华为手机吗?...
  15. JAVA web中的一点东西
  16. 机器学习之Kmeans
  17. 网络安全全面防应对难察觉网络威胁!
  18. 渲染优化-从GPU的结构谈起
  19. 全新修复版/官方代付系统/支付宝微信代付/企业付款/提现秒到
  20. 经典python项目源码_建议收藏,22个Python迷你项目(附源码)

热门文章

  1. PHP单商户商城系统 全套源代码【源码+文档+搭建部署教程】
  2. Hadoop——Hadoop优势、组成、大数据技术生态体系、系统框架图
  3. MyBioSource丨艾美捷抗eIF4E抗体
  4. U盘数据丢失误删恢复办法
  5. WGET批量下载风云卫星数据
  6. Linux嵌入式开发——压缩与解压缩
  7. NXP(Freescale) QorIQ T2080 Memory Map分析
  8. [解决] 电脑没有Realtek 音频管理器 响度均衡
  9. 易点易动设备管理系统,让设备管理迈入智能化时代
  10. .Net Core 图片合成、证书生成