最简洁的单片机状态机模型(X-状态机)
2023-01-30 来源:zhihu
单片机如果控制的设备趋向复杂,使用状态机模式写程序会更清晰,但是网上讲的状态机图晦涩难懂,实际状态机就是一张表,并且这张表对应单片机的输入输出引脚就能完成大部分状态罗列。
看问题看本质,单片机内部运行的程序用户不关心,但是单片机显示、发声、按键操作、控制设备是最关注的。
把输入组合作为条件,输出仅做动作,就是最简洁的单片机状态机。
为了与通常所讲的状态机模型区分,个人给这个状态机模型起个名字: 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>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; //... } }