最简洁的单片机状态机模型(X-状态机)

2023-01-30  

单片机如果控制的设备趋向复杂,使用状态机模式写程序会更清晰,但是网上讲的状态机图晦涩难懂,实际状态机就是一张表,并且这张表对应单片机的输入输出引脚就能完成大部分状态罗列。


看问题看本质,单片机内部运行的程序用户不关心,但是单片机显示、发声、按键操作、控制设备是最关注的。


把输入组合作为条件,输出仅做动作,就是最简洁的单片机状态机。


为了与通常所讲的状态机模型区分,个人给这个状态机模型起个名字: X-状态机 (X-FSM)


输入可以是传感器、按键、开关信号等

输出可以是开关信号、数码管显示、LCD显示、蜂鸣器、语音喇叭发声、指示灯等。


一图胜万言,一表见真章:

表一(行输出,列输入)

表二

有限状态机FSM(Finite State Machine)及实现方式介绍

表一 是向导个人简化的单片机状态表

表二 是目前通用的状态机表示方法

首先为什么简化成关注单片机输出输出的状态机模型:


单片机资源容量受限 所以将传感器、按键等输入作组合

直接借鉴单片机的状态寄存器作为条件组合

X-状态机与流行的通用状态机模型区别:


通用型实际上是将输出组合的变化作为初态和次态

X-状态机实际上主要输入作为条件,输出作为状态

通用型状态机关注于初态、次态、条件判断

X-状态机不关心初态、次态,依照单个输出反推输入条件组合

更好的理解X-状态机本质:


简化后的X-状态机将多维(多个条件组合)的结构变成单层结构,简化设计

事物的本质之一为 (输入-->本体-->输出),也就是接收到一定刺激、做出一定反馈

输入基本上可以分为两种,一种是连续量(模拟量)一种是开关量(数字量)

连续量: 例如温度变化、时间变化,开关量:各种开关

处理连续量除非是显示,实际上也可以转换为有限判断,例如温度到达多少度作为一个条件

连续量分几档(看表一 温度状态)+开关---组合成一组(本例16位的条件组合)

开关量一般是多个开关一起判断,例如开机状态下,是否按下某个键

实现:


客户需求变更,程序员只关注增加一个开关量或者一组8位、16位开关量、连续量判断

以一个输出的变化作为状态反推需要的条件组合 举例,加热1输出只有开关两种状态,开的条件和关的条件依照输入确定,可以先关掉,依照一定条件触发开,条件组合少的优先作为触发条件。默认状态是条件组合多的。

其他就是C语言和单片机的通用规则

划重点:

输入组合作为条件

输出仅做动作,输出的动作是输入的变化引起的

输出的变更(客户需求变更)必然引发输入条件变化

最关键的是表一,用传统方式写,状态机表清楚一样事半功倍。

放码过来:


#include "fsm_x.h"



//inputState bit  16bit

#define TEMP_BIT_0 0

#define TEMP_BIT_1_7 1

#define TEMP_BIT_8_13 2

#define TEMP_BIT_14_SETLOW 3

#define TEMP_BIT_SETLOW_SETHIGH 4

#define TEMP_BIT_SETHIGH_HIGH 5

#define TEMP_BIT_HIGH_HHTEMP 6

#define TEMP_BIT_HHTEMP 7  //同

#define WATER_LEVEL_BIT 8

#define POWER_BIT 9

#define TIMER_BIT 10

#define ADD_DEC_BIT 11

//#define DEC_BIT 12

#define WARMUP_BIT 12

#define SLEEP_BIT 13

#define ANION_BIT 14    //彩屏产品用

#define BABY_LOCK_BIT 15 //彩屏产品用


//state bit 扩展位



#define LoadWaterLevelState isWaterLevelLow?Set16(inputState,WATER_LEVEL_BIT):Clr16(inputState,WATER_LEVEL_BIT)

#define LoadPowerState  powerOpen?Set16(inputState,POWER_BIT):Clr16(inputState,POWER_BIT)

#define LoadTimerState  timerOpen?Set16(inputState,TIMER_BIT):Clr16(inputState,TIMER_BIT)

#define LoadAddDecState    isAddOrDec?Set16(inputState,ADD_DEC_BIT):Clr16(inputState,ADD_DEC_BIT)

#define LoadWarmUpState     warmupOpen?Set16(inputState,WARMUP_BIT):Clr16(inputState,WARMUP_BIT)

#define LoadSleepState    sleepOpen?Set16(inputState,SLEEP_BIT):Clr16(inputState,SLEEP_BIT)

#define LoadAnionState    anionOpen?Set16(inputState,SLEEP_BIT):Clr16(inputState,SLEEP_BIT)

#define LoadBabyLockstate babyLockOpen?Set16(inputState,SLEEP_BIT):Clr16(inputState,SLEEP_BIT)


//其他开关量状态装箱

void LoadSwitchState(Bit state,unsigned char stateBit)

{

    state?Set16(inputState,stateBit):Clr16(inputState,stateBit);

}


//此处专用于温度传感器状态装箱

void LoadSensorState(unsigned char sensorValue)

{

    sensorValue<=0? Set16(inputState,TEMP_BIT_0):Clr16(inputState,TEMP_BIT_0);

    (sensorValue>=1)&&(sensorValue<=7)? Set16(inputState,TEMP_BIT_1_7):Clr16(inputState,TEMP_BIT_1_7);

    (sensorValue>=8)&&(sensorValue<=13)? Set16(inputState,TEMP_BIT_8_13):Clr16(inputState,TEMP_BIT_8_13);

    (sensorValue>=14)&&(sensorValue    (sensorValue>=TEMP_NORMAL_DL)&&(sensorValue<=gTempSet)? Set16(inputState,TEMP_BIT_SETLOW_SETHIGH):Clr16(inputState,TEMP_BIT_SETLOW_SETHIGH);

    (sensorValue>gTempSet)&&(sensorValue<=TEMP_HIGH_WARNING)? Set16(inputState,TEMP_BIT_SETHIGH_HIGH):Clr16(inputState,TEMP_BIT_SETHIGH_HIGH);

    (sensorValue>TEMP_HIGH_WARNING)&&(sensorValue<=TEMP_SENSOR_WARNING)? Set16(inputState,TEMP_BIT_HIGH_HHTEMP):Clr16(inputState,TEMP_BIT_HIGH_HHTEMP);

    sensorValue>TEMP_HIGH_WARNING? Set16(inputState,TEMP_BIT_HHTEMP):Clr16(inputState,TEMP_BIT_HHTEMP);

}


void RunFsm(void)

{

    //iputState32=inputState8[3]<<24|inputState8[2]<<16|inputState8[1]<<8|inputState8[0]; //4个字节状态寄存器组合成32位

    //iputState32=inputState16[1]<<16|inputState16[0];//2个字状态寄存器组合成32位状态机 32位状态寄存器

    //switch(inputState32){...}

    //也可以状态表画好(关键) 回归传统方式 直接if(inputState8[0]&&inputState8[1])else if()... 

    switch(inputState)

    {

    case 0b0000000000000001://温度<=0 状态

        CloseHeat1; //关加热器1

        CloseHeat2; //关加热器2

        OpenPump;   //水泵不关 仍然水循环

        show(errorCode,1); //数码管显示错误值

        SendVoice(0x0f); //语音播报温度故障

        errorCode=0xE0; //温度小于等于0度

        FlashLed(); //所有Led闪烁

        break;


    case 0b0000000000000010:

        //温度在1~7状态

        break;

        //...

    }

}


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。