18.7.1 二值信号量同步实验

信号量同步实验是在 FreeRTOS 中创建了两个任务,一个是获取信号量任务,一个是释放互斥量任务,两个任务独立运行,获取信号量任务是一直在等待信号量,其等待时间是 portMAX_DELAY,等到获取到信号量之后, 任务开始执行任务代码,如此反复等待另外任务释放的信号。
释放信号量任务在检测按键是否按下,如果按下则释放信号量,此时释放信号量会唤醒获取任务,获取任务开始运行,然后形成两个任务间的同步,因为如果没按下按键,那么信号量就不会释放,只有当信号量释放的时候,获取信号量的任务才会被唤醒,如此一来就达到任务与任务的同步, 同时程序的运行会在串口打印出相关信息。

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

18.7.2 计数信号量实验

  1. 计数型信号量实验是模拟停车场工作运行。在创建信号量的时候初始化 5 个可用的信号量,并且创建了两个任务:一个是获取信号量任务,一个是释放信号量任务,两个任务独立运行,获取信号量任务是通过按下 KEY1 按键进行信号量的获取,模拟停车场停车操作,其等待时间是 0,在串口调试助手输出相应信息 <br /> 释放信号量任务则是信号量的释放,释放信号量任务也是通过按下 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("车位默认值为5个,按下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. /* 创建Test_Queue */
  93. CountSem_Handle = xSemaphoreCreateCounting(5,5);
  94. if(NULL != CountSem_Handle)
  95. printf("CountSem_Handle计数信号量创建成功!\r\n");
  96. /* 创建Take_Task任务 */
  97. xReturn = xTaskCreate((TaskFunction_t )Take_Task, /* 任务入口函数 */
  98. (const char* )"Take_Task",/* 任务名字 */
  99. (uint16_t )512, /* 任务栈大小 */
  100. (void* )NULL, /* 任务入口函数参数 */
  101. (UBaseType_t )2, /* 任务的优先级 */
  102. (TaskHandle_t* )&Take_Task_Handle);/* 任务控制块指针 */
  103. if(pdPASS == xReturn)
  104. printf("创建Take_Task任务成功!\r\n");
  105. /* 创建Give_Task任务 */
  106. xReturn = xTaskCreate((TaskFunction_t )Give_Task, /* 任务入口函数 */
  107. (const char* )"Give_Task",/* 任务名字 */
  108. (uint16_t )512, /* 任务栈大小 */
  109. (void* )NULL,/* 任务入口函数参数 */
  110. (UBaseType_t )3, /* 任务的优先级 */
  111. (TaskHandle_t* )&Give_Task_Handle);/* 任务控制块指针 */
  112. if(pdPASS == xReturn)
  113. printf("创建Give_Task任务成功!\n\n");
  114. vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务
  115. taskEXIT_CRITICAL(); //退出临界区
  116. }
  117. /**********************************************************************
  118. * @ 函数名 : Take_Task
  119. * @ 功能说明: Take_Task任务主体
  120. * @ 参数 :
  121. * @ 返回值 : 无
  122. ********************************************************************/
  123. static void Take_Task(void* parameter)
  124. {
  125. BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  126. /* 任务都是一个无限循环,不能返回 */
  127. while (1)
  128. {
  129. //如果KEY1被单击
  130. if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON )
  131. {
  132. /* 获取一个计数信号量 */
  133. xReturn = xSemaphoreTake(CountSem_Handle, /* 计数信号量句柄 */
  134. 0); /* 等待时间:0 */
  135. if ( pdTRUE == xReturn )
  136. printf( "KEY1被按下,成功申请到停车位。\n" );
  137. else
  138. printf( "KEY1被按下,不好意思,现在停车场已满!\n" );
  139. }
  140. vTaskDelay(20); //每20ms扫描一次
  141. }
  142. }
  143. /**********************************************************************
  144. * @ 函数名 : Give_Task
  145. * @ 功能说明: Give_Task任务主体
  146. * @ 参数 :
  147. * @ 返回值 : 无
  148. ********************************************************************/
  149. static void Give_Task(void* parameter)
  150. {
  151. BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为pdPASS */
  152. /* 任务都是一个无限循环,不能返回 */
  153. while (1)
  154. {
  155. //如果KEY2被单击
  156. if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON )
  157. {
  158. /* 获取一个计数信号量 */
  159. xReturn = xSemaphoreGive(CountSem_Handle);//给出计数信号量
  160. if ( pdTRUE == xReturn )
  161. printf( "KEY2被按下,释放1个停车位。\n" );
  162. else
  163. printf( "KEY2被按下,但已无车位可以释放!\n" );
  164. }
  165. vTaskDelay(20); //每20ms扫描一次
  166. }
  167. }
  168. /***********************************************************************
  169. * @ 函数名 : BSP_Init
  170. * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
  171. * @ 参数 :
  172. * @ 返回值 : 无
  173. *********************************************************************/
  174. static void BSP_Init(void)
  175. {
  176. /*
  177. * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
  178. * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
  179. * 都统一用这个优先级分组,千万不要再分组,切忌。
  180. */
  181. NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
  182. /* LED 初始化 */
  183. LED_GPIO_Config();
  184. /* 按键初始化 */
  185. Key_GPIO_Config();
  186. /* 串口初始化 */
  187. USART_Config();
  188. }
  189. /********************************END OF FILE****************************/