22.5.1 任务通知代替队列消息

任务通知代替消息队列是在 FreeRTOS 中创建了三个任务, 其中两个任务是用于接收任务通知,另一个任务发送任务通知。三个任务独立运行,发送消息任务是通过检测按键的按下情况来发送消息通知,另两个任务获取消息通知,在任务通知中没有可用的通知之前就一直等待消息,一旦获取到消息通知就把消息打印在串口调试助手里。

  1. /*
  2. *************************************************************************
  3. * 包含的头文件
  4. *************************************************************************
  5. */
  6. /* FreeRTOS头文件 */
  7. #include "FreeRTOS.h"
  8. #include "task.h"
  9. /* 开发板硬件bsp头文件 */
  10. #include "bsp_led.h"
  11. #include "bsp_usart.h"
  12. #include "bsp_key.h"
  13. #include "limits.h"
  14. /**************************** 任务句柄 ********************************/
  15. /*
  16. * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
  17. * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
  18. * 这个句柄可以为NULL。
  19. */
  20. static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
  21. static TaskHandle_t Receive1_Task_Handle = NULL;/* Receive1_Task任务句柄 */
  22. static TaskHandle_t Receive2_Task_Handle = NULL;/* Receive2_Task任务句柄 */
  23. static TaskHandle_t Send_Task_Handle = NULL;/* Send_Task任务句柄 */
  24. /********************************** 内核对象句柄 *********************************/
  25. /*
  26. * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
  27. * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
  28. * 们就可以通过这个句柄操作这些内核对象。
  29. *
  30. * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
  31. * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
  32. * 来完成的
  33. *
  34. */
  35. /******************************* 全局变量声明 ************************************/
  36. /*
  37. * 当我们在写应用程序的时候,可能需要用到一些全局变量。
  38. */
  39. /******************************* 宏定义 ************************************/
  40. /*
  41. * 当我们在写应用程序的时候,可能需要用到一些宏定义。
  42. */
  43. #define USE_CHAR 0 /* 测试字符串的时候配置为 1 ,测试变量配置为 0 */
  44. /*
  45. *************************************************************************
  46. * 函数声明
  47. *************************************************************************
  48. */
  49. static void AppTaskCreate(void);/* 用于创建任务 */
  50. static void Receive1_Task(void* pvParameters);/* Receive1_Task任务实现 */
  51. static void Receive2_Task(void* pvParameters);/* Receive2_Task任务实现 */
  52. static void Send_Task(void* pvParameters);/* Send_Task任务实现 */
  53. static void BSP_Init(void);/* 用于初始化板载相关资源 */
  54. /*****************************************************************
  55. * @brief 主函数
  56. * @param 无
  57. * @retval 无
  58. * @note 第一步:开发板硬件初始化
  59. 第二步:创建APP应用任务
  60. 第三步:启动FreeRTOS,开始多任务调度
  61. ****************************************************************/
  62. int main(void)
  63. {
  64. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  65. /* 开发板硬件初始化 */
  66. BSP_Init();
  67. printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替消息队列实验!\n");
  68. printf("按下KEY1或者KEY2进行任务消息通知发送 \n");
  69. /* 创建AppTaskCreate任务 */
  70. xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
  71. (const char* )"AppTaskCreate",/* 任务名字 */
  72. (uint16_t )512, /* 任务栈大小 */
  73. (void* )NULL,/* 任务入口函数参数 */
  74. (UBaseType_t )1, /* 任务的优先级 */
  75. (TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
  76. /* 启动任务调度 */
  77. if(pdPASS == xReturn)
  78. vTaskStartScheduler(); /* 启动任务,开启调度 */
  79. else
  80. return -1;
  81. while(1); /* 正常不会执行到这里 */
  82. }
  83. /***********************************************************************
  84. * @ 函数名 : AppTaskCreate
  85. * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  86. * @ 参数 : 无
  87. * @ 返回值 : 无
  88. **********************************************************************/
  89. static void AppTaskCreate(void)
  90. {
  91. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  92. taskENTER_CRITICAL(); //进入临界区
  93. /* 创建Receive1_Task任务 */
  94. xReturn = xTaskCreate((TaskFunction_t )Receive1_Task, /* 任务入口函数 */
  95. (const char* )"Receive1_Task",/* 任务名字 */
  96. (uint16_t )512, /* 任务栈大小 */
  97. (void* )NULL, /* 任务入口函数参数 */
  98. (UBaseType_t )2, /* 任务的优先级 */
  99. (TaskHandle_t* )&Receive1_Task_Handle);/* 任务控制块指针 */
  100. if(pdPASS == xReturn)
  101. printf("创建Receive1_Task任务成功!\r\n");
  102. /* 创建Receive2_Task任务 */
  103. xReturn = xTaskCreate((TaskFunction_t )Receive2_Task, /* 任务入口函数 */
  104. (const char* )"Receive2_Task",/* 任务名字 */
  105. (uint16_t )512, /* 任务栈大小 */
  106. (void* )NULL, /* 任务入口函数参数 */
  107. (UBaseType_t )3, /* 任务的优先级 */
  108. (TaskHandle_t* )&Receive2_Task_Handle);/* 任务控制块指针 */
  109. if(pdPASS == xReturn)
  110. printf("创建Receive2_Task任务成功!\r\n");
  111. /* 创建Send_Task任务 */
  112. xReturn = xTaskCreate((TaskFunction_t )Send_Task, /* 任务入口函数 */
  113. (const char* )"Send_Task",/* 任务名字 */
  114. (uint16_t )512, /* 任务栈大小 */
  115. (void* )NULL,/* 任务入口函数参数 */
  116. (UBaseType_t )4, /* 任务的优先级 */
  117. (TaskHandle_t* )&Send_Task_Handle);/* 任务控制块指针 */
  118. if(pdPASS == xReturn)
  119. printf("创建Send_Task任务成功!\n\n");
  120. vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  121. taskEXIT_CRITICAL(); //退出临界区
  122. }
  123. /**********************************************************************
  124. * @ 函数名 : Receive_Task
  125. * @ 功能说明: Receive_Task任务主体
  126. * @ 参数 :
  127. * @ 返回值 : 无
  128. ********************************************************************/
  129. static void Receive1_Task(void* parameter)
  130. {
  131. BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  132. #if USE_CHAR
  133. char *r_char;
  134. #else
  135. uint32_t r_num;
  136. #endif
  137. while (1)
  138. {
  139. /* BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
  140. uint32_t ulBitsToClearOnExit,
  141. uint32_t *pulNotificationValue,
  142. TickType_t xTicksToWait );
  143. * ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取
  144. 反值进行按位与运算,当此参数为Oxfffff或者ULONG_MAX的时候就会将任务通知值清零。
  145. * ulBits ToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将
  146. 任务通知值与此参数的取反值进行按位与运算,当此参数为0xfffff或者ULONG MAX的时候
  147. 就会将任务通知值清零。
  148. * pulNotification Value:此参数用来保存任务通知值。
  149. * xTick ToWait:阻塞时间。
  150. *
  151. * 返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。
  152. */
  153. //获取任务通知 ,没获取到则一直等待
  154. xReturn=xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
  155. ULONG_MAX, //退出函数的时候清除所有的bit
  156. #if USE_CHAR
  157. (uint32_t *)&r_char, //保存任务通知值
  158. #else
  159. &r_num, //保存任务通知值
  160. #endif
  161. portMAX_DELAY); //阻塞时间
  162. if( pdTRUE == xReturn )
  163. #if USE_CHAR
  164. printf("Receive1_Task 任务通知消息为 %s \n",r_char);
  165. #else
  166. printf("Receive1_Task 任务通知消息为 %d \n",r_num);
  167. #endif
  168. LED1_TOGGLE;
  169. }
  170. }
  171. /**********************************************************************
  172. * @ 函数名 : Receive_Task
  173. * @ 功能说明: Receive_Task任务主体
  174. * @ 参数 :
  175. * @ 返回值 : 无
  176. ********************************************************************/
  177. static void Receive2_Task(void* parameter)
  178. {
  179. BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  180. #if USE_CHAR
  181. char *r_char;
  182. #else
  183. uint32_t r_num;
  184. #endif
  185. while (1)
  186. {
  187. /* BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
  188. uint32_t ulBitsToClearOnExit,
  189. uint32_t *pulNotificationValue,
  190. TickType_t xTicksToWait );
  191. * ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取
  192. 反值进行按位与运算,当此参数为Oxfffff或者ULONG_MAX的时候就会将任务通知值清零。
  193. * ulBits ToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将
  194. 任务通知值与此参数的取反值进行按位与运算,当此参数为0xfffff或者ULONG MAX的时候
  195. 就会将任务通知值清零。
  196. * pulNotification Value:此参数用来保存任务通知值。
  197. * xTick ToWait:阻塞时间。
  198. *
  199. * 返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。
  200. */
  201. //获取任务通知 ,没获取到则一直等待
  202. xReturn=xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
  203. ULONG_MAX, //退出函数的时候清除所有的bit
  204. #if USE_CHAR
  205. (uint32_t *)&r_char, //保存任务通知值
  206. #else
  207. &r_num, //保存任务通知值
  208. #endif
  209. portMAX_DELAY); //阻塞时间
  210. if( pdTRUE == xReturn )
  211. #if USE_CHAR
  212. printf("Receive2_Task 任务通知消息为 %s \n",r_char);
  213. #else
  214. printf("Receive2_Task 任务通知消息为 %d \n",r_num);
  215. #endif
  216. LED2_TOGGLE;
  217. }
  218. }
  219. /**********************************************************************
  220. * @ 函数名 : Send_Task
  221. * @ 功能说明: Send_Task任务主体
  222. * @ 参数 :
  223. * @ 返回值 : 无
  224. ********************************************************************/
  225. static void Send_Task(void* parameter)
  226. {
  227. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  228. #if USE_CHAR
  229. char test_str1[] = "this is a mail test 1";/* 邮箱消息test1 */
  230. char test_str2[] = "this is a mail test 2";/* 邮箱消息test2 */
  231. #else
  232. uint32_t send1 = 1;
  233. uint32_t send2 = 2;
  234. #endif
  235. while (1)
  236. {
  237. /* KEY1 被按下 */
  238. if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
  239. {
  240. /* 原型:BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
  241. uint32_t ulValue,
  242. eNotifyAction eAction );
  243. * eNoAction = 0,通知任务而不更新其通知值。
  244. * eSetBits, 设置任务通知值中的位。
  245. * eIncrement, 增加任务的通知值。
  246. * eSetvaluewithoverwrite,覆盖当前通知
  247. * eSetValueWithoutoverwrite 不覆盖当前通知
  248. *
  249. * pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,
  250. * 如果任务通知值没有更新成功就返回pdFAIL。
  251. * pdPASS: eAction 设置为其他选项的时候统一返回pdPASS。
  252. */
  253. xReturn = xTaskNotify( Receive1_Task_Handle, /*任务句柄*/
  254. #if USE_CHAR
  255. (uint32_t)&test_str1, /* 发送的数据,最大为4字节 */
  256. #else
  257. send1, /* 发送的数据,最大为4字节 */
  258. #endif
  259. eSetValueWithOverwrite );/*覆盖当前通知*/
  260. if( xReturn == pdPASS )
  261. printf("Receive1_Task_Handle 任务通知消息发送成功!\r\n");
  262. }
  263. /* KEY2 被按下 */
  264. if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
  265. {
  266. xReturn = xTaskNotify( Receive2_Task_Handle, /*任务句柄*/
  267. #if USE_CHAR
  268. (uint32_t)&test_str2, /* 发送的数据,最大为4字节 */
  269. #else
  270. send2, /* 发送的数据,最大为4字节 */
  271. #endif
  272. eSetValueWithOverwrite );/*覆盖当前通知*/
  273. /* 此函数只会返回pdPASS */
  274. if( xReturn == pdPASS )
  275. printf("Receive2_Task_Handle 任务通知消息发送成功!\r\n");
  276. }
  277. vTaskDelay(20);
  278. }
  279. }
  280. /***********************************************************************
  281. * @ 函数名 : BSP_Init
  282. * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  283. * @ 参数 :
  284. * @ 返回值 : 无
  285. *********************************************************************/
  286. static void BSP_Init(void)
  287. {
  288. /*
  289. * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
  290. * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
  291. * 都统一用这个优先级分组,千万不要再分组,切忌。
  292. */
  293. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
  294. /* LED 初始化 */
  295. LED_GPIO_Config();
  296. /* 串口初始化 */
  297. USART_Config();
  298. /* 按键初始化 */
  299. Key_GPIO_Config();
  300. }
  301. /********************************END OF FILE****************************/

22.5.2 任务通知代替二值信号量

任务通知代替消息队列是在 FreeRTOS 中创建了三个任务,其中两个任务是用于接收任务通知,另一个任务发送任务通知。三个任务独立运行,发送通知任务是通过检测按键的按下情况来发送通知,另两个任务获取通知,在任务通知中没有可用的通知之前就一直等待任务通知,获取到通知以后就将通知值清 0,这样子是为了代替二值信号量,任务同步成功则继续执行,然后在串口调试助手里将运行信息打印出来

  1. /**
  2. *********************************************************************
  3. * @file main.c
  4. * @author fire
  5. * @version V1.0
  6. * @date 2018-xx-xx
  7. * @brief FreeRTOS V9.0.0 + STM32 任务通知代替二值信号量
  8. *********************************************************************
  9. * @attention
  10. *
  11. * 实验平台:野火 STM32全系列开发板
  12. * 论坛 :http://www.firebbs.cn
  13. * 淘宝 :https://fire-stm32.taobao.com
  14. *
  15. **********************************************************************
  16. */
  17. /*
  18. *************************************************************************
  19. * 包含的头文件
  20. *************************************************************************
  21. */
  22. /* FreeRTOS头文件 */
  23. #include "FreeRTOS.h"
  24. #include "task.h"
  25. /* 开发板硬件bsp头文件 */
  26. #include "bsp_led.h"
  27. #include "bsp_usart.h"
  28. #include "bsp_key.h"
  29. /**************************** 任务句柄 ********************************/
  30. /*
  31. * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
  32. * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
  33. * 这个句柄可以为NULL。
  34. */
  35. static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
  36. static TaskHandle_t Receive1_Task_Handle = NULL;/* Receive1_Task任务句柄 */
  37. static TaskHandle_t Receive2_Task_Handle = NULL;/* Receive2_Task任务句柄 */
  38. static TaskHandle_t Send_Task_Handle = NULL;/* Send_Task任务句柄 */
  39. /********************************** 内核对象句柄 *********************************/
  40. /*
  41. * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
  42. * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
  43. * 们就可以通过这个句柄操作这些内核对象。
  44. *
  45. * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
  46. * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
  47. * 来完成的
  48. *
  49. */
  50. /******************************* 全局变量声明 ************************************/
  51. /*
  52. * 当我们在写应用程序的时候,可能需要用到一些全局变量。
  53. */
  54. /******************************* 宏定义 ************************************/
  55. /*
  56. * 当我们在写应用程序的时候,可能需要用到一些宏定义。
  57. */
  58. /*
  59. *************************************************************************
  60. * 函数声明
  61. *************************************************************************
  62. */
  63. static void AppTaskCreate(void);/* 用于创建任务 */
  64. static void Receive1_Task(void* pvParameters);/* Receive1_Task任务实现 */
  65. static void Receive2_Task(void* pvParameters);/* Receive2_Task任务实现 */
  66. static void Send_Task(void* pvParameters);/* Send_Task任务实现 */
  67. static void BSP_Init(void);/* 用于初始化板载相关资源 */
  68. /*****************************************************************
  69. * @brief 主函数
  70. * @param 无
  71. * @retval 无
  72. * @note 第一步:开发板硬件初始化
  73. 第二步:创建APP应用任务
  74. 第三步:启动FreeRTOS,开始多任务调度
  75. ****************************************************************/
  76. int main(void)
  77. {
  78. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  79. /* 开发板硬件初始化 */
  80. BSP_Init();
  81. printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替二值信号量实验!\n");
  82. printf("按下KEY1或者KEY2进行任务与任务间的同步\n");
  83. /* 创建AppTaskCreate任务 */
  84. xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
  85. (const char* )"AppTaskCreate",/* 任务名字 */
  86. (uint16_t )512, /* 任务栈大小 */
  87. (void* )NULL,/* 任务入口函数参数 */
  88. (UBaseType_t )1, /* 任务的优先级 */
  89. (TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
  90. /* 启动任务调度 */
  91. if(pdPASS == xReturn)
  92. vTaskStartScheduler(); /* 启动任务,开启调度 */
  93. else
  94. return -1;
  95. while(1); /* 正常不会执行到这里 */
  96. }
  97. /***********************************************************************
  98. * @ 函数名 : AppTaskCreate
  99. * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  100. * @ 参数 : 无
  101. * @ 返回值 : 无
  102. **********************************************************************/
  103. static void AppTaskCreate(void)
  104. {
  105. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  106. taskENTER_CRITICAL(); //进入临界区
  107. /* 创建Receive1_Task任务 */
  108. xReturn = xTaskCreate((TaskFunction_t )Receive1_Task, /* 任务入口函数 */
  109. (const char* )"Receive1_Task",/* 任务名字 */
  110. (uint16_t )512, /* 任务栈大小 */
  111. (void* )NULL, /* 任务入口函数参数 */
  112. (UBaseType_t )2, /* 任务的优先级 */
  113. (TaskHandle_t* )&Receive1_Task_Handle);/* 任务控制块指针 */
  114. if(pdPASS == xReturn)
  115. printf("创建Receive1_Task任务成功!\r\n");
  116. /* 创建Receive2_Task任务 */
  117. xReturn = xTaskCreate((TaskFunction_t )Receive2_Task, /* 任务入口函数 */
  118. (const char* )"Receive2_Task",/* 任务名字 */
  119. (uint16_t )512, /* 任务栈大小 */
  120. (void* )NULL, /* 任务入口函数参数 */
  121. (UBaseType_t )3, /* 任务的优先级 */
  122. (TaskHandle_t* )&Receive2_Task_Handle);/* 任务控制块指针 */
  123. if(pdPASS == xReturn)
  124. printf("创建Receive2_Task任务成功!\r\n");
  125. /* 创建Send_Task任务 */
  126. xReturn = xTaskCreate((TaskFunction_t )Send_Task, /* 任务入口函数 */
  127. (const char* )"Send_Task",/* 任务名字 */
  128. (uint16_t )512, /* 任务栈大小 */
  129. (void* )NULL,/* 任务入口函数参数 */
  130. (UBaseType_t )4, /* 任务的优先级 */
  131. (TaskHandle_t* )&Send_Task_Handle);/* 任务控制块指针 */
  132. if(pdPASS == xReturn)
  133. printf("创建Send_Task任务成功!\n\n");
  134. vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  135. taskEXIT_CRITICAL(); //退出临界区
  136. }
  137. /**********************************************************************
  138. * @ 函数名 : Receive_Task
  139. * @ 功能说明: Receive_Task任务主体
  140. * @ 参数 :
  141. * @ 返回值 : 无
  142. ********************************************************************/
  143. static void Receive1_Task(void* parameter)
  144. {
  145. while (1)
  146. {
  147. /* uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
  148. * xClearCountOnExit:pdTRUE 在退出函数的时候任务任务通知值清零,类似二值信号量
  149. * pdFALSE 在退出函数ulTaskNotifyTakeO的时候任务通知值减一,类似计数型信号量。
  150. */
  151. //获取任务通知 ,没获取到则一直等待
  152. ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
  153. printf("Receive1_Task 任务通知获取成功!\n\n");
  154. LED1_TOGGLE;
  155. }
  156. }
  157. /**********************************************************************
  158. * @ 函数名 : Receive_Task
  159. * @ 功能说明: Receive_Task任务主体
  160. * @ 参数 :
  161. * @ 返回值 : 无
  162. ********************************************************************/
  163. static void Receive2_Task(void* parameter)
  164. {
  165. while (1)
  166. {
  167. /* uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
  168. * xClearCountOnExit:pdTRUE 在退出函数的时候任务任务通知值清零,类似二值信号量
  169. * pdFALSE 在退出函数ulTaskNotifyTakeO的时候任务通知值减一,类似计数型信号量。
  170. */
  171. //获取任务通知 ,没获取到则一直等待
  172. ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
  173. printf("Receive2_Task 任务通知获取成功!\n\n");
  174. LED2_TOGGLE;
  175. }
  176. }
  177. /**********************************************************************
  178. * @ 函数名 : Send_Task
  179. * @ 功能说明: Send_Task任务主体
  180. * @ 参数 :
  181. * @ 返回值 : 无
  182. ********************************************************************/
  183. static void Send_Task(void* parameter)
  184. {
  185. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  186. while (1)
  187. {
  188. /* KEY1 被按下 */
  189. if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
  190. {
  191. /* 原型:BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); */
  192. xReturn = xTaskNotifyGive(Receive1_Task_Handle);
  193. /* 此函数只会返回pdPASS */
  194. if( xReturn == pdTRUE )
  195. printf("Receive1_Task_Handle 任务通知发送成功!\r\n");
  196. }
  197. /* KEY2 被按下 */
  198. if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
  199. {
  200. xReturn = xTaskNotifyGive(Receive2_Task_Handle);
  201. /* 此函数只会返回pdPASS */
  202. if( xReturn == pdPASS )
  203. printf("Receive2_Task_Handle 任务通知发送成功!\r\n");
  204. }
  205. vTaskDelay(20);
  206. }
  207. }
  208. /***********************************************************************
  209. * @ 函数名 : BSP_Init
  210. * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  211. * @ 参数 :
  212. * @ 返回值 : 无
  213. *********************************************************************/
  214. static void BSP_Init(void)
  215. {
  216. /*
  217. * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
  218. * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
  219. * 都统一用这个优先级分组,千万不要再分组,切忌。
  220. */
  221. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
  222. /* LED 初始化 */
  223. LED_GPIO_Config();
  224. /* 串口初始化 */
  225. USART_Config();
  226. /* 按键初始化 */
  227. Key_GPIO_Config();
  228. }
  229. /********************************END OF FILE****************************/

22.5.3 任务通知代替计数信号量

任务通知代替计数信号量是基于计数型信号量实验修改而来,模拟停车场工作运行。并且在 FreeRTOS 中创建了两个任务:一个是获取任务通知,一个是发送任务通知,两个任务独立运行,获取通知的任务是通过按下 KEY1 按键获取,模拟停车场停车操作,其等待时间是 0;发送通知的任务则是通过检测 KEY2 按键按下进行通知的发送,模拟停车场取车操作,并且在串口调试助手输出相应信息。

  1. /*
  2. *************************************************************************
  3. * 包含的头文件
  4. *************************************************************************
  5. */
  6. /* FreeRTOS头文件 */
  7. #include "FreeRTOS.h"
  8. #include "task.h"
  9. #include "queue.h"
  10. #include "semphr.h"
  11. /* 开发板硬件bsp头文件 */
  12. #include "bsp_led.h"
  13. #include "bsp_usart.h"
  14. #include "bsp_key.h"
  15. /**************************** 任务句柄 ********************************/
  16. /*
  17. * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
  18. * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
  19. * 这个句柄可以为NULL。
  20. */
  21. static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
  22. static TaskHandle_t Take_Task_Handle = NULL;/* Take_Task任务句柄 */
  23. static TaskHandle_t Give_Task_Handle = NULL;/* Give_Task任务句柄 */
  24. /********************************** 内核对象句柄 *********************************/
  25. /*
  26. * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
  27. * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
  28. * 们就可以通过这个句柄操作这些内核对象。
  29. *
  30. * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
  31. * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
  32. * 来完成的
  33. *
  34. */
  35. SemaphoreHandle_t CountSem_Handle =NULL;
  36. /******************************* 全局变量声明 ************************************/
  37. /*
  38. * 当我们在写应用程序的时候,可能需要用到一些全局变量。
  39. */
  40. /******************************* 宏定义 ************************************/
  41. /*
  42. * 当我们在写应用程序的时候,可能需要用到一些宏定义。
  43. */
  44. /*
  45. *************************************************************************
  46. * 函数声明
  47. *************************************************************************
  48. */
  49. static void AppTaskCreate(void);/* 用于创建任务 */
  50. static void Take_Task(void* pvParameters);/* Take_Task任务实现 */
  51. static void Give_Task(void* pvParameters);/* Give_Task任务实现 */
  52. static void BSP_Init(void);/* 用于初始化板载相关资源 */
  53. /*****************************************************************
  54. * @brief 主函数
  55. * @param 无
  56. * @retval 无
  57. * @note 第一步:开发板硬件初始化
  58. 第二步:创建APP应用任务
  59. 第三步:启动FreeRTOS,开始多任务调度
  60. ****************************************************************/
  61. int main(void)
  62. {
  63. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  64. /* 开发板硬件初始化 */
  65. BSP_Init();
  66. printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替计数信号量实验!\n");
  67. printf("车位默认值为0个,按下KEY1申请车位,按下KEY2释放车位!\n\n");
  68. /* 创建AppTaskCreate任务 */
  69. xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
  70. (const char* )"AppTaskCreate",/* 任务名字 */
  71. (uint16_t )512, /* 任务栈大小 */
  72. (void* )NULL,/* 任务入口函数参数 */
  73. (UBaseType_t )1, /* 任务的优先级 */
  74. (TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */
  75. /* 启动任务调度 */
  76. if(pdPASS == xReturn)
  77. vTaskStartScheduler(); /* 启动任务,开启调度 */
  78. else
  79. return -1;
  80. while(1); /* 正常不会执行到这里 */
  81. }
  82. /***********************************************************************
  83. * @ 函数名 : AppTaskCreate
  84. * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  85. * @ 参数 : 无
  86. * @ 返回值 : 无
  87. **********************************************************************/
  88. static void AppTaskCreate(void)
  89. {
  90. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  91. taskENTER_CRITICAL(); //进入临界区
  92. /* 创建Take_Task任务 */
  93. xReturn = xTaskCreate((TaskFunction_t )Take_Task, /* 任务入口函数 */
  94. (const char* )"Take_Task",/* 任务名字 */
  95. (uint16_t )512, /* 任务栈大小 */
  96. (void* )NULL, /* 任务入口函数参数 */
  97. (UBaseType_t )2, /* 任务的优先级 */
  98. (TaskHandle_t* )&Take_Task_Handle);/* 任务控制块指针 */
  99. if(pdPASS == xReturn)
  100. printf("创建Take_Task任务成功!\r\n");
  101. /* 创建Give_Task任务 */
  102. xReturn = xTaskCreate((TaskFunction_t )Give_Task, /* 任务入口函数 */
  103. (const char* )"Give_Task",/* 任务名字 */
  104. (uint16_t )512, /* 任务栈大小 */
  105. (void* )NULL,/* 任务入口函数参数 */
  106. (UBaseType_t )3, /* 任务的优先级 */
  107. (TaskHandle_t* )&Give_Task_Handle);/* 任务控制块指针 */
  108. if(pdPASS == xReturn)
  109. printf("创建Give_Task任务成功!\n\n");
  110. vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  111. taskEXIT_CRITICAL(); //退出临界区
  112. }
  113. /**********************************************************************
  114. * @ 函数名 : Take_Task
  115. * @ 功能说明: Take_Task任务主体
  116. * @ 参数 :
  117. * @ 返回值 : 无
  118. ********************************************************************/
  119. static void Take_Task(void* parameter)
  120. {
  121. uint32_t take_num = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  122. /* 任务都是一个无限循环,不能返回 */
  123. while (1)
  124. {
  125. //如果KEY1被单击
  126. if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
  127. {
  128. /* uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
  129. * xClearCountOnExit:pdTRUE 在退出函数的时候任务任务通知值清零,类似二值信号量
  130. * pdFALSE 在退出函数ulTaskNotifyTakeO的时候任务通知值减一,类似计数型信号量。
  131. */
  132. //获取任务通知 ,没获取到则不等待
  133. take_num=ulTaskNotifyTake(pdFALSE,0);//
  134. if(take_num > 0)
  135. printf( "KEY1被按下,成功申请到停车位,当前车位为 %d \n", take_num - 1);
  136. else
  137. printf( "KEY1被按下,车位已经没有了,请按KEY2释放车位\n" );
  138. }
  139. vTaskDelay(20); //每20ms扫描一次
  140. }
  141. }
  142. /**********************************************************************
  143. * @ 函数名 : Give_Task
  144. * @ 功能说明: Give_Task任务主体
  145. * @ 参数 :
  146. * @ 返回值 : 无
  147. ********************************************************************/
  148. static void Give_Task(void* parameter)
  149. {
  150. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  151. /* 任务都是一个无限循环,不能返回 */
  152. while (1)
  153. {
  154. //如果KEY2被单击
  155. if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
  156. {
  157. /* 原型:BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); */
  158. /* 释放一个任务通知 */
  159. xTaskNotifyGive(Take_Task_Handle);//发送任务通知
  160. /* 此函数只会返回pdPASS */
  161. if ( pdPASS == xReturn )
  162. printf( "KEY2被按下,释放1个停车位。\n" );
  163. }
  164. vTaskDelay(20); //每20ms扫描一次
  165. }
  166. }
  167. /***********************************************************************
  168. * @ 函数名 : BSP_Init
  169. * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  170. * @ 参数 :
  171. * @ 返回值 : 无
  172. *********************************************************************/
  173. static void BSP_Init(void)
  174. {
  175. /*
  176. * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
  177. * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
  178. * 都统一用这个优先级分组,千万不要再分组,切忌。
  179. */
  180. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
  181. /* LED 初始化 */
  182. LED_GPIO_Config();
  183. /* 串口初始化 */
  184. USART_Config();
  185. /* 按键初始化 */
  186. Key_GPIO_Config();
  187. }
  188. /********************************END OF FILE****************************/

22.4 任务通知代替事件组

任务通知代替事件组实验是在事件标志组实验基础上进行修改,实验任务通知替代事件实现事件类型的通信,该实验是在 FreeRTOS 中创建了两个任务,一个是发送事件通知任务,一个是等待事件通知任务,两个任务独立运行, 发送事件通知任务通过检测按键的按下情况设置不同的通知值位,等待事件通知任务则获取这任务通知值,并且根据通知值判断两个事件是否都发生,如果是则输出相应信息, LED 进行翻转。 等待事件通知任务的等待时间是 portMAX_DELAY,一直在等待事件通知的发生, 等待获取到事件之后清除对应的任务通知值的位,。

  1. /**
  2. *********************************************************************
  3. * @file main.c
  4. * @author fire
  5. * @version V1.0
  6. * @date 2018-xx-xx
  7. * @brief FreeRTOS V9.0.0 + STM32 任务通知代替事件组
  8. *********************************************************************
  9. * @attention
  10. *
  11. * 实验平台:野火 STM32全系列开发板
  12. * 论坛 :http://www.firebbs.cn
  13. * 淘宝 :https://fire-stm32.taobao.com
  14. *
  15. **********************************************************************
  16. */
  17. /*
  18. *************************************************************************
  19. * 包含的头文件
  20. *************************************************************************
  21. */
  22. /* FreeRTOS头文件 */
  23. #include "FreeRTOS.h"
  24. #include "task.h"
  25. #include "event_groups.h"
  26. /* 开发板硬件bsp头文件 */
  27. #include "bsp_led.h"
  28. #include "bsp_usart.h"
  29. #include "bsp_key.h"
  30. #include "limits.h"
  31. /**************************** 任务句柄 ********************************/
  32. /*
  33. * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
  34. * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
  35. * 这个句柄可以为NULL。
  36. */
  37. static TaskHandle_t AppTaskCreate_Handle = NULL;/* 创建任务句柄 */
  38. static TaskHandle_t LED_Task_Handle = NULL;/* LED_Task任务句柄 */
  39. static TaskHandle_t KEY_Task_Handle = NULL;/* KEY_Task任务句柄 */
  40. /********************************** 内核对象句柄 *********************************/
  41. /*
  42. * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
  43. * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
  44. * 们就可以通过这个句柄操作这些内核对象。
  45. *
  46. * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
  47. * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
  48. * 来完成的
  49. *
  50. */
  51. /******************************* 全局变量声明 ************************************/
  52. /*
  53. * 当我们在写应用程序的时候,可能需要用到一些全局变量。
  54. */
  55. /******************************* 宏定义 ************************************/
  56. /*
  57. * 当我们在写应用程序的时候,可能需要用到一些宏定义。
  58. */
  59. #define KEY1_EVENT (0x01 << 0)//设置事件掩码的位0
  60. #define KEY2_EVENT (0x01 << 1)//设置事件掩码的位1
  61. /*
  62. *************************************************************************
  63. * 函数声明
  64. *************************************************************************
  65. */
  66. static void AppTaskCreate(void);/* 用于创建任务 */
  67. static void LED_Task(void *pvParameters);/* LED_Task 任务实现 */
  68. static void KEY_Task(void *pvParameters);/* KEY_Task 任务实现 */
  69. static void BSP_Init(void);/* 用于初始化板载相关资源 */
  70. /*****************************************************************
  71. * @brief 主函数
  72. * @param 无
  73. * @retval 无
  74. * @note 第一步:开发板硬件初始化
  75. 第二步:创建APP应用任务
  76. 第三步:启动FreeRTOS,开始多任务调度
  77. ****************************************************************/
  78. int main(void)
  79. {
  80. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  81. /* 开发板硬件初始化 */
  82. BSP_Init();
  83. printf("这是一个[野火]-STM32全系列开发板-FreeRTOS任务通知代替事件组实验!\n");
  84. printf("按下KEY1|KEY2发送任务事件通知!\n");
  85. /* 创建AppTaskCreate任务 */
  86. xReturn = xTaskCreate((TaskFunction_t)AppTaskCreate, /* 任务入口函数 */
  87. (const char *)"AppTaskCreate", /* 任务名字 */
  88. (uint16_t)512, /* 任务栈大小 */
  89. (void *)NULL, /* 任务入口函数参数 */
  90. (UBaseType_t)1, /* 任务的优先级 */
  91. (TaskHandle_t *)&AppTaskCreate_Handle); /* 任务控制块指针 */
  92. /* 启动任务调度 */
  93. if (pdPASS == xReturn)
  94. vTaskStartScheduler(); /* 启动任务,开启调度 */
  95. else
  96. return -1;
  97. while (1); /* 正常不会执行到这里 */
  98. }
  99. /***********************************************************************
  100. * @ 函数名 : AppTaskCreate
  101. * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
  102. * @ 参数 : 无
  103. * @ 返回值 : 无
  104. **********************************************************************/
  105. static void AppTaskCreate(void)
  106. {
  107. BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
  108. taskENTER_CRITICAL(); //进入临界区
  109. /* 创建LED_Task任务 */
  110. xReturn = xTaskCreate((TaskFunction_t)LED_Task, /* 任务入口函数 */
  111. (const char *)"LED_Task", /* 任务名字 */
  112. (uint16_t)512, /* 任务栈大小 */
  113. (void *)NULL, /* 任务入口函数参数 */
  114. (UBaseType_t)2, /* 任务的优先级 */
  115. (TaskHandle_t *)&LED_Task_Handle); /* 任务控制块指针 */
  116. if (pdPASS == xReturn)
  117. printf("创建LED_Task任务成功!\r\n");
  118. /* 创建KEY_Task任务 */
  119. xReturn = xTaskCreate((TaskFunction_t)KEY_Task, /* 任务入口函数 */
  120. (const char *)"KEY_Task", /* 任务名字 */
  121. (uint16_t)512, /* 任务栈大小 */
  122. (void *)NULL, /* 任务入口函数参数 */
  123. (UBaseType_t)3, /* 任务的优先级 */
  124. (TaskHandle_t *)&KEY_Task_Handle); /* 任务控制块指针 */
  125. if (pdPASS == xReturn)
  126. printf("创建KEY_Task任务成功!\n");
  127. vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  128. taskEXIT_CRITICAL(); //退出临界区
  129. }
  130. /**********************************************************************
  131. * @ 函数名 : LED_Task
  132. * @ 功能说明: LED_Task任务主体
  133. * @ 参数 :
  134. * @ 返回值 : 无
  135. ********************************************************************/
  136. static void LED_Task(void *parameter)
  137. {
  138. uint32_t r_event = 0; /* 定义一个事件接收变量 */
  139. uint32_t last_event = 0;/* 定义一个保存事件的变量 */
  140. BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  141. /* 任务都是一个无限循环,不能返回 */
  142. while (1)
  143. {
  144. /* BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
  145. uint32_t ulBitsToClearOnExit,
  146. uint32_t *pulNotificationValue,
  147. TickType_t xTicksToWait );
  148. * ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取
  149. 反值进行按位与运算,当此参数为Oxfffff或者ULONG_MAX的时候就会将任务通知值清零。
  150. * ulBits ToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将
  151. 任务通知值与此参数的取反值进行按位与运算,当此参数为0xfffff或者ULONG MAX的时候
  152. 就会将任务通知值清零。
  153. * pulNotification Value:此参数用来保存任务通知值。
  154. * xTick ToWait:阻塞时间。
  155. *
  156. * 返回值:pdTRUE:获取到了任务通知。pdFALSE:任务通知获取失败。
  157. */
  158. //获取任务通知 ,没获取到则一直等待
  159. xReturn = xTaskNotifyWait(0x0, //进入函数的时候不清除任务bit
  160. ULONG_MAX, //退出函数的时候清除所有的bitR
  161. &r_event, //保存任务通知值
  162. portMAX_DELAY); //阻塞时间
  163. if (pdTRUE == xReturn)
  164. {
  165. last_event |= r_event;
  166. /* 如果接收完成并且正确 */
  167. if (last_event == (KEY1_EVENT | KEY2_EVENT))
  168. {
  169. last_event = 0; /* 上一次的事件清零 */
  170. printf("Key1与Key2都按下\n");
  171. LED1_TOGGLE; //LED1 反转
  172. }
  173. else /* 否则就更新事件 */
  174. last_event = r_event; /* 更新上一次触发的事件 */
  175. }
  176. }
  177. }
  178. /**********************************************************************
  179. * @ 函数名 : KEY_Task
  180. * @ 功能说明: KEY_Task任务主体
  181. * @ 参数 :
  182. * @ 返回值 : 无
  183. ********************************************************************/
  184. static void KEY_Task(void *parameter)
  185. {
  186. /* 任务都是一个无限循环,不能返回 */
  187. while (1)
  188. {
  189. if (Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON)
  190. {
  191. printf("KEY1被按下\n");
  192. /* 原型:BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
  193. uint32_t ulValue,
  194. eNotifyAction eAction );
  195. * eNoAction = 0,通知任务而不更新其通知值。
  196. * eSetBits, 设置任务通知值中的位。
  197. * eIncrement, 增加任务的通知值。
  198. * eSetvaluewithoverwrite,覆盖当前通知
  199. * eSetValueWithoutoverwrite 不覆盖当前通知
  200. *
  201. * pdFAIL:当参数eAction设置为eSetValueWithoutOverwrite的时候,
  202. * 如果任务通知值没有更新成功就返回pdFAIL。
  203. * pdPASS: eAction 设置为其他选项的时候统一返回pdPASS。
  204. */
  205. /* 触发一个事件1 */
  206. xTaskNotify((TaskHandle_t)LED_Task_Handle, //接收任务通知的任务句柄
  207. (uint32_t)KEY1_EVENT, //要触发的事件
  208. (eNotifyAction)eSetBits); //设置任务通知值中的位
  209. }
  210. if (Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN) == KEY_ON)
  211. {
  212. printf("KEY2被按下\n");
  213. /* 触发一个事件2 */
  214. xTaskNotify((TaskHandle_t)LED_Task_Handle, //接收任务通知的任务句柄
  215. (uint32_t)KEY2_EVENT, //要触发的事件
  216. (eNotifyAction)eSetBits); //设置任务通知值中的位
  217. }
  218. vTaskDelay(20); //每20ms扫描一次
  219. }
  220. }
  221. /***********************************************************************
  222. * @ 函数名 : BSP_Init
  223. * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  224. * @ 参数 :
  225. * @ 返回值 : 无
  226. *********************************************************************/
  227. static void BSP_Init(void)
  228. {
  229. /*
  230. * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
  231. * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
  232. * 都统一用这个优先级分组,千万不要再分组,切忌。
  233. */
  234. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  235. /* LED 初始化 */
  236. LED_GPIO_Config();
  237. /* 串口初始化 */
  238. USART_Config();
  239. /* 按键初始化 */
  240. Key_GPIO_Config();
  241. }
  242. /********************************END OF FILE****************************/