微前端架构如何改变企业的开发模式与效率提升
844
2022-10-12
STM32状态机编程实例——全自动洗衣机(下)
上篇文章,通过状态机编程,实现了全自动洗衣机的逻辑控制,并通过串口打印的方式显示各个状态。
本篇,为了更加直观的感受状态机的运行,使用0.96寸OLED来显示各个状态,并搭配对应的动态图标来体现洗衣机工作的各个状态。
先来看下演示效果:OLED图片显示
为了能方便的在OLED上显示文字和图片,可以借助一些图形库来帮我们显示,这里使用的是U8g2图形库。
1.1 U8g2库移植
U8g2库在STM32上的移植,之前的文章已经介绍过,具体的移植过程可以参考这篇:
移植成功后,可以使用测试例程验证U8g2库的显示效果。
1.2 图片显示
图片相比较文字,可以展示更加丰富的内容,因此本篇通过简单的单色图片来展示洗衣机的工作状态。
U8g2库显示图片,可以使用u8g2_DrawXBM函数,需要先将图片转为数组。
OLED屏幕要想显示洗衣机的工作状态,就需要获取状态机的具体工作状态。这里自定义了一些展示需要用到的数据,组成一个结构体,状态机在运行过程中,对各个成员变量进行修改,然后OLED端获取这些数据,再进行展示。
typedef struct{ WASHER_STATUS washerStatus; /*洗衣机的工作状态*/ int targetWaterLevel; /*洗衣机的目标水位*/ int targetWashTimes; /*洗衣机的目标清洗次数*/ int remainingTime; /*洗衣机的剩余工作时间(暂未使用)*/ int curWaterLevel; /*洗衣机当前的水位*/ bool hasNewData; /*是否有新的数据(用于告诉OLED是否刷新显示)*/}WASHER_OUTPUT_DATA;
对于OLED的展示逻辑,这里是在状态机的每个循环结束后,调用下面的程序逻辑进行展示:
void show_washer_status(WASHER_OUTPUT_DATA washerOutPutData){ if (washerOutPutData.hasNewData) { WASHER_STATUS s = washerOutPutData.washerStatus; printf("u8g2 get status:%d(%s)\r\n", s, washer_status_name[s]); switch(s) { case WS_INIT: showWasherInit(&u8g2, washerOutPutData); break; case WS_IDLE: showWasherIdle(&u8g2, washerOutPutData); break; case WS_ADD_WATER: showWasherAddWater(&u8g2, washerOutPutData); break; case WS_WASH: showWasherWash(&u8g2, washerOutPutData); break; case WS_DRAIN_WATER: showWasherDrainWater(&u8g2, washerOutPutData); break; case WS_SPIN_DRY: showWasherSpinDry(&u8g2, washerOutPutData); break; case WS_PAUSE: showWasherPause(&u8g2, washerOutPutData); break; case WS_DONE: showWasherDone(&u8g2, washerOutPutData); break; default: break; } }}
当此轮状态循环有新的数据产生时,则根据状态机的主状态,分别显示对应状态下的图片或动画。
比如加水状态,会根据当前加的水位,不断更新图片展示的水位:
void drawCurWaterLevel(u8g2_t *u8g2, int level){ switch(level) { case 0: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_0); break; case 1: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_1); break; case 2: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_2); break; case 3: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_3); break; case 4: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_4); break; case 5: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_5); break; case 6: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_6); break; case 7: u8g2_DrawXBM(u8g2,64, 16, 48, 48, pic_water_7); break; default: break; }}void showWasherAddWater(u8g2_t *u8g2, WASHER_OUTPUT_DATA data){ char strStatus[14] = "AddWater"; u8g2_ClearBuffer(u8g2); u8g2_SetFont(u8g2,u8g2_font_ncenB10_tr); u8g2_DrawStr(u8g2,0,15,strStatus); drawCurWaterLevel(u8g2, data.curWaterLevel); u8g2_SendBuffer(u8g2);}
对于主程序的结构,和上篇一样,只是增加了OLED的显示:
int main(void){ delay_init(); //延时函数初始化 LED_Init(); //初始化与LED连接的硬件接口 oled_init(); KEY_Init(); uart_init(115200); TIM3_Int_Init(500-1,7200-1); //调用定时器使得50ms产生一个中断 printf("hello\r\n"); while(1) { washer_run_loop(); WASHER_OUTPUT_DATA data = get_washer_output_data(); show_washer_status(data); delay_ms(100); }}
在状态机每运行一个循环,获取一下具体的状态数据,然后使用OLED将具体的状态数据展示出来。
3 具体演示
再来对比看下这个状态图,实验测试状态机的执行。
正常的洗衣流程
不考虑暂停这个状态,洗衣机上点开始后,依次经历空闲、加水、清洗、排水、甩干这几个流程即结束,若清洗计数设置了不知1次,则加水、清洗、排水这3个动作会循环执行对应的次数。
洗衣流程中暂停再继续
在洗衣机的运行状态:加水、清洗、排水、甩干,通过暂停按钮,可以暂停这些状态的执行,此时状态机会运行于暂停模式,再按继续(暂停/继续的一个按钮),则会继续执行洗衣工作。
暂停后修改水量或次数后再继续
在洗衣过程中,如果想要修改洗衣的水量或次数,可以先通过暂停键来暂停洗衣机的运行,然后通过水位或次数按钮,使状态机从暂停状态先切换到空闲状态,进行水位或次数的调整后,再继续,即会按照新的设置参数继续运行洗衣程序。
比如本来的清洗水位是3,清洗次数是1,在第一次清洗的加水时按下暂停,再将清洗参数进行修改,比如水位设为5,次数设为2,再继续后,会再次进入加水状态,并将水位补到5后,继续清洗,并清洗2遍结束。
注:本状态机还有继续优化的空间,比如:水量只会补加,多了此轮清洗不会排出。比如先设置的水位是5,在加到3个时候,暂停并修改为2,再继续后,判断大于目标水位则会直接开始清洗,不会先由水位3再排水到水位2再清洗任何清洗状态(加水、清洗、排水)按下暂停调整水位后,再继续,都会默认跳到加水重新新的清洗循环,如果是在排水状态,调整了水位后,此次的水还没有排位,就又重新加水开始洗,不太合理
以上3种测试方式的演示效果,可以再对比看下演示视频:总结
本篇在上篇全自动洗衣机的状态机编程实例的基础上,增加了OLED来更新直观的展示洗衣机的工作状态,并通过3种测试场景来展示洗衣机工作状态机的执行。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~