改变嵌软开发思维方式之:状态机的三种实现方法

嵌入式大杂烩 2021-09-15 22:53

关注「嵌入式大杂烩」,选择「星标公众号」一起进步!

作者 | Alicedodo

上一篇推文:咱们是时候改变一下嵌入式软件开发思维方式了!提到了状态机。在之前的推文中:干货 | 嵌入式之状态机编程。有简单介绍了状态机的示例。这次我们一起来学习C语言实现状态机的三种方法解析。

状态机的实现无非就是 3 个要素:状态事件响应。转换成具体的行为就 3 句话。

  • 发生了什么事?
  • 现在系统处在什么状态?
  • 在这样的状态下发生了这样的事,系统要干什么?

用 C 语言实现状态机主要有 3 种方法:switch—case 法表格驱动法函数指针法

switch—case 法

状态用 switch—case 组织起来, 将事件也用switch—case 组织起来, 然后让其中一个 switch—case 整体插入到另一个 switch—case 的每一个 case 项中  。

「程序清单 List4  :」

switch(StateVal)
{
    case S0:
  switch(EvntID)
  {
   case E1:
    action_S0_E1(); /*S0 状态下 E1 事件的响应*/
    StateVal = new state value;/*状态迁移,不迁移则没有此行*/
    break;
   case E2:
    action_S0_E2(); /*S0 状态下 E2 事件的响应*/
    StateVal = new state value;
    break;
   ......
   case Em:
    action_S0_Em(); /*S0 状态下 Em 事件的响应*/
    StateVal = new state value;
    break;
   default:
    break;
  }
  break;
    case S1:
  ......
  break;
    ......
    case Sn:
  ......
  break;
    default:
  break;
}

上面的伪代码示例只是通用的情况,实际应用远没有这么复杂。虽然一个系统中事件可能有很多种,但在实际应用中,许多事件可能对某个状态是没有意义的。

例如在程序清单 List4中,如果 E2、······ Em 对处在 S0 状态下的系统没有意义,那么在 S0 的 case 下有关事件E2、······ Em 的代码根本没有必要写,状态 S0 只需要考虑事件 E1 的处理就行了。

既然是两个 switch—case 之间的嵌套, 那么就有一个谁嵌套谁的问题, 所以说 switch—case法有两种写法:状态嵌套事件事件嵌套状态。这两种写法都可以, 各有利弊, 至于到底选用哪种方式就留给设计人员根据具体情况自行决断吧。

关于 switch—case 法还有最后一点要说明, 因为 switch—case 的原理是从上到下挨个比较,越靠后,查找耗费的时间就越长,所以要注意状态和事件在各自的 switch 语句中的安排顺序,不推荐程序清单 List4 那样按顺序号排布的方式。出现频率高或者实时性要求高的状态和事件的位置应该尽量靠前。

表格驱动法

如果说 switch—case 法是线性的,那么表格驱动法则是平面的。表格驱动法的实质就是将状态和事件之间的关系固化到一张二维表格里, 把事件当做纵轴,把状态当做横轴,交点[Sn , Em]则是系统在 Sn 状态下对事件 Em 的响应  。

如图 4, 我把表格中的 Node_SnEm 叫做状态机节点, 状态机节点 Node_SnEm 是系统在 Sn状态下对事件 Em 的响应。这里所说的响应包含两个方面:输出动作和状态迁移。状态机节点一般是一个类似程序清单 List5 中的结构体变量 。

struct fsm_node
{

    void (*fpAction)(void* pEvnt);
    INT8U u8NxtStat;
};

程序清单 List5 中的这个结构体有两个成员:fpAction 和 u8NxtStat。fpAction 是一个函数指针, 指向一个形式为 void func(void * pEvnt)的函数, func 这个函数是对状态转移中动作序列的标准化封装。

也就是说, 状态机在状态迁移的时候, 不管输出多少个动作、操作多少个变量、调用多少个函数,这些行为统统放到函数 func 中去做。

把动作封装好了之后,再把封装函数 func 的地址交给函数指针 fpAction,这样,想要输出动作,只需要调用函数指针 fpAction 就行了。

再看看上面的 func 函数,会发现函数有一个形参 pEvnt,这是一个类型为 void * 的指针, 在程序实际运行时指向一个能存储事件的变量,通过这个指针我们就能获知关于事件的全部信息,这个形参是很有必要的。事件一般包括两个属性:事件的类型和事件的内容。

例如一次按键事件,我们不仅要知道这是一个按键事件,还要知道按下的到底是哪个键。事件的类型和状态机当前的状态可以让我们在图 4 的表格中迅速定位,确定该调用哪个动作封装函数, 但是动作封装函数要正确响应事件还需要知道事件的内容是什么, 这也就是形参pEvnt 的意义。

由于事件的多样性,存储事件内容的数据格式不一定一样,所以就把 pEvnt 定义成了 void * 型,以增加灵活性。有关 fpAction 的最后一个问题:如果事件 Em 对状态 Sn 没有意义,那么状态机节点Node_SnEm 中的 fpAction 该怎么办?我的答案是:那就让它指向一个空函数呗!前面不是说过么,什么也不干也叫响应。

u8NxtStat 存储的是状态机的一个状态值。我们知道, 状态机响应事件要输出动作, 也就是调用函数指针 fpAction 所指向的那个封装函数, 函数调用完毕后程序返回主调函数, 状态机对事件的响应就算结束了, 下一步就要考虑状态迁移的问题了。

可能要保持本状态不变, 也可能要迁移到一个新的状态,该如何抉择呢?u8NxtStat 存储的状态就是状态机想要的答案!

图 4 的这张表格反映在 C 语言代码里就是一个二维数组,第 1 维就是状态机的状态,第 2维就是统一分类的事件,而数组的元素则是程序清单 List5 中的结构体常量。如果程序中使用表格驱动法,还需要注意一些特别的事项。要将状态当做表格的横轴,那么就要求状态值集合必须满足以下条件:

(1) 该集合是一个递增的等差整数数列

(2) 该数列初值为 0

(3) 该数列等差值为 1

“事件” 作为纵轴,其特点和要求与用来做横轴的“状态” 完全一致。在 C 语言提供的数据类型中, 没有比枚举更符合以上要求的可选项了, 极力推荐将状态集合和事件类型集合做成枚举常量表格驱动法的优点:调用接口统一 ,定位快速。

表格驱动法屏蔽了不同状态下处理各个事件的差异性,因此可以将处理过程中的共性部分提炼出来,做成标准统一的框架式代码,形成统一的调用接口。根据程序清单 List5 中的状态机节点结构体,做成的框架代码如程序清单 List6 所示。

表格驱动法查找目标实际上就是一次二维数组的寻址操作,所以它的平均效率要远高于switch—case 法。

「程序清单 List6  :」

extern struct fsm_node g_arFsmDrvTbl[][]; /*状态机驱动表格*/
INT8U u8CurStat = 0/*状态暂存*/
INT8U u8EvntTyp = 0/*事件类型暂存*/
void* pEvnt = NULL/*事件变量地址暂存*/
struct fsm_node stNodeTmp = {NULL0}; /*状态机节点暂存*/
u8CurStat = get_cur_state(); /*读取当前状态*/
u8EvntTyp = get_cur_evnt_typ(); /*读取当前触发事件类型*/
pEvnt = (void*)get_cur_evnt_ptr(); /*读取事件变量地址*/
stNodeTmp = g_arFsmDrvTbl[u8CurStat ][u8EvntTyp ];/*定位状态机节点*/
stNodeTmp.fpAction(pEvnt ); /*动作响应*/
set_cur_state(stNodeTmp.u8NxtStat); /*状态迁移*/
.....

表格驱动法好则好矣,但用它写出来的程序还有点儿小问题,我们先来看看按照表格驱动法写出来的程序有什么特点 。

前面说过,表格驱动法可以把状态机调度的部分做成标准统一的框架代码,这个框架适用性极强, 不管用状态机来实现什么样的应用, 框架代码都不需要做改动, 我们只需要根据实际应用场合规划好状态转换图,然后将图中的各个要素(状态、事件、动作、迁移,有关“条件”要素一会儿再说)用代码实现就行了,我把这部分代码称作应用代码。

在应用代码的.c 文件中, 你会看到一个声明为 const 的二维数组, 也就是图 4 所示的状态驱动表格, 还会看到许多彼此之间毫无关联的函数, 也就是前面提到的动作封装函数。这样的一份代码, 如果手头上没有一张状态转换图, 让谁看了也会一头雾水, 这样的格式直接带来了代码可读性差的问题。

如果我们想给状态机再添加一个状态,反映到代码上就是给驱动表格再加一列内容,同时也要新添加若干个动作封装函数。如果驱动表格很大, 做这些工作是很费事儿的, 而且容易出错。如果不小心在数组中填错了位置, 那么程序跑起来就和设计者的意图南辕北辙了,

远没有在 switch—case 法中改动来得方便、安全。上面说的只是小瑕疵, 其实最让我不爽的是表格驱动法不能使用干货 | 嵌入式之状态机编程 中 g_stFSM那样的 Extended State Machine(对这个词组还有印象吧?)!Extended State Machine 的最大特点就是状态机响应事件之前先判断条件,根据判定结果选择执行哪些动作,转向哪个状态。

也就是说,系统在状态 Sn 下发生了事件 Em 后,转向的状态不一定是唯一的,这种灵活性是 Extended State Machine 的最有价值的优点。

回过头来看看程序清单 List5 中给出的状态机节点结构体,如果系统在状态 Sn 下发生了事件 Em, 状态机执行完 fpAction 所给出的动作响应之后, 必须转到 u8NxtStat 指定的状态。

表格驱动法的这个特性直接杜绝了 Extended State Machine 在表格驱动法中应用的可能性, 所以表格驱动法的代码实现中不存在“条件” 这个状态机要素。ESM,你是如此的优秀,我怎么舍得抛弃你 ?!

再看图 4 所示的表格驱动法示例图,如果我们把表格中的代表事件的纵轴去掉,只留下代表状态的横轴,将一列合并成一格,前文提到的问题是不是能得到解决呢?不错!这就是失传江湖多年的《葵花宝典》 ——阉割版表格驱动法 !!

阉割版表格驱动法,又名压缩表格驱动法,一维状态表格与事件 switch—case 的合体。压缩表格驱动法使用了一维数组作为驱动表格,数组的下标即是状态机的各个状态。

表格中的元素叫做压缩状态机节点, 节点的主要内容还是一个指向动作封装函数的函数指针, 只不过这个动作封装函数不是为某个特定事件准备的, 而是对所有的事件都有效的。

节点中不再强制指定状态机输出动作完毕后所转向的状态, 而是让动作封装函数返回一个状态, 并把这个状态作为状态机新的状态。

压缩表格驱动法的这个特点, 完美的解决了 Extended State Machine 不能在表格驱动法中使用的问题 。

程序清单 List7 中的示例代码包含了压缩状态机节点结构体和状态机调用的框架代码。

「程序清单 List7:」

struct fsm_node /*压缩状态机节点结构体*/
{

 INT8U (*fpAction)(void* pEvnt); /*事件处理函数指针*/
 INT8U u8StatChk; /*状态校验*/
};
......
u8CurStat = get_cur_state(); /*读取当前状态*/
......
if(stNodeTmp.u8StatChk == u8CurStat )
{
 u8CurStat = stNodeTmp.fpAction(pEvnt ); /*事件处理*/
 set_cur_state(u8CurStat ); /*状态迁移*/
}
else
{
 state_crash(u8CurStat ); /*非法状态处理*/
}
.....

对照程序清单 List5,就会发现程序清单 List7 中 struct fsm_node 结构体的改动之处。首先, fpAction 所指向函数的函数形式变了,动作封装函数 func 的模样成了这样的了:

INT8U func(void * pEvnt);

现在的动作封装函数 func 是要返回类型为 INT8U 的返回值的,这个返回值就是状态机要转向的状态, 也就是说, 压缩表格驱动法中的状态机节点不负责状态机新状态的确定, 而把这项任务交给了动作封装函数 func, func 返回哪个状态, 状态机就转向哪个状态。

新状态由原来的常量变成了现在的变量,自然要灵活许多。上面说到现在的动作封装函数 func 要对当前发生的所有的事件都要负责, 那么 func 怎么会知道到底是哪个事件触发了它呢?看一下 func 的形参 void * pEvnt 。

在程序清单 List5 中我们提到过,这个形参是用来向动作封装函数传递事件内容的,但是从前文的叙述中我们知道, pEvnt 所指向的内存包含了事件的所有信息, 包括事件类型和事件内容 , 所以通过形参 pEvnt , 动作封装函数 func 照样可以知道事件的类型。

程序清单 List7 中 struct fsm_node 结构体还有一个成员 u8StatChk , 这里面存储的是状态机 的一个状态,干什么用的呢?玩 C 语言数组的人都知道,要严防数组寻址越界

要知道,压缩表格驱动法的驱动表格是一个以状态值为下标的一维数组, 数组元素里面最重要的部分就是一个个动作封装函数的地址。

函数地址在单片机看来无非就是一段二进制数据, 和内存中其它的二进制数据没什么两样,不管程序往单片机 PC 寄存器里塞什么值,单片机都没意见。假设程序由于某种意外而改动了存储状态机当前状态的变量,使变量值变成了一个非法状态。

再发生事件时, 程序就会用这个非法的状态值在驱动表格中寻址, 这时候就会发生内存泄露,程序拿泄露内存中的未知数据当函数地址跳转,不跑飞才怪!

为了防止这种现象的发生, 压缩状态机节点结构体中又添加了成员 u8StatChk 。u8StatChk中存储的是压缩状态机节点在一维驱动表格的位置, 例如某节点是表格中的第 7 个元素, 那么这个节点的成员 u8StatChk 值就是 6。

看一下程序清单 List7 中的框架代码示例, 程序在引用函数指针 fpAction 之前, 先检查当前状态和当前节点成员 u8CurStat 的值是否一致,一致则认为状态合法,事件正常响应,如果不一致,则认为当前状态非法,转至意外处理,最大限度保证程序运行的安全。当然,如果泄露内存中的数据恰好和 u8CurStat 一致,那么这种方法真的就回天乏力了。

还有一个方法也可以防止状态机跑飞,如果状态变量是枚举,那么框架代码就可以获知状态值的最大值, 在调用动作封装函数之前判断一下当前状态值是否在合法的范围之内, 同样能保证状态机的安全运行。

压缩表格驱动法中动作封装函数的定义形式我们已经知道了,函数里面到底是什么样子的呢?程序清单 List8 是一个标准的示例。

「程序清单List8:」

INT8U action_S0(void* pEvnt)
{
 INT8U u8NxtStat = 0;
 INT8U u8EvntTyp = get_evnt_typ(pEvnt);
 switch(u8EvntTyp )
 {
  case E1:
   action_S0_E1(); /*事件 E1 的动作响应*/
   u8NxtStat = new state value; /*状态迁移,不迁移也必须有本行*/
   break;
   ......
  case Em:
   action_S0_Em(); /*事件 Em 的动作响应*/
   u8NxtStat = new state value; /*状态迁移,不迁移也必须有本行*/
   break;
  default:
   ; /*不相关事件处理*/
   break;
 }
 return u8NxtStat ; /*返回新状态*/
}

从程序清单 List8 可以看出, 动作封装函数其实就是事件 switch—case 的具体实现。函数根据形参 pEvnt 获知事件类型, 并根据事件类型选择动作响应, 确定状态机迁移状态, 最后将新的状态作为执行结果返回给框架代码。

有了这样的动作封装函数, Extended State Machine 的应用就可以完全不受限制了!到此,有关压缩表格驱动法的介绍就结束了。

个人认为压缩表格驱动法是相当优秀的,它既有表格驱动法的简洁、高效、标准,又有 switch—case 法的直白、灵活、多变,相互取长补短,相得益彰。

函数指针法

上面说过,用 C 语言实现状态机主要有 3 种方法(switch—case 法、表格驱动法、函数指针法), 其中函数指针法是最难理解的, 它的实质就是把动作封装函数的函数地址作为状态来看待。不过,有了之前压缩表格驱动法的铺垫,函数指针法就变得好理解了,因为两者本质上是相同的。

压缩表格驱动法的实质就是一个整数值(状态机的一个状态)到一个函数地址(动作封装函数)的一对一映射, 压缩表格驱动法的驱动表格就是全部映射关系的直接载体。在驱动表格中通过状态值就能找到函数地址,通过函数地址同样能反向找到状态值。

我们用一个全局的整型变量来记录状态值,然后再查驱动表格找函数地址,那干脆直接用一个全局的函数指针来记录状态得了,还费那劳什子劲干吗?!这就是函数指针法的前世今生。

用函数指针法写出来的动作封装函数和程序清单 List8 的示例函数是很相近的, 只不过函数的返回值不再是整型的状态值, 而是下一个动作封装函数的函数地址, 函数返回后, 框架代码再把这个函数地址存储到全局函数指针变量中。

相比压缩表格驱动法,在函数指针法中状态机的安全运行是个大问题,我们很难找出一种机制来检查全局函数指针变量中的函数地址是不是合法值。如果放任不管, 一旦函数指针变量中的数据被篡改,程序跑飞几乎就不可避免了。

小节

有关状态机的东西说了那么多,相信大家都已经感受到了这种工具的优越性,状态机真的是太好用了!其实我们至始至终讲的都是有限状态机(Finite State Machine 现在知道为什么前面的代码中老是有 fsm 这个缩写了吧!), 还有一种比有限状态机更 NB 更复杂的状态机, 那就是层次状态机(Hierarchical State Machine 一般简写为 HSM)。

通俗的说,系统中只存在一个状态机的叫做有限状态机,同时存在多个状态机的叫做层次状态机(其实这样解释层次状态机有些不严谨, 并行状态机也有多个状态机, 但层次状态机各个状态机之间是上下级关系,而并行状态机各个状态机之间是平级关系)。

层次状态机是一种父状态机包含子状态机的多状态机结构,里面包含了许多与面向对象相似的思想, 所以它的功能也要比有限状态机更加强大, 当一个问题用有限状态机解决起来有些吃力的时候, 就需要层次状态机出马了。

层次状态机理论我理解得也不透彻, 就不在这里班门弄斧了,大家可以找一些有关状态机理论的专业书籍来读一读。要掌握状态机编程,理解状态机(主要指有限状态机)只是第一步,也是最简单的一步,更重要的技能是能用状态机这个工具去分析解剖实际问题:划分状态、 提取事件、 确定转换关系、规定动作等等,形成一张完整的状态转换图,最后还要对转换图进行优化,达到最佳。

把实际问题变成了状态转换图, 工作的一大半就算完成了, 这个是具有架构师气质的任务,剩下的问题就是按照状态图编程写代码了,这个是具有代码工特色的工作。

本文来源网络版权归原作者所有。如涉及作品版权问题,请联系我进行删除。

往期推荐:

往期推荐



咱们是时候改变一下嵌入式软件开发思维方式了!

什么是不完全类型?

C语言、嵌入式中几个非常实用的宏技巧

干货 | 浅析程序开机自启动


嵌入式大杂烩 专注于嵌入式技术,包括但不限于C/C++、嵌入式、物联网、Linux等编程学习笔记,同时,内包含大量的学习资源。欢迎关注,一同交流学习,共同进步!
评论
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 141浏览
  • 随着消费者对汽车驾乘体验的要求不断攀升,汽车照明系统作为确保道路安全、提升驾驶体验以及实现车辆与环境交互的重要组成,日益受到业界的高度重视。近日,2024 DVN(上海)国际汽车照明研讨会圆满落幕。作为照明与传感创新的全球领导者,艾迈斯欧司朗受邀参与主题演讲,并现场展示了其多项前沿技术。本届研讨会汇聚来自全球各地400余名汽车、照明、光源及Tier 2供应商的专业人士及专家共聚一堂。在研讨会第一环节中,艾迈斯欧司朗系统解决方案工程副总裁 Joachim Reill以深厚的专业素养,主持该环节多位
    艾迈斯欧司朗 2025-01-16 20:51 193浏览
  • 本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。源码修改修改源码根目录下文件device/rockchip/rk3562/package_performance.xml并添加以下内容,注意"+"号为添加内容,"com.tencent.mm"为AP
    Industio_触觉智能 2025-01-17 14:09 161浏览
  • 日前,商务部等部门办公厅印发《手机、平板、智能手表(手环)购新补贴实施方案》明确,个人消费者购买手机、平板、智能手表(手环)3类数码产品(单件销售价格不超过6000元),可享受购新补贴。每人每类可补贴1件,每件补贴比例为减去生产、流通环节及移动运营商所有优惠后最终销售价格的15%,每件最高不超过500元。目前,京东已经做好了承接手机、平板等数码产品国补优惠的落地准备工作,未来随着各省市关于手机、平板等品类的国补开启,京东将第一时间率先上线,满足消费者的换新升级需求。为保障国补的真实有效发放,基于
    华尔街科技眼 2025-01-17 10:44 221浏览
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 120浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 154浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 95浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 46浏览
  • 电竞鼠标应用环境与客户需求电竞行业近年来发展迅速,「鼠标延迟」已成为决定游戏体验与比赛结果的关键因素。从技术角度来看,传统鼠标的延迟大约为20毫秒,入门级电竞鼠标通常为5毫秒,而高阶电竞鼠标的延迟可降低至仅2毫秒。这些差异看似微小,但在竞技激烈的游戏中,尤其在对反应和速度要求极高的场景中,每一毫秒的优化都可能带来致胜的优势。电竞比赛的普及促使玩家更加渴望降低鼠标延迟以提升竞技表现。他们希望通过精确的测试,了解不同操作系统与设定对延迟的具体影响,并寻求最佳配置方案来获得竞技优势。这样的需求推动市场
    百佳泰测试实验室 2025-01-16 15:45 334浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 293浏览
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 105浏览
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 73浏览
  • 80,000人到访的国际大展上,艾迈斯欧司朗有哪些亮点?感未来,光无限。近日,在慕尼黑electronica 2024现场,ams OSRAM通过多款创新DEMO展示,以及数场前瞻洞察分享,全面展示自身融合传感器、发射器及集成电路技术,精准捕捉并呈现环境信息的卓越能力。同时,ams OSRAM通过展会期间与客户、用户等行业人士,以及媒体朋友的深度交流,向业界传达其以光电技术为笔、以创新为墨,书写智能未来的深度思考。electronica 2024electronica 2024构建了一个高度国际
    艾迈斯欧司朗 2025-01-16 20:45 378浏览
  •  光伏及击穿,都可视之为 复合的逆过程,但是,复合、光伏与击穿,不单是进程的方向相反,偏置状态也不一样,复合的工况,是正偏,光伏是零偏,击穿与漂移则是反偏,光伏的能源是外来的,而击穿消耗的是结区自身和电源的能量,漂移的载流子是 客席载流子,须借外延层才能引入,客席载流子 不受反偏PN结的空乏区阻碍,能漂不能漂,只取决于反偏PN结是否处于外延层的「射程」范围,而穿通的成因,则是因耗尽层的过度扩张,致使跟 端子、外延层或其他空乏区 碰触,当耗尽层融通,耐压 (反向阻断能力) 即告彻底丧失,
    MrCU204 2025-01-17 11:30 179浏览
我要评论
1
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦