软件设计开发笔记1:基于状态机的程序设计

  在编码实现的过程中,我们会经常使用到条件判断结构,而且使用起来很方便。但是在需要转移的状态比较多,或是条件比较复杂时,我们就可能需要很长的条件判断结构来处理。不过,过于复杂的条件判断结构会给代码的编写和维护带来很大的困扰,所以我们希望探索其他的方法来简化这类条件结构。

1、原理概述

  条件判断在代码实现中非常有用,有时候甚至是必不可少的。但过于复杂的条件结构却会让程序逻辑变得冗长而繁琐,而在某些情况下我们希望采取方法避免这一情况出现。

1.1、问题提出

  在项目开发中经常会遇到if/esle语句以及switch/case语句之类,或者是嵌套的多分支条件判断的结构。这类结构一旦过于复杂或冗长就会使程序的逻辑结构非常繁琐。所以很多时候我们希望避免使用过于复杂的条件结构。基于这一目的,我们希望探索一些方法来简化这类问题。

  在我们实践的过程中,我们发现有些复杂的条件结构实际控制的是同一事物在不同状态下的转换。这就让我们想到了状态机,那么是否可以借用状态机的机制来解决这一类的问题呢?在这一篇中我们就来分析和实现这一议题。

1.2、什么是状态机

  我们先来看看什么是状态机。一般来说,状态机(state machine)包含有5个要素,分别是状态(state)、迁移(transition)、事件(event)、动作(action)、条件(guard)。我们来具体看看这5个要素都是什么。

  • 状态:一个系统在某一时刻所存在的稳定的工作情况,系统在整个工作周期中可能有多个状态。一个状态机需要在状态集合中选取一个状态作为初始状态。

  • 迁移:系统从一个状态转移到另一个状态的过程称作迁移,迁移不是自动发生的,需要外界对系统施加影响。

  • 事件:某一时刻发生的对系统有意义的事情,状态机之所以发生状态迁移,就是因为出现了事件。

  • 动作:在状态机的迁移过程中,状态机会做出一些其它的行为,这些行为就是动作,动作是状态机对事件的响应。

  • 条件:状态机对事件并不是有求必应的,状态机还要满足一定的条件才能发生状态迁移并实现对事件的响应。

  对于状态机,我们的理解是,一个事物存在多个状态,这些状态可以相互转换,有些可能是双向的,有些可能是单向的。当一定的外部事件发生时,会促使事物的状态发生转变,这一过程就是发生了状态的迁移。当事物的状态发生转换后会执行一定的动作。但这些动作有可能是在状态转换进入时执行,也可能是状态持续过程中执行,这就要看动作执行的前提条件。

2、分析设计

  接下来我们将以BLDC操作面板的实际操作项目来分析如何实现通过状态机机制简化条件结构。在这个BLDC操作面板项目中,我们使用按键操作来实现BLDC的操作控制以及LED显示菜单的切换。

  首先我们来看一看BLDC的操作控制实现。对于BLDC的操作的操作,我们希望按下启动停止按钮时,BLDC启动并按设定的速度持续运行。在BLDC正常运行的过程中,如果长按启动停止按钮则进入全速状态,如果是短按启动停止按钮则停止。如果是在全速状态,如果长按启动停止按钮则停止,如果是短按启动停止按钮则回到常规速度状态。

  对于这个需求如果我们使用条件结构则需要使用if/else语句或者switch/case语句来判断状态,然后在各个分支中通过条件判断按钮的动作以实现对应的操作。在这一方式下,我们需要使用条件结构的嵌套来实现这个过程,从逻辑结构上来说过于复杂而且不同功能模块的耦合比较紧密。

  接下来,我们以状态机的机制来分析一下。我们注意到BLDC实际有3种状态,分别是停止状态、常速运行状态、全速运行状态。而这3种状态之间可以相互转化,但并无直接关联,在不同的状态下将执行不同的操作。它们之间将根据按钮的事件产生转换。对比前面我们对状态机的要素的表述,实际上已经完全具备了状态机的全部要素,所以我们将其状态转化过程表述如下图:

  接下来我们看一看菜单的切换问题。菜单的切换更复杂一点,就是在不同的情况下,会有不同的显示。我们将其归为5类,也就是5个菜单,这些菜单根据按键的不同显示不同的菜单。我们将每个显示菜单定义为一种状态,那么其实就已经具备了状态机的全部5个要素,具体状态转换过程如下图所示:

  我们将BLDC的控制以及显示菜单的切换抽象为状态机,以避免冗长的条件选择结构,简化程序逻辑结构,使得程序更为清晰。

3、软件实现

  前述,我们已经分析了将BLDC的控制及显示菜单的切换使用状态机来实现的方法。接下来我们就来考虑其具体的实现方式。

  首先,我们来分析BLDC的控制。我们已经知道BLDC的控制要求有3个状态:停止状态、常速状态、全速状态。通过按键事件来控制状态产生迁移并执行动作。我们需要一个变量来记录按键事件对BLDC产生的命令,这个命令变量取值0、1、2以对应3个状态的迁移命令。之所以去这样的3个值并没有什么特殊之处,仅仅只是为了我们在后续的处理中方便使用而已。同样我们需要变量来记录当前所处的状态,取值也用0、1、2对应3个状态。当然我们还需要定义每种状态下所对应的动作,为了操作方便我们将每种状态下的动作定义为一个单独的函数,也就是每种状态有一个响应函数。至于响应函数的实现则根据需求而定,函数中包括相应条件。具体如下:

void (*BldcControl[3])(void)={BldcStopHandler,BldcNormalSpeedHandler,BldcFullSpeedHandler};BldcControl[aPara.phyPara.pumpStartStop]();

  其中aPara.phyPara.pumpStartStop变量记录的是对按键事件的记录,状态机根据变量的值来调用状态响应函数来迁移并维持在指定的状态。三个函数对应三种状态下的响应函数。这样就实现了不同的事件迁移到不同的状态的状态机结构,相比于条件分支判断结构要简化很多。

  接下来,我们再来看看菜单显示状态的实现。前面我们已经描述过菜单显示划分为5种状态,我们使用一个变量来记录状态及迁移。这个变量取值0、1、2、3、4分别对应当前速度显示状态、量程显示状态、全速设定显示状态、系数设定显示状态、速度设定显示状态。具体如下:

void (*LedDisPlay[5])(void)={SpeedCurrentDisplay,SpeedUpperDisplay,SpeedFullDisplay,SpeedFactorDisplay,SpeedSettingDisplay};LedDisPlay[aPara.phyPara.menuIndex]();

  同样的aPara.phyPara.menuIndex是状态迁移及状态记录变量,而5个函数则对应不同状态下的响应函数。

4、小结

  在这一篇中,我们以一个BLDC驱动控制板的实例描述了使用状态机代替复杂的条件分支判断结构的过程及方法。我们实现了使用状态机机制编码BLDC的驱动控制和菜单显示切换的功能。这一实例已经应用于多个项目之中,效果良好。

  这一方式其实适用于很多需要条件判断来切换控制的场合。事实上,我们在多个电机控制、流程控制等应用场合都是用了类似的方法,而且应用的结果都比较满意。当然,我们并不是建议读者使用此法,只是提供一种思路,我们认为所谓结构优化本就是见仁见智的事情。

欢迎关注:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499306.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

外设驱动库开发笔记38:RTD热电阻测温驱动

我们已经讨论过多种温度检测方式,但我们尚未关注热电阻温度检测,但热电阻测温在工业环境中是非常常见的。尽管有很多集成的数字式的热电阻接口元器件,但这些器件不但成本较高,灵活性也大打折扣。所以我们有时会使用更简单灵活的电…

外设驱动库开发笔记39:按键操作驱动

按键在我们的项目中是经常使用到的组件。一般来说,我们都是在用到按键时直接针对编码,但这样每次都做很多重复性的工作。所以在这里我们考虑做一般性抽象得到一个可应用于按键操作的通用性驱动程序。 1、功能概述 按键操作在我们的产品种经常用到&#…

外设驱动库开发笔记40:AT25xxx外部存储器驱动

我们在前面开发过AT24CXX系列EEPROM存储器,它使用的是I2C接口。不过有时候我们也会使用SPI接口的EEPROM存储器。在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计、实现及使用。 1、功能概述 AT25XXX系列EEPROM存储器采用SPI接口,因其操作简单且…

闪存中的键值对:无文件系统 minINI

许多嵌入式系统应用需要以持久的方式存储某种数据:校准值、设置或日志信息。对于较少的数据量,使用外部存储器或文件系统是一种过度大材小用。在许多系统中,我使用minINI以“ini-file”的方式存储键值解析,但它需要使用某种文件系…

外设驱动库开发笔记41:ADS1256 ADC驱动

我们经常会碰到多通道AD采集的需求,有时候甚至需要高精度的ADC器件。本篇我们将来设计并实现ADS1256模数转换器的驱动。并简单讨论该驱动使用方式。 1、功能概述 ADS1256是TI公司推出的一款低噪声高分辨率的24位Sigma-Delta(E-v)模数转换器(ADC)。E-vADC与传统的逐…

PID参数自整定库之一:继电反馈整定算法

在前述的篇章中,我们实现了PID控制器并在后续对其进行了改进。但作为经典PID控制器还存在PID参数整定的问题。通常我们可以采取人工整定的办法,但人工整定涉及到比较专业的知识,而且找到合适的参数本身也不是一件容易的事,所以人们…

外设驱动库开发笔记42:DAC8552 DAC驱动

模拟信号输出是经常会遇到的应用需求,解决的办法应多种,但我们使用最多的还是数模转换。对于不同的数模转换器我们需要为其编写适用的驱动程序,在这一篇中我们就来考虑如何实现DAC8552高精度模数转换器的驱动程序。 1、功能概述 该DAC8552是…

软件设计开发笔记2:基于QT设计串口调试工具

串口通信是我们经常会遇到的问题。很多时候当我们设计一个串口应用时,我们希望有一个简便的、可视的方式来验证它。这一篇中我们就来基于QT设计一个串口调试工具。 1、概述 在开始软件设计之前,我们来简略地分析一下这样一个小软件其要包含的主要内容有…

外设驱动库开发笔记43:GPIO模拟SPI驱动

SPI总线是我们常用的串行设备接口,一般情况下我们都会适应硬件SPI接口,但有些时候当硬件端口不足时,我们也希望可以使用软件来模拟SPI硬件接口,特别是要求不是很高的时候。在这一篇中我们将来讨论如何使用GPIO和软件来模拟SPI通讯…

外设驱动库开发笔记44:DDC114 ADC驱动

在产品设计过程中,很多时候都会用到ADC器件,而在一些特殊场合还需要一些特别的ADC器件。我们在这篇中将讨论常用于医疗器件方面的,DDC114这款电流输入ADC,并为其设计一个驱动程序。 1、功能概述 模数转换器DDC114是一款电流输入型…

PID控制器改进笔记之七:改进PID控制器之防超调设定

我们已经设计了PID控制器,并根据实际使用的情况对器进行了诸多的改进。在这一篇中我们将讨论如何改进PID控制器超调的问题。 1、问题提出 在前面的文章中,我们曾推导过增量式PID控制器的公式,并且对其进行了离散化以适用于程序实现&#xff…

软件设计开发笔记3:基于QT的Modbus RTU主站

Modbus是一种常见的工业系统通讯协议。在我们的设计开发工作中经常使用到它。在这一篇中我们将简单实现一个基于QT的Modbus RTU主站上位工具。 1、概述 Modbus RTU主站应用很常见,有一些是通用的,有一些是专用的。而这里我们希望实现一个主要针对我们的…

外设驱动库开发笔记45:MS4515DO压力传感器驱动

很多时候我们需要检测流量和压力这些参数,比如我们要检测大气压,或者通过测量差压来获得输送流体的流量等,都需要用到压力传感器。这一篇我们就来讨论MS4515DO压力传感器的数据获取。 1、功能概述 MS4515DO是TE公司推出的一款基于PCB安装的小…

外设驱动库开发笔记48:MCP4725单通道DAC驱动

在产品设计过程中,我们经常会遇到数模转换的应用需求。在本篇种我们就来讨论一下MCP4725单通道数模转换器的驱动设计与实现。 1、功能概述 MCP4725是一个低功耗,高精度,单通道,12位缓冲电压输出数字到模拟转换器(DAC)与非易失性存…

如何确保不使用动态内存

在许多嵌入式应用程序中,内存分配必须是静态的,而不是动态的。意味着在应用程序中不应使用对malloc()或free()等内容的调用,因为它们可能会在运行时失败(内存不足、堆碎片)。 但是,当与第三方库甚至 C/C 标…

go 单元测试 testing 打印输出_2020,你需掌握go 单元测试进阶篇

本文说明go语言自带的测试框架未提供或者未方便地提供的测试方案,主要是用于解决写单元测试中比较头痛的依赖问题。也就是伪造模式,经典的伪造模式有桩对象(stub),模拟对象(mock)和伪对象(fake)。比较幸运的是,社区有丰富的第三方测试框架支持…

一文读懂Git工作流

Git是目前最流行的代码管理工具,相信大家也都是在用Git来管理自己团队的源代码。 团队一般为了规范开发,保持良好的代码提交记录以及维护 Git 分支结构清晰,方便后续维护等,都会迫切需要一个比较规范的 Git 工作流。 本文就是在…

xbox360fsd更新游戏封面_游戏类短视频创作指南

一.起步阶段1.内容发布垂直,整体风格一致,选定一个品类的游戏内容风格持续更新注意:冷启动时期不要频繁更换游戏类型2.账号IP化 根据自身风格特色打造独特的风格账号。有利延长账号生命周期,提升粉丝转化率。搞笑、中二…

开发者们都在关注的网站

开发者们都在关注的网站 😉 综合类(5个) 1、GitHub 全球最大的编程开源社区,很多优秀的开源项目都在上边,不知道这个都不要说自己是程序员😂 访问地址:https://github.com 2、CSDN 全球最大中…

ios framework 调用第三方 framework_Python基础:标准库和常用的第三方库

Python的标准库有:名称作用datetime为日期和时间处理同时提供了简单和复杂的方法。zlib直接支持通用的数据打包和压缩格式:zlib,gzip,bz2,zipfile,以及 tarfile。random提供了生成随机数的工具。math为浮点…