1.对处理器和现代编程的思考
- 看过一个电影(什么名字忘记了),讲述的是未来人工智能高度发展,AI和人类谈恋爱的故事。人工智能在未来拥有了感情,与一个人类相恋爱,在AI更新换代之后,由于机器人的处理速度越来越快,对她来说与人类对一次话就好像相隔数年之久,因为受不了这种寂寞,最终AI离他而去。
- 在这个电影中,因为AI的速度非常快,在人类处理一条信息的时候,她可以处理完成上万亿条信息,对她来说,人类的世界时间仿佛过得非常缓慢。
- 在电影 “超体” 中,讲的其实也是这个事情,寡姐由于吃下了一种特殊的粒子,大脑被完全开发,他的时间与普通人类的时间就变得截然不同。
- 现代CPU一般具有几十M的处理速度,假设一个CPU的主频为100M(单位HZ),相当于在你的程序中的while循环里,每1/100000000 s(即10ns)会执行一次。如果CPU是一个超人类,那么他的世界将和你的世界完全不同,他可以如你正常交流,但是他每与你交流一次就要等好久好久,尽管我们觉得很快。
- 我认为这种就是机器的思想,在编程时,要站在他们的时间、他们的世界中去思考,这个问题怎样解决,怎么样处理这个问题,一些我们觉得很难办到的额事情,在这种高速处理的状态下、在另一个世界中,就会有新的简单的解决方法,可能更简单、更方便。
2.有限状态机定义
- 有限状态机(finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的 数学模型。
- 现实生活中的问题,转换到程序化的思想中来,都可以用状态与状态之间的跳转来实现。
3.为何使用有限状态机
- 在嵌入式,机器人领域,由于多的复杂逻辑状态,我们编写程序的时候不得不考虑很多种情况,容易造成功能间的冲突。
- 一般较为大型的软件工程尝尝需要在许多不同的状态下进行跳转,例如需要只执行一次的初始化动作、不同频率的周期性任务。
- 在我的理解中,状态机是一种模型,就好比做事情可以遵循的方法,这种方法主要解决编程中的问题。状态机也是一种思想,与人的思想有些区别,程序的思想正如状态机思想,程序是不断重复的周期性动作,不要有延时去占有CPU的时间,浪费CPU资源,CPU时间应当花费在不断地处理有用的事情上。
- 那么延时怎么办?如果我需要过一段时间执行一次任务怎么办?这个事情只需要获取CPU时间,每一个程序周期对时间进行判断,当前时间减去上一个周期执行该任务的时间是否等于需要等待的时间,如果是则运行一次你的任务,并且记录本次执行任务的时间,进行判断即可。
4.如何使用有限状态机
举个例子,在我制作的一个简单机器人开机之后,有初始化状态、运行状态、完成状态、错误状态,四个状态需要处理。
- 初始化状态:在开机之后,程序会自动进入初始化状态,该状态下屏幕上显示初始化状态,捕获按键的输入,设置每个电机的速度,检测完成按键是否按下,如果按下则赋予电机,使蜂鸣器响0.2s,之后跳转到运行状态。
- 运行状态:运行状态中实时控制电机速度,根据程序中每个周期脉冲的变化,电机速度=微分脉冲/时间,计算出电机的真实转速,反馈到屏幕上进行显示。检测按键短按变化,改变当前选中的值,旋转旋钮可以对电机调速。检测按键长按变化,使蜂鸣器响0.2s,之后跳转到完成状态。
- 完成状态:屏幕上显示完成状态。将电机失能,控制电机速度始终为0。检测按键长按变化,使蜂鸣器响0.2s,之后跳转到初始化状态。
- 错误状态:屏幕显示错误状态,并且闪烁显示,将电机失能,控制电机速度始终为0。检测按键长按变化,使蜂鸣器响0.2s,之后跳转到初始化状态。任何状态中按下急停按键都会进入错误状态。
5.代码例子
#define STATE_INIT 0 //初始状态
#define STATE_RUN 1 //运行状态
#define STATE_DONE 2 //完成状态
#define STATE_ERROR 3 //错误状态
int MIAN_STATE = 0; //主逻辑状态标志
switch (MIAN_STATE) //主逻辑状态机
{
/******************************* STATE_INIT *******************************/
case STATE_INIT: //初始状态
if (millis() - lastMilli[0] > 2) //2MS 执行一次 按键短按判断
{
lastMilli[0] = millis(); //记录当前系统时间
}
if (millis() - lastMilli[1] > 500) //500MS 执行一次
{
lastMilli[1] = millis(); //记录当前系统时间
}
if (millis() - lastMilli[2] > 100) //100MS 执行一次 按键长按判断
{
lastMilli[2] = millis(); //记录当前系统时间
}
break;
/******************************* STATE_RUN *******************************/
case STATE_RUN: //运行状态
if (millis() - lastMilli[0] > 2) //2MS 执行一次 按键短按判断
{
lastMilli[0] = millis(); //记录当前系统时间
}
if (millis() - lastMilli[1] > 500) //500MS 执行一次
{
lastMilli[1] = millis(); //记录当前系统时间
}
if (millis() - lastMilli[2] > 100) //100MS 执行一次 按键长按判断
{
lastMilli[2] = millis(); //记录当前系统时间
}
break;
/******************************* STATE_DONE *******************************/
case STATE_DONE: //完成状态
if (millis() - lastMilli[0] > 500) //500MS 执行一次
{
lastMilli[0] = millis(); //记录当前系统时间
}
if (millis() - lastMilli[2] > 100) //100MS 执行一次 按键长按判断
{
lastMilli[2] = millis(); //记录当前系统时间
}
break;
/******************************* STATE_ERROR *******************************/
case STATE_ERROR: //错误状态
if (millis() - lastMilli[0] > 500) //500MS 执行一次
{
lastMilli[0] = millis(); //记录当前系统时间
}
if (millis() - lastMilli[2] > 100) //100MS 执行一次 按键长按判断
{
lastMilli[2] = millis(); //记录当前系统时间
}
break;
default:
break;
}