STM32状态机编程实例——全自动洗衣机(上)

这篇具有很好参考价值的文章主要介绍了STM32状态机编程实例——全自动洗衣机(上)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前面几篇文章,以按键功能,介绍了状态机的原理与按键状态机实例,实现按键单击、双击、长按等状态的检测。

本篇,继续使用状态机编程,来实现一个更有趣的功能——全自动洗衣机。

1 全自动洗衣机功能分析

下面是一个全自动洗衣机的控制面板:

STM32状态机编程实例——全自动洗衣机(上)

面板上有4个按键:

  • 电源:控制洗衣机通电与断电
  • 水位:选择洗衣时需要的水位,有1~8个水位
  • 程序:选择不同的洗衣模式,有1~10个模式
    • 01:标准
    • 02:轻柔
    • 03:快速
    • 10:桶风干
  • 启动/暂停:用于启动或暂停洗衣任务

面板上还有一个数码管,用于显示当前的工作状态与剩余时间,可显示的工作模式有:

  • AA:浸泡
  • BB:洗涤
  • CC:漂洗
  • DD:脱水

本篇,就按照这款洗衣机的操作方式实现对应的洗衣机控制逻辑。需注意的是:

  • 实际的洗衣机有水位检测传感器,本篇中,暂用时间延时模拟水位的增加,且默认开机时水位为0
  • 实际的洗衣机中的洗衣模式,会根据不同的模式自动设置清洗次数每次清洗的时间以及清洗力度,本篇为了简化起见,清洗模式的设置仅用于区分不同的清洗次数
  • 某些特殊的清洗模式,如单独的脱水,桶风干等,本篇暂不实现
  • 对于状态的显示 ,本篇先以串口打印的实现展示,下篇使用OLED小屏幕来显示不同清洗状态的图标等信息

2 画状态图

根据上面分析的全自动洗衣机的功能,以及我们自己使用洗衣机时的经验,可以画出如下的全自动洗衣机的状态图:

STM32状态机编程实例——全自动洗衣机(上)

首先是上电开机,洗衣机可能会开机自检,检测洗衣机的各个部件是否正常,次过程很短。

然后就处于空闲状态,此时用户可以设置水位与清洗模式,若不设置,则为默认的水位与洗衣模式。

接着触发开始按键后,就开始清洗了,一般流程就是:加水、清洗、排水、甩干、结束。

根据不同的清洗模式,加水、清洗和排水这3个过程会循环执行一定的次数。

另外,在不同的工作阶段,按下暂停键可以让洗衣任务暂停,再按继续可让洗衣任务继续。

3 编程实现

3.1 多按键检测功能

本篇介绍的洗衣机的按键,仅需要检测按键单击即可,不需要双击与长按功能,因此,可使用之前文章介绍的最基础的按键状态机来为洗衣机状态机提供按键事件。

之前介绍的按键状态机,只有一个按键,本篇需要用到4个按键(除去电源键,3个也可以),因此,需要对按键状态机稍加修改,实现按键状态机的复用。

之前介绍的按键状态机,使用了几个全局变量来表示状态,更合理的做法是将其封装起来:

typedef struct {
	KEY_STATUS keyStatus;    //当前循环结束的(状态机的)状态
	KEY_STATUS nowKeyStatus; //当前状态(每次循环后与pKeyFsmData->keyStatus保持一致)
	KEY_STATUS lastKeyStatus; //上次状态(用于记录前一状态以区分状态的来源)
	int keyIdx;
}KeyFsmData;

注意,额外增加了一个按键索引值,用来告诉状态机要检测哪个按键。

再将原来的按键状态机程序,通过入参的形式将上述定义的结构体传入,并通过函数返回的形式返回按键是否被按下。

这样修改后的按键状态机,就是一个独立的模块了,可以通过传入不同的参数,实现不同按键的检测。

bool key_press_check(KeyFsmData *pKeyFsmData)
{
	bool bPress = false;
		
	switch(pKeyFsmData->keyStatus)
	{
//省略...

对于本篇需要的4个按键的检测,就可以定义4个数据结构体,分别调用4次状态机函数即可,实现代码的复用。

KeyFsmData pKey0FsmData;
KeyFsmData pKey1FsmData;
KeyFsmData pKey2FsmData;
KeyFsmData pKey3FsmData;

void key_check_init(KeyFsmData *pKeyFsmData, int keyIdx)
{
	pKeyFsmData->keyStatus = KS_RELEASE;
	pKeyFsmData->lastKeyStatus = KS_RELEASE;
	pKeyFsmData->nowKeyStatus = KS_RELEASE;
	pKeyFsmData->keyIdx = keyIdx;
}

void multi_key_check_init()
{
	key_check_init(&pKey0FsmData, 0);
	key_check_init(&pKey1FsmData, 1);
	key_check_init(&pKey2FsmData, 2);
	key_check_init(&pKey3FsmData, 3);
}

int g_keyPressIdx = -1;
void multi_key_check()
{
	bool key0 = key_press_check(&pKey0FsmData);
	bool key1 = key_press_check(&pKey1FsmData);
	bool key2 = key_press_check(&pKey2FsmData);
	bool key3 = key_press_check(&pKey3FsmData);
	
	if (key0 | key1 | key2 | key3)
	{
		//printf("key0:%d, key1:%d, key2:%d, key3:%d, \r\n", key0, key1, key2, key3);
		
		if (key0)
		{
			g_keyPressIdx = 0;
		}
		else if (key1)
		{
			g_keyPressIdx = 1;
		}
		else if (key2)
		{
			g_keyPressIdx = 2;
		}
		else if (key3)
		{
			g_keyPressIdx = 3;
		}
	}
}

int get_press_key_idx()
{
	int idx = g_keyPressIdx;
	g_keyPressIdx = -1;
	
	return idx;
}

其中,multi_key_check函数,放到50ms周期的定时器中断服务函数中,使按键状态机程序运行起来。

get_press_key_idx函数,用于洗衣机程序来获取不同按键的按下事件,每次获取后,将按键事件清除(g_keyPressIdx设为无效值-1)

3.2 洗衣功能

按照上面绘制的洗衣机状态图,使用switch-case法编写对应的程序即可:

#define WASHER_STATUS_ENUM(STATUS)                     \
	STATUS(WS_INIT)         /*开机初始化自检*/         \
	STATUS(WS_IDLE)         /*空闲(等待模式设置)状态*/ \
	STATUS(WS_ADD_WATER)    /*加水状态*/               \
	STATUS(WS_WASH)         /*清洗状态*/               \
	STATUS(WS_DRAIN_WATER)  /*排水状态*/               \
	STATUS(WS_SPIN_DRY)     /*甩干状态*/               \
	STATUS(WS_PAUSE)        /*暂停状态*/               \
	STATUS(WS_NUM)          /*状态总数(无效状态)*/ 

typedef enum
{
	WASHER_STATUS_ENUM(ENUM_ITEM)
}WASHER_STATUS;

const char* washer_status_name[] = {
	WASHER_STATUS_ENUM(ENUM_STRING)
};

WASHER_STATUS g_washerStatus = WS_INIT;    //当前循环结束的(状态机的)状态
WASHER_STATUS g_nowWasherStatus = WS_INIT; //当前状态(每次循环后与pKeyFsmData->keyStatus保持一致)
WASHER_STATUS g_lastWasherStatus = WS_INIT; //上次状态(用于记录前一状态以区分状态的来源)

int g_WorkLoopCnt = 0;
int g_WaterLevel = 5; //1~8
int g_WashMode = 2; //暂定为清洗次数:1~3
int g_curWashCnt = 0;

void washer_run_loop()
{
	switch(g_washerStatus)
	{
		/*开机初始化自检*/ 
		case WS_INIT:
			g_washerStatus = washer_do_init();
			break;
		
		/*空闲(等待模式设置)状态*/
		case WS_IDLE:
			g_washerStatus = washer_do_idle();
			break;
		
		/*加水状态*/
		case WS_ADD_WATER:
			g_washerStatus = washer_do_add_water();
			break;
		
		/*清洗状态*/
		case WS_WASH:
			g_washerStatus = washer_do_wash();
			break;
		
		/*排水状态*/
		case WS_DRAIN_WATER:
			g_washerStatus = washer_do_drain_water();
			break;
		
		/*甩干状态*/
		case WS_SPIN_DRY:
			g_washerStatus = washer_do_spin_dry();
			break;
		
		/*暂停状态*/
		case WS_PAUSE:
			g_washerStatus = washer_do_pause();
			break;
		
		default: break;
	}
	
	if (g_washerStatus != g_nowWasherStatus)
	{
		g_lastWasherStatus = g_nowWasherStatus;
		g_nowWasherStatus = g_washerStatus;
		//printf("new washer status:%d(%s)\r\n", g_washerStatus, washer_status_name[g_washerStatus]);
	}
}

这里将洗衣机不同状态时的处理逻辑,都分别使用函数来实现,并将其返回值作为下一个要跳转的状态。

各个状态的处理函数如下:

/*开机初始化自检*/ 
WASHER_STATUS washer_do_init()
{
	WASHER_STATUS nextStatus = WS_INIT;
	
	g_WorkLoopCnt++;
	if (10 == g_WorkLoopCnt) //自检结束
	{
		printf("default water level:%d\r\n", g_WaterLevel);
		printf("default wash mode:%d\r\n", g_WashMode);
		printf("washer idle...\r\n");
		
		g_WorkLoopCnt = 0;
		nextStatus = WS_IDLE;
	}
	
	return nextStatus;
}

/*空闲(等待模式设置)状态*/
WASHER_STATUS washer_do_idle()
{
	WASHER_STATUS nextStatus = WS_IDLE;
	
	const WASHER_KEY key = check_key_press();
	switch(key)
	{
		case W_KEY_START_PAUSE:
			g_WorkLoopCnt = 0;
			nextStatus = WS_ADD_WATER;
			printf("add water...\r\n");
		break;
		
		case W_KEY_WATER_LEVEL:
			g_WaterLevel = (g_WaterLevel == 8) ? 1 : (g_WaterLevel+1);
			printf("set water level:%d\r\n", g_WaterLevel);
		break;
			
		case W_KEY_WASH_MODE:
			g_WashMode = (g_WashMode == 3) ? 1 : (g_WashMode+1);
			printf("set wash mode:%d\r\n", g_WashMode);
		break;
		
		default: break;
	}
	
	return nextStatus;
}

/*加水状态*/
WASHER_STATUS washer_do_add_water()
{
	WASHER_STATUS nextStatus = WS_ADD_WATER;
	
	const WASHER_KEY key = check_key_press();
	if (key == W_KEY_START_PAUSE)
	{
		nextStatus = WS_PAUSE;
		printf("[%s] pause...\r\n", __func__);
	}
	else
	{
		g_WorkLoopCnt++;
		if (g_WaterLevel == (g_WorkLoopCnt / 10)) //加水结束
		{
			g_WorkLoopCnt = 0;
			nextStatus = WS_WASH;
			printf("[%s] done\r\n", __func__);
			printf("wash...\r\n");
		}
	}
	
	return nextStatus;
}

/*清洗状态*/
WASHER_STATUS washer_do_wash()
{
	WASHER_STATUS nextStatus = WS_WASH;
	
	const WASHER_KEY key = check_key_press();
	if (key == W_KEY_START_PAUSE)
	{
		nextStatus = WS_PAUSE;
		printf("[%s] pause...\r\n", __func__);
	}
	else
	{
		g_WorkLoopCnt++;
		if (6 == (g_WorkLoopCnt / 10)) //清洗结束
		{
			g_WorkLoopCnt = 0;
			nextStatus = WS_DRAIN_WATER;
			printf("[%s] done\r\n", __func__);
			printf("drain water...\r\n");
		}
	}
	
	return nextStatus;
}

/*排水状态*/
WASHER_STATUS washer_do_drain_water()
{
	WASHER_STATUS nextStatus = WS_DRAIN_WATER;
	
	const WASHER_KEY key = check_key_press();
	if (key == W_KEY_START_PAUSE)
	{
		nextStatus = WS_PAUSE;
		printf("[%s] pause...\r\n", __func__);
	}
	else
	{
		g_WorkLoopCnt++;
		if (3 == (g_WorkLoopCnt / 10)) //排水结束
		{
			printf("[%s] done\r\n", __func__);
			g_curWashCnt++;
			printf("current wash and drain count:%d(target:%d)\r\n", g_curWashCnt, g_WashMode);
			g_WorkLoopCnt = 0;
			if (g_WashMode == g_curWashCnt)
			{
				printf("spin dry...\r\n");
				nextStatus = WS_SPIN_DRY;
			}
			else
			{
				printf("add water...\r\n");
				nextStatus = WS_ADD_WATER;
			}
		}
	}
	
	return nextStatus;
}

/*甩干状态*/
WASHER_STATUS washer_do_spin_dry()
{
	WASHER_STATUS nextStatus = WS_SPIN_DRY;
	
	const WASHER_KEY key = check_key_press();
	if (key == W_KEY_START_PAUSE)
	{
		nextStatus = WS_PAUSE;
		printf("[%s] pause...\r\n", __func__);
	}
	else
	{
		g_WorkLoopCnt++;
		if (100 == g_WorkLoopCnt) //甩干结束
		{
			g_WorkLoopCnt = 0;
			nextStatus = WS_IDLE;
			printf("[%s] done\r\n", __func__);
			printf("enter idle mode\r\n");
		}
	}
	
	return nextStatus;
}

/*暂停状态*/
WASHER_STATUS washer_do_pause()
{
	WASHER_STATUS nextStatus = WS_PAUSE;
	
	const WASHER_KEY key = check_key_press();
	if (key != W_KEY_NULL)
	{
		switch (g_lastWasherStatus)
		{
			case WS_ADD_WATER:   nextStatus = WS_ADD_WATER;   printf("resume...\r\n"); break;
			case WS_WASH:        nextStatus = WS_WASH;        printf("resume...\r\n"); break;
			case WS_DRAIN_WATER: nextStatus = WS_DRAIN_WATER; printf("resume...\r\n"); break;
			case WS_SPIN_DRY:    nextStatus = WS_SPIN_DRY;    printf("resume...\r\n"); break;
			default: break;
		}
	}
	
	return nextStatus;
}

对于按键的获取,这里定义了几个对应的功能按键,并从按键状态机函数中获取按键索引,再转为洗衣机程序所需的对应功能按键:

typedef enum
{
	W_KEY_NULL,        //没有按键按下
	W_KEY_POWER,       //电源键按下
	W_KEY_WATER_LEVEL, //水位键按下
	W_KEY_WASH_MODE,   //清洗模式键按下
	W_KEY_START_PAUSE  //启动/暂停键按下
}WASHER_KEY;

WASHER_KEY check_key_press()
{
	WASHER_KEY washerKey = W_KEY_NULL;
	int idx = get_press_key_idx();
	if (idx != -1)
	{
		switch(idx)
		{
			case 0: washerKey = W_KEY_POWER; break; //电源键按下
			case 1: washerKey = W_KEY_WATER_LEVEL; break; //水位键按下
			case 2: washerKey = W_KEY_WASH_MODE; break; //清洗模式键按下
			case 3: washerKey = W_KEY_START_PAUSE; break; //启动/暂停键按下
			default: break;
		}
		//printf("%s idx:%d -> washerKey:%d\r\n", __func__, idx, washerKey);
	}
	
	return washerKey;
}

洗衣机状态机主程序,可以放到main函数中,每隔100ms调用一次,使其运行起来:

int main(void)
{	
	delay_init();
	KEY_Init();
	uart_init(115200);
	TIM3_Int_Init(500-1,7200-1); //调用定时器使得50ms产生一个中断

	printf("hello\r\n");
	
	while(1)
	{
		washer_run_loop();
		delay_ms(100);
	}
}

3.3 测试

将代码烧写到STM32板子中,通过3个按键(电源键暂不使用)操作,并通过串口打印,查看全自动洗衣机的运行情况:

STM32状态机编程实例——全自动洗衣机(上)

4 总结

本篇实现了一款全自动洗衣机的基础洗衣控制流程,可实现不同水位与清洗次数的设置,以及任务的暂停与继续。此外,通过对之前按键状态机的进一步优化修改,实现了按键状态机的复用,实现多个按键的检测。下篇文章将进一步进行功能优化,添加OLED小屏幕实现不同状态的可视化展示。文章来源地址https://www.toymoban.com/news/detail-492886.html

到了这里,关于STM32状态机编程实例——全自动洗衣机(上)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 全自动模拟量采集软件框架详解

    Monitor.Analog采用一种MVVM架构模式,用于将用户界面(View)与业务逻辑(Model)进行分离,并通过ViewModel来进行连接和交互。以下是MVVM框架的介绍: 模拟量采集/老化房采集软件 1. Model(模型):模型代表应用程序的业务逻辑和数据。它包含数据模型、数据访问、业务规则等。

    2024年02月12日
    浏览(49)
  • Spring动态添加数据源(全自动)

    一、使用场景 1、现在网上所有写的动态数据源,都是假动态数据,都是预先知道要连接几个数据库,但这不满足具体真正需要动态添加连接数据库的需求; 2、在很多业务场景下,如项目默认连一个主数据库,服务启动后,因业务不停的变更,需要动态连接其它数据库处理数

    2024年02月01日
    浏览(53)
  • 全自动网页生成系统网站源码重构版

    所有模板经过精心审核与修改,完美兼容小屏手机大屏手机,以及各种平板端、电脑端和360浏览器、谷歌浏览器、火狐浏览器等等各大浏览器显示。 为用户使用方便考虑,全自动网页制作系统无需繁琐的注册与登入,直接选择喜欢的模板输入网页信息即可制作,并且所以模板

    2024年01月22日
    浏览(45)
  • 12吋全自动划片机有哪些功能?

    1、大面积工作盘:可容纳多个工件,并自动对位。 2、轴光/环光:采用合适的光源照射,显示影像更能呈现工作物表面特征。 3、双倍率显微镜头:视野更大,精准快速进行对准校正工作。 4、非接触测高:消除刀具因测高而损伤的可能性,实时补偿下刀高度误差,提升切削

    2024年02月07日
    浏览(53)
  • 一个开源的全自动视频生成软件MoneyPrinterTurbo

    只需提供一个视频 主题 或 ,就可以全自动生成视频文案、视频素材、视频字幕、视频背景音乐,然后合成一个高清的短视频。 一:功能特性 完整的 MVC架构,代码 结构清晰,易于维护,支持 API 和 Web界面 支持视频文案 AI自动生成,也可以自定义文案 支持多种 高清

    2024年04月17日
    浏览(51)
  • 新版百度答题手机脚本,半自动脚本(全自动辅助抢题,手动答题)

    标题:《探秘半自动百度答题手机脚本:提升答题效率的辅助利器》 篇文: 引言: 在现代社会,知识获取和学习已成为人们追求进步和提高自我的重要途径。而移动互联网时代的到来,使得移动答题应用如百度答题成为了广大用户在碎片化时间中获取知识的首选方式。本文

    2024年01月19日
    浏览(92)
  • CASAIM与大疆达成全自动化测量技术合作,CASAIM IS全自动化蓝光测量仪实现无人机叶片全尺寸检测及质量控制

    近期,CASAIM与大疆达成全自动化测量技术合作,CASAIM IS全自动化蓝光测量仪实现无人机叶片全尺寸检测及质量控制。 无人机行业在过去几年里取得了迅猛发展, 大疆是全球领先的无人飞行器控制系统及无人机解决方案的研发商和生产商, 客户遍布全球100多个国家。随着技术

    2024年02月16日
    浏览(47)
  • Jmeter +Maven+jenkins 接口性能全自动化测试

    背景: 首先用jmeter录制或者书写性能测试的脚本,用maven添加相关依赖,把性能测试的代码提交到github,在jenkins配置git下载性能测试的代码,配置运行脚本和测试报告,配置运行失败自动发邮件通知,这样一来性能测试的job配置完成。接着,把性能测试的job配置成开发job的下

    2024年02月06日
    浏览(87)
  • 自定义WEB框架结合Jenkins实现全自动测试

    1.allure–纯命令运行 -固定的–稍微记住对应的单词即可。 2 安装,2个步骤: 1.下载allure包,然后配置环境变量。 https://github.com/allure-framework/allure2/releases/tag/2.22.4 2.在你的终端去安装一个插件 pip install allure-pytest 具体的命令 第一步:生成对应的allure的数据 pytest.main([“-vs”,“

    2024年02月12日
    浏览(44)
  • Python爬虫使用selenium爬取qq群的成员信息(全自动实现自动登陆)(1)

    browser.find_element_by_xpath(‘.//ul[@id=“headerNav”]/li[4]’).click() 8.点击群管理之后,进入群管理界面,我们需要的是成员管理 WebDriverWait(browser, 1000).until( EC.presence_of_all_elements_located( (By.CLASS_NAME, ‘color-tit’) ) ) browser.find_element_by_class_name(‘color-tit’).click() 9.点击成员管理之后会重新

    2024年04月28日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包