浅尝 FreeRTOS

目录

RTOS通识

什么是RTOS

 如何下载:以下官网下载链接

nullhttps://freertos.org/zh-cn-cmn-s/

FreeRTOS 实现多任务的原理

移植 FreeRTOS 到上官二号平台 

​编辑 freertos选项卡​编辑

 任务的创建与删除

任务创建与删除相关函数  

任务动态创建与静态创建的区别:

 任务调度

FreeRTOS的任务调度规则是怎样的?

任务的状态 

创建四个线程(任务)---- 代码段及API

消息队列

 写队列​编辑

读队列 

消息队列发送接收 ---- 代码段

 信号量

二值信号量 ---- 代码段

 计数型信号量 ---- 相比二值信号量可以存放更多,更多锁

互斥量 

 使用Mutex互斥量替换二值信号量

 事件标志组

任务通知

任务通知 ---- 信号量

 任务通知 ---- 代码段二值信号量

任务通知 ---- 代码段计数型信号量

 任务通知 ---- 代码段事件组标志位组​编辑

 软件定时器

软件定时器相关配置

 相关API​编辑

 中断管理


RTOS通识

什么是RTOS

Free 即免费的, RTOS 的全称是 Real time operating system ,中文就是实时操作系统。
注意: RTOS不是指某一个确定的系统,而是指一类操作系统 。比如: uc/OS FreeRTOS RTX , RT-Thread等这些都是 RTOS 类操作系统。
FreeRTOS 是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管 理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满 足较小系统的需要。 由于RTOS 需占用一定的系统资源 ( 尤其是 RAM 资源 ) ,只有 μC/OS-II 、 embOS、 salvo FreeRTOS 等少数实时操作系统能在小 RAM 单片机上运行。相 μC/OS-II 、 embOS等商业操作系统, FreeRTOS 操作系统是完全免费的操作系统,具有源码公开、可移 植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行,其最新版本为

 如何下载:以下官网下载链接

nullicon-default.png?t=N7T8https://freertos.org/zh-cn-cmn-s/

FreeRTOS 实现多任务的原理

严格来说 FreeRTOS 并不是实时操作系统,因为它是分时复用的。 系统将时间分割成很多时间片,然后轮流执行各个任务。 每个任务都是独立运行的,互不影响,由于切换的频率很快,就感觉像是同时运行的一样。

移植 FreeRTOS 到上官二号平台 

手动移植
过程复杂且繁琐,对新手不友好。如有需要手动移植,可参照以下文章:

 使用CubeMX快速移植

 

 

 freertos选项卡

 任务的创建与删除

任务可以理解为进程 / 线程,创建一个任务,就会在内存开辟一个空间。
比如:
玩游戏、陪女朋友,都可以视为任务
Windows 系统中的 MarkText 、谷歌浏览器、记事本,都是任务。
任务通常都含有 while(1) 死循环。

任务创建与删除相关函数  

任务动态创建与静态创建的区别:

动态创建任务的堆栈由系统分配,而静态创建任务的堆栈由用户自己传递。
通常情况下使用动态方式创建任务。

 

1. pvTaskCode :指向任务函数的指针,任务必须实现为永不返回(即连续循环);
2. pcName :任务的名字,主要是用来调试,默认情况下最大长度是 16
3. pvParameters :指定的任务栈的参数;
4. uxPriority :任务优先级, 数值越大,优先级越大 5. pxCreatedTask :用于返回已创建任务的句柄可以被引用。
5. usStackDepth 指定任务栈的大小
6.pxCreatedTask 任务句柄

 

 


vTaskDelete 函数原型 void vTaskDelete ( TaskHandle_t xTaskToDelete );
只需将待删除的任务句柄传入该函数,即可将该任务删除。
当传入的参数为 NULL ,则代表删除任务自身(当前正在运行的任务)。

 任务调度

调度器就是使用相关的调度算法来决定当前需要执行的哪个任务。
FreeRTOS 中开启任务调度的函数是 vTaskStartScheduler() ,但在 CubeMX 中被封装为
osKernelStart()

FreeRTOS的任务调度规则是怎样的?

FreeRTOS 是一个实时操作系统,它所奉行的调度规则:
1. 高优先级抢占低优先级任务,系统永远执行最高优先级的任务(即抢占式调度
2. 同等优先级的任务轮转调度(即时间片调度
还有一种调度规则是协程式调度,但官方已明确表示不更新,主要是用在小容量的芯片上,用得
也不多。

 抢占式调度

总结:
1. 高优先级任务,优先执行;
2. 高优先级任务不停止,低优先级任务无法执行;
3. 被抢占的任务将会进入就绪态
时分时调度
总结:
1. 同等优先级任务,轮流执行,时间片流转;
2. 一个时间片大小,取决为滴答定时器中断周期;
3. 注意没有用完的时间片不会再使用,下次任务 Task3 得到执行,还是按照一个时间片的时钟
节拍运行

任务的状态 

FreeRTOS 中任务共存在 4 种状态:
Running 运行态
当任务处于实际运行状态称之为运行态,即 CPU 的使用权被这个任务占用(同一时间仅一个任务 处于运行态)。
Ready 就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同 优先级或更高优先级的任务正在运行。
Blocked 阻塞态
如果一个任务因延时,或等待信号量、消息队列、事件标志组等而处于的状态被称之为阻塞态。
Suspended 挂起态
类似暂停,通过调用函数 vTaskSuspend() 对指定任务进行挂起,挂起后这个任务将不被执行, 只有调用函数 xTaskResume() 才可以将这个任务从挂起态恢复。
总结:
1. 仅就绪态可转变成运行态
2. 其他状态的任务想运行,必须先转变成就绪态

创建四个线程(任务)---- 代码段及API

相关函数API

xTaskCreate() 动态方式创建任务
xTaskCreateStatic() 静态方式创建任务
vTaskDelete() 删除任务
/填充这个线程属性参数:线程名字,函数名字--起始地址,优先级、线程最大实例函数、堆栈大小
 osThreadDef(myTaskLED1,StartTaskLED1,osPriorityNormal,0,128);//osThreadCreate类似xTaskCreate 创建线程,传入参数为NULL,  最后返回线程ID(指针指向的ID)
 myTaskLED1Handle = osThreadCreate(osThread(myTaskLED1),NULL);
osThreadTerminate(myTaskLED1Handle);    //根据线程指针 销毁任务一
myTaskLED1Handle = NULL;                //线程一指针指向NULL 不加指向会段错误
osThreadSuspend(myTaskLED2Handle);            //osThreadSuspend把线程指针的ID传入把该线程挂起
osThreadResume(myTaskLED2Handle);            //再次按下时恢复线程2工作
  1. 包含了FreeRTOS相关的头文件,以及stdio.h库。
  2. 定义了四个任务句柄,分别是myTaskKey2Handle、myTaskLED1Handle、myTaskLED2Handle和myTaskKey1Handle,分别对应四个不同的任务。
  3. 在MX_FREERTOS_Init函数中,定义并创建了这四个任务。每个任务都对应一个回调函数,如StartTaskLED1、StartTaskLED2等,这些函数包含了各自的任务执行逻辑。
  4. 这四个任务均具有相同的优先级,并分配了128字节的栈空间。然后调用osThreadCreate函数创建并启动这四个任务。这些任务会按照优先级和抢占式的调度方式进行执行。
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId myTaskKey2Handle;//指向线程的指针osThreadId   指向myTaskKey2Handle
osThreadId myTaskLED1Handle;
osThreadId myTaskLED2Handle;
osThreadId myTaskKey1Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskLED1(void const * argument);
void StartTaskLED2(void const * argument);
void StartTaskKey1(void const * argument);
void StartTaskKey2(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of myTaskKey2 */
  //填入线程参数、用osThreadCreate创建这个线程(类似xTaskCreate),并返回这个线程的ID
  osThreadDef(myTaskKey2, StartTaskKey2, osPriorityNormal, 0, 128);
  myTaskKey2Handle = osThreadCreate(osThread(myTaskKey2), NULL);

  /* definition and creation of myTaskLED1 */
  osThreadDef(myTaskLED1, StartTaskLED1, osPriorityNormal, 0, 128);
  myTaskLED1Handle = osThreadCreate(osThread(myTaskLED1), NULL);

  /* definition and creation of myTaskLED2 */
  osThreadDef(myTaskLED2, StartTaskLED2, osPriorityNormal, 0, 128);
  myTaskLED2Handle = osThreadCreate(osThread(myTaskLED2), NULL);

  /* definition and creation of myTaskKey1 */
  osThreadDef(myTaskKey1, StartTaskKey1, osPriorityNormal, 0, 128);
  myTaskKey1Handle = osThreadCreate(osThread(myTaskKey1), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskKey2 */
/**
  * @brief  Function implementing the myTaskKey2 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskKey2 */
void StartTaskKey2(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey2 */
  /* Infinite loop */
	uint8_t Flog = 0;
	
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
	{
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);//如果按键还是按下状态则循环等待,松开跳出循环
		printf("KEY2 ON\r\n");
		if(Flog == 0){									//第一次按键按下把任务二挂起
			osThreadSuspend(myTaskLED2Handle);			//osThreadSuspend把线程指针的ID传入把该线程挂起
			printf("任务二停止挂起\r\n");
			Flog = 1;									//标志位为1
		}else{
			osThreadResume(myTaskLED2Handle);			//再次按下时恢复线程2工作
			printf("任务二恢复工作");
			Flog = 0;
		}
		
	}
  }
  /* USER CODE END StartTaskKey2 */
}

/* USER CODE BEGIN Header_StartTaskLED1 */
/**
* @brief Function implementing the myTaskLED1 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskLED1 */
void StartTaskLED1(void const * argument)
{
  /* USER CODE BEGIN StartTaskLED1 */
  /* Infinite loop */
  for(;;)
  {
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
    osDelay(500);
  }
  /* USER CODE END StartTaskLED1 */
}

/* USER CODE BEGIN Header_StartTaskLED2 */
/**
* @brief Function implementing the myTaskLED2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskLED2 */
void StartTaskLED2(void const * argument)
{
  /* USER CODE BEGIN StartTaskLED2 */
  /* Infinite loop */
  for(;;)
  {
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
    osDelay(1000);
  }
  /* USER CODE END StartTaskLED2 */
}

/* USER CODE BEGIN Header_StartTaskKey1 */
/**
* @brief Function implementing the myTaskKey1 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKey1 */
void StartTaskKey1(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey1 */
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
	{
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);//如果按键还是按下状态则循环等待,松开跳出循环
		printf("KEY1 ON\r\n");
		if(myTaskLED1Handle == NULL){				//如果任务一的指针 指向为NULL
			printf("任务一不存在,开始创建任务一\r\n");
			//填充这个线程属性参数:线程名字,函数名字--起始地址,优先级、线程最大实例函数、堆栈大小
			osThreadDef(myTaskLED1,StartTaskLED1,osPriorityNormal,0,128);
			//osThreadCreate类似xTaskCreate 创建线程,传入参数为NULL,  最后返回线程ID(指针指向的ID)
			myTaskLED1Handle = osThreadCreate(osThread(myTaskLED1),NULL);
		}else{
			printf("任务一存在,开始销毁任务一\r\n");
			osThreadTerminate(myTaskLED1Handle);	//根据线程指针 销毁任务一
			myTaskLED1Handle = NULL;				//线程一指针指向NULL 不加指向会段错误
		}
		
	}

  }
  /* USER CODE END StartTaskKey1 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

消息队列

队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任
务间传递信息。
队列项目 :队列中的每一个数据;
队列长度 :队列能够存储队列项目的最大数量;
创建队列时,需要指定队列长度及队列项目大小
队列特点
1. 数据入队出队方式
通常采用 先进先出 FIFO )的数据存储缓冲机制,即先入队的数据会先从队列中被读取。
也可以配置为后进先出( LIFO )方式,但用得比较少。
2. 数据传递方式
采用实际值传递,即将数据拷贝到队列中进行传递,也可以传递指针,在传递较大的数据的时候
采用指针传递。
3. 多任务访问
队列不属于某个任务,任何任务和中断都可以向队列发送 / 读取消息
4. 出队、入队阻塞 当任务向一个队列发送消息时,可以指定一个阻塞时间,假设此时当队列已满无法入队。
阻塞时间如果设置为:
0 :直接返回不会等待;
0~port_MAX_DELAY :等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不
再等待;
port_MAX_DELAY :死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;

 

 写队列

BaseType_t xQueueSend(
  QueueHandle_t xQueue, // 队列句柄,指定要发送数据的队列
  const void * pvItemToQueue, // 指向要发送的数据项的指针
  TickType_t xTicksToWait // 等待时间,以滴答为单位
);
注意返回值 用这个类型来接收 pdTURE返回值为真 pdFALSE返回失败
返回值:
如果成功写入数据,返回 pdTRUE ,否则返回 errQUEUE_FULL

读队列 

 

BaseType_t xQueueReceive (
QueueHandle_t xQueue ,// 队列句柄,指定要接收数据的队列
void * pvBuffer , / 指向要接收的数据项的指针
TickType_t xTicksToWait//等待时间,以滴答为单位
);

 

返回值:
成功返回 pdTRUE ,否则返回 pdFALSE

消息队列发送接收 ---- 代码段

 QueueStatus = xQueueSend(myQueue01Handle,&SendData,0);         //发送这个队列,参数:句柄、内容地址、等待时间

   BaseType_t     QueueStatus = xQueueReceive(myQueue01Handle,&ReceiveData,0);//接收这个消息队列 存在放ReceiveData中 等待  返回值为这个类型

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId TaskKey1Handle;
osThreadId TaskKey2Handle;
osMessageQId myQueue01Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskKey1(void const * argument);
void StartTaskKey2(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */
  /* definition and creation of myQueue01 */
  osMessageQDef(myQueue01, 16, uint16_t);
  myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskKey1 */
  osThreadDef(TaskKey1, StartTaskKey1, osPriorityNormal, 0, 128);
  TaskKey1Handle = osThreadCreate(osThread(TaskKey1), NULL);

  /* definition and creation of TaskKey2 */
  osThreadDef(TaskKey2, StartTaskKey2, osPriorityNormal, 0, 128);
  TaskKey2Handle = osThreadCreate(osThread(TaskKey2), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskKey1 */
/**
  * @brief  Function implementing the TaskKey1 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskKey1 */
void StartTaskKey1(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey1 */
  /* Infinite loop */
  uint16_t SendData = 100;
  BaseType_t QueueStatus;//队列状态
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);//延时消抖
		printf("KEY1 ON!\r\n");
		//相当于替换这个宏函数BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
		QueueStatus = xQueueSend(myQueue01Handle,&SendData,0);		 //发送这个队列,参数:句柄、内容地址、等待时间
		if(QueueStatus == pdTRUE){									//返回状态pdPASS发送成功 pdLALSE失败
			printf("队列发送成功!,%d\r\n",SendData);
		}else{
			printf("队列发送失败!\r\n");		
		}		
	}

  }
  /* USER CODE END StartTaskKey1 */
}
	
/* USER CODE BEGIN Header_StartTaskKey2 */
/**
* @brief Function implementing the TaskKey2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKey2 */
void StartTaskKey2(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey2 */
  /* Infinite loop */
  uint16_t ReceiveData;
  BaseType_t QueueStatus;//队列状态
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);//延时消抖
		printf("KEY2 ON!\r\n");
		QueueStatus = xQueueReceive(myQueue01Handle,&ReceiveData,0);//接收这个消息队列 存在放ReceiveData中 等待0
		if(QueueStatus == pdTRUE){
			printf("队列收到消息,%d\r\n",ReceiveData);
		}else{
			printf("队列收到失败!\r\n");
		}		
	}

  }
  /* USER CODE END StartTaskKey2 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

也可以通过消息队列发送结构体

void StartTaskKey1(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey1 */
  /* Infinite loop */
	struct demo{
		int a;
		char b;
		char* str;
	};
		struct demo SendData = {5,'A',"HELLO"};
//  uint16_t SendData = 100;
  BaseType_t QueueStatus;//队列状态
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);//延时消抖
		printf("KEY1 ON!\r\n");
		//相当于替换这个宏函数BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
		QueueStatus = xQueueSend(myQueue01Handle,&SendData,0);		 //发送这个队列,参数:句柄、内容地址、等待时间
		if(QueueStatus == pdTRUE){									//返回状态pdPASS发送成功 pdLALSE失败
			printf("队列发送成功!%d,%c,%s\r\n",SendData.a,SendData.b,SendData.str);
		}else{
			printf("队列发送失败!\r\n");		
		}		
	}

  }
  /* USER CODE END StartTaskKey1 */
}
	
/* USER CODE BEGIN Header_StartTaskKey2 */
/**
* @brief Function implementing the TaskKey2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKey2 */
void StartTaskKey2(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey2 */
  /* Infinite loop */
	struct demo{
		int a;
		char b;
		char* str;
	};
	struct demo ReceiveData = {5,'A',"HELLO"};
//  uint16_t ReceiveData;
  BaseType_t QueueStatus;//队列状态
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);//延时消抖
		printf("KEY2 ON!\r\n");
		QueueStatus = xQueueReceive(myQueue01Handle,&ReceiveData,0);//接收这个消息队列 存在放ReceiveData中 等待0
		if(QueueStatus == pdTRUE){
			printf("队列收到消息,%d,%c,%s\r\n",ReceiveData.a,ReceiveData.b,ReceiveData.str);
		}else{
			printf("队列收到失败!\r\n");
		}		
	}

  }
  /* USER CODE END StartTaskKey2 */
}

 信号量

 

什么是信号量?
信号量( Semaphore ),是在多任务环境下使用的一种机制,是可以用来保证两个或多个关键代码段不被并发调用。
信号量这个名字,我们可以把它拆分来看,信号可以起到通知信号的作用,然后我们的量还可以 用来表示资源的数量,当我们的量只有0 1 的时候,它就可以被称作二值信号量,只有两个状 态,当我们的那个量没有限制的时候,它就可以被称作为计数型信号量。 信号量也是队列的一种。
什么是二值信号量?
二值信号量其实就是一个长度为1,大小为零的队列,只有01 两种状态,通常情况下,我们用 它来进行互斥访问或任务同步。 互斥访问:比如门钥匙,只有获取到钥匙才可以开门
任务同步:

 

1. 创建二值信号量
    BinarySem01Handle = xSemaphoreCreateBinary();//创建二值信号量    
参数: 值: 成功,返回对应二值信号量的句柄; 失败,返回 NULL
2. 释放二值信号量
BaseType_t xSemaphoreGive ( SemaphoreHandle_t xSemaphore )
参数: xSemaphore:要释放的信号量句柄
返回值: 成功,返回 pdPASS ; 失败,返回 errQUEUE_FULL
3. 获取二值信号量
BaseType_t xSemaphoreTake ( SemaphoreHandle_t xSemaphore ,
TickType_t xTicksToWait );
参数: xSemaphore:要获取的信号量句柄 xTicksToWait:超时时间, 0 表示不超时, portMAX_DELAY 表示卡死等待;
返回值: 成功pdTURE,返回 pdPASS

二值信号量 ---- 代码段

 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
//以下私人函数  typedef void * osThreadId 这样的指针指向这个函数
/* USER CODE END Variables */
osThreadId TaskKey1GiveHandle;
osThreadId TaskKey2TakeHandle;
osSemaphoreId BinarySem01Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
// 以下私人函数声明
/* USER CODE END FunctionPrototypes */

void StartTaskKey1Give(void const * argument);
void StartTaskKey2Take(void const * argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
//以上这是一个静态函数,用于获取空闲任务的内存信息。它接受三个参数
//xIdleTaskTCBBuffer是一个StaticTask_t类型的变量,用于存储空闲任务的TCB缓冲区这是一个StackType_t类型的数组,用于存储空闲任务的栈缓冲区。
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

//过调用这个函数,可以获取到空闲任务的相关信息,例如任务的堆栈地址和大小等
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
//RTOS初始化
  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of BinarySem01 */
	
//  osSemaphoreDef(BinarySem01);
//  BinarySem01Handle = osSemaphoreCreate(osSemaphore(BinarySem01), 1);

  /* USER CODE BEGIN RTOS_SEMAPHORES */
	BinarySem01Handle = xSemaphoreCreateBinary();//创建二值信号量	
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskKey1Give */
  osThreadDef(TaskKey1Give, StartTaskKey1Give, osPriorityNormal, 0, 128);//句柄属性填充:线程名字、函数名地址、优先级、几个任务,大小
  TaskKey1GiveHandle = osThreadCreate(osThread(TaskKey1Give), NULL);

  /* definition and creation of TaskKey2Take */
  osThreadDef(TaskKey2Take, StartTaskKey2Take, osPriorityNormal, 0, 128);
  TaskKey2TakeHandle = osThreadCreate(osThread(TaskKey2Take), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskKey1Give */
/**
  * @brief  Function implementing the TaskKey1Give thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskKey1Give */
void StartTaskKey1Give(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey1Give */
  /* Infinite loop */
	BaseType_t SemaphoreStart;
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
		printf("KEY1 ON!\r\n");
		//osSemaphoreCreate
		SemaphoreStart = xSemaphoreGive(BinarySem01Handle);//释放这个锁并返回状态
		if(SemaphoreStart == pdTRUE){
			printf("Semaphore 释放锁成功!\r\n");
		}else{
			printf("Semaphore 释放锁失败!\r\n");			
		}
	}
  }
  /* USER CODE END StartTaskKey1Give */
}

/* USER CODE BEGIN Header_StartTaskKey2Take */
/**
* @brief Function implementing the TaskKey2Take thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKey2Take */
void StartTaskKey2Take(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey2Take */
  /* Infinite loop */
	BaseType_t SemaphoreStart;
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		printf("KEY2 ON!\r\n");
		//释放这个信号量的锁并返回状态 0表示不超时 portMAX_DELAY表示死等不往后执行
//		SemaphoreStart = xSemaphoreTake(BinarySem01Handle,0);
		SemaphoreStart = xSemaphoreTake(BinarySem01Handle,portMAX_DELAY);
		if(SemaphoreStart == pdTRUE){
			printf("Semaphore 拿锁成功!\r\n");
		}else{
			printf("Semaphore 拿锁失败!\r\n");			
		}
	}
  }
  /* USER CODE END StartTaskKey2Take */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

 计数型信号量 ---- 相比二值信号量可以存放更多,更多锁

 

什么是计数型信号量?
计数型信号量相当于队列长度大于 1 的队列,因此计数型信号量能够容纳多个资源,这在计数型
信号量被创建的时候确定的。
参数:
uxMaxCount :可以达到的最大计数值 uxInitialCount :创建信号量时分配给信号量的计数值
返回值:
成功,返回对应计数型信号量的句柄;
失败,返回 NULL

​​​​​​

 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
//以下私人函数  typedef void * osThreadId 这样的指针指向这个函数
/* USER CODE END Variables */
osThreadId TaskKey1GiveHandle;
osThreadId TaskKey2TakeHandle;
osSemaphoreId myCountingSemHandle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
// 以下私人函数声明
/* USER CODE END FunctionPrototypes */

void StartTaskKey1Give(void const * argument);
void StartTaskKey2Take(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
//以上这是一个静态函数,用于获取空闲任务的内存信息。它接受三个参数
//xIdleTaskTCBBuffer是一个StaticTask_t类型的变量,用于存储空闲任务的TCB缓冲区这是一个StackType_t类型的数组,用于存储空闲任务的栈缓冲区。
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

//过调用这个函数,可以获取到空闲任务的相关信息,例如任务的堆栈地址和大小等
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
//RTOS初始化
  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of myCountingSem */
//  osSemaphoreDef(myCountingSem);
//  myCountingSemHandle = osSemaphoreCreate(osSemaphore(myCountingSem), 5);
	myCountingSemHandle = xSemaphoreCreateCounting(5,0);//创建计数值信号量 参数:最大个数已经配置,初始值多少
	
  /* USER CODE BEGIN RTOS_SEMAPHORES */

  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskKey1Give */
  osThreadDef(TaskKey1Give, StartTaskKey1Give, osPriorityNormal, 0, 128);
  TaskKey1GiveHandle = osThreadCreate(osThread(TaskKey1Give), NULL);

  /* definition and creation of TaskKey2Take */
  osThreadDef(TaskKey2Take, StartTaskKey2Take, osPriorityNormal, 0, 128);
  TaskKey2TakeHandle = osThreadCreate(osThread(TaskKey2Take), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskKey1Give */
/**
  * @brief  Function implementing the TaskKey1Give thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskKey1Give */
void StartTaskKey1Give(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey1Give */
  /* Infinite loop */
	BaseType_t SemaphoreStart;
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
		printf("KEY1 ON!\r\n");
		//osSemaphoreCreate
		SemaphoreStart = xSemaphoreGive(myCountingSemHandle);//释放这个锁并返回状态
		if(SemaphoreStart == pdTRUE){
			printf("SemaphoreCount 释放计数信号量成功!\r\n");
		}else{
			printf("SemaphoreCount 释放计数信号量失败!\r\n");			
		}
	}
  }
  /* USER CODE END StartTaskKey1Give */
}

/* USER CODE BEGIN Header_StartTaskKey2Take */
/**
* @brief Function implementing the TaskKey2Take thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKey2Take */
void StartTaskKey2Take(void const * argument)
{
  /* USER CODE BEGIN StartTaskKey2Take */
  /* Infinite loop */
	BaseType_t SemaphoreStart;
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		printf("KEY2 ON!\r\n");
		//释放这个信号量的锁并返回状态 0表示不超时 portMAX_DELAY表示死等不往后执行
//		SemaphoreStart = xSemaphoreTake(BinarySem01Handle,0);//二值信号量
		SemaphoreStart = xSemaphoreTake(myCountingSemHandle,0);//计数型信号量
		if(SemaphoreStart == pdTRUE){
			printf("SemaphoreCount 计数信号量拿锁成功!\r\n");
		}else{
			printf("SemaphoreCount 计数信号量拿锁失败!\r\n");			
		}
	}
  }
  /* USER CODE END StartTaskKey2Take */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

互斥量 

 

  1. 互斥量(Mutex):

    • 互斥量是二进制信号量的一个变种。
    • 主要用于保护共享资源,防止多个任务同时访问同一个资源。
    • 任意时刻互斥量的状态只有两种,开锁或闭锁。
    • 开启互斥量需要在头文件FreeRTOSConfig.h中设置configUSE_MUTEXES为1。
    • 互斥量支持互斥量所有权、递归访问以及防止优先级翻转的特性。
  2. 信号量:

    • 信号量分为四种:二值信号量、互斥信号量、计数信号量和递归互斥信号量。
    • 二值信号量是一种特殊的信号量,用于实现任务之间的同步和通信。
    • 计数信号量用于管理系统多个共享资源,用计数值表示可用资源数目。
    • 互斥信号量与二值信号量类似,但不支持计数和具有优先级继承的功能。
    • 递归互斥信号量允许任务多次请求同一资源,而不会导致死锁。

当两个任务使用同一个锁(互斥量)时,他们实际上是在竞争CPU资源。互斥量是一种同步机制,用于保护共享资源,防止多个任务同时访问该资源,从而避免数据竞争和不一致性。

当一个任务需要访问共享资源时,它会尝试获取互斥量。如果互斥量的标志位为0,表示该资源未被锁定,该任务就可以获取互斥量,并将标志位设置为1,表示该资源已被锁定。如果互斥量的标志位为1,表示该资源已被锁定,该任务就会被阻塞,加入等待队列中,直到互斥量被解锁。

关于互斥量如何解决优先级倒置的问题,FreeRTOS为互斥量赋予了优先级继承的特性。这样,即使一个高优先级的任务等待低优先级任务释放互斥量,它也会被阻塞,以防止优先级翻转。

 

 且三个任务、线程之间争相抢夺cpu资源

互斥量也是信号量的一种

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId TaskHHandle;
osThreadId TaskMHandle;
osThreadId TaskLHandle;
osSemaphoreId myBinarySemHandle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTaskH(void const * argument);
void StartTaskM(void const * argument);
void StartTaskL(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* Create the semaphores(s) */
  /* definition and creation of myBinarySem */
  osSemaphoreDef(myBinarySem);
	//通过dudemx创建二值信号量之后里面就有锁, 自己的函数创建里面则无锁
  myBinarySemHandle = osSemaphoreCreate(osSemaphore(myBinarySem), 1);

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskH */
  osThreadDef(TaskH, StartTaskH, osPriorityAboveNormal, 0, 128);
  TaskHHandle = osThreadCreate(osThread(TaskH), NULL);

  /* definition and creation of TaskM */
  osThreadDef(TaskM, StartTaskM, osPriorityNormal, 0, 128);
  TaskMHandle = osThreadCreate(osThread(TaskM), NULL);

  /* definition and creation of TaskL */
  osThreadDef(TaskL, StartTaskL, osPriorityBelowNormal, 0, 128);
  TaskLHandle = osThreadCreate(osThread(TaskL), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskH */
/**
  * @brief  Function implementing the TaskH thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskH */
void StartTaskH(void const * argument)
{
  /* USER CODE BEGIN StartTaskH */
  /* Infinite loop */
  for(;;)
  {
	  xSemaphoreTake(myBinarySemHandle,portMAX_DELAY);//拿锁,阻塞等待,并提升优先级,不让高优先级拿锁进屋 。系统创建的二值信号量里面有锁,
	  printf("TaskH:成功拿锁,期间TaskL进不来。\r\n");
	  HAL_Delay(3000);//拿锁后延时2s
	  printf("TaskH:成功还锁,TaskL准备进来。\r\n");	  
	  xSemaphoreGive(myBinarySemHandle);//放回锁 让高优先级TaskH进来
	  osDelay(1000);
  }
  /* USER CODE END StartTaskH */
}

/* USER CODE BEGIN Header_StartTaskM */
/**
* @brief Function implementing the TaskM thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskM */
void StartTaskM(void const * argument)
{
  /* USER CODE BEGIN StartTaskM */
  /* Infinite loop */
  for(;;)
  {
	printf("TaskM:你们争吧,我不进去。。。\r\n");
    osDelay(1000);
  }
  /* USER CODE END StartTaskM */
}

/* USER CODE BEGIN Header_StartTaskL */
/**
* @brief Function implementing the TaskL thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskL */
void StartTaskL(void const * argument)//优先级最低 cudemx里面配置的
{
  /* USER CODE BEGIN StartTaskL */
  /* Infinite loop */
  for(;;)
  {
	  xSemaphoreTake(myBinarySemHandle,portMAX_DELAY);//拿锁,阻塞等待,并提升优先级,不让高优先级拿锁进屋 。系统创建的二值信号量里面有锁,
	  printf("TaskL:成功拿锁,期间TaskH进不来。\r\n");
	  HAL_Delay(3000);//拿锁后延时5s
	  printf("TaskL:成功还锁,TaskH准备进来。\r\n");	  
	  xSemaphoreGive(myBinarySemHandle);//放回锁 让高优先级TaskH进来
	  osDelay(1000);
  }
  /* USER CODE END StartTaskL */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

 使用Mutex互斥量替换二值信号量

void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */
  /* Create the mutex(es) */
  /* definition and creation of myMutex */
  osMutexDef(myMutex);//系统创建的Mutex互斥量锁
  myMutexHandle = osMutexCreate(osMutex(myMutex));

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskH */
  osThreadDef(TaskH, StartTaskH, osPriorityAboveNormal, 0, 128);
  TaskHHandle = osThreadCreate(osThread(TaskH), NULL);

  /* definition and creation of TaskM */
  osThreadDef(TaskM, StartTaskM, osPriorityNormal, 0, 128);
  TaskMHandle = osThreadCreate(osThread(TaskM), NULL);

  /* definition and creation of TaskL */
  osThreadDef(TaskL, StartTaskL, osPriorityBelowNormal, 0, 128);
  TaskLHandle = osThreadCreate(osThread(TaskL), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskH */
/**
  * @brief  Function implementing the TaskH thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskH */
void StartTaskH(void const * argument)
{
  /* USER CODE BEGIN StartTaskH */
  /* Infinite loop */
  for(;;)
  {
	  xSemaphoreTake(myMutexHandle,portMAX_DELAY);//拿锁,阻塞等待,并提升优先级,不让高优先级拿锁进屋 。系统创建的二值信号量里面有锁,
	  printf("TaskH:成功拿锁,期间TaskL进不来。\r\n");
	  HAL_Delay(3000);//拿锁后延时2s
	  printf("TaskH:成功还锁,TaskL准备进来。\r\n");	  
	  xSemaphoreGive(myMutexHandle);//放回锁 让高优先级TaskH进来
	  osDelay(1000);
  }
  /* USER CODE END StartTaskH */
}

/* USER CODE BEGIN Header_StartTaskM */
/**
* @brief Function implementing the TaskM thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskM */
void StartTaskM(void const * argument)
{
  /* USER CODE BEGIN StartTaskM */
  /* Infinite loop */
  for(;;)
  {
	printf("TaskM:你们争吧,我不进去。。。\r\n");
    osDelay(1000);
  }
  /* USER CODE END StartTaskM */
}

/* USER CODE BEGIN Header_StartTaskL */
/**
* @brief Function implementing the TaskL thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskL */
void StartTaskL(void const * argument)
{
  /* USER CODE BEGIN StartTaskL */
  /* Infinite loop */
  for(;;)
  {
	  xSemaphoreTake(myMutexHandle,portMAX_DELAY);//拿锁,阻塞等待,并提升优先级,不让高优先级拿锁进屋 。系统创建的二值信号量里面有锁,
	  printf("TaskL:成功拿锁,期间TaskH进不来。\r\n");
	  HAL_Delay(3000);//拿锁后延时5s
	  printf("TaskL:成功还锁,TaskH准备进来。\r\n");	  
	  xSemaphoreGive(myMutexHandle);//放回锁 让高优先级TaskH进来
	  osDelay(1000);
  }
  /* USER CODE END StartTaskL */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

 事件标志组

 

什么是事件标志组?
事件标志位 :表明某个事件是否发生,联想:全局变量 flag 。通常按位表示,每一个位表示一个
事件(高 8 位不算)
事件标志组 是一组事件标志位的集合, 可以简单的理解事件标志组,就是一个整数。
事件标志组本质是一个 16 位或 32 位无符号的数据类型 EventBits_t ,由 configUSE_16_BIT_TICKS
决定。
虽然使用了 32 位无符号的数据类型变量来存储事件标志, 但其中的高 8 位用作存储事件标志组的
控制信息,低 24 位用作存储事件标志 ,所以说一个事件组最多可以存储 24 个事件标志!

 

1.创建事件标志组
EventGroupHandle_t xEventGroupCreate ( void );
参数: 返回值: 成功,返回对应事件标志组的句柄;失败,返回 NULL
2. 设置事件标志位
EventBits_t xEventGroupSetBits ( EventGroupHandle_t xEventGroup ,
const EventBits_t uxBitsToSet );
参数: xEventGroup:对应事件组句柄。 uxBitsToSet :指定要在事件组中设置的一个或多个位的按位 值。
返回值: 设置之后事件组中的事件标志位值。
3. 清除事件标志位
EventBits_t xEventGroupClearBits ( EventGroupHandle_t xEventGroup ,
const EventBits_t uxBitsToClear );
参数: xEventGroup:对应事件组句柄。 uxBitsToClear :指定要在事件组中清除的一个或多个位的按位 值。
返回值: 清零之前事件组中事件标志位的值。
4. 等待事件标志位
EventBits_t xEventGroupWaitBits (
const EventGroupHandle_t xEventGroup ,
const EventBits_t uxBitsToWaitFor ,
const BaseType_t xClearOnExit ,
const BaseType_t xWaitForAllBits ,
TickType_t xTicksToWait );
参数: xEventGroup :对应的事件标志组句柄 uxBitsToWaitFor :指定事件组中要等待的一个或多个事件 位的按位值 xClearOnExit pdTRUE—— 清除对应事件位, pdFALSE—— 不清除 xWaitForAllBits : pdTRUE——所有等待事件位全为 1 (逻辑与), pdFALSE—— 等待的事件位有一个为 1 (逻辑或) xTicksToWait:超时
返回值: 等待的事件标志位值:等待事件标志位成功,返回等待到的事件标志位 其他值:等待事件标志位 失败,返回事件组中的事件标志位

 xEventGroupWaitBits():等待标志位函数
xEventGroup:事件组句柄,用于标识要等待的事件组。
uxBitsToWaitFor:标志位的值 最低位和次低位可以表示为0x0001 | 0x0002 = 0x0003
xClearOnExit:是否清除指定标志位,清除pdTURE 不清除pdFALSE
xWairForAllBit:标志位一二都发生 pdTURE 只要一个就触发则按位或pdFALSE
xTicksToWait:最大等待时间一般 portMAX_DELAY,

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
EventGroupHandle_t EventHandle;//创建这样一个指针 
/* USER CODE END Variables */
osThreadId Task02Handle;
osThreadId Task01Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartTask02(void const * argument);
void StartTask01(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of Task02 */
  osThreadDef(Task02, StartTask02, osPriorityNormal, 0, 128);
  Task02Handle = osThreadCreate(osThread(Task02), NULL);

  /* definition and creation of Task01 */
  osThreadDef(Task01, StartTask01, osPriorityNormal, 0, 128);
  Task01Handle = osThreadCreate(osThread(Task01), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  EventHandle = xEventGroupCreate();//创建这样一个事件 指针指向这个事件
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTask02 */
/**
  * @brief  Function implementing the Task02 thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */

	//两个按下时 及之后都是0x0003;
  for(;;)
  { 
	  //检测按键1按下,至标志位最低位为1 0x0001;
	  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		  osDelay(20);
		  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			xEventGroupSetBits(EventHandle,0x01);	//设置事件组标志位		  
		  }
		  while( HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	  }	  
	  //按键2 按下,至标志位0x0002;但是
	  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		  osDelay(20);
		  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){ 
			xEventGroupSetBits(EventHandle,0x02);	//设置事件组标志位			  
		  }
		  while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
	  }
      osDelay(2);
	   
  }
  /* USER CODE END StartTask02 */
}

/* USER CODE BEGIN Header_StartTask01 */
/**
* @brief Function implementing the Task01 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask01 */
void StartTask01(void const * argument)
{
  /* USER CODE BEGIN StartTask01 */
  /* Infinite loop */
/*
xEventGroupWaitBits():等待标志位函数
	
xEventGroup:事件组句柄,用于标识要等待的事件组。
uxBitsToWaitFor:标志位的值 最低位和次低位可以表示为0x0001 | 0x0002 = 0x0003
xClearOnExit:是否清除指定标志位,清除pdTURE 不清除pdFALSE 如果不清楚标志位则一直成立 会不断打印输出
xWairForAllBit:标志位一二都发生时 pdTURE    只要一个就触发则按位或pdFALSE
xTicksToWait:最大等待时间一般 portMAX_DELAY,
*/
  EventBits_t EventReturnStatus = 0;
  for(;;)
  {
	  EventReturnStatus = xEventGroupWaitBits(EventHandle,0x01|0x02,pdTRUE,pdTRUE,portMAX_DELAY);//这个事件,其中标志位或上,每次清除标志位,两个标志位都发是,等待
	  printf("EventReturnStatus = %#x\r\n",EventReturnStatus);
      osDelay(1);
  }

  /* USER CODE END StartTask01 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

任务通知

什么是任务通知?及优势、作用。
每个任 务都有一个 32 位的通知值 。按照 FreeRTOS 官方的说法,使用消息通知比 通过二进制信号量方式解除阻塞任务快 45% , 并且 更加 省内存(无需创建队列 )。
在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的 RAM 更少!
任务通知值的更新方式
FreeRTOS 提供以下几种方式发送通知给任务 :
发送消息给任务,如果有通知未读, 不覆盖通知值
发送消息给任务,直接覆盖通知值
发送消息给任务,设置通知值的一个或者多个位
发送消息给任务,递增通知值
通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。
任务通知的优势和劣势
任务通知的优势
1. 使用任务通知向任务发送事件或数据,比使用队列、事件标志组或信号量快得多。
2. 使用其他方法时都要先创建对应的结构体,使用任务通知时无需额外创建结构体。
任务通知的劣势
1. 只有任务可以等待通知,中断服务函数中不可以,因为中断没有 TCB
2. 通知只能一对一,因为通知必须指定任务。
3. 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待。
4. 任务通知是通过更新任务通知值来发送数据的,任务结构体中只有一个任务通知值,只能保 持一个数据。任务通知相关 API 函数

 

 

 

xTaskToNotify返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify ,
uint32_t ulValue ,
eNotifyAction eAction ,
uint32_t * pulPreviousNotifyValue );
参数:
xTaskToNotify :需要接收通知的任务句柄; ulValue :用于更新接收任务通知值, 具体如何更新 由形参 eAction 决定; eAction :一个枚举,代表如何使用任务通知的值;
pulPreviousNotifyValue :对象任务的上一个任务通知值,如果为 NULL , 则不需要回传, 这个时 候就等价于函数 xTaskNotify()
返回值:
如果被通知任务还没取走上一个通知,又接收了一个通知,则这次通知值未能更新并返回
pdFALSE , 而其他情况均返回 pdPASS
BaseType_t xTaskNotifyGive ( TaskHandle_t xTaskToNotify ); 参数:
xTaskToNotify :接收通知的任务句柄, 并让其自身的任务通知值加 1
返回值:
总是返回 pdPASS
2. 等待通知
等待通知 API 函数只能用在任务,不可应用于中断中!

任务通知 ---- 信号量

BaseType_t xTaskNotifyGive ( TaskHandle_t xTaskToNotify ); 参数:
xTaskToNotify :接收通知的任务句柄, 并让其自身的任务通知值加 1
返回值:
总是返回 pdPASS

 

 任务通知 ---- 代码段二值信号量

发送端不会阻塞 接收端会阻塞

 

//通过两个任务通知传递 信号量 。发送端不会阻塞,接收端会阻塞
void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			printf("KEY1:任务通知 发送二值信号量\r\n");
			xTaskNotifyGive(TaskReceiveHandle);//任务通知 发送信号量--发送到接收任务 发送端且不会阻塞,但是接收端会阻塞
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	}
    osDelay(1);
  }
  /* USER CODE END StartTaskSend */
}

/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t Status = 0;
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){

			Status = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);//任务通知 接收信号量 参数:TURE二值信号量| FALSE计数型信号量、超时时间
			if(Status != 0){
				printf("KEY2:任务通知 接收二值信号量 %d\r\n",Status);				
			}
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	}
    osDelay(1);
  }
  /* USER CODE END StartTaskReceive */
}

任务通知 ---- 代码段计数型信号量

void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			printf("KEY1:任务通知 发送二值信号量\r\n");
			xTaskNotifyGive(TaskReceiveHandle);//任务通知 发送信号量--发送到接收任务 发送端且不会阻塞,但是接收端会阻塞
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	}
    osDelay(1);
  }
  /* USER CODE END StartTaskSend */
}

/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t Status = 0;
  /* Infinite loop */
  for(;;)
  {
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){

			Status = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);//任务通知 接收信号量 参数:TURE二值信号量| FALSE计数型信号量、超时时间
			if(Status != 0){
				printf("KEY2:任务通知 接收计数型信号量 %d\r\n",Status);	//会阻塞读取 发送任务发送便可立即读取			
			}
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	}
    osDelay(1);
  }
  /* USER CODE END StartTaskReceive */
}

 任务通知 ---- 代码段事件组标志位组

 

 

 

            xTaskNotify(TaskReceiveHandle,0x0001,eSetBits);//向哪个任务发送,发送什么,发送方式

* @brief xTaskNotifyWait根据用户指定的参数的不同,"可以灵活的用于实现轻量级的消息队列队列、三值信号量、讦数信号量和事件组功能,
* @param 当事件发生时,对应的事件位将被设置为1;当事件未发生时,对应的事件位保持为0
* @param 0xFFFFFFFF(ULONG_MAX),那么在退出函数前接收到的任务通知值的所有位都会被清 0
* @param 用于保存接收到的任务通知值。
* @param 等待超时时间,单位为系统节拍周期。
* @retval 成功返回pdTRUE 失败返回pdFALSE 

void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
	//按键KEY1按下 任务通知向接收任务发送一个事件组eSetBits
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			
			printf("KEY1:发送0x0001\r\n");
			xTaskNotify(TaskReceiveHandle,0x0001,eSetBits);//向哪个任务发送,发送什么,发送方式
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	}
	//按键KEY2按下 任务通知向接收任务发送一个事件组eSetBits	
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			
			printf("KEY2:发送0x0002\r\n");
			xTaskNotify(TaskReceiveHandle,0x0002,eSetBits);
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
	}
    osDelay(1);
  }
  /* USER CODE END StartTaskSend */
}

/* USER CODE BEGIN Header_StartTaskReceive */
/**
* @brief Function implementing the TaskReceive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskReceive */
void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t RcvData = 0,RcvBit = 0;
/*
	
参数1:当事件发生时,对应的事件位将被设置为1;当事件未发生时,对应的事件位保持为0
参数1:	
	*/
/**
* @brief xTaskNotifyWait根据用户指定的参数的不同,"可以灵活的用于实现轻量级的消息队列队列、三值信号量、讦数信号量和事件组功能,
* @param 当事件发生时,对应的事件位将被设置为1;当事件未发生时,对应的事件位保持为0
* @param 0xFFFFFFFF(ULONG_MAX),那么在退出函数前接收到的任务通知值的所有位都会被清 0
* @param 用于保存接收到的任务通知值。
* @param 等待超时时间,单位为系统节拍周期。
* @retval 成功返回pdTRUE 失败返回pdFALSE
*/
  /* Infinite loop */
  for(;;)
  {
	if(xTaskNotifyWait(0,0xFFFFFFFF,&RcvBit,portMAX_DELAY) == pdTRUE){
		printf("成功收到\r\n");
	}
	
	if(RcvBit & 0x0001){
		RcvData |= 0x0001;
	}
	if(RcvBit & 0x0002){
		RcvData |= 0x0002;
	}	
	if(RcvData == 0x0003){
		printf("bit2 RcvData = %x\r\n",RcvData);
		printf("任务通知模拟事件标志组接收成功!\r\n");		
		RcvData = 0;
	}
	

    osDelay(1);
  }
  /* USER CODE END StartTaskReceive */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

 

 任务通知 ---- 代码段消息队列

void StartTaskSend(void const * argument)
{
  /* USER CODE BEGIN StartTaskSend */
  /* Infinite loop */
  for(;;)
  {
	//按键KEY1按下 任务通知向接收任务发送一个事件组eSetBits
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			
			printf("KEY1:按下\r\n");
			xTaskNotify(TaskReceiveHandle,1,eSetValueWithOverwrite);//向哪个任务发送,发送什么,发送方式(消息队列覆盖写入)
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
	}
	//按键KEY2按下 任务通知向接收任务发送一个事件组eSetBits	
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
		osDelay(20);
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			
			printf("KEY2:按下\r\n");
			xTaskNotify(TaskReceiveHandle,2,eSetValueWithOverwrite);
		}
		while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
	}
    osDelay(1);
  }
  /* USER CODE END StartTaskSend */
}


void StartTaskReceive(void const * argument)
{
  /* USER CODE BEGIN StartTaskReceive */
	uint32_t RcvData;
/*
	
参数1:当事件发生时,对应的事件位将被设置为1;当事件未发生时,对应的事件位保持为0
参数1:	
	*/
/**
* @brief xTaskNotifyWait根据用户指定的参数的不同,"可以灵活的用于实现轻量级的消息队列队列、三值信号量、讦数信号量和事件组功能,
* @param 当事件发生时,对应的事件位将被设置为1;当事件未发生时,对应的事件位保持为0
* @param 0xFFFFFFFF(ULONG_MAX),那么在退出函数前接收到的任务通知值的所有位都会被清 0
* @param 用于保存接收到的任务通知值。
* @param 等待超时时间,单位为系统节拍周期。
* @retval 成功返回pdTRUE 失败返回pdFALSE
*/
  /* Infinite loop */
  for(;;)
  {
	if(xTaskNotifyWait(0,0xFFFFFFFF,&RcvData,portMAX_DELAY) == pdTRUE){
		printf("成功收到 RcvData = %d\r\n",RcvData);
	}
	

    osDelay(1);
  }
  /* USER CODE END StartTaskReceive */
}

 软件定时器

什么是定时器?
简单可以理解为闹钟,到达指定一段时间后,就会响铃。
STM32 芯片自带硬件定时器,精度较高,达到定时时间后会触发中断,也可以生成 PWM 、输入捕获、输出 比较,等等,功能强大,但是由于硬件的限制,个数有限。
软件定时器也可以实现定时功能,达到定时时间后可调用回调函数,可以在回调函数里处理信息。
软件定时器优缺点
优点: 1. 简单、成本低; 2. 只要内存足够,可创建多个;
缺点: 精度较低,容易受中断影响。在大多数情况下够用,但对于精度要求比较高的场合不建议使用。
软件定时器原理
定时器是一个可选的、不属于 FreeRTOS 内核的功能,它是由定时器服务任务来提供的。
在调用函数 vTaskStartScheduler() 开启任务调度器的时候,会创建一个用于管理软件定时器的任务 ,这个
任务就叫做软件定时器服务任务。
1. 负责 软件定时器超时的逻辑判断
2. 调用超时软件定时器的超时回调函数
3. 处理软件定时器命令队列
FreeRTOS 提供了很多定时器有关的 API 函数,这些 API 函数大多都使用 FreeRTOS 的队列发送命令给定时器服 务任务。这个队列叫做定时器命令队列 。定时器命令队列是提供给 FreeRTOS 的软件定时器使用的, 用户不 能直接访问

 

 

软件定时器相关配置

软件定时器有一个定时器服务任务和定时器命令队列,这两个东西肯定是要配置的,相关的配置也是放到文 件FreeRTOSConfig.h 中的,涉及到的配置如下:
1、 configUSE_TIMERS
如果要使用软件定时器的话宏 configUSE_TIMERS一定要设置为1 ,当设置为 1 的话定时器服务任务就会在启 动FreeRTOS 调度器的时候自动创建。
2 configTIMER_TASK_PRIORITY
设置软件定时器服务 任务的任务优先级 ,可以为 0~(configMAX_PRIORITIES-1) 。优先级一定要根据实际的应 用要求来设置。如果定时器服务任务的优先级设置的高的话,定时器命令队列中的命令和定时器回调函数就 会及时的得到处理。
3 configTIMER_QUEUE_LENGTH
此宏用来设置定时器命令队列的队列长度。
4 configTIMER_TASK_STACK_DEPTH
此宏用来设置定时器服务任务的任务堆栈大小。
单次定时器和周期定时器
单次定时器: 只超时一次,调用一次回调函数。可手动再开启定时器;
周期定时器: 多次超时,多次调用回调函数。

 相关API

 

 

 

 工程创建

 

 

//在任务中开启两个定时器,并设置定时时间,必须是时基整数倍
//cubeMX配置好了单次定时器和周期定时器,
//在回调函数中实现功能,不易时间过长

void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* Create the timer(s) */
  /* definition and creation of myTimerPeriodicity */
	//xTimerCreate 创建定时器函数,参数:定时器周期(1s)、类型(周期)、ID	
  osTimerDef(myTimerPeriodicity, CallbackTimerPeriodicity);
  myTimerPeriodicityHandle = osTimerCreate(osTimer(myTimerPeriodicity), osTimerPeriodic, NULL);

  /* definition and creation of myTimerOnce */
	//xTimerCreate 创建定时器函数,参数:定时器周期(1s)、类型(单次)、ID	
  osTimerDef(myTimerOnce, CallbackTimerOnce);
  myTimerOnceHandle = osTimerCreate(osTimer(myTimerOnce), osTimerOnce, NULL);

    //是用于更改 X 窗口系统中的定时器的回调函数 参数:哪个定时器、定时周期、等待时间
//	xTimerChangePeriod(myTimerOnceHandle,pdMS_TO_TICKS(1000),0);
	
  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of TaskTimer */
  osThreadDef(TaskTimer, StartTaskTimer, osPriorityNormal, 0, 128);
  TaskTimerHandle = osThreadCreate(osThread(TaskTimer), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartTaskTimer */
/**
  * @brief  Function implementing the TaskTimer thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartTaskTimer */
void StartTaskTimer(void const * argument)
{
  /* USER CODE BEGIN StartTaskTimer */
	//osTimerStart 底层调用xTimerStart
	osTimerStart(myTimerPeriodicityHandle,1000);//在这个任务中启动这个定时器,等待时间2s	
	osTimerStart(myTimerOnceHandle,2000);//在这个任务中启动这个定时器,等待时间1s
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartTaskTimer */
}

/* CallbackTimerPeriodicity function */
void CallbackTimerPeriodicity(void const * argument)
{
  /* USER CODE BEGIN CallbackTimerPeriodicity */
	printf("周期模式\r\n");
  /* USER CODE END CallbackTimerPeriodicity */
}

/* CallbackTimerOnce function */
void CallbackTimerOnce(void const * argument)
{
  /* USER CODE BEGIN CallbackTimerOnce */
	printf("单次模式\r\n");
  /* USER CODE END CallbackTimerOnce */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

 中断管理

 

 重写回调函数 

 

在it.c中通过extern传过来队列指针#include "cmsis_os.h"和extern osMessageQId myQueue01Handle;
按键按下时通过中断函数 中队列发送数据

在rtos任务中通过消息队列来接收数据


#include "cmsis_os.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern osMessageQId myQueue01Handle;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern TIM_HandleTypeDef htim2;

/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M3 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f1xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles EXTI line0 interrupt.
  */
void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */

  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}

/**
  * @brief This function handles TIM2 global interrupt.
  */
void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */

  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	uint8_t QueueSendData = 0x06;
	//按键按下产生按键中断,通过extern传过来队列地址,往这个队列地址中写入数据,
	xQueueSendFromISR(myQueue01Handle,&QueueSendData,NULL);
	
}
/* USER CODE END 1 */
//rtos
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId defaultTaskHandle;
osMessageQId myQueue01Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */
  /* definition and creation of myQueue01 */
  osMessageQDef(myQueue01, 16, uint16_t);
  myQueue01Handle = osMessageCreate(osMessageQ(myQueue01), NULL);

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void const * argument)
{
  /* USER CODE BEGIN StartDefaultTask */
	uint8_t Receive;
  /* Infinite loop */
  for(;;)
  {
	  //如果在该任务中接收到这个队列传过来的数据则打印
	  if(xQueueReceiveFromISR(myQueue01Handle,&Receive,NULL) == pdTRUE){
		  printf("RECEIVE:%d\r\n",Receive);
	  }
	  
    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值