Github

整个RTT学习过程中对工程的完善和开发都记录在Github上的一个项目中,项目还会随着学习的深入进行更新
项目URL:https://github.com/HITLIVING/-RT-Thread-.git
读者可以通过Pull下来整个项目进一步了解
对模拟状态机的实现需要格外关注
schedule_app.c
schedule_app.h
两个文件

综述

当工程实现的功能越来越复杂,需要设计专门的调度器来合理的支配工程中的各个模块
尤其是当设计的工程需要面对多任务,多流程环节的场合,往往需要设计一个机构,配合各种条件来完成任务
在RTT学习过程中,学会驱动了显示屏触摸屏等各个模块后,可以尝试开发一个具有界面和可视化管理的系统

外设配置

STM32F1RCT6芯片
野火MINI开发板的板载外设

典型结构

在单片机上实现模拟状态机,或者说是调度器,更多的是使用定时器和循环结构的配合
循环结构则是利用 switch-case 的检索,或者 if-else 的条件来实现
switch-caseif-case 效率稍高一些,因为 swich-case 基于查找表检索
但是 switch-case 只能实现常量对比,而无法实现逻辑比较的检索
两种循环结构是单片机上实现调度的典型结构,但都存在一定问题
在执行时都需要检索,而且随着功能的丰富,调度树会原来越长,较难维护
这里介绍一种基于 函数指针 的调度器实现方法
该方法来源于网络,同时做出了一定的发展
在2019ROBOCON比赛的决策树采用了此结构运行状况良好

基本思路

定义了函数指针后,对函数可以采用变量式的管理方法,通过对变量的赋值即可实现特定函数的执行
使用此方法可以将调度器中的各个任务相互分隔,易于维护
同时仅需要对一变量赋不同的值即可实现状态的跳转

函数结构

声明

  • 头文件:
  1. enum MainSchStateType
  2. {
  3. //主界面状态
  4. Interface_State,
  5. //菜单状态
  6. Menu_State,
  7. //触屏校准状态
  8. CheckTouch_State,
  9. //画板状态
  10. Palette_State,
  11. //类型边界
  12. Type_Num
  13. };

头文件里声明了一个枚举类型
用来声明不同状态的标志符

  • 源文件: ```c typedef enum MainSchStateType MainSch_State; typedef MainSch_State (*MainSch_Procedure)(void);

//当前执行状态 enum MainSchStateType MainSchStep = Interface_State; //上一周期状态 enum MainSchStateType Last_MainSchStep = Type_Num;

  1. 源文件将枚举类型重定义,方便使用<br />将函数指针重定义,方便使用(函数的返回类型为 `MainSch_State` ,函数指针的变量名为 `MainSch_Procedure` ,无传入参数)<br />记录当前需要执行的状态标志符,用来指定状态<br />记录上一周期执行的状态标志符,用来自动执行状态切换时的初始化
  2. <a name="aXPui"></a>
  3. ## 定义
  4. 为方便说明,这里将各个状态的函数实体删去,详细内容可以参照原工程
  5. ```c
  6. typedef enum MainSchStateType MainSch_State;
  7. typedef MainSch_State (*MainSch_Procedure)(void);
  8. enum MainSchStateType MainSchStep = Interface_State;
  9. enum MainSchStateType Last_MainSchStep = Type_Num;
  10. /*****************Main Schedule Procedure**********************/
  11. MainSch_State step_Interface(void)
  12. {
  13. if(Last_MainSchStep!=MainSchStep)
  14. {
  15. /* step init */
  16. //当状态切换时这里只执行一次
  17. }
  18. //更新,避免反复执行状态的初始化
  19. Last_MainSchStep = MainSchStep;
  20. /*自定义应用程序*/
  21. ...
  22. //返回当前状态的标识符,使该状态循环
  23. return MainSchStep;
  24. }
  25. MainSch_State step_Menu(void)
  26. {
  27. if(Last_MainSchStep!=MainSchStep)
  28. {
  29. /* step init */
  30. //当状态切换时这里只执行一次
  31. }
  32. Last_MainSchStep = MainSchStep;
  33. /*自定义应用程序*/
  34. ...
  35. //返回当前状态的标识符,使该状态循环
  36. return MainSchStep;
  37. }
  38. MainSch_State step_CheckTouch(void)
  39. {
  40. if(Last_MainSchStep!=MainSchStep)
  41. {
  42. /* step init */
  43. //当状态切换时这里只执行一次
  44. }
  45. /*自定义应用程序*/
  46. ...
  47. //返回某个标识符,使该状态仅被执行一次
  48. return Menu_State;
  49. }
  50. MainSch_State step_Palette(void)
  51. {
  52. if(Last_MainSchStep!=MainSchStep)
  53. {
  54. /* step init */
  55. //当状态切换时这里只执行一次
  56. }
  57. Last_MainSchStep = MainSchStep;
  58. /*自定义应用程序*/
  59. ...
  60. //返回当前状态的标识符,使该状态循环
  61. return MainSchStep;
  62. }
  63. MainSch_State step_Num(void)
  64. {
  65. /*自定义应用程序*/
  66. /*执行到这里有越界的风险*/
  67. ...
  68. //返回某个安全的标识符,使该状态仅被执行一次
  69. return Interface_State;
  70. }
  71. /* 各个状态的函数指针数组 */
  72. /* 这里即实现了用数组来管理函数 */
  73. /* 注意和标识符区分 */
  74. MainSch_Procedure State_ProceSteps[] =
  75. {
  76. step_Interface,
  77. step_Menu,
  78. step_CheckTouch,
  79. step_Palette,
  80. step_Num
  81. };
  82. /**
  83. * @brief System main Schedule Thread Entry
  84. * @param None
  85. * @return None
  86. * @note Provide a timescale用线程做定时器
  87. */
  88. static void thread_main_entry(void *parameter)
  89. {
  90. while(1)
  91. {
  92. /*定时器周期*/
  93. rt_thread_mdelay(1);
  94. /*根据标识符执行对应函数*/
  95. /*并将当前状态号记录下来循环调用*/
  96. /*当通过外部中断或其它地方修改了状态号,则执行修改后的状态号对应的函数*/
  97. MainSchStep = (*State_ProceSteps[MainSchStep])();
  98. }
  99. }
  100. #define MAIN_THREAD_PRIORITY 21
  101. #define MAIN_THREAD_STACK_SIZE 1024
  102. #define MAIN_THREAD_TIMESLICE 100
  103. /**
  104. * @brief System main Schedule Thread Init
  105. * @param None
  106. * @return None
  107. * @note Provide a timescale用线程做定时器
  108. */
  109. ALIGN(RT_ALIGN_SIZE)
  110. void thread_System_Schedule_init(void)
  111. {
  112. rt_thread_t thread_main_obj = RT_NULL;
  113. /* Build a thread */
  114. thread_main_obj = rt_thread_create("thread_System_Schedule",
  115. thread_main_entry,
  116. RT_NULL,
  117. MAIN_THREAD_STACK_SIZE,
  118. MAIN_THREAD_PRIORITY,
  119. MAIN_THREAD_TIMESLICE);
  120. /* If get the thread object, start it */
  121. if (thread_main_obj != RT_NULL)
  122. {
  123. rt_thread_startup(thread_main_obj);
  124. }
  125. else
  126. {
  127. /*自定义提示信息*/
  128. error_SchFailed();
  129. }
  130. }

上机实验

开机界面(初始化时完成) 第一个状态(Interface) 第二个状态(Menu)
IMG_5564.JPGIMG_5565.JPGIMG_5566.JPG

第三个状态(CheckTouch) 第四个状态(Platte)
IMG_5569.JPGIMG_5570.JPG

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