美文网首页树莓派STM32
STM32CubeMX学习笔记(29)——FreeRTOS实时操

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

作者: Leung_ManWah | 来源:发表于2021-12-28 10:36 被阅读0次

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

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

    • 裸机运行
      可以通过 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 创建队列Queue

    Tasks and Queues 进行配置。

    创建一个消息队列TestQueue,


    • Queue Name: 队列名称
    • Queue Size: 队列能够存储的最大单元数目,即队列深度
    • Queue Size: 队列中数据单元的长度,以字节为单位
    • Allocation: 分配方式:Dynamic 动态内存创建
    • Buffer Name: 缓冲区名称
    • Buffer Size: 缓冲区大小
    • Conrol Block Name: 控制块名称

    4.3 创建任务Task

    我们创建两个任务,一个消息接收任务,一个消息发送任务。



    • 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: 控制块名称

    五、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 相关API说明

    8.1.1 osMessageQId

    队列ID。例如,对osMessageCreate的调用返回。可用作参数到osMessageDelete以删除队列。

    /// Message ID identifies the message queue (pointer to a message queue control block).
    /// \note CAN BE CHANGED: \b os_messageQ_cb is implementation specific in every CMSIS-RTOS.
    typedef QueueHandle_t osMessageQId;
    

    8.1.2 osMessageCreate

    使用动态内存的方式创建一个新的队列。

    函数 osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id)
    参数 queue_def: 引用由osMessageQDef定义的队列

    thread_id: 队列ID或NULL
    返回值 成功返回队列ID,失败返回0

    8.1.3 osMessageDelete

    队列删除函数是根据消息队列ID直接删除的,删除之后这个消息队列的所有信息都会被系统回收清空,而且不能再次使用这个消息队列了。

    函数 osStatus osMessageDelete (osMessageQId queue_id)
    参数 queue_id: 消息队列ID,表示的是要删除哪个想队列
    返回值 错误码

    8.2 示例

    osMessageQId TestQueueHandle;
    
    /* 创建 Test_Queue */ 
    /* Create the queue(s) */
    /* definition and creation of TestQueue */
    osMessageQDef(TestQueue, 16, uint32_t);/* 第2参数:消息队列的长度,第3参数:消息的大小 */
    TestQueueHandle = osMessageCreate(osMessageQ(TestQueue), NULL);
    

    九、消息发送与接收

    9.1 相关API说明

    9.1.1 osMessagePut

    用于向队列尾部发送一个队列消息。消息以拷贝的形式入队,而不是以引用的形式。可用在中断服务程序中。

    函数 osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)
    参数 queue_id: 目标队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

    info: 发送数据的指针。其指向将要复制到目标队列中的数据单元。由于在创建队列时设置了队列中数据单元的长度,所以会从该指针指向的空间复制对应长度的数据到队列的存储区域。

    millisec: 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。
    返回值 错误码

    9.1.2 osMessageGet

    用于从一个队列中接收消息并把消息从队列中删除。接收的消息是以拷贝的形式进行的,所以我们必须提供一个足够大空间的缓冲区。具体能够拷贝多少数据到缓冲区,这个在队列创建的时候已经设定。可用在中断服务程序中。

    函数 osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)
    参数 queue_id: 被读队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

    millisec: 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。
    返回值 错误码

    9.1.3 osMessagePeek

    osMessagePeek() 也是从从队列中接收数据单元,不同的是并不从队列中删出接收到的单元。osMessagePeek() 从队列首接收到数据后,不会修改队列中的数据,也不会改变数据在队列中的存储序顺。可用在中断服务程序中。

    函数 osEvent osMessagePeek (osMessageQId queue_id, uint32_t millisec)
    参数 queue_id: 被读队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值

    millisec: 队列空时,阻塞超时的最大时间。如果该参数设置为 0,函数立刻返回。超时时间的单位为系统节拍周期,常量 portTICK_PERIOD_MS 用于辅助计算真实的时间,单位为 ms。如果 INCLUDE_vTaskSuspend 设置成 1,并且指定延时为 portMAX_DELAY 将导致任务无限阻塞(没有超时)。
    返回值 错误码

    9.2 示例

    9.2.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 */
    
    /* 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;
    
    osThreadId defaultTaskHandle;
    osThreadId ReceiveHandle;
    osThreadId SendHandle;
    osMessageQId TestQueueHandle;
    /* 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 ReceiveTask(void const * argument);
    void SendTask(void const * 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 */
    
      /* 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 TestQueue */
      osMessageQDef(TestQueue, 16, uint32_t);
      TestQueueHandle = osMessageCreate(osMessageQ(TestQueue), 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);
    
      /* definition and creation of Receive */
      osThreadDef(Receive, ReceiveTask, osPriorityIdle, 0, 128);
      ReceiveHandle = osThreadCreate(osThread(Receive), NULL);
    
      /* definition and creation of Send */
      osThreadDef(Send, SendTask, osPriorityIdle, 0, 128);
      SendHandle = osThreadCreate(osThread(Send), 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_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 : 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 const * argument)
    {
      /* USER CODE BEGIN 5 */
      /* Infinite loop */
      for(;;)
      {
        osDelay(1);
      }
      /* USER CODE END 5 */
    }
    
    /* 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 const * argument)
    {
      /* USER CODE BEGIN ReceiveTask */
      osEvent event;
      /* Infinite loop */
      for(;;)
      {
        event = osMessageGet(TestQueueHandle, /* 消息队列的句柄 */ 
                              osWaitForever); /* 等待时间 一直等 */ 
        if(osEventMessage == event.status) 
        {
            printf("receive data:%d\n\n", event.value.v); 
        }
        else 
        {
            printf("error: 0x%d\n", event.status); 
        }
      }
      /* USER CODE END ReceiveTask */
    }
    
    /* 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 const * argument)
    {
      /* USER CODE BEGIN SendTask */
      osEvent xReturn;
      uint32_t send_data1 = 1; 
      /* Infinite loop */
      for(;;)
      {
        if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET) 
        { 
            /* KEY1 被按下 */ 
            printf("send_data1!\n"); 
            xReturn.status = osMessagePut(TestQueueHandle, /* 消息队列的句柄 */ 
                                          send_data1,      /* 发送的消息内容 */ 
                                          0);              /* 等待时间 0 */ 
            if(osOK != xReturn.status)  
            {
                printf("send fail!\n\n"); 
            }
        } 
        osDelay(100);
      }
      /* USER CODE END SendTask */
    }
    
    /**
      * @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 */
    

    9.3 工程代码

    链接:https://pan.baidu.com/s/1uNVONKwr1SvarOK-kPNVdA 提取码:bur5

    十、查询消息个数

    10.1 相关API说明

    10.1.1 osMessageWaiting

    用于查询队列中当前有效数据单元个数。

    函数 uint32_t osMessageWaiting(osMessageQId queue_id)
    参数 queue_id: 目标队列ID。这个句柄即是调用 osMessageCreate() 创建该队列时的返回值
    返回值 当前队列中保存的数据单元个数。返回 0 表明队列为空

    十一、注意事项

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


    • 由 Leung 写于 2021 年 12 月 28 日

    • 参考:STM32CubeMX之FreeRTOS
        STM32通过STM32cueMX生成FreeRTOS操作队列消息
        STM32CubeIDE(十一):FreeRTOS选项中Disable、CMSIS_V1和CMSIS_V2的区别
        HAL库中的 SYS Timebase Source 和 SysTick_Handler()

    相关文章

      网友评论

        本文标题:STM32CubeMX学习笔记(29)——FreeRTOS实时操

        本文链接:https://www.haomeiwen.com/subject/jophqrtx.html