目录
nullhttps://freertos.org/zh-cn-cmn-s/
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 操作系统是完全免费的操作系统,具有源码公开、可移 植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行,其最新版本为
如何下载:以下官网下载链接
nullhttps://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的任务调度规则是怎样的?
抢占式调度
总结: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工作
- 包含了FreeRTOS相关的头文件,以及stdio.h库。
- 定义了四个任务句柄,分别是myTaskKey2Handle、myTaskLED1Handle、myTaskLED2Handle和myTaskKey1Handle,分别对应四个不同的任务。
- 在MX_FREERTOS_Init函数中,定义并创建了这四个任务。每个任务都对应一个回调函数,如StartTaskLED1、StartTaskLED2等,这些函数包含了各自的任务执行逻辑。
- 这四个任务均具有相同的优先级,并分配了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,大小为零的队列,只有0和1 两种状态,通常情况下,我们用 它来进行互斥访问或任务同步。 互斥访问:比如门钥匙,只有获取到钥匙才可以开门任务同步:
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 */
互斥量
互斥量(Mutex):
- 互斥量是二进制信号量的一个变种。
- 主要用于保护共享资源,防止多个任务同时访问同一个资源。
- 任意时刻互斥量的状态只有两种,开锁或闭锁。
- 开启互斥量需要在头文件FreeRTOSConfig.h中设置configUSE_MUTEXES为1。
- 互斥量支持互斥量所有权、递归访问以及防止优先级翻转的特性。
信号量:
- 信号量分为四种:二值信号量、互斥信号量、计数信号量和递归互斥信号量。
- 二值信号量是一种特殊的信号量,用于实现任务之间的同步和通信。
- 计数信号量用于管理系统多个共享资源,用计数值表示可用资源数目。
- 互斥信号量与二值信号量类似,但不支持计数和具有优先级继承的功能。
- 递归互斥信号量允许任务多次请求同一资源,而不会导致死锁。
当两个任务使用同一个锁(互斥量)时,他们实际上是在竞争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 */