STM32

STM32CubeMX学习笔记(32)——FreeRTOS实时操

2021-12-31  本文已影响0人  Leung_ManWah

一、FreeRTOS简介

FreeRTOS 是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。FreeRTOS 提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。

FreeRTOS 是用 C 和汇编来写的,其中绝大部分都是用 C 语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,FreeRTOS 结构简洁,可读性很强!最主要的是非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。

最新版本 V9.0.0(2016年),尽管现在 FreeRTOS 的版本已经更新到 V10.4.1 了,但是我们还是选择 V9.0.0,因为内核很稳定,并且网上资料很多,因为 V10.0.0 版本之后是亚马逊收购了FreeRTOS之后才出来的版本,主要添加了一些云端组件,一般采用 V9.0.0 版本足以。

二、新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

2. 选择 MCU 和封装

3. 配置时钟
RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)


选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式
非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

三、SYS Timebase Source

System Core 中选择 SYS ,对 Timebase Source 进行设置,选择 TIM1 作为HAL库的时基(除了 SysTick 外都可以)。

在基于STM32 HAL的项目中,一般需要维护的 “时基” 主要有2个:

  1. HAL的时基,SYS Timebase Source
  2. OS的时基(仅在使用OS的情况下才考虑)

而这些 “时基” 该去如何维护,主要分为两种情况考虑:

强烈建议用户在使用FreeRTOS的时候,不要使用 SysTick(滴答定时器)作为 “HAL的时基”,因为FreeRTOS要用,最好是要换一个!!!如果共用,潜在一定风险。

四、FreeRTOS

4.1 参数配置

如果要使用事件,要在 Middleware 中选择 FREERTOS 设置,并选择 CMSIS_V2 接口版本


CMSIS是一种接口标准,目的是屏蔽软硬件差异以提高软件的兼容性。RTOS v1使得软件能够在不同的实时操作系统下运行(屏蔽不同RTOS提供的API的差别),而RTOS v2则是拓展了RTOS v1,兼容更多的CPU架构和实时操作系统。因此我们在使用时可以根据实际情况选择,如果学习过程中使用STM32F1、F4等单片机时没必要选择RTOS v2,更高的兼容性背后时更加冗余的代码,理解起来比较困难。

Config parameters 进行具体参数配置。

Kernel settings:

Memory management settings:

Hook function related definitions:

Run time and task stats gathering related definitions:

Co-routine related definitions:

Software timer definitions:

Interrupt nesting behaviour configuration:

4.2 创建事件Event

要想使用事件必须在 Middleware 中选择 FREERTOS 设置,并选择 CMSIS_V2 接口版本。

Events 进行配置。

4.3 创建任务Task

我们创建两个任务,一个触发事件任务,一个等待事件任务。



五、KEY

5.1 参数配置

System Core 中选择 GPIO 设置。


在右边图中找到按键对应引脚,选择 GPIO_Input

六、UART串口打印

查看 STM32CubeMX学习笔记(6)——USART串口使用

七、生成代码

输入项目名和项目路径


选择应用的 IDE 开发环境 MDK-ARM V5

每个外设生成独立的 ’.c/.h’ 文件
不勾:所有初始化代码都生成在 main.c
勾选:初始化代码生成在对应的外设文件。 如 GPIO 初始化代码生成在 gpio.c 中。

点击 GENERATE CODE 生成代码

八、互斥量

8.1 基本概念

事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。 与信号量不同的是,它可以实现一对多,多对多的同步。即一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。

每一个事件组只需要很少的 RAM 空间来保存事件组的状态。事件组存储在一个 EventBits_t 类型的变量中,该变量在事件组结构体中定义 。在 STM32 中 , 我们一般 configUSE_16_BIT_TICKS 定义为 0,那么 uxEventBits 是 32 位的,有 24 个位用来实现事 件标志组。每一位代表一个事件,任务通过“逻辑与”或“逻辑或”与一个或多个事件建立关联,形成一个事件组。 事件的“逻辑或”也被称作是独立型同步,指的是任务感兴趣的所有事件任一件发生即可被唤醒;事件“逻辑与”则被称为是关联型同步,指的是任务感兴趣的若干事件都发生时才被唤醒,并且事件发生的时间可以不同步。

多任务环境下,任务、中断之间往往需要同步操作,一个事件发生会告知等待中的任务,即形成一个任务与任务、中断与任务间的同步。 事件可以提供一对多、多对多的同步操作。一对多同步模型:一个任务等待多个事件的触发,这种情况是比较常见的;多对多同步模型:多个任务等待多个事件的触发。
任务可以通过设置事件位来实现事件的触发和等待操作。FreeRTOS 的事件仅用于同步,不提供数据传输功能。

FreeRTOS 提供的事件具有如下特点:

  • 事件只与任务相关联,事件相互独立,一个 32 位的事件集合(EventBits_t 类型的变量,实际可用与表示事件的只有 24 位),用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0 表示该事件类型未发生、1 表示该事件类型已经发生),一共 24 种事件类型。
  • 事件仅用于同步,不提供数据传输功能。
  • 事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读走),等效于只设置一次。
  • 允许多个任务对同一事件进行读写操作。
  • 支持事件等待超时机制。

在 FreeRTOS 事件中,每个事件获取的时候,用户可以选择感兴趣的事件,并且选择读取事件信息标记,它有三个属性,分别是逻辑与,逻辑或以及是否清除标记。当任务等待事件同步时,可以通过任务感兴趣的事件位和事件信息标记来判断当前接收的事件是否满足要求,如果满足则说明任务等待到对应的事件,系统将唤醒等待的任务;否则,任务会根据用户指定的阻塞超时时间继续等待下去。

8.2 运作机制


接收事件时,可以根据感兴趣的参事件类型接收事件的单个或者多个事件类型。事件接收成功后,必须使用 xClearOnExit 选项来清除已接收到的事件类型,否则不会清除已接收到的事件,这样就需要用户显式清除事件位。用户可以自定义通过传入参数 xWaitForAllBits 选择读取模式,是等待所有感兴趣的事件还是等待感兴趣的任意一个事件。

设置事件时,对指定事件写入指定的事件类型,设置事件集合的对应事件位为 1,可以一次同时写多个事件类型,设置事件成功可能会触发任务调度。清除事件时,根据入参数事件句柄和待清除的事件类型,对事件对应位进行清 0 操作。事件不与任务相关联,事件相互独立,一个 32 位的变量(事件集合,实际用于表示事件的只有 24 位),用于标识该任务发生的事件类型,其中每一位表示一种事件类型(0 表示该事件类型未发生、1 表示该事件类型已经发生),一共 24 种事件类型。



事件唤醒机制,当任务因为等待某个或者多个事件发生而进入阻塞态,当事件发生的时候会被唤醒。

任务 1 对事件 3 或事件 5 感兴趣(逻辑或),当发生其中的某一个事件都会被唤醒,并且执行相应操作。而任务 2 对事件 3 与事件 5 感兴趣(逻辑与),当且仅当事件 3 与事件 5 都发生的时候,任务 2 才会被唤醒,如果只有一个其中一个事件发生,那么任务还是会继续等待事件发生。如果接在收事件函数中设置了清除事件位 xClearOnExit,那么当任务唤醒后将把事件 3 和事件 5 的事件标志清零,否则事件标志将依然存在。

九、相关API说明

9.1 osEventFlagsNew

用于创建一个事件组,并返回对应的ID。

函数 osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr)
参数 attr: 引用由osEventFlagsAttr_t定义的事件属性
返回值 成功返回事件组ID,失败返回0

9.2 osEventFlagsDelete

当系统不再使用事件对象时,可以通过删除事件对象控制块来释放系统资源。

函数 osStatus_t osEventFlagsDelete (osEventFlagsId_t ef_id)
参数 ef_id: 事件组ID
返回值 错误码

9.3 osEventFlagsSet

用于置位事件组中指定的位,当位被置位之后,阻塞在该位上的任务将会被解锁。使用该函数接口时,通过参数指定的事件标志来设定事件的标志位,然后遍历等待在事件对象上的事件等待列表,判断是否有任务的事件激活要求与当前事件对象标志值匹配,如果有,则唤醒该任务。简单来说,就是设置我们自己定义的事件标志位为 1,并且看看有没有任务在等待这个事件,有的话就唤醒它。该函数可以在中断中使用。

函数 uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags)
参数 ef_id: 事件组ID

flags: 指定事件中的事件标志位。如设置 uxBitsToSet 为 0x08 则只置位位 3,如果设置 uxBitsToSet 为 0x09 则位 3 和位 0 都需要被置位
返回值 返回调用 osEventFlagsSet() 时事件组中的值

要想在中断中使用该函数必须在 Include parameters 中把 xEventGroupSetBitFromISR 选择 Enabled 来使能。

9.4 osEventFlagsWait

用于获取事件组中的一个或多个事件发生标志,当要读取的事件标志位没有被置位时任务将进入阻塞等待状态。

函数 uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout)
参数 ef_id: 事件组ID

flags: 指定事件中的事件标志位。如设置 uxBitsToSet 为 0x08 则只置位位 3,如果设置 uxBitsToSet 为 0x09 则位 3 和位 0 都需要被置位

options:osFlagsNoClear是否清除flags指定的事件标志位,osFlagsWaitAll是否 等待flags指定的位都置位的时候才满足任务唤醒的条件

timeout:最大超时时间,单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助把时间转换成 MS
返回值 返回事件中的哪些事件标志位被置位,返回值很可能并不是用户指定的事件位,需要对返回值进行判断再处理

9.5 osEventFlagsClear

用于清除事件组指定的位,如果在获取事件的时候没有将对应的标志位清除,那么就需要用这个函数来进行显式清除。该函数可以在中断中使用。

函数 uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags)
参数 ef_id: 事件组ID

flags: 指定事件组中的哪个位需要清除。如设置 uxBitsToSet 为 0x08 则只清除位 3,如果设置 uxBitsToSet 为 0x09 则位 3 和位 0 都需要被清除
返回值 事件在还没有清除指定位之前的值

十、示例

10.1 任务式

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

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define KEY1_EVENT (0x01 << 0)//设置事件掩码的位 0 
#define KEY2_EVENT (0x01 << 1)//设置事件掩码的位 1
/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};
/* Definitions for Send */
osThreadId_t SendHandle;
const osThreadAttr_t Send_attributes = {
  .name = "Send",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for Receive */
osThreadId_t ReceiveHandle;
const osThreadAttr_t Receive_attributes = {
  .name = "Receive",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for Event */
osEventFlagsId_t EventHandle;
const osEventFlagsAttr_t Event_attributes = {
  .name = "Event"
};
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART1_UART_Init(void);
void StartDefaultTask(void *argument);
void SendTask(void *argument);
void ReceiveTask(void *argument);

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

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

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
    
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    
  /* USER CODE END 2 */

  /* Init scheduler */
  osKernelInitialize();

  /* 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) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* creation of Send */
  SendHandle = osThreadNew(SendTask, NULL, &Send_attributes);

  /* creation of Receive */
  ReceiveHandle = osThreadNew(ReceiveTask, NULL, &Receive_attributes);

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

  /* Create the event(s) */
  /* creation of Event */
  EventHandle = osEventFlagsNew(&Event_attributes);

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

  /* Start scheduler */
  osKernelStart();

  /* We should never get here as control is now taken by the scheduler */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, LED_G_Pin|LED_B_Pin|LED_R_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin : KEY2_Pin */
  GPIO_InitStruct.Pin = KEY2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(KEY2_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : KEY1_Pin */
  GPIO_InitStruct.Pin = KEY1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(KEY1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : LED_G_Pin LED_B_Pin LED_R_Pin */
  GPIO_InitStruct.Pin = LED_G_Pin|LED_B_Pin|LED_R_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */
/**
  * @brief 重定向c库函数printf到USARTx
  * @retval None
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * @brief 重定向c库函数getchar,scanf到USARTx
  * @retval None
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}
/* USER CODE END 4 */

/* 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 *argument)
{
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END 5 */
}

/* USER CODE BEGIN Header_SendTask */
/**
* @brief Function implementing the Send thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_SendTask */
void SendTask(void *argument)
{
  /* USER CODE BEGIN SendTask */
  /* Infinite loop */
  for(;;)
  {
    //如果 KEY1 被按下 
    if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET) 
    { 
        printf("KEY1 down\n"); 
        /* 触发一个事件 1 */ 
        osEventFlagsSet(EventHandle, KEY1_EVENT); 
    } 
    //如果 KEY2 被按下 
    if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_SET) 
    { 
        printf("KEY2 down\n"); 
        /* 触发一个事件 2 */ 
        osEventFlagsSet(EventHandle, KEY2_EVENT); 
    } 
    osDelay(100);
  }
  /* USER CODE END SendTask */
}

/* USER CODE BEGIN Header_ReceiveTask */
/**
* @brief Function implementing the Receive thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_ReceiveTask */
void ReceiveTask(void *argument)
{
  /* USER CODE BEGIN ReceiveTask */
  uint32_t r_event; /* 定义一个事件接收变量 */ 
  /* Infinite loop */
  for(;;)
  {
    /************************************************************* 
    * 等待接收事件标志 
    * 
    * 如果不设置为 osFlagsNoClear,那么在 osEventFlagsWait()返回之前, 
    * 如果满足等待条件(如果函数返回的原因不是超时),那么在事件组中设置 
    * 的 flags 中的任何位都将被清除。 
    * 如果设置为 osFlagsNoClear, 
    * 则在调用 osEventFlagsWait()时,不会更改事件组中设置的位。 
    *
    * 如果设置为 osFlagsWaitAll,则当 flags 中 
    * 的所有位都设置或指定的块时间到期时,osEventFlagsWait()才返回。 
    * 如果不设置为 osFlagsWaitAll,则当设置 flasgs 中设置的任何 
    * 一个位置 1 或指定的块时间到期时,osEventFlagsWait()都会返回。 
    * 阻塞时间由 timeout 参数指定。 
    *********************************************************/ 
    r_event = osEventFlagsWait(EventHandle,             /* 事件对象句柄 */ 
                              KEY1_EVENT|KEY2_EVENT,    /* 接收任务感兴趣的事件 */ 
                              osFlagsWaitAll,           /* 退出时清除事件位,同时满足感兴趣的所有事件 */ 
                              osWaitForever);           /* 指定超时事件,一直等 */ 
  
    if((r_event & (KEY1_EVENT|KEY2_EVENT)) == (KEY1_EVENT|KEY2_EVENT)) 
    { 
        /* 如果接收完成并且正确 */ 
        printf ("KEY1 and KEY2 down\n"); 
    } 
    else 
    {
        printf ("Event Error!\n"); 
    }
  }
  /* USER CODE END ReceiveTask */
}

/**
  * @brief  Period elapsed callback in non blocking mode
  * @note   This function is called  when TIM1 interrupt took place, inside
  * HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
  * a global variable "uwTick" used as application time base.
  * @param  htim : TIM handle
  * @retval None
  */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* USER CODE BEGIN Callback 0 */

  /* USER CODE END Callback 0 */
  if (htim->Instance == TIM1) {
    HAL_IncTick();
  }
  /* USER CODE BEGIN Callback 1 */

  /* USER CODE END Callback 1 */
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

查看打印:
只有当两个按键都按下过才触发


10.2 工程代码

链接:https://pan.baidu.com/s/13Uh4eFqd3R9590nxYxR5iw 提取码:nomi

十一、注意事项

用户代码要加在 USER CODE BEGIN NUSER CODE END N 之间,否则下次使用 STM32CubeMX 重新生成代码后,会被删除。


• 由 Leung 写于 2021 年 12 月 31 日

• 参考:STM32CubeIDE(十一):FreeRTOS选项中Disable、CMSIS_V1和CMSIS_V2的区别
    HAL库中的 SYS Timebase Source 和 SysTick_Handler()

上一篇下一篇

猜你喜欢

热点阅读