AT组件的实现过程和代码的调用逻辑

at组件的核心处理逻辑是将收到的 at 模组的应答信息放到 recv_line_buf 缓冲区中,然后每次读一行数据(\r\n)进行处理,然后判断属于哪一类的消息,调用不同的函数。
  本文以 ec200x 模组为例,详细分析了 at 组件的实现过程和代码的调用逻辑,帮助在使用 at 组件过程中遇到问题的开发者快速定位问题出现的位置。
1 at 组件 1.1 at 组件调试信息级别设置   可以通过修改 env 中的如下内容来控制是否启用 at 组件的 debug log 功能,开启后可以看到日志级别为 debug 的相关日志。使能该选项后将在 `rtconfig.h` 中生成 `#define at_debug`,at 组件日志级别的控制是在 `rt-thread/components/net/at/include/at_log.h` 文件中实现的。
rt-thread components -> network -> at commands -> [*] enable debug log output /* 选中表示修改日志级别为 debug */   特别注意:打开上述功能后,ec200x 线程(在 packages/at_device-2.0.4/class/ec200x/at_device_ec200x.c 文件中由 ec200x_netdev_check_link_status() 函数创建)会提示栈溢出,使用的栈空间约为 1598bytes,建议将 ec200x_netdev_check_link_status() 函数中的 ec200x_link_thread_stack_size 宏更改为 1024 +1024 即 2048 个字节,以解决栈溢出的问题。
使能 at 调试日志输出界面 1.2 at 命令打印使能设置   在调试时可以通过修改 env 中的如下内容来控制是否使能 at 组件的收发 at 指令的显示,开启后可以看到每次执行的 at 指令以及返回的执行结果。
rt-thread components -> network -> at commands -> [*] enable print raw format at command communication data /* 选中表示打印执行的at指令 */   上述选项选中后,在执行测试时,打印的 at 指令示例如下
[d/at] recvline: 0000-0020: 41 54 0d 0d 0a at...[d/at] recvline: 0000-0020: 4f 4b 0d 0a ok..[d/at] sendline: 0000-0020: 41 54 45 30 ate0[d/at] recvline: 0000-0020: 41 54 45 30 0d 0d 0a ate0...[d/at] recvline: 0000-0020: 4f 4b 0d 0a ok..[d/at] sendline: 0000-0020: 41 54 2b 49 50 52 3f at+ipr?01-01 00:40:19 d/at.clnt: execute command (at+ipr?) timeout (300 ticks)![d/at] recvline: 0000-0020: 0d 0a ..[d/at] recvline: 0000-0020: 2b 49 50 52 3a 20 31 31 35 32 30 30 0d 0a +ipr: 115200..[d/at] recvline: 0000-0020: 0d 0a ..[d/at] recvline: 0000-0020: 4f 4b 0d 0a ok..[d/at] recvline: 0000-0020: 0d 0a ..[d/at] recvline: 0000-0020: 52 44 59 0d 0a rdy..01-01 00:40:22 i/at.dev.ec200x: ec200x device initialize retry...[d/at] sendline: 0000-0020: 41 54 45 30 ate001-01 00:40:24 d/at.clnt: execute command (ate0) timeout (300 ticks)![d/at] recvline: 0000-0020: 0d 0a ..[d/at] recvline: 0000-0020: 4f 4b 0d 0a ok..[d/at] recvline: 0000-0020: 0d 0a ..[d/at] recvline: 0000-0020: 4f 4b 0d 0a ok.. 1.3 gprs 网络注册状态检查   at组件中会自动创建一个 gprs 网络注册状态检查的线程,使用 at+cgreg? 指令进行 gprs 网络注册状态的检查,并根据指令返回的结果修改网卡设备的标志位,该指令返回结果中的 等于 1 或者 5,表示模块已在 umts/lte 网络注册上 ps 业务。
  gprs 网络注册状态检查线程的名字是模拟网卡的名字,在本例中为 ec200x,线程的入口函数是 ec200x_check_link_status_entry(packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c),该线程没间隔 60s 发送一条 at 指令进行 gprs 网络注册状态的检查,并根据返回的结果在函数 netdev_low_level_set_link_status` 中修改 `netdev->flags。各种情况的执行结果分析如下所示
上一次网络注册状态 这次网络注册状态 操作1 操作2
未注册 未注册 无 无
未注册 注册 标记 netdev->flags 的 netdev_flag_link_up 标志 调用 sal_check_netdev_internet_up() 函数将任务放入 sys_work 队列,然后通过和 link.rt-thread.org 的 8101 端口交互来修改 netdev->flags 中的 netdev_flag_internet_up 标志
注册  未注册  清除 netdev->flags 的 netdev_flag_link_up 和 netdev_flag_internet_up 标志 调用 netdev_auto_change_default() 函数切换到第一个连接的网络设备,本例中只用到了一个网络设
注册  注册  无 无
特别注意:gprs 网络注册状态检查线程会检查模组的 power_status,power_status 在 ec200x_init() 函数中默认初始化为 rt_false,然后在名为 ec200x_net 的线程的 ec200x_init_thread_entry() 线程入口函数中调用 ec200x_power_on() 函数来修改 power_status 的值。ec200x_power_on() 函数中 power_status 的值修改为 rt_true 依赖于 power_pin 引脚的定义,因此在 env 中必须定义 power_pin 的编号,默认为 -1,定义规则在文件 drivers/drv_gpio.c 中。另外还需要注意的是在代码中 power_pin 为低电平表示模组处于上电状态,power_pin 为高电平表示模组处于断电状态。
  power_pin 编号的定义在禁用网卡(netdev_set_down)与启用网卡(netdev_set_up)的实现中也至关重要,网卡的禁用与启用最终实际调用的函数为 ec200x_power_off() 和 ec200x_power_on(),因此必须定义power_pin 的编号,网卡禁用与启用的示例代码如下
/* ec200x 名字来源于设备注册时使用的名字,在文件 packages/at_device-v2.0.4/samples/at_sample_ec200x.c 中定义 */struct at_device * dev = at_device_get_by_name(at_device_nametype_netdev, ec200x);/* 根据名字查找 at 设备 */netdev_set_up(dev->netdev); /* 启用相应的网卡设备 */netdev_set_down(dev->netdev); /* 禁用相应的网卡设备 */ 1.4 ec200x 是否能连接外网日志输出   rt-thread/components/net/sal_socket/src/sal_socket.c 中 check_netdev_internet_up_work() 函数会自动连接 link.rt-thread.org` 的 `8101 端口进行数据收发测试,从而判断是否可以连接外网。该文件中默认的打印级别为 dbg_info,为了方便看出是否可以连接外网,将 check_netdev_internet_up_work() 函数结尾的部分的测试结果打印由 log_d 修改为 log_i。
  该函数的执行的过程大致为在 ec200x 初始化线程 ec200x_init_thread_entry() 中将将外网检查任务提交到 sys_work 工作队列中,系统工作队列处理线程 _workqueue_thread_entry() 会不断的检测是否有需要运行的任务,如果有则执行相应的任务,即执行外网连接检查任务。外网连接检查任务提交到工作队列的执行过程如下
ec200x_init_thread_entry /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_power_on /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> at_obj_exec_cmd() /* 发送各种at指令初始化ec200x rt-thread/components/net/at/src/at_client.c */ |-> ec200x_netdev_set_info /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> at_device_get_by_name /* packages/at_device-v2.0.4/src/at_device.c */ |-> netdev_low_level_set_status /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev->flags |= netdev_flag_link_up; /* 网络设备的状态改为连接 */ |-> netdev_low_level_set_link_status /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev_low_level_set_dhcp_status /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev_low_level_set_ipaddr /* 设置本地的ip地址 rt-thread/components/net/netdev/src/netdev.c */ |-> sal_check_netdev_internet_up /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> rt_delayed_work_init(net_work, check_netdev_internet_up_work) /* 初始化外网连接检查任务 rt-thread/components/drivers/src/workqueue.c */ |-> (&work->work)->work_func = check_netdev_internet_up_work |-> rt_work_submit(&(net_work->work), rt_tick_per_second); /* rt-thread/components/drivers/src/workqueue.c */ |-> rt_workqueue_submit_work(sys_workq, work, time) /* 将任务提交到系统工作队列里面 rt-thread/components/drivers/src/workqueue.c */ |-> netdev_low_level_set_dns_server /* 设置dns服务器 rt-thread/components/net/netdev/src/netdev.c */ |-> ec200x_netdev_check_link_status /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> rt_thread_create(ec200x_check_link_status_entry) /* 线程名字为ec200x,创建 gprs 网络注册状态检查线程 packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> rt_thread_startup(ec200x_check_link_status_entry) /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c *//* gprs 网络注册状态检查线程 */ec200x_check_link_status_entry /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */  |-> ec200x_check_link_status /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */  |-> at_obj_exec_cmd(at+cgreg?) /* 发送at指令检查网络状态 */ |-> netdev_low_level_set_link_status /* rt-thread/components/net/netdev/src/netdev.c */   系统工作队列初始化和任务执行过程如下
rt_work_sys_workqueue_init /* rt-thread/components/drivers/src/workqueue.c */ |-> rt_workqueue_create(sys_work) /* 创建系统工作队列 rt-thread/components/drivers/src/workqueue.c */ |-> rt_thread_create(_workqueue_thread_entry) /* 线程名字为sys_work,创建队列线程 rt-thread/components/drivers/src/workqueue.c */_workqueue_thread_entry /* 系统工作队列处理线程 rt-thread/components/drivers/src/workqueue.c */ |-> if(rt_list_isempty) |-> rt_thread_suspend(rt_thread_self()); /* 挂起自身 */ |-> rt_schedule(); /* 任务列表为空,挂起自身,切换线程 */ |-> rt_hw_interrupt_disable /* 任务列表不为空,依次往下执行,关闭中断 */ |-> rt_list_entry /* 找到要处理的任务节点 */ |-> rt_list_remove /* 将找到的任务节点从任务列表中移除 */ |-> rt_hw_interrupt_enable /* 使能中断 */ |-> work->work_func(work, work->work_data); /* 执行任务 */   外网连接检查任务的执行过程如下
check_netdev_internet_up_work /* 外网连接检查任务 rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> 与 link.rt-thread.org 的 8101 端口建立连接,进行收发数据测试 |-> 打印测试结果 |-> 收发测试成功 log_i(set network interface device(%s) internet status up., netdev->name); |-> netdev->flags |= netdev_flag_internet_up; |-> 收发测试失败 log_i(set network interface device(%s) internet status down., netdev->name); |-> netdev->flags &= ~netdev_flag_internet_up; 1.5 at 设备注册过程at_device_register /* packages/at_device-v2.0.4/samples/at_sample_ec200x.c */ |-> class = at_device_class_get(class_id) /* packages/at_device-v2.0.4/src/at_device.c */ |-> class->device_ops->init(device) /* packages/at_device-v2.0.4/src/at_device.c */ |-> ec200x_init /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x->power_status = rt_false; /* default power is off. packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x->sleep_status = rt_false; /* default sleep is disabled. packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> at_client_init /* components/net/at/src/at_client.c */ |-> at_client_para_init /* components/net/at/src/at_client.c */ |-> rt_thread_create(client_parser) /* 线程名字为at_clnt0,创建at解析线程 components/net/at/src/at_client.c */ |-> client_parser /* at解析线程的具体实现 components/net/at/src/at_client.c */ |-> rt_device_find /* 寻找串口设备 rt-thread/src/device.c */ |-> rt_device_open /* 打开串口设备 rt-thread/src/device.c */ |-> rt_thread_startup(client->parser) /* rt-thread/src/thread.c */ |-> ec200x_socket_init /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> ec200x_netdev_add(ec200x) /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> netdev_get_by_name(ec200x) /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev->ops = &ec200x_netdev_ops; /* 网络设备操作集 packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> sal_at_netdev_set_pf_info /* rt-thread/components/net/sal_socket/impl/af_inet_at.c */ |-> netdev_register /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev->status_callback = rt_null; /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev->addr_callback = rt_null; /* rt-thread/components/net/netdev/src/netdev.c */ |-> ec200x->power_pin /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x->power_status_pin /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x->wakeup_pin /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_netdev_set_up /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> at_device_get_by_name /* packages/at_device-v2.0.4/src/at_device.c */ |-> ec200x_net_init /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> rt_thread_create(ec200x_init_thread_entry) /* 线程名字为ec200x_net,执行各种at指令初始化网络 packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> rt_thread_startup(ec200x_init_thread_entry) /* 启动网络初始化线程 packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> netdev_low_level_set_status /* rt-thread/components/net/netdev/src/netdev.c */ 1.6 at 设备类注册过程
 ec200x_device_class_register /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_socket_class_register /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> class->socket_num = at_device_ec200x_sockets_num; /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> class->socket_ops = &ec200x_socket_ops; /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> class->device_ops = &ec200x_device_ops; /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> at_device_class_register /* packages/at_device-v2.0.4/src/at_device.c */ 1.7 网卡的启用与禁用 1.7.1 启用网卡的执行过程
 /* 启用网卡 */netdev_set_up(netdev) /* components/net/netdev/src/netdev.c */ |-> netdev->ops->set_up(netdev) /* netdev->ops = &ec200x_netdev_ops; packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_netdev_set_up ------------------------------| /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_net_init | /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_init_thread_entry | /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |/* module init */ |ec200x_init | /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> at_client_init | /* components/net/at/src/at_client.c */ |-> ec200x_socket_init | /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> ec200x_netdev_add | /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> netdev_register | /* rt-thread/components/net/netdev/src/netdev.c */ |-> ec200x_netdev_set_up ----------------------------------| /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_net_init /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_init_thread_entry /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ 1.7.2 禁用网卡的执行过程/* 禁用网卡 */netdev_set_down(netdev) /* components/net/netdev/src/netdev.c */ |-> netdev->ops->set_down(netdev); /* components/net/netdev/src/netdev.c */ |-> ec200x_netdev_set_down ----------------------------| /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_power_off | /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> netdev_low_level_set_status(netdev, rt_false) | /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev->flags &= ~netdev_flag_up | /* rt-thread/components/net/netdev/src/netdev.c */ |-> netdev_auto_change_default | /* rt-thread/components/net/netdev/src/netdev.c */ |/* module deinit */ |ec200x_deinit | /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_netdev_set_down --------------------------------| /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ |-> ec200x_power_off /* packages/at_device-v2.0.4/class/ec200x/at_device_ec200x.c */ 1.8 socket 编程源码分析 1.8.1 socket 执行过程
 #define socket(domain, type, protocol) sal_socket(domain, type, protocol) /* rt-thread/components/net/sal_socket/include/socket/sys_socket/sys/socket.h */ |-> sal_socket /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> socket_new /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> socket_alloc /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sock = st->sockets[idx]; /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sock->socket = idx + sal_socket_offset; /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sock->magic = sal_socket_magic; /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sock->netdev = rt_null; /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sock->user_data = rt_null; /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_get_socket /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> socket_init /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sock->netdev = netdev; /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_netdev_socketops_valid /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> pf->skt_ops->socket /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> at_socket /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> alloc_socket /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> sock->type = socket_type; /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> sock->state = at_socket_open; /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> sock->ops->at_set_event_cb(at_socket_evt_recv, at_recv_notice_cb); /* 设置接收回调函数 rt-thread/components/net/at/at_socket/at_socket.c */ |-> ec200x_socket_set_event_cb /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> at_evt_cb_set[event] = cb; /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> sock->ops->at_set_event_cb(at_socket_evt_closed, at_closed_notice_cb); /* 设置关闭套接字回调函数 rt-thread/components/net/at/at_socket/at_socket.c */ |-> ec200x_socket_set_event_cb /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ |-> at_evt_cb_set[event] = cb; /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ 1.8.2 connect 函数执行过程
 #define connect(s, name, namelen) sal_connect(s, name, namelen) /* rt-thread/components/net/sal_socket/include/socket/sys_socket/sys/socket.h */ |-> sal_connect /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_socket_obj_get /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_netdev_is_up /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_netdev_socketops_valid /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> pf->skt_ops->connect /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> at_connect /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> at_get_socket /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> sock->ops->at_connect /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> ec200x_socket_connect /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c * 1.8.3 send 函数执行过程#define send(s, dataptr, size, flags) sal_sendto(s, dataptr, size, flags, null, null) /* rt-thread/components/net/sal_socket/include/socket/sys_socket/sys/socket.h */#define sendto(s, dataptr, size, flags, to, tolen) sal_sendto(s, dataptr, size, flags, to, tolen) /* rt-thread/components/net/sal_socket/include/socket/sys_socket/sys/socket.h */ |-> sal_sendto /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_socket_obj_get /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_netdev_is_up /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> sal_netdev_socketops_valid /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> pf->skt_ops->sendto /* rt-thread/components/net/sal_socket/src/sal_socket.c */ |-> at_sendto /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> at_get_socket /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> sock->ops->at_send /* rt-thread/components/net/at/at_socket/at_socket.c */ |-> ec200x_socket_send /* packages/at_device-v2.0.4/class/ec200x/at_socket_ec200x.c */ 1.8.4 recv 函数执行过程
  recv() 函数执行时,ec200x 接收的 urc 函数将数据放到 `recvpkt_list` 中,然后 recv() 函数从 `recvpkt_list` 中取出数据。


LOGO!箱式电阻炉温度控制设计
一文解析铁电存储器使用及工作原理
英国宣布将放宽规划立法,简化建造存储可再生能源的大型蓄电池流程
Facebook开发出可自动生成食谱的AI系统
二手笔记本电脑购买需知
AT组件的实现过程和代码的调用逻辑
简单图神经网络(GNN)的基础知识
ADI发布惯性传感器ADIS16365,具有更快响应时间和更
简易数字直流电压表电路及程序
华为mate10什么时候上市?华为mate10最新消息:令人期待的华为mate10要来了,不仅配置顶级外观更是完美
地铁空调通风系统监测环境温度和空气湿度生的温湿度传感器
基于ZigBee的无线传感网络与互联网关是怎样接入的
傅里叶变换和反变换公式
iOS10.3最新消息:iOS10.3.2beta4发现罕见的Bug,“当机”、“卡顿”接踵而来
能合法上路的F1方程式赛车:梅赛德斯-AMG Project One来袭!
为什么要推进IPv6?为每一粒沙提供一个IP
传2022年三星3纳米工艺将使用MBCFET技术
ADI正式完成对Linear的并购,在电源管理领域有哪些创新技术和产品?
一文详解温补衰减器
为什么5G的到来将使VR/AR行业爆发