博客
关于我
2017年嵌入式第八届省赛真题解析
阅读量:798 次
发布时间:2023-04-16

本文共 7659 字,大约阅读时间需要 25 分钟。

一、题目分析

本文将围绕状态机的设计与应用,重点分析电梯控制系统的实现逻辑。通过对现有外设的使用与状态机的结合,展现如何将复杂的逻辑控制过程分解为多个状态机进行管理。

状态机的基本概念

状态机是系统设计中常用的模型,它通过将系统划分为多个状态,实现对复杂逻辑的分步管理。状态机的核心要素包括:

  • 状态:系统在某一时刻所处的稳定工作情况。
  • 迁移:系统从一个状态转移到另一个状态的过程,迁移通常需要外界施加条件或事件触发。
  • 事件:引发状态迁移的外部或内部信号。
  • 动作:状态迁移时执行的操作行为。
  • 条件:决定是否发生状态迁移的判定依据。

状态机的设计过程需要明确每个状态的初始条件、迁移条件以及对应的动作。

状态机的应用

以电梯控制系统为例,状态机可以很好地实现电梯运行的逻辑控制。通过将电梯运行分为多个状态,系统能够根据当前状态、输入事件和条件,判断下一步动作,从而完成特定的功能。

二、程序实现

按键扫描

按键扫描是系统的基础功能,用于获取用户输入。以下是按键扫描的实现逻辑:

void Key_Proc(void) {    if ((uwTick - uwTick_Key_Set_Point) < 50) {        return; //减速函数    }    uwTick_Key_Set_Point = uwTick;    ucKey_Val = Key_Scan();    unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val);    ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);    ucKey_Old = ucKey_Val;    if (state == 0) { //初始状态,按键有效        if (unKey_Down == 1) { //第1键按下            if (platform != 1) {                layer_set &= ~(1 << 0); //清第1位                layer_set |= (1 << 0); //置第1位            }        }        if (unKey_Down == 2) { //第2键按下            if (platform != 2) {                layer_set &= ~(1 << 1); //清第2位                layer_set |= (1 << 1); //置第2位            }        }        if (unKey_Down == 3) { //第3键按下            if (platform != 3) {                layer_set &= ~(1 << 2); //清第3位                layer_set |= (1 << 2); //置第3位            }        }        if (unKey_Down == 4) { //第4键按下            if (platform != 4) {                layer_set &= ~(1 << 3); //清第4位                layer_set |= (1 << 3); //置第4位            }        }        ucLed = 0xF0; //清低4位        ucLed |= layer_set; //合并层设置        if (unKey_Down != 0) { //按键按下,开始计时            uwTick_Set_Point = uwTick;        }    }}

状态机设计

状态机是电梯控制系统的核心,通过分步处理不同状态,实现电梯运行的逻辑控制。以下是状态机的实现逻辑:

void run(void) {    if (layer_set) { //检测是否有层设置        switch (state) {            case 0: { //初始状态                if ((uwTick - uwTick_Set_Point) >= 1000) { //1秒后进入状态1                    state = 1;                }                break;            case 1: { //运行中,关闭门                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); //关门                uwTick_Set_Point = uwTick;                //启动电机                HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);                __HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 300); //设置占空比                state = 2;                break;            case 2: { //门关闭                if ((uwTick - uwTick_Set_Point) >= 4000) { //4秒后进入状态3                    HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1); //停止电机                    state = 3;                }                break;            case 3: { //选择运行方向                if (layer_set > (1 << (platform - 1))) { //上行                    dir = 1;                    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); //上行信号                    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);                    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 800); //设置占空比                } else if (layer_set < (1 << (platform - 1))) { //下行                    dir = 2;                    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); //下行信号                    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);                    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 600); //设置占空比                }                uwTick_Set_Point = uwTick;                state = 4;                break;            case 4: { //运行到目标层                if ((uwTick - uwTick_Set_Point) >= 6000) { //6秒后进入状态5                    HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1); //停止电机                    if (dir == 1) {                        platform++;                    } else if (dir == 2) {                        platform--;                    }                    ucLed = 0x0F; //清高4位                    LED_Disp(ucLed);                    sprintf((char *)Lcd_Disp_String, "         %d   ", platform);                    LCD_DisplayStringLine(Line2, Lcd_Disp_String);                    sprintf((char *)Lcd_Disp_String, "Elev Runned 1 Floor        ");                    LCD_DisplayStringLine(Line8, Lcd_Disp_String);                    state = 5;                } else { //流水灯显示                    if (dir == 1) { //上行                        if (led_set == 0x08) {                            led_set = 0x80;                        }                        LED_Disp(ucLed);                        HAL_Delay(300);                        led_set = led_set >> 1;                    } else if (dir == 2) { //下行                        if (led_set2 == 0x00) {                            led_set2 = 0x10;                        }                        LED_Disp(ucLed);                        HAL_Delay(300);                        led_set2 = led_set2 << 1;                    }                }                break;            case 5: { //判断目标层                if ((1 << (platform - 1)) & layer_set) { //已到达目标层                    //显示闪烁效果                    sprintf((char *)Lcd_Disp_String, "              ");                    LCD_DisplayStringLine(Line2, Lcd_Disp_String);                    HAL_Delay(300);                    sprintf((char *)Lcd_Disp_String, "         %d   ", platform);                    LCD_DisplayStringLine(Line2, Lcd_Disp_String);                    HAL_Delay(300);                    sprintf((char *)Lcd_Disp_String, "              ");                    LCD_DisplayStringLine(Line2, Lcd_Disp_String);                    HAL_Delay(300);                    sprintf((char *)Lcd_Disp_String, "         %d   ", platform);                    LCD_DisplayStringLine(Line2, Lcd_Disp_String);                    HAL_Delay(300);                    //停止电机并开门                    HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);                    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);                    HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);                    __HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 300);                    sprintf((char *)Lcd_Disp_String, "Comed , Door Opening            ");                    LCD_DisplayStringLine(Line8, Lcd_Disp_String);                    state = 6;                } else { //继续运行                    uwTick_Set_Point = uwTick;                    state = 4;                }                break;            case 6: { //开门                if ((uwTick - uwTick_Set_Point) >= 4000) { //4秒后进入状态7                    HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);                    //清除目标层设置                    layer_set &= ~(1 << (platform - 1));                    ucLed = 0xF0;                    ucLed |= layer_set;                    LED_Disp(ucLed);                    state = 7;                }                break;            case 7: { //等待                if (layer_set) { //有设置                    uwTick_Set_Point = uwTick;                    sprintf((char *)Lcd_Disp_String, "Waitting 2s           ");                    LCD_DisplayStringLine(Line8, Lcd_Disp_String);                    state = 8;                } else { //无设置,回到初始状态                    state = 0;                    sprintf((char *)Lcd_Disp_String, "                  ");                    LCD_DisplayStringLine(Line8, Lcd_Disp_String);                    break;                }            }            case 8: { //等待结束后重复                if ((uwTick - uwTick_Set_Point) >= 2000) { //2秒后回到状态1                    sprintf((char *)Lcd_Disp_String, "                  ");                    LCD_DisplayStringLine(Line8, Lcd_Disp_String);                    state = 1;                }                break;            }        }    }}

状态机优化

通过状态机设计,系统能够更清晰地分解电梯运行的逻辑流程。每个状态代表一个功能模块,状态迁移则由输入事件和条件决定。这种设计方式有助于提升系统的可维护性和扩展性。

三、总结

状态机是处理复杂逻辑控制系统的有效方法。在本文中,通过将电梯控制系统分解为多个状态机,实现了对复杂逻辑的分步管理。状态机的设计需要结合实际应用场景,明确各个状态的定义、迁移条件和动作响应。通过合理的状态机设计,可以显著提升系统的可靠性和可维护性。

转载地址:http://xygfk.baihongyu.com/

你可能感兴趣的文章
MySQL _ MySQL常用操作
查看>>
MySQL – 导出数据成csv
查看>>
MySQL —— 在CentOS9下安装MySQL
查看>>
MySQL —— 视图
查看>>
mysql 不区分大小写
查看>>
mysql 两列互转
查看>>
MySQL 中开启二进制日志(Binlog)
查看>>
MySQL 中文问题
查看>>
MySQL 中日志的面试题总结
查看>>
mysql 中的all,5分钟了解MySQL5.7中union all用法的黑科技
查看>>
MySQL 中的外键检查设置:SET FOREIGN_KEY_CHECKS = 1
查看>>
Mysql 中的日期时间字符串查询
查看>>
mysql 中索引的问题
查看>>
MySQL 中锁的面试题总结
查看>>
MySQL 中随机抽样:order by rand limit 的替代方案
查看>>
MySQL 为什么需要两阶段提交?
查看>>
mysql 为某个字段的值加前缀、去掉前缀
查看>>
mysql 主从
查看>>
mysql 主从 lock_mysql 主从同步权限mysql 行锁的实现
查看>>
mysql 主从互备份_mysql互为主从实战设置详解及自动化备份(Centos7.2)
查看>>