你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
STMCU小助手 发布时间:2022-8-14 15:04
1 全自动洗衣机功能分析

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


2.jpg


面板上有4个按键:

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

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

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

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

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


2 画状态图

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


1.png


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

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

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

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

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


3 编程实现3.1 多按键检测功能

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

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

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

  1. typedefstruct {
  2.         KEY_STATUS keyStatus;    <i>//当前循环结束的(状态机的)状态</i>
  3.         KEY_STATUS nowKeyStatus; <i>//当前状态(每次循环后与pKeyFsmData->keyStatus保持一致)</i>
  4.         KEY_STATUS lastKeyStatus; <i>//上次状态(用于记录前一状态以区分状态的来源)</i>
  5.         int keyIdx;
  6. }KeyFsmData;
复制代码

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

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

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

  1. bool key_press_check(KeyFsmData *pKeyFsmData)
  2. {
  3.         bool bPress = false;
  4.                
  5.         switch(pKeyFsmData->keyStatus)
  6.         {
  7. <i>//省略...</i>
复制代码

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

  1. KeyFsmData pKey0FsmData;
  2. KeyFsmData pKey1FsmData;
  3. KeyFsmData pKey2FsmData;
  4. KeyFsmData pKey3FsmData;

  5. void key_check_init(KeyFsmData *pKeyFsmData, int keyIdx)
  6. {
  7.         pKeyFsmData->keyStatus = KS_RELEASE;
  8.         pKeyFsmData->lastKeyStatus = KS_RELEASE;
  9.         pKeyFsmData->nowKeyStatus = KS_RELEASE;
  10.         pKeyFsmData->keyIdx = keyIdx;
  11. }

  12. void multi_key_check_init()
  13. {
  14.         key_check_init(&pKey0FsmData, 0);
  15.         key_check_init(&pKey1FsmData, 1);
  16.         key_check_init(&pKey2FsmData, 2);
  17.         key_check_init(&pKey3FsmData, 3);
  18. }

  19. int g_keyPressIdx = -1;
  20. void multi_key_check()
  21. {
  22.         bool key0 = key_press_check(&pKey0FsmData);
  23.         bool key1 = key_press_check(&pKey1FsmData);
  24.         bool key2 = key_press_check(&pKey2FsmData);
  25.         bool key3 = key_press_check(&pKey3FsmData);
  26.         
  27.         if (key0 | key1 | key2 | key3)
  28.         {
  29.                 <i>//printf("key0:%d, key1:%d, key2:%d, key3:%d, \r\n", key0, key1, key2, key3);</i>
  30.                
  31.                 if (key0)
  32.                 {
  33.                         g_keyPressIdx = 0;
  34.                 }
  35.                 elseif (key1)
  36.                 {
  37.                         g_keyPressIdx = 1;
  38.                 }
  39.                 elseif (key2)
  40.                 {
  41.                         g_keyPressIdx = 2;
  42.                 }
  43.                 elseif (key3)
  44.                 {
  45.                         g_keyPressIdx = 3;
  46.                 }
  47.         }
  48. }

  49. int get_press_key_idx()
  50. {
  51.         int idx = g_keyPressIdx;
  52.         g_keyPressIdx = -1;
  53.         
  54.         return idx;
  55. }
复制代码

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

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


3.2 洗衣功能

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

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

  10. typedefenum
  11. {
  12.         WASHER_STATUS_ENUM(ENUM_ITEM)
  13. }WASHER_STATUS;

  14. constchar* washer_status_name[] = {
  15.         WASHER_STATUS_ENUM(ENUM_STRING)
  16. };

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

  20. int g_WorkLoopCnt = 0;
  21. int g_WaterLevel = 5; <i>//1~8</i>
  22. int g_WashMode = 2; <i>//暂定为清洗次数:1~3</i>
  23. int g_curWashCnt = 0;

  24. void washer_run_loop()
  25. {
  26.         switch(g_washerStatus)
  27.         {
  28.                 <i>/*开机初始化自检*/</i>
  29.                 case WS_INIT:
  30.                         g_washerStatus = washer_do_init();
  31.                         break;
  32.                
  33.                 <i>/*空闲(等待模式设置)状态*/</i>
  34.                 case WS_IDLE:
  35.                         g_washerStatus = washer_do_idle();
  36.                         break;
  37.                
  38.                 <i>/*加水状态*/</i>
  39.                 case WS_ADD_WATER:
  40.                         g_washerStatus = washer_do_add_water();
  41.                         break;
  42.                
  43.                 <i>/*清洗状态*/</i>
  44.                 case WS_WASH:
  45.                         g_washerStatus = washer_do_wash();
  46.                         break;
  47.                
  48.                 <i>/*排水状态*/</i>
  49.                 case WS_DRAIN_WATER:
  50.                         g_washerStatus = washer_do_drain_water();
  51.                         break;
  52.                
  53.                 <i>/*甩干状态*/</i>
  54.                 case WS_SPIN_DRY:
  55.                         g_washerStatus = washer_do_spin_dry();
  56.                         break;
  57.                
  58.                 <i>/*暂停状态*/</i>
  59.                 case WS_PAUSE:
  60.                         g_washerStatus = washer_do_pause();
  61.                         break;
  62.                
  63.                 default: break;
  64.         }
  65.         
  66.         if (g_washerStatus != g_nowWasherStatus)
  67.         {
  68.                 g_lastWasherStatus = g_nowWasherStatus;
  69.                 g_nowWasherStatus = g_washerStatus;
  70.                 <i>//printf("new washer status:%d(%s)\r\n", g_washerStatus, washer_status_name[g_washerStatus]);</i>
  71.         }
  72. }
复制代码

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

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

  1. <i>/*开机初始化自检*/</i>
  2. WASHER_STATUS washer_do_init()
  3. {
  4.         WASHER_STATUS nextStatus = WS_INIT;
  5.         
  6.         g_WorkLoopCnt++;
  7.         if (10 == g_WorkLoopCnt) <i>//自检结束</i>
  8.         {
  9.                 printf("default water level:%d\r\n", g_WaterLevel);
  10.                 printf("default wash mode:%d\r\n", g_WashMode);
  11.                 printf("washer idle...\r\n");
  12.                
  13.                 g_WorkLoopCnt = 0;
  14.                 nextStatus = WS_IDLE;
  15.         }
  16.         
  17.         return nextStatus;
  18. }

  19. <i>/*空闲(等待模式设置)状态*/</i>
  20. WASHER_STATUS washer_do_idle()
  21. {
  22.         WASHER_STATUS nextStatus = WS_IDLE;
  23.         
  24.         const WASHER_KEY key = check_key_press();
  25.         switch(key)
  26.         {
  27.                 case W_KEY_START_PAUSE:
  28.                         g_WorkLoopCnt = 0;
  29.                         nextStatus = WS_ADD_WATER;
  30.                         printf("add water...\r\n");
  31.                 break;
  32.                
  33.                 case W_KEY_WATER_LEVEL:
  34.                         g_WaterLevel = (g_WaterLevel == 8) ? 1 : (g_WaterLevel+1);
  35.                         printf("set water level:%d\r\n", g_WaterLevel);
  36.                 break;
  37.                         
  38.                 case W_KEY_WASH_MODE:
  39.                         g_WashMode = (g_WashMode == 3) ? 1 : (g_WashMode+1);
  40.                         printf("set wash mode:%d\r\n", g_WashMode);
  41.                 break;
  42.                
  43.                 default: break;
  44.         }
  45.         
  46.         return nextStatus;
  47. }

  48. <i>/*加水状态*/</i>
  49. WASHER_STATUS washer_do_add_water()
  50. {
  51.         WASHER_STATUS nextStatus = WS_ADD_WATER;
  52.         
  53.         const WASHER_KEY key = check_key_press();
  54.         if (key == W_KEY_START_PAUSE)
  55.         {
  56.                 nextStatus = WS_PAUSE;
  57.                 printf("[%s] pause...\r\n", __func__);
  58.         }
  59.         else
  60.         {
  61.                 g_WorkLoopCnt++;
  62.                 if (g_WaterLevel == (g_WorkLoopCnt / 10)) <i>//加水结束</i>
  63.                 {
  64.                         g_WorkLoopCnt = 0;
  65.                         nextStatus = WS_WASH;
  66.                         printf("[%s] done\r\n", __func__);
  67.                         printf("wash...\r\n");
  68.                 }
  69.         }
  70.         
  71.         return nextStatus;
  72. }

  73. <i>/*清洗状态*/</i>
  74. WASHER_STATUS washer_do_wash()
  75. {
  76.         WASHER_STATUS nextStatus = WS_WASH;
  77.         
  78.         const WASHER_KEY key = check_key_press();
  79.         if (key == W_KEY_START_PAUSE)
  80.         {
  81.                 nextStatus = WS_PAUSE;
  82.                 printf("[%s] pause...\r\n", __func__);
  83.         }
  84.         else
  85.         {
  86.                 g_WorkLoopCnt++;
  87.                 if (6 == (g_WorkLoopCnt / 10)) <i>//清洗结束</i>
  88.                 {
  89.                         g_WorkLoopCnt = 0;
  90.                         nextStatus = WS_DRAIN_WATER;
  91.                         printf("[%s] done\r\n", __func__);
  92.                         printf("drain water...\r\n");
  93.                 }
  94.         }
  95.         
  96.         return nextStatus;
  97. }

  98. <i>/*排水状态*/</i>
  99. WASHER_STATUS washer_do_drain_water()
  100. {
  101.         WASHER_STATUS nextStatus = WS_DRAIN_WATER;
  102.         
  103.         const WASHER_KEY key = check_key_press();
  104.         if (key == W_KEY_START_PAUSE)
  105.         {
  106.                 nextStatus = WS_PAUSE;
  107.                 printf("[%s] pause...\r\n", __func__);
  108.         }
  109.         else
  110.         {
  111.                 g_WorkLoopCnt++;
  112.                 if (3 == (g_WorkLoopCnt / 10)) <i>//排水结束</i>
  113.                 {
  114.                         printf("[%s] done\r\n", __func__);
  115.                         g_curWashCnt++;
  116.                         printf("current wash and drain count:%d(target:%d)\r\n", g_curWashCnt, g_WashMode);
  117.                         g_WorkLoopCnt = 0;
  118.                         if (g_WashMode == g_curWashCnt)
  119.                         {
  120.                                 printf("spin dry...\r\n");
  121.                                 nextStatus = WS_SPIN_DRY;
  122.                         }
  123.                         else
  124.                         {
  125.                                 printf("add water...\r\n");
  126.                                 nextStatus = WS_ADD_WATER;
  127.                         }
  128.                 }
  129.         }
  130.         
  131.         return nextStatus;
  132. }

  133. <i>/*甩干状态*/</i>
  134. WASHER_STATUS washer_do_spin_dry()
  135. {
  136.         WASHER_STATUS nextStatus = WS_SPIN_DRY;
  137.         
  138.         const WASHER_KEY key = check_key_press();
  139.         if (key == W_KEY_START_PAUSE)
  140.         {
  141.                 nextStatus = WS_PAUSE;
  142.                 printf("[%s] pause...\r\n", __func__);
  143.         }
  144.         else
  145.         {
  146.                 g_WorkLoopCnt++;
  147.                 if (100 == g_WorkLoopCnt) <i>//甩干结束</i>
  148.                 {
  149.                         g_WorkLoopCnt = 0;
  150.                         nextStatus = WS_IDLE;
  151.                         printf("[%s] done\r\n", __func__);
  152.                         printf("enter idle mode\r\n");
  153.                 }
  154.         }
  155.         
  156.         return nextStatus;
  157. }

  158. <i>/*暂停状态*/</i>
  159. WASHER_STATUS washer_do_pause()
  160. {
  161.         WASHER_STATUS nextStatus = WS_PAUSE;
  162.         
  163.         const WASHER_KEY key = check_key_press();
  164.         if (key != W_KEY_NULL)
  165.         {
  166.                 switch (g_lastWasherStatus)
  167.                 {
  168.                         case WS_ADD_WATER:   nextStatus = WS_ADD_WATER;   printf("resume...\r\n"); break;
  169.                         case WS_WASH:        nextStatus = WS_WASH;        printf("resume...\r\n"); break;
  170.                         case WS_DRAIN_WATER: nextStatus = WS_DRAIN_WATER; printf("resume...\r\n"); break;
  171.                         case WS_SPIN_DRY:    nextStatus = WS_SPIN_DRY;    printf("resume...\r\n"); break;
  172.                         default: break;
  173.                 }
  174.         }
  175.         
  176.         return nextStatus;
  177. }
复制代码

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

  1. typedefenum
  2. {
  3.         W_KEY_NULL,        <i>//没有按键按下</i>
  4.         W_KEY_POWER,       <i>//电源键按下</i>
  5.         W_KEY_WATER_LEVEL, <i>//水位键按下</i>
  6.         W_KEY_WASH_MODE,   <i>//清洗模式键按下</i>
  7.         W_KEY_START_PAUSE  <i>//启动/暂停键按下</i>
  8. }WASHER_KEY;

  9. WASHER_KEY check_key_press()
  10. {
  11.         WASHER_KEY washerKey = W_KEY_NULL;
  12.         int idx = get_press_key_idx();
  13.         if (idx != -1)
  14.         {
  15.                 switch(idx)
  16.                 {
  17.                         case0: washerKey = W_KEY_POWER; break; <i>//电源键按下</i>
  18.                         case1: washerKey = W_KEY_WATER_LEVEL; break; <i>//水位键按下</i>
  19.                         case2: washerKey = W_KEY_WASH_MODE; break; <i>//清洗模式键按下</i>
  20.                         case3: washerKey = W_KEY_START_PAUSE; break; <i>//启动/暂停键按下</i>
  21.                         default: break;
  22.                 }
  23.                 <i>//printf("%s idx:%d -> washerKey:%d\r\n", __func__, idx, washerKey);</i>
  24.         }
  25.         
  26.         return washerKey;
  27. }
复制代码

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

  1. int main(void)
  2. {        
  3.         delay_init();
  4.         KEY_Init();
  5.         uart_init(115200);
  6.         TIM3_Int_Init(500-1,7200-1); <i>//调用定时器使得50ms产生一个中断</i>

  7.         printf("hello\r\n");
  8.         
  9.         while(1)
  10.         {
  11.                 washer_run_loop();
  12.                 delay_ms(100);
  13.         }
  14. }
复制代码

3.3 测试

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


640.png

4 总结

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


转载自 码农爱学习

收藏 评论0 发布时间:2022-8-14 15:04

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版