状态机思想在程序设计中的应用(上)

题目: 状态机思想在程序设计中的应用 (上) 主讲人:自 2008-4 贺龙 时间:2011.3.13 主讲内容:

一:状态机的概念。 (简单描述即可)
有限状态机(以下用 FSM 指代)是一 种算法思想,简单而言,有限状态机由一组 状态、一个初始状态、输入和根据输入及现 有状态转换为下一个状态的转换函数组成。 状态机的要素 状态机可归纳为 4 个要素,即现态、条 件、动作、次态。这样的归纳,主要是出于 对状态机的内在因果关系的考虑。“现态”和 “条件”是因,“动作”和“次态”是果。详解如 下: ①现态:是指当前所处的状态。 ②条件:又称为“事件”。当一个条件被 满足,将会触发一个动作,或者执行一次状 态的迁移。

③动作:条件满足后执行的动作。动作 执行完毕后,可以迁移到新的状态,也可以 仍旧保持原状态。动作不是必需的,当条件 满足后,也可以不执行任何动作,直接迁移 到新状态。 ④次态:条件满足后要迁往的新状态。 “次态”是相对于“现态”而言的,“次态”一旦 被激活,就转变成新的“现态”了。 如果我们进一步归纳,把“现态”和“次 态”统一起来, 而把“动作”忽略 (降格处理) , 则只剩下两个最关键的要素,即:状态、迁 移条件。 状态迁移表

二:实例(1) :按键扫描
#define key_input PIND.7 // 按键输入口 #define key_state_0 0 #define key_state_1 1 #define key_state_2 2 char read_key(void) { static char key_state = 0; char key_press, key_return = 0; key_press = key_input; // 读按键 I/O 电平 switch (key_state) { case key_state_0: // 按键初始态 if (!key_press) key_state = key_state_1; // 键被按下,状态转换到键确认态 break; case key_state_1: // 按键确认态 if (!key_press) {

key_return = 1; // 按键仍按下,按键确认输出为“1” key_state = key_state_2; // 状态转换到键释放态 } else key_state = key_state_0; // 按键已抬起,转换到按键初始态 break; case key_state_2: if (key_press) key_state = key_state_0; //按键已释放,转换到按键初始态 break; } return key_return; }

# 按键分三个状态:无按键状态,有按键状 态,等待释放状态 # 不用插入 10MS 延时,利用定时器产生 10ms 的标志,每隔 10ms 再去检测一次按键, 这样做 CPU 的效率很高,要知道 10ms 单片机 可以做很多的事情 # 应用实例
bit flag_keyscan = 0; bit key_value = 0; char read_key(void) { .... .... } int main(void) { while(1) { if(flag_keyscan)//10MS 定时到,调用扫描 { flag_keyscan = 0 ; key_value = read_key();

if(key_value) { 调用键盘处理程序 } } } } void timer0_init (void) interrupt 1 { TH0 = 0XXX;//10ms TL0 = 0XXX; flag_keyscan = 1; }

三:实例(2) :赛道计时器
/*****************************************************************/ //赛道计时器完成任务: //小车通过起跑线时开始计时,并鸣响蜂鸣器 //小车完成一圈再次通过起跑线时停止计时,并鸣响蜂鸣器 /*****************************************************************/ #include <mega8.h> #include <delay.h> #define HC164_data PORTC.4 #define HC164_clk PORTC.5 #define K1 PIND.2 #define K2 PIND.3 #define LED_T PINB.0 //发射管 #define LED_R PIND.0 //接收管 #define FMQ PORTD.7 //蜂鸣器 #define key_state_0 0 //初始状态 #define key_state_1 1 //消抖动并确认状态 #define key_state_2 2 //等待按键释放状态 unsigned char LED_STATE = 0; //接收管的状态 flash unsigned char led_7[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; flash unsigned char position[4] = {0x3e,0x3d,0x37,0x3b}; unsigned int time = 0; unsigned char disbuffer[4]; //数据缓冲区

unsigned char posit = 0; unsigned int cnt1 = 0; //10MS 计时,用于按键扫描和主程序 while 循环 unsigned int cnt2 = 0; //10MS 计时,用于秒表计数 unsigned int cnt3 = 0; //2S 计时,用于第一次检测到小车后延时 unsigned int cnt4 = 0; //60MS 计时,用于蜂鸣器第一次鸣响时间 unsigned int cnt5 = 0; //60MS 计时,用于蜂鸣器第二次鸣响时间 unsigned char time_10ms_ok = 0;

/****************************************************************** - 功能描述:读取按键 1 的值 - 隶属模块:按键检测模块 - 函数属性:内部 - 参数说明:无 - 返回说明:为 1 说明按键按下 - 注:该函数 10MS 扫描一次 ******************************************************************/ unsigned char read_key1() { static unsigned char key1_state = 0; unsigned char key_press,key_return = 0; key_press = K1; switch (key1_state) { case key_state_0: if (!K1) key1_state = key_state_1; break; case key_state_1: if (!K1) { key_return = 1; key1_state = key_state_2; } else key1_state = key_state_0; break; case key_state_2: if (K1) key1_state = key_state_0; break; } return key_return;

} /****************************************************************** - 功能描述:读取按键 2 的值 - 隶属模块:按键检测模块 - 函数属性:内部 - 参数说明:无 - 返回说明:为 1 说明按键按下 - 注:该函数 10MS 扫描一次 ******************************************************************/ unsigned char read_key2() { static unsigned char key2_state = 0; unsigned char key_press,key_return = 0; key_press = K2; switch (key2_state) { case key_state_0: if (!K2) key2_state = key_state_1; break; case key_state_1: if (!K2) { key_return = 1; key2_state = key_state_2; } else key2_state = key_state_0; break; case key_state_2: if (K2) key2_state = key_state_0; break; } return key_return; } /****************************************************************** - 功能描述:74HC164 的数据处理 - 隶属模块:数码管显示模块 - 函数属性:内部 - 参数说明:byte 为串入并出的数据

- 返回说明:无 - 注:74HC164 核心模块为一个移位寄存器,根据与数码管的接口,先送入低位数据,后送 入高位数据 ******************************************************************/ void HC164_send_byte(char byte) { char i; for(i=0;i<8;i++) { HC164_clk = 0; HC164_data = byte & 0x01; HC164_clk = 1; byte >>= 1; } } /****************************************************************** - 功能描述:将秒表时间的值送到数据缓冲区 - 隶属模块:数码管显示模块 - 函数属性:内部 - 参数说明:无 - 返回说明:无 - 注:不用循环结构,加快传输速度 ******************************************************************/ void time_to_disbuffer(void) { unsigned int temp; temp = time; disbuffer[0] = temp%10; temp = temp/10; disbuffer[1] = temp%10; temp = temp/10; disbuffer[2] = temp%10; temp = temp/10; disbuffer[3] = temp%10; } /****************************************************************** - 功能描述:数码管显示缓冲区的数据 - 隶属模块:数码管显示模块 - 函数属性:内部 - 参数说明:无

- 返回说明:无 - 注:该函数为数码管显示的灵魂,在中断服务程序中 2MS 调用一次,并显示数码管的某 一位 ******************************************************************/ void display() { PORTC = 0X3F; if(posit == 2) { HC164_send_byte(led_7[disbuffer[posit]]&0x7f); } else { HC164_send_byte(led_7[disbuffer[posit]]); } PORTC = position[posit]; if(++posit == 4) posit = 0; } /****************************************************************** - 功能描述:TIMER2 比较匹配中断服务程序,根据状态标量,让数码管和蜂鸣器显示相应 内容 - 隶属模块:独立的中断服务 - 函数属性:内部 - 参数说明:无 - 返回说明:无 - 注: 处理状态机的状态放在 MAIN 函数里判断,各种状态下的处理内容在中断中处理, 效率高 ******************************************************************/ interrupt [TIM2_COMP] void timer2_comp_isr(void) { display(); cnt1++; if(cnt1 == 5) { cnt1 = 0; time_10ms_ok = 1; } if(LED_STATE == 0) //等待状态,不计时 {

time = 0; } else if(LED_STATE == 1) //计时状态 { cnt2 ++; cnt4 ++; if(cnt2 == 5) { cnt2 = 0; time++; if(time == 10000) { time = 0; } time_to_disbuffer(); } if(cnt4 <= 30) { FMQ = 0; } else { //cnt4 = 0; FMQ = 1; } } else if(LED_STATE == 2) //停止计时 { cnt2 = 0; cnt3 = 0; cnt5++; time = time; if(cnt5 <= 30) { FMQ = 0; } else

{ FMQ = 1; } }

}

/****************************************************************** - 功能描述:初始化,状态机的状态判断 - 隶属模块:主函数 - 函数属性:内部 - 参数说明:无 - 返回说明:无 - 注:处理状态机时,状态越少越好,防止状态混乱,WHILE 循环每 10MS 循环一次执行 各种状态的判断 ******************************************************************/ void main(void) { //端口初始化 DDRC = 0x3f; PORTC = 0x3f; DDRD = 0X80; PORTD = 0x80; DDRB = 0x02; PORTB = 0x02; //TIMER2 初始化 TCCR2 = 0X0C; TCNT2 = 0X00; OCR2 = 0X7C; TIMSK = 0X80; //允许比较匹配中断 #asm("sei"); //开全局中断 while(1) { if(time_10ms_ok==1) { time_10ms_ok = 0; switch(LED_STATE) { case 0: //等待状态

if(LED_R == 1) LED_STATE = 1; break; case 1: //开始计时 if(++cnt3 < 200) { break; } else if(LED_R == 1) { cnt3 = 0; LED_STATE = 2; } break; case 2: //等待小车越过终点 if(read_key2()) { LED_STATE = 0; } break; default: break; } if(read_key1()) { LED_STATE = 1; } if(read_key2()) { LED_STATE = 0; } } } }

三:总结
//! 定义状态名称与状态值之间的关系,增加可读性

#define FSM_START #define FSM_STATE_A #define FSM_STATE_B … #define FSM_RESET

0x00 0x01 0x02

0xFF //!< 定义状态变量

bool fsm_example_A( <</span>形参列表> ) { static uint8_t s_chFSMState = FSM_START; … switch ( s_chFSMState ) { case FSM_START: //! 这里添加状态机初始化代码 … s_chFSMState = FSM_STATE_A; break; case FSM_STATE_A: //! 这里添加状态机 A 进入下一状态的检测代码 if (<</span>某某条件>) { //! 这里做一些进入下一状态时要做的准备工作 s_chFSMState = FSM_STATE_B; } break; case FSM_STATE_B: //! 这里添加状态机 A 进入下一状态的检测代码 if (<</span>某某条件>) { //! 这里做一些进入下一状态时要做的准备工作 s_chFSMState = FSM_STATE_A; } else if (<</span>某某条件>) { } else if (<</span>某某条件>) { … } else { } break; … case FSM_STOP: case FSM_RESET: default: //! 这里添加状态机复位相关的代码 … chFSMState = FSM_START; //! 返回 false 表示状态机已经不需要继续运行了 return false; } //!< 状态机复位 //!< 进入下一状态 //!< 进入下一状态 //!< 进入下一状态

//! 返回 true 表示状态机正在运行 return true; }

初学单片机的人的开发模式: 1、画(状态)流程图 2、翻译状态流程图为状态机 3、优化、调试 形成习惯以后,能大大加快学习嵌入式系统 的速度。不会被诸如 按键扫描程序和数码管扫描程序无法很好 协调工作的问题困惑, 甚至在缺乏引导的情况下,养成非常不好的 习惯。 状态机是一种计算机软件开发的万能语言 模式,他让我们能随心 所欲的表达自己的思想。在这种基础下,自 己的想法能够得到充 分的表达,从而自己的思想也能得到实践的 充分验证。于是思维 和实践能够得到充分的交互,尽早的形成良 性循环。这个时候,

书本上那些理论,只要能够理解,你就有足 够的语言能力去检验 于是也就能很快的体会其中的好坏。

下期精彩预告:
置初始状态 0 [顺序结构] 检测状态 0 无条件执行动作>条件>状态转移 1 检测状态 1 无条件执行动作>条件>状态转移 2

[选择分支结构] 检测状态 2 无条件执行动作>条件 1>状态转移 3 条件 2>状态转移 5 检测状态 3 无条件执行动作>条件>状态转移 4 检测状态 4

无条件执行动作>条件>状态转移 7 检测状态 5 无条件执行动作>条件>状态转移 6 检测状态 6 无条件执行动作>条件>状态转移 7

[并行分支结构] 检测状态 7 无条件执行动作>条件>状态转移 8+状态转 移 11 状态转移 8 无条件执行动作>条件>状态转移 9 状态转移 9 无条件执行动作>条件>状态转移 10 状态转移 11 无条件执行动作>条件>状态转移 12 状态转移 12 无条件执行动作>条件>状态转移 13 状态转移 9+状态转移 13 无条件执行动作>条件>状态转移 0


相关文档

状态机思路在程序设计中的应用
状态机思路在单片机程序设计中的应用
基于状态机思想的程序设计
状态机原理在控制程序设计中的应用
有限状态机在单片机编程中的应用
状态机思路在单片机程序设计中的应用状态机的概念状态机是软件
状态机的编程思想
状态机编程
状态机在单片机程序设计中的应用
基于LabVIEW状态机的程序设计
电脑版