Github
整个RTT学习过程中对工程的完善和开发都记录在Github上的一个项目中,项目还会随着学习的深入进行更新
项目URL:https://github.com/HITLIVING/-RT-Thread-.git
读者可以通过Pull下来整个项目进一步了解
对模拟状态机的实现需要格外关注schedule_app.cschedule_app.h
两个文件
综述
当工程实现的功能越来越复杂,需要设计专门的调度器来合理的支配工程中的各个模块
尤其是当设计的工程需要面对多任务,多流程环节的场合,往往需要设计一个机构,配合各种条件来完成任务
在RTT学习过程中,学会驱动了显示屏触摸屏等各个模块后,可以尝试开发一个具有界面和可视化管理的系统
外设配置
STM32F1RCT6芯片
野火MINI开发板的板载外设
典型结构
在单片机上实现模拟状态机,或者说是调度器,更多的是使用定时器和循环结构的配合
循环结构则是利用 switch-case 的检索,或者 if-else 的条件来实现switch-case 比 if-case 效率稍高一些,因为 swich-case 基于查找表检索
但是 switch-case 只能实现常量对比,而无法实现逻辑比较的检索
两种循环结构是单片机上实现调度的典型结构,但都存在一定问题
在执行时都需要检索,而且随着功能的丰富,调度树会原来越长,较难维护
这里介绍一种基于 函数指针 的调度器实现方法
该方法来源于网络,同时做出了一定的发展
在2019ROBOCON比赛的决策树采用了此结构运行状况良好
基本思路
定义了函数指针后,对函数可以采用变量式的管理方法,通过对变量的赋值即可实现特定函数的执行
使用此方法可以将调度器中的各个任务相互分隔,易于维护
同时仅需要对一变量赋不同的值即可实现状态的跳转
函数结构
声明
- 头文件:
enum MainSchStateType{//主界面状态Interface_State,//菜单状态Menu_State,//触屏校准状态CheckTouch_State,//画板状态Palette_State,//类型边界Type_Num};
头文件里声明了一个枚举类型
用来声明不同状态的标志符
- 源文件: ```c typedef enum MainSchStateType MainSch_State; typedef MainSch_State (*MainSch_Procedure)(void);
//当前执行状态 enum MainSchStateType MainSchStep = Interface_State; //上一周期状态 enum MainSchStateType Last_MainSchStep = Type_Num;
源文件将枚举类型重定义,方便使用<br />将函数指针重定义,方便使用(函数的返回类型为 `MainSch_State` ,函数指针的变量名为 `MainSch_Procedure` ,无传入参数)<br />记录当前需要执行的状态标志符,用来指定状态<br />记录上一周期执行的状态标志符,用来自动执行状态切换时的初始化<a name="aXPui"></a>## 定义为方便说明,这里将各个状态的函数实体删去,详细内容可以参照原工程```ctypedef enum MainSchStateType MainSch_State;typedef MainSch_State (*MainSch_Procedure)(void);enum MainSchStateType MainSchStep = Interface_State;enum MainSchStateType Last_MainSchStep = Type_Num;/*****************Main Schedule Procedure**********************/MainSch_State step_Interface(void){if(Last_MainSchStep!=MainSchStep){/* step init *///当状态切换时这里只执行一次}//更新,避免反复执行状态的初始化Last_MainSchStep = MainSchStep;/*自定义应用程序*/...//返回当前状态的标识符,使该状态循环return MainSchStep;}MainSch_State step_Menu(void){if(Last_MainSchStep!=MainSchStep){/* step init *///当状态切换时这里只执行一次}Last_MainSchStep = MainSchStep;/*自定义应用程序*/...//返回当前状态的标识符,使该状态循环return MainSchStep;}MainSch_State step_CheckTouch(void){if(Last_MainSchStep!=MainSchStep){/* step init *///当状态切换时这里只执行一次}/*自定义应用程序*/...//返回某个标识符,使该状态仅被执行一次return Menu_State;}MainSch_State step_Palette(void){if(Last_MainSchStep!=MainSchStep){/* step init *///当状态切换时这里只执行一次}Last_MainSchStep = MainSchStep;/*自定义应用程序*/...//返回当前状态的标识符,使该状态循环return MainSchStep;}MainSch_State step_Num(void){/*自定义应用程序*//*执行到这里有越界的风险*/...//返回某个安全的标识符,使该状态仅被执行一次return Interface_State;}/* 各个状态的函数指针数组 *//* 这里即实现了用数组来管理函数 *//* 注意和标识符区分 */MainSch_Procedure State_ProceSteps[] ={step_Interface,step_Menu,step_CheckTouch,step_Palette,step_Num};/*** @brief System main Schedule Thread Entry* @param None* @return None* @note Provide a timescale用线程做定时器*/static void thread_main_entry(void *parameter){while(1){/*定时器周期*/rt_thread_mdelay(1);/*根据标识符执行对应函数*//*并将当前状态号记录下来循环调用*//*当通过外部中断或其它地方修改了状态号,则执行修改后的状态号对应的函数*/MainSchStep = (*State_ProceSteps[MainSchStep])();}}#define MAIN_THREAD_PRIORITY 21#define MAIN_THREAD_STACK_SIZE 1024#define MAIN_THREAD_TIMESLICE 100/*** @brief System main Schedule Thread Init* @param None* @return None* @note Provide a timescale用线程做定时器*/ALIGN(RT_ALIGN_SIZE)void thread_System_Schedule_init(void){rt_thread_t thread_main_obj = RT_NULL;/* Build a thread */thread_main_obj = rt_thread_create("thread_System_Schedule",thread_main_entry,RT_NULL,MAIN_THREAD_STACK_SIZE,MAIN_THREAD_PRIORITY,MAIN_THREAD_TIMESLICE);/* If get the thread object, start it */if (thread_main_obj != RT_NULL){rt_thread_startup(thread_main_obj);}else{/*自定义提示信息*/error_SchFailed();}}
上机实验
开机界面(初始化时完成) 第一个状态(Interface) 第二个状态(Menu)


第三个状态(CheckTouch) 第四个状态(Platte)

完整工程见Github(-RT-Thread-)
