最近在准备嵌入式国赛,想把近年的试题刷一刷,了解了状态机可能是一个很重要的方向,打算自己认真准备一下这个典型的题目。
以下程序是我个人的第二版,第一版花了我一天的时间,状态的转化确实比较复杂,但是也是有一定的方法技巧的。比如说提前在演草纸上写好各种相关转化的关系以及转化对应的条件,这样非常有利于编程,此外,我个人感觉用宏定义来标识各种状态也是一个很好的方法,这里我要感谢电子设计工坊所提供的这个宏定义的这个思路,确实比较方便。
以下是我的代码:
#include "stm32f10x.h" #include "lcd.h" #include "io.h" #include "timer.h" #include "key.h" #include "i2c.h" #include "stdio.h" //sprintf #include "rtc.h" #include "pwm.h" #define LIFT_DOOR_OPENED 0 #define LIFT_DOOR_CLOSED 1 #define LIFT_UPING 2 #define LIFT_DOWNING 3 #define LIFT_DOOR_OPENING 4 #define LIFT_DOOR_CLOSING 5 _Bool flag_lcd_brink; _Bool trogo_lcd = 1; _Bool flag_lcd_update; _Bool flag_lift_running; _Bool flag_door_running; _Bool flag_wait_opening; _Bool flag_key_wait = 1000; u8 count_led_move; u8 num_trask; u8 now_plotform = 1; u8 status_lift = LIFT_DOOR_OPENED; u16 count_lift_running; u16 count_wait_opening; u16 count_door_running; u16 LED_Store; u16 count_key_wait; u16 count_wait_time; u32 count_1ms; u32 TimingDelay = 0; int trask_floor[5] = {0,0,0,0,0}; unsigned char temp[20]; __IO uint32_t TimeDisplay; extern uint32_t THH, TMM, TSS; extern float PA6_Duty; extern float PA7_Duty; void Delay_Ms(u32 nTime); void Key_Process(void); void LCD_Display(void); void LIFT_Control(void); void LIFT_Control(void); void Out_Control(void); int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); SysTick_Config(SystemCoreClock/1000); Delay_Ms(200); LED_Init(); PA45_Init(); Timer_Init(); TIM3_PWM_Init(); Key_Init(); STM3210B_LCD_Init(); LCD_Clear(White); i2c_init(); RTC_Configuration(); Time_Adjust(12,50,55); LED_Control(0x00,1); LCD_DisplayStringLineDec(Line2," Current Plotform",now_plotform); while(1){ Key_Process(); LIFT_Control(); Out_Control(); LCD_Display(); } } void Key_Process(void){ if(!(count_1ms%10)){ Key_Read(); if(status_lift == LIFT_DOOR_OPENED){ if(Trg == 0x01){ if(trask_floor[1] != 1 && now_plotform != 1){ trask_floor[1] = 1; num_trask++; LED_Store = LED_Store&0xfe|0x01; LED_Control(LED_Store,1); flag_key_wait = 1; count_key_wait = 0; } }else if(Trg == 0x02){ if(trask_floor[2] != 1 && now_plotform != 2){ trask_floor[2] = 1; num_trask++; LED_Store = LED_Store&0xfd|0x02; LED_Control(LED_Store,1); flag_key_wait = 1; count_key_wait = 0; } }else if(Trg == 0x04 ){ if(trask_floor[3] != 1 && now_plotform != 3){ trask_floor[3] = 1; num_trask++; LED_Store = LED_Store&0xfb|0x04; LED_Control(LED_Store,1); flag_key_wait = 1; count_key_wait = 0; } }else if(Trg == 0x08){ if(trask_floor[4] != 1 && now_plotform != 4){ trask_floor[4] = 1; num_trask++; LED_Store = LED_Store&0xf7|0x08; LED_Control(LED_Store,1); flag_key_wait = 1; count_key_wait = 0; } } } } } void LCD_Display(void){ if(flag_lcd_update){ flag_lcd_update = 0; if(trogo_lcd) sprintf((char*)temp," %1d",now_plotform); else sprintf((char*)temp," "); LCD_DisplayStringLineDec(Line4, temp, 1); Time_Display(RTC_GetCounter()); sprintf((char*)temp," %2d:%2d:%2d ",THH,TMM,TSS); LCD_DisplayStringLineDec(Line6, temp, 1); } } void LIFT_Control(void){ if(status_lift == LIFT_DOOR_CLOSED && num_trask){//说明有任务需要去处理 int i; _Bool go_floor = 0; for(i = now_plotform + 1; i <= 4; i++){ if(trask_floor[i]){ // 说明上方有任务楼层需要到达 status_lift = LIFT_UPING; count_led_move = 0; go_floor = 1; } } if(!go_floor){//上方没有任务楼层 status_lift = LIFT_DOWNING; count_led_move = 0; } }else if(status_lift == LIFT_UPING){ flag_lift_running = 1; }else if(status_lift == LIFT_DOWNING){ flag_lift_running = 1; }else if(status_lift == LIFT_DOOR_OPENING){ flag_door_running = 1; }else if(status_lift == LIFT_DOOR_CLOSING){ flag_door_running = 1; }else if(status_lift == LIFT_DOOR_OPENED){ if(num_trask){ flag_wait_opening = 1; }else{//等待按键 保持开门的状态 } } } void Out_Control(void){ if(status_lift == LIFT_DOOR_OPENED){ PA6_Duty = 0;PA7_Duty = 0; }else if(status_lift == LIFT_DOOR_CLOSED){ PA6_Duty = 0;PA7_Duty = 0; }else if(status_lift == LIFT_UPING){ PA4_H;PA6_Duty = 0.8;PA7_Duty = 0; LED_Store = LED_Store&0x0f|(0x10<<count_led_move); LED_Control(LED_Store,1); }else if(status_lift == LIFT_DOWNING){ PA4_L;PA6_Duty = 0.6;PA7_Duty = 0; LED_Store = LED_Store&0x0f|(0x80>>count_led_move); LED_Control(LED_Store,1); }else if(status_lift == LIFT_DOOR_OPENING){ PA5_H;PA7_Duty = 0.6;PA6_Duty = 0; LED_Store = LED_Store&0x0f|0x00;LED_Control(LED_Store,1);//这句话不能放在中断中,否则可能执行不完全 }else if(status_lift == LIFT_DOOR_CLOSING){ PA5_L;PA7_Duty = 0.5;PA6_Duty = 0; } } void Delay_Ms(u32 nTime) { TimingDelay = nTime; while(TimingDelay != 0); } void TIM4_IRQHandler(void){ if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM4, TIM_IT_Update); count_1ms++; if(!(count_1ms%20)){ flag_lcd_update = 1; } if(!(count_1ms%200)){ if(++count_led_move == 4){ count_led_move = 0; } } if(flag_lcd_brink){ static int count_trogo_lcd = 0; if(!(count_1ms%250)){ trogo_lcd = !trogo_lcd; if(++count_trogo_lcd == 5){ count_trogo_lcd = 0; flag_lcd_brink = 0; trogo_lcd = 1; } } } if(flag_key_wait){ if(++count_key_wait == 1000){ count_key_wait = 0; flag_key_wait = 0; } } if(flag_lift_running){ if(++count_lift_running == 6000){//上下电梯运行时间到 flag_lift_running = 0; count_lift_running = 0; if(status_lift == LIFT_UPING){ now_plotform++; if(trask_floor[now_plotform]){//有该层的任务 trask_floor[now_plotform] = 0; num_trask--; status_lift = LIFT_DOOR_OPENING;//触发任务查询 flag_lcd_brink = 1; if(now_plotform == 1)LED_Store = LED_Store&0xfe|0x00; else if(now_plotform == 2)LED_Store = LED_Store&0xfd|0x00; else if(now_plotform == 3)LED_Store = LED_Store&0xfb|0x00; else if(now_plotform == 4)LED_Store = LED_Store&0xf7|0x00; LED_Control(LED_Store,1); }else{//没有该层的任务 status_lift = LIFT_UPING; count_led_move = 0; } }else if(status_lift == LIFT_DOWNING){ now_plotform--; if(trask_floor[now_plotform]){//有该层的任务 trask_floor[now_plotform] = 0; num_trask--; status_lift = LIFT_DOOR_OPENING; flag_lcd_brink = 1; if(now_plotform == 1)LED_Store = LED_Store&0xfe|0x00; else if(now_plotform == 2)LED_Store = LED_Store&0xfd|0x00; else if(now_plotform == 3)LED_Store = LED_Store&0xfb|0x00; else if(now_plotform == 4)LED_Store = LED_Store&0xf7|0x00; LED_Control(LED_Store,1); }else{ status_lift = LIFT_DOWNING; count_led_move = 0; } } } } if(flag_door_running){ if(++count_door_running == 4000){ count_door_running = 0; flag_door_running = 0; if(status_lift == LIFT_DOOR_OPENING) status_lift = LIFT_DOOR_OPENED; else if(status_lift == LIFT_DOOR_CLOSING) status_lift = LIFT_DOOR_CLOSED; } } if(flag_wait_opening){ if(flag_key_wait){ count_wait_time = 1000; }else{ count_wait_time = 2000; } if(++count_wait_opening == count_wait_time){ flag_wait_opening = 0; count_wait_opening = 0; status_lift = LIFT_DOOR_CLOSING;//还有任务 2s后关闭 LED_Store = LED_Store&0x0f|0x00; } } } }
最后,我再总结一下编程的一般思路和需要注意的点:
一、一般思路
1、写出整个工程的要点,整理骨干流程(在这里是按键的操作)
2、设置状态机,分析状态之间的转化,在草稿上备注好之间的转换关系
3、补充因为状态的转变而缺失的输出控制
二、注意
1、中断不要放一些硬件处理函数,而是放一些标志位的处理。比如LED_Control不要放在中断中,如果本身中断进入的频率比较高,而处理的时间又比较长就可能无法完成中断处理。
2、代码的规范很重要。尽量不要把定义的变量和函数随意放置,尤其是在主函数比较长的时候,这样不方便后续查找和定位。
好啦,第一次写博客文章,希望能提供读者一些参考,如果代码有什么不对的地方请不吝赐教啊。