当前位置: 首页 > news >正文

嵌入式软件有限状态机的 C 语言实现

状态机模式是一种行为模式,通过多态实现不同状态的调转行为的确是一种很好的方法,只可惜在嵌入式环境下,有时只能写纯C代码,并且还需要考虑代码的重入和多任务请求跳转等情形,因此实现起来着实需要一番考虑。

近日在看到了一个状态机的实现,也学着写了一个,与大家分享。

首先,分析一下一个普通的状态机究竟要实现哪些内容。

状态机存储从开始时刻到现在的变化,并根据当前输入,决定下一个状态。这意味着,状态机要存储状态、获得输入(我们把它叫做跳转条件)、做出响应。

如上图所示,{s1, s2, s3}均为状态,箭头c1/a1表示在s1状态、输入为c1时,跳转到s2,并进行a1操作。

最下方为一组输入,状态机应做出如下反应:

图片

当某个状态遇到不能识别的输入时,就默认进入陷阱状态,在陷阱状态中,不论遇到怎样的输入都不能跳出。

为了表达上面这个自动机,我们定义它们的状态和输入类型:

typedef int State;
typedef int Condition;#define STATES 3 + 1
#define STATE_1 0
#define STATE_2 1
#define STATE_3 2
#define STATE_TRAP 3#define CONDITIONS 2
#define CONDITION_1 0
#define CONDITION_2 1

在嵌入式环境中,由于存储空间比较小,因此把它们全部定义成宏。此外,为了降低执行时间的不确定性,我们使用O(1)的跳转表来模拟状态的跳转。

首先定义跳转类型:

typedef void (*ActionType)(State state, Condition condition);typedef struct
{State next;ActionType action;
} Trasition, * pTrasition;

然后按照上图中的跳转关系,把三个跳转加一个陷阱跳转先定义出来:

// (s1, c1, s2, a1)
Trasition t1 = {STATE_2,action_1
};// (s2, c2, s3, a2)
Trasition t2 = {STATE_3,action_2
};// (s3, c1, s2, a3)
Trasition t3 = {STATE_2,action_3
};// (s, c, trap, a1)
Trasition tt = {STATE_TRAP,action_trap
};

其中的动作,由用户自己完成,在这里仅定义一条输出语句。

void action_1(State state, Condition condition)
{printf("Action 1 triggered.\n");
}

最后定义跳转表:

pTrasition transition_table[STATES][CONDITIONS] = {
/*      c1,  c2*/
/* s1 */&t1, &tt,
/* s2 */&tt, &t2,
/* s3 */&t3, &tt,
/* st */&tt, &tt,
};

即可表达上文中的跳转关系。

最后定义状态机,如果不考虑多任务请求,那么状态机仅需要存储当前状态便行了。例如:

typedef struct
{State current;
} StateMachine, * pStateMachine;State step(pStateMachine machine, Condition condition)
{pTrasition t = transition_table[machine->current][condition];(*(t->action))(machine->current, condition);machine->current = t->next;return machine->current;
}

但是考虑到当一个跳转正在进行的时候,同时又有其他任务请求跳转,则会出现数据不一致的问题。

举个例子:task1(s1, c1/a1 –> s2)和task2(s2, c2/a2 –> s3)先后执行,是可以顺利到达s3状态的,但若操作a1运行的时候,执行权限被task2抢占,则task2此时看到的当前状态还是s1,s1遇到c2就进入陷阱状态,而不会到达s3了,也就是说,状态的跳转发生了不确定,这是不能容忍的。

因此要重新设计状态机,增加一个“事务中”条件和一个用于存储输入的条件队列。修改后的代码如下:

#define E_OK        0
#define E_NO_DATA   1
#define E_OVERFLOW  2typedef struct
{Condition queue[QMAX];int head;int tail;bool overflow;
} ConditionQueue, * pConditionQueue;int push(ConditionQueue * queue, Condition c)
{   unsigned int flags;Irq_Save(flags);if ((queue->head == queue->tail + 1) || ((queue->head == 0) && (queue->tail == 0))){queue->overflow = true;Irq_Restore(flags);return E_OVERFLOW;}else{queue->queue[queue->tail] = c;queue->tail = (queue->tail + 1) % QMAX;Irq_Restore(flags);}return E_OK;
}int poll(ConditionQueue * queue, Condition * c)
{unsigned int flags;Irq_Save(flags);if (queue->head == queue->tail){Irq_Restore(flags);return E_NO_DATA;}else{*c = queue->queue[queue->head];queue->overflow = false;queue->head = (queue->head + 1) % QMAX;Irq_Restore(flags);}return E_OK;
}typedef struct
{State current;bool inTransaction;ConditionQueue queue;
} StateMachine, * pStateMachine;static State __step(pStateMachine machine, Condition condition)
{State current = machine -> current;pTrasition t = transition_table[current][condition];(*(t->action))(current, condition);current = t->next;machine->current = current;return current;
}State step(pStateMachine machine, Condition condition)
{Condition next_condition;int status;State current;if (machine->inTransaction){push(&(machine->queue), condition);return STATE_INTRANSACTION;}else{machine->inTransaction = true;current = __step(machine, condition);status = poll(&(machine->queue), &next_condition);while(status == E_OK){__step(machine, next_condition);status = poll(&(machine->queue), &next_condition);}machine->inTransaction = false;return current;}
}void initialize(pStateMachine machine, State s)
{machine->current = s;machine->inTransaction = false;machine->queue.head = 0;machine->queue.tail = 0;machine->queue.overflow = false;
}

相关文章:

嵌入式软件有限状态机的 C 语言实现

状态机模式是一种行为模式,通过多态实现不同状态的调转行为的确是一种很好的方法,只可惜在嵌入式环境下,有时只能写纯C代码,并且还需要考虑代码的重入和多任务请求跳转等情形,因此实现起来着实需要一番考虑。 近日在看…...

面试题常考:LRU缓存

题目: 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存 int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值&…...

Redis 教程 - 持久化

Redis 教程 - 持久化 在 Redis 中,持久化是指将数据从内存保存到磁盘上,以便在重启或服务器故障后仍能恢复数据。Redis 提供了两种持久化方式:RDB(Redis Database)和 AOF(Append-Only File)。本…...

2023 大学生数学建模竞赛-C题-第一问

题目: 在生鲜商超中,一般蔬菜类商品的保鲜期都比较短,且品相随销售时间的增加而变差, 大部分品种如当日未售出,隔日就无法再售。因此,商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销…...

设计模式3 观察者模式

一 观察者模式 1.1 概述 观察者模式是一种行为模式,又称之为“发布/订阅”模式,在该模式中被观察的对象叫主题,依赖主题的对象被称为观察者,当主题发生改变时,会通知所有观察者进行更新。多个对象存在一对多的关系&a…...

如何防止网络安全攻击

为了防止网络安全攻击,以下是一些常见的防御措施和建议: 使用强密码:确保使用足够长、复杂且随机的密码,并定期更改密码。不要在多个账户中重复使用相同的密码。 更新和修补软件:定期更新操作系统、应用程序和安全补丁…...

怎么从0到1实现一个PHP框架?

写在前面 本人开发的框架在2021年年初开发完成,后面没有再做过任何维护和修改。是仅供大家参考交流的学习项目,请勿使用在生产环境,也勿用作商业用途。 框架地址: https://github.com/yijiebaiyi/fast_framework 整体思路 开发…...

脚本:python实现樱花树

文章目录 代码效果 代码 from turtle import * from random import * from math import * def tree(n, l):pd () # 下笔# 阴影效果t cos ( radians ( heading () 45 ) ) / 8 0.25pencolor ( t, t, t )pensize ( n / 3 )forward ( l ) # 画树枝if n > 0:b random () *…...

公司内部传文件怎么安全——「用绿盾透明加密软件」

为保证公司内部文件传递的安全性,可以使用天锐绿盾透明加密软件来进行保护。以下是具体的操作步骤: 在公司内部部署天锐绿盾加密软件,确保需要传递的文件都能受到加密保护。 在员工的工作电脑上安装天锐绿盾客户端,并设置好相关的…...

提高使用VS Code工作效率的技巧

提高使用VS Code工作效率的技巧 时间轴视图:本地源代码控制 时间轴视图为我们提供了内置的源代码控制。 我们中的许多人都知道 Git 和其他源代码控制工具有多么有用,它们可以帮助我们轻松跟踪文件更改并在需要时恢复到之前的状态。 因此,…...

软件系统兼容性测试都要注意哪些问题?

兼容性 软件兼容性测试具有相同的含义,它是任何第三方 Web 应用程序测试服务不可分割的一部分。在众多不同的设置中,最重要的是完全的客户满意度,并且可以通过全面的兼容性测试来达到最佳效果。众所周知,软件质量保证是克服 IT 挑…...

索尼 toio™应用创意开发征文|toio俄罗斯方块游戏

目录 引言 摘要 创意简述 准备工作|手工开始 代码编写|合理集成 使用体验|近乎奇妙 引言 索尼toio™编程机器人是一款引领技术创新的产品,为开发者提供了一个全新的编程和创造平台。toio™的设计旨在将技术、塑性和乐趣融为…...

C#事件event

事件模型的5个组成部分 事件拥有者(event source)(类对象)(有些书将其称为事件发布者) 事件成员(event)(事件拥有者的成员)(事件成员就是事件本身…...

气传导耳机什么牌子好?盘点五款好用的气传导耳机分享

​对于气传导耳机,大家最关心的可能是佩戴会不会不舒服?音质好不好?会不会漏音?等问题。面对这些问题,今天我就为大家推荐几款市面上最好的气传导耳机,总有一款适合你的! ①NANK南卡00压气传导…...

业绩走低,毛利率下滑,海外市场能否成为极米科技救命稻草?

撰稿|行星 来源|贝多财经 8月30日,成都极米科技股份有限公司(SH:688696,下称“极米科技”)发布2023年半年度业绩报告。财报显示,极米科技2023年上半年的业绩出现了大幅下滑,其中收入同比减少两成&#xf…...

轻松敏捷开发流程之Scrum

Scrum是一种敏捷开发流程,它旨在使软件开发更加高效和灵活。Scrum将软件开发过程分为多个短期、可重复的阶段,称为“Sprint”。每个Sprint通常为两周,旨在完成一部分开发任务。 在Scrum中,有一个明确的角色分工: 产品…...

Vue3+Element Plus实现el-table跨行显示(非脚手架)

Vue3Element Plus实现el-table跨行显示 app组件内容使用:span-method"objectSpanMethod"自定义方法实现跨行显示查询方法初始化挂载新建一个html即可进行测试&#xff0c;完整代码如下效果图 app组件内容 <div id"app"><!-- 远程搜索 --><e…...

生成订单30分钟未支付,则自动取消,该怎么实现?

今天给大家上一盘硬菜&#xff0c;并且是支付中非常重要的一个技术解决方案&#xff0c;有这块业务的同学注意自己试一把了哈&#xff01; 在开发中&#xff0c;往往会遇到一些关于延时任务的需求。例如 生成订单30分钟未支付&#xff0c;则自动取消 生成订单60秒后,给用户…...

WebGIS外包开发流程

WebGIS开发流程需要综合考虑前端和后端开发、地理信息数据处理、用户需求和安全性等多个方面。成功的WebGIS应用程序需要不断地进行更新和维护&#xff0c;以适应变化的需求和技术。WebGIS开发是一个复杂的过程&#xff0c;通常包括以下主要步骤。北京木奇移动技术有限公司&…...

pytorch学习——LSTM和GRU

参考书籍&#xff1a;https://zh-v2.d2l.ai/chapter_recurrent-modern/lstm.html 参考论文&#xff1a; https://colah.github.io/posts/2015-08-Understanding-LSTMs/ 简介&#xff1a; LSTM&#xff08;长短期记忆网络&#xff09;和GRU&#xff08;门控循环单元&#xff09;…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...