一、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 版本足以。

  • FreeRTOS官网:http://www.freertos.org/
  • 代码托管网站:https://sourceforge.net/projects/freertos/files/FreeRTOS/

二、新建工程

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的情况下才考虑)

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

  • 裸机运行
    可以通过 SysTick(滴答定时器)或 (TIMx)定时器 的方式来维护 SYS Timebase Source,也就是HAL库中的 uwTick,这是HAL库中维护的一个全局变量。在裸机运行的情况下,我们一般选择默认的 SysTick(滴答定时器) 方式即可,也就是直接放在 SysTick_Handler() 中断服务函数中来维护。

  • 带OS运行
    前面提到的 SYS Timebase Source 是STM32的HAL库中的新增部分,主要用于实现 HAL_Delay() 以及作为各种 timeout 的时钟基准。

    在使用了OS(操作系统)之后,OS的运行也需要一个时钟基准(简称“时基”),来对任务和时间等进行管理。而OS的这个 时基 一般也都是通过 SysTick(滴答定时器) 来维护的,这时就需要考虑 “HAL的时基” 和 “OS的时基” 是否要共用 SysTick(滴答定时器) 了。

    如果共用SysTick,当我们在CubeMX中选择启用FreeRTOS之后,在生成代码时,CubeMX一定会报如下提示:

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

四、FreeRTOS

4.1 参数配置

Middleware 中选择 FREERTOS 设置,并选择 CMSIS_V1 接口版本


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

Config parameters 进行具体参数配置。

Kernel settings:

  • USE_PREEMPTION: Enabled:RTOS使用抢占式调度器;Disabled:RTOS使用协作式调度器(时间片)。
  • TICK_RATE_HZ: 值设置为1000,即周期就是1ms。RTOS系统节拍中断的频率,单位为HZ。
  • MAX_PRIORITIES: 可使用的最大优先级数量。设置好以后任务就可以使用从0到(MAX_PRIORITIES - 1)的优先级,其中0位最低优先级,(MAX_PRIORITIES - 1)为最高优先级。
  • MINIMAL_STACK_SIZE: 设置空闲任务的最小任务堆栈大小,以字为单位,而不是字节。如该值设置为128 Words,那么真正的堆栈大小就是 128*4 = 512 Byte。
  • MAX_TASK_NAME_LEN: 设置任务名最大长度。
  • IDLE_SHOULD_YIELD: Enabled 空闲任务放弃CPU使用权给其他同优先级的用户任务。
  • USE_MUTEXES: 为1时使用互斥信号量,相关的API函数会被编译。
  • USE_RECURSIVE_MUTEXES: 为1时使用递归互斥信号量,相关的API函数会被编译。
  • USE_COUNTING_SEMAPHORES: 为1时启用计数型信号量, 相关的API函数会被编译。
  • QUEUE_REGISTRY_SIZE: 设置可以注册的队列和信号量的最大数量,在使用内核调试器查看信号量和队列的时候需要设置此宏,而且要先将消息队列和信号量进行注册,只有注册了的队列和信号量才会在内核调试器中看到,如果不使用内核调试器的话次宏设置为0即可。
  • USE_APPLICATION_TASK_TAG: 为1时可以使用vTaskSetApplicationTaskTag函数。
  • ENABLE_BACKWARD_COMPATIBILITY: 为1时可以使V8.0.0之前的FreeRTOS用户代码直接升级到V8.0.0之后,而不需要做任何修改。
  • USE_PORT_OPTIMISED_TASK_SELECTION: FreeRTOS有两种方法来选择下一个要运行的任务,一个是通用的方法,另外一个是特殊的方法,也就是硬件方法,使用MCU自带的硬件指令来实现。STM32有计算前导零指令吗,所以这里强制置1。
  • USE_TICKLESS_IDLE: 置1:使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行。假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用ISP下载办法解决。
  • USE_TASK_NOTIFICATIONS: 为1时使用任务通知功能,相关的API函数会被编译。开启了此功能,每个任务会多消耗8个字节。
  • RECORD_STACK_HIGH_ADDRESS: 为1时栈开始地址会被保存到每个任务的TCB中(假如栈是向下生长的)。

Memory management settings:

  • Memory Allocation: Dynamic/Static 支持动态/静态内存申请
  • TOTAL_HEAP_SIZE: 设置堆大小,如果使用了动态内存管理,FreeRTOS在创建 task, queue, mutex, software timer or semaphore的时候就会使用heap_x.c(x为1~5)中的内存申请函数来申请内存。这些内存就是从堆ucHeap[configTOTAL_HEAP_SIZE]中申请的。
  • Memory Management scheme: 内存管理策略 heap_4

Hook function related definitions:

  • USE_IDLE_HOOK: 置1:使用空闲钩子(Idle Hook类似于回调函数);置0:忽略空闲钩子。
  • USE_TICK_HOOK: 置1:使用时间片钩子(Tick Hook);置0:忽略时间片钩子。
  • USE_MALLOC_FAILED_HOOK: 使用内存申请失败钩子函数。
  • CHECK_FOR_STACK_OVERFLOW: 大于0时启用堆栈溢出检测功能,如果使用此功能用户必须提供一个栈溢出钩子函数,如果使用的话此值可以为1或者2,因为有两种栈溢出检测方法。

Run time and task stats gathering related definitions:

  • GENERATE_RUN_TIME_STATS: 启用运行时间统计功能。
  • USE_TRACE_FACILITY: 启用可视化跟踪调试。
  • USE_STATS_FORMATTING_FUNCTIONS: 与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数prvWriteNameToBuffer()、vTaskList()、vTaskGetRunTimeStats()。

Co-routine related definitions:

  • USE_CO_ROUTINES: 启用协程。
  • MAX_CO_ROUTINE_PRIORITIES: 协程的有效优先级数目。

Software timer definitions:

  • USE_TIMERS: 启用软件定时器。

Interrupt nesting behaviour configuration:

  • LIBRARY_LOWEST_INTERRUPT_PRIORITY: 中断最低优先级。
  • LIBRARY_LOWEST_INTERRUPT_PRIORITY: 系统可管理的最高中断优先级。

4.2 创建任务Task

Tasks and Queues 进行配置。

默认空闲任务是在系统无其它任务执行时执行。

然后我们创建两个LED任务。

  • Task Name: 任务名称
  • Priority: 优先级,在 FreeRTOS 中,数值越大优先级越高,0 代表最低优先级
  • Stack Size (Words): 堆栈大小,单位为字,在32位处理器(STM32),一个字等于4字节,如果传入512那么任务大小为512*4字节
  • Entry Function: 入口函数
  • Code Generation Option: 代码生成选项
  • Parameter: 任务入口函数形参,不用的时候配置为0或NULL即可
  • Allocation: 分配方式:Dynamic 动态内存创建
  • Buffer Name: 缓冲区名称
  • Conrol Block Name: 控制块名称

五、LED

5.1 参数配置

System Core 中选择 GPIO 设置。

在右边图中找到 LED 灯对应引脚,选择 GPIO_Output

GPIO output level 中选择 Low 输出低电平点亮,可以添加自定义标签(这样生成代码也会根据标签设置引脚的宏定义)。

六、UART串口打印

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

七、生成代码

输入项目名和项目路径

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

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

点击 GENERATE CODE 生成代码

八、任务创建与启动

8.1 相关API说明

8.1.1 osThreadId

任务ID。例如,对osThreadCreate的调用返回。可用作参数到osThreadTerminate以删除任务。

/// Thread ID identifies the thread (pointer to a thread control block).
/// \note CAN BE CHANGED: \b os_thread_cb is implementation specific in every CMSIS-RTOS.
typedef TaskHandle_t osThreadId;

8.1.2 osThreadCreate

使用动态/静态内存的方法创建一个任务。

函数 osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
参数 thread_def: 引用由osThreadDef定义的任务

argument: 任务入口函数形参

返回值 成功返回任务ID,失败返回0

8.1.3 osThreadTerminate

删除任务。任务被删除后就不复存在,也不会再进入运行态。

函数 osStatus osThreadTerminate (osThreadId thread_id)
参数 thread_id: 被删除任务的ID
返回值 错误码

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

8.1.4 osKernelStart

在创建完任务的时候,我们需要开启调度器,因为创建仅仅是把任务添加到系统中,还没真正调度,并且空闲任务也没实现,定时器任务也没实现,这些都是在开启调度函数 osKernelStart() 中实现的。为什么要空闲任务?因为 FreeRTOS 一旦启动,就必须要保证系统中每时每刻都有一个任务处于运行态(Runing),并且空闲任务不可以被挂起与删除,空闲任务的优先级是最低的,以便系统中其他任务能随时抢占空闲任务的 CPU 使用权。

函数 osStatus osKernelStart (void)
参数
返回值 错误码

8.2 示例

/* 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 *//* 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;/****************** 任务句柄 ******************/
/* * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么* 这个句柄可以为NULL。*//* 空闲任务句柄 */
osThreadId defaultTaskHandle;
/* LED1 任务句柄 */
osThreadId LED1Handle;
/* LED2 任务句柄 */
osThreadId LED2Handle;
/* 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 const * argument);
void LED1_Task(void const * argument);  /* LED1_Task 任务实现 */
void LED2_Task(void const * argument);  /* LED2_Task 任务实现 *//* 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 *//* 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 defaultTask */osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);/* definition and creation of LED1 */osThreadDef(LED1, LED1_Task, osPriorityIdle, 0, 128);LED1Handle = osThreadCreate(osThread(LED1), NULL);/* definition and creation of LED2 */osThreadDef(LED2, LED2_Task, osPriorityIdle, 0, 128);LED2Handle = osThreadCreate(osThread(LED2), NULL);/* USER CODE BEGIN RTOS_THREADS *//* add threads, ... *//* USER CODE END RTOS_THREADS *//* 启动任务调度 *//* 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_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOA_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 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 const * argument)
{/* USER CODE BEGIN 5 *//* Infinite loop */for(;;){osDelay(1);}/* USER CODE END 5 */
}/* USER CODE BEGIN Header_LED1_Task */
/**
* @brief Function implementing the LED1 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_LED1_Task */
void LED1_Task(void const * argument)
{/* USER CODE BEGIN LED1_Task *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(GPIOB, LED_G_Pin);printf("LED1_Task\n");osDelay(1000);  /* 延时 1000 个 tick */ }/* USER CODE END LED1_Task */
}/* USER CODE BEGIN Header_LED2_Task */
/**
* @brief Function implementing the LED2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_LED2_Task */
void LED2_Task(void const * argument)
{/* USER CODE BEGIN LED2_Task *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(GPIOB, LED_B_Pin);printf("LED2_Task\n");osDelay(500);   /* 延时 500 个 tick */ }/* USER CODE END LED2_Task */
}/*** @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 */

8.3 工程代码

链接:https://pan.baidu.com/s/1hcYRL-bKEKX8i_s0vnGW0g 提取码:ax4a

九、任务延时

9.1 相关API说明

9.1.1 osDelay

相对延时函数。用于阻塞延时,调用该函数后,任务将进入阻塞状态,进入阻塞态的任务将让出 CPU 资源。

函数 osStatus osDelay (uint32_t millisec)
参数 millisec: 毫秒数
返回值 错误码

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

9.1.2 osThreadTerminate

绝对延时函数。常用于较精确的周期运行任务,比如我有一个任务,希望它以固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。

函数 osStatus osDelayUntil (uint32_t *PreviousWakeTime, uint32_t millisec)
参数 PreviousWakeTime: 任务上一次离开阻塞态(被唤醒)的时刻。这个时刻被用作一个参考点来计算该任务下一次离开阻塞态的时刻

millisec: 毫秒数

返回值 错误码

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

9.2 示例

9.2.1 相对延时

void vTaskA( void * pvParameters )
{while (1) {// ...// 这里为任务主体代码// .../* 调用相对延时函数,阻塞 1000 个 tick */ osDelay( 1000 ); }
}

9.2.2 绝对延时

注意:在使用的时候要将延时时间转化为系统节拍,在任务主体之前要调用延时函数。

void vTaskA( void * pvParameters )
{/* 用于保存上次时间。调用后系统自动更新 */ static portTickType PreviousWakeTime; /* 设置延时时间,将时间转为节拍数 */ const portTickType TimeIncrement = pdMS_TO_TICKS(1000); /* 获取当前系统时间 */ PreviousWakeTime = osKernelSysTick();while (1){/* 调用绝对延时函数,任务时间间隔为 1000 个 tick */ osDelayUntil( &PreviousWakeTime,TimeIncrement ); // ...// 这里为任务主体代码 // ...}
}

十、任务挂起与恢复

10.1 相关API说明

10.1.1 osThreadSuspend

挂起指定任务。被挂起的任务绝不会得到 CPU 的使用权,不管该任务具有什么优先级。

函数 osStatus osThreadSuspend (osThreadId thread_id)
参数 thread_id: 挂起指定任务的任务ID
返回值 错误码

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

10.1.2 osThreadSuspendAll

将所有的任务都挂起。

函数 osStatus osThreadSuspendAll (void)
参数
返回值 错误码

10.1.3 osThreadResume

让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续运行。如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。

函数 osStatus osThreadResume (osThreadId thread_id)
参数 thread_id: 挂起指定任务的任务ID
返回值 错误码

10.1.4 osThreadResume

让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候根据挂起时的状态继续运行。如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。可用在中断服务程序中。

函数 osStatus osThreadResume (osThreadId thread_id)
参数 thread_id: 挂起指定任务的任务ID
返回值 错误码

要想在中断服务程序中使用该函数必须在 Include parameters 中把 vTaskResumeFromISR 选择 Enabled 来使能。

10.1.5 osThreadResumeAll

将所有的任务都恢复。

函数 osStatus osThreadResumeAll (void)
参数
返回值 错误码

10.2 示例

10.2.1 挂起

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
osThreadId LED1Handle;/* LED 任务句柄 */ static void KEY_Task(void* parameter)
{while (1) {if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) {/* K1 被按下 */printf("挂起 LED 任务!\n");osThreadSuspend(LED1Handle);/* 挂起 LED 任务 */ }osDelay(20);/* 延时 20 个 tick */}
}

10.2.2 恢复

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
osThreadId LED1Handle;/* LED 任务句柄 */ static void KEY_Task(void* parameter)
{while (1) {if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {/* K2 被按下 */printf("恢复 LED 任务!\n");osThreadResume(LED1Handle);/* 恢复 LED 任务! */ }osDelay(20);/* 延时 20 个 tick */}
}

10.2.3 中断服务中恢复

使用 xTaskResumeFromISR()的时候有几个需要注意的地方:

  1. 当函数的返回值为 pdTRUE 时:恢复运行的任务的优先级等于或高于正在运行的任务,表明在中断服务函数退出后必 须进行一次上下文切换 , 使用 portYIELD_FROM_ISR() 进行上下文切换。当函数的返回值为 pdFALSE 时:恢复运行的任务的优先级低于当前正在运行的任务,表明在中断服务函数退出后不需 要进行上下文切换。
  2. xTaskResumeFromISR() 通常被认为是一个危险的函数,因为它的调用并非是固定的,中断可能随时来来临。所以 xTaskResumeFromISR()不能用于任务和中断间的同步,如果中断恰巧在任务被挂起之前到达,这就会导致一次中断丢失(任务还没有挂起,调用 xTaskResumeFromISR()函数是没有意义的,只能等下一次中断)。这种情况下,可以使用信号量或者任务通知来同步就可以避免这种情况。
void vAnExampleISR( void )
{BaseType_t xYieldRequired;/* 恢复被挂起的任务 */ xYieldRequired = osThreadResume( xHandle ); if ( xYieldRequired == pdTRUE ) { /* 执行上下文切换, ISR 返回的时候将运行另外一个任务 */ portYIELD_FROM_ISR(); }
}

10.2.4 全部恢复

osThreadResumeAll 函数的使用方法很简单,但是要注意,调用了多少次 osThreadSuspendAll() 函数就必须同样调用多少次 osThreadResumeAll() 函数。

void vDemoFunction( void )
{osThreadSuspendAll();/* 处理 xxx 代码 */osThreadSuspendAll();/* 处理 xxx 代码 */osThreadSuspendAll();/* 处理 xxx 代码 */osThreadResumeAll(); osThreadResumeAll(); osThreadResumeAll();
}

十一、获取任务状态

11.1 相关API说明

11.1.1 osThreadGetState

获取任务当前状态。

函数 osThreadState osThreadGetState(osThreadId thread_id)
参数 thread_id: 任务ID
返回值 以下值
/* Thread state returned by osThreadGetState */
typedef enum {osThreadRunning   = 0x0,       /* A thread is querying the state of itself, so must be running. */osThreadReady     = 0x1 ,                 /* The thread being queried is in a read or pending ready list. */osThreadBlocked   = 0x2,             /* The thread being queried is in the Blocked state. */osThreadSuspended = 0x3,          /* The thread being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */osThreadDeleted   = 0x4,                /* The thread being queried has been deleted, but its TCB has not yet been freed. */   osThreadError     = 0x7FFFFFFF
} osThreadState;

十二、注意事项

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


• 由 Leung 写于 2021 年 12 月 20 日

• 参考:STM32CubeMX之FreeRTOS
    STM32CUBEMX的freertos一般使用方法笔记
    STM32CubeIDE(十一):FreeRTOS选项中Disable、CMSIS_V1和CMSIS_V2的区别
    HAL库中的 SYS Timebase Source 和 SysTick_Handler()

STM32CubeMX学习笔记(28)——FreeRTOS实时操作系统使用(任务管理)相关推荐

  1. FreeRTOS实时操作系统(七)时间片调度及RTOS的滴答定时器

    系列文章目录 FreeRTOS实时操作系统(一)RTOS的基本概念 FreeRTOS实时操作系统(二)任务创建与任务删除(HAL库) FreeRTOS实时操作系统(三)任务挂起与恢复 FreeRTOS ...

  2. STM32CubeMX学习笔记(25)——FatFs文件系统使用(操作SPI Flash)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  3. STM32CubeMX学习笔记(16)——电源管理(PWR)低功耗停止模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  4. STM32CubeMX学习笔记(27)——FatFs文件系统使用(操作SD卡)

    一.FatFs简介 FatFs 是面向小型嵌入式系统的一种通用的 FAT 文件系统.它完全是由 ANSI C 语言编写并且完全独立于底层的 I/O 介质.因此它可以很容易地不加修改地移植到其他的处理器 ...

  5. STM32CubeMX学习笔记(17)——电源管理(PWR)低功耗待机模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  6. 网络存储 linux 访问,Linux基础教程学习笔记28——使用Samba访问网络存储

    Linux基础教程学习笔记28--使用Samba访问网络存储 SMB用于Windows和类Linux系统直接的文件共享 安装samba client包: [root@linuxidc~]# yum i ...

  7. STM32CubeMX学习笔记(15)——电源管理(PWR)低功耗睡眠模式

    一.低功耗模式简介 系统提供了多个低功耗模式,可在 CPU 不需要运行时(例如等待外部事件时)节省功耗.由用户根据应用选择具体的低功耗模式,以在低功耗.短启动时间和可用唤醒源之间寻求最佳平衡. 睡眠模 ...

  8. STM32CubeMX学习笔记(24)——通用定时器接口使用(电容按键检测)

    一.电容按键简介 电容器(简称为电容)就是可以容纳电荷的器件,两个金属块中间隔一层绝缘体就可以构成一个最简单的电容.如图 32-1(俯视图),有两个金属片,之间有一个绝缘介质,这样就构成了一个电容.这 ...

  9. STM32CubeMX学习笔记(38)——FSMC接口使用(TFT-LCD屏显示)

    一.TFT-LCD简介 TFT-LCD(Thin Film Transistor-Liquid Crystal Display) 即薄膜晶体管液晶显示器.TFT-LCD 与无源 TN-LCD. STN ...

最新文章

  1. 【已解决】烂泥:耳机有声音,话筒却没有输入……
  2. SQL2008 提示评估期已过的解决方法
  3. java cache缓存_涨姿势:为什么Java中“1000==1000”为false,而”100==100“为true?
  4. Qt控制台输出QString
  5. HTML5学习笔记(十八):闭包
  6. error: Your local changes to the following files would be overwritten by merge
  7. 查询性能优化(使用 Explain 进行分析、优化数据访问、重构查询方式)、存储引擎(InnoDB/MyISAM)
  8. [学习笔记] 初次见面,请多关照 (公式推导+题集)——杜教筛
  9. 定时器实现方式之TimerTask、Timer
  10. 小学教师计算机国培培训总结,小学教师国培培训心得体会
  11. 华为将正式发布鸿蒙手机操作系统;清华成立量子信息班;美团:外卖是微利业务,直接降低抽成无法持续|极客头条...
  12. Python抖音机器人制作!让你有看不完的小姐姐
  13. 解决eclipse为什么不能查看源码
  14. Linux环境SOCKET编程5:定时器接口timerfd
  15. 南京大学计算机考研复试名单,南大的考研复试名单能查出来吗?
  16. BUUCTF [GXYCTF2019]Ping Ping Ping 1
  17. 网络攻击与防御基本概念
  18. 让HTML页面也可以实现全屏效果
  19. svn用户名和密码都正确,但是登录不了
  20. Visio XP与VisualSourceSafe简介

热门文章

  1. 计算机开启后显示器黑屏,电脑开机后显示器黑屏怎么办
  2. python提取句子_科学网—Python提取句子 - 吕波的博文
  3. 华为高管认为区块链有助于实现智慧城市
  4. 网站提交谷歌站长工具验证操作流程
  5. 雷鸟邮件--exchange server
  6. 创业初期公司股权结构的设计
  7. Transparent native-to-ascii conversion properties配置乱码
  8. vue二次确认弹窗组件
  9. 取消的html,关于CSS:如何取消HTML的“对焦”行为?
  10. java mock 数据库_Java-Mock简化单元测试