zoukankan      html  css  js  c++  java
  • 【FreeRTOS学习01】CubeIDE快速整合FreeRTOS创建第一个任务

    整个专栏主要是博主结合自身对FreeRTOS的实战学习以及源码分析,基于STM32F767 Nucleo-144平台,在CubeIDE下进行开发,结合官方的HAL库,将硬件环节的问题减少到最小,将精力主要放在RTOS的学习上.

    相关文章

    【FreeRTOS实战汇总】小白博主的RTOS学习实战快速进阶之路(持续更新)

    1 FreeRTOS

    FreeRTOS是免费的嵌入式实时系统,可以访问官网,至少目前是免费,并且社区相当活跃,但是以后会不会被割韭菜就不清楚了,所以感觉还是支持国产比较好,大家可以参考一下RT-Thread

    1.1 获取源码

    我们可以登陆到官网下载源码的压缩包,如下图所示;
    在这里插入图片描述
    FreeRTOS还将源码托管到github上,登陆官网可以看到下图的两个仓库;

    在这里插入图片描述

    • FreeRTOS-Kernel:这个仓库是RTOS最核心的东西,很纯净,包括调度算法,信号量,内存管理等等,它同时作为一个子模块存在于FreeRTOS仓库中;
    • FreeRTOS:这个仓库是比较全的源码,除了核心的部分,还包括对各个芯片平台的支持以及各个主流IDE的demo,因此如果要移植的话,主要还是下载这个源码;

    但是本文暂不会介绍如何移植RTOS,在远古时期,因为第三方的支持力度不够,因此有一些平台只能自己移植,现在第三方的支持相当于给力,直接拿来用即可.

    1.2 源码结构

    自动生成的代码中找到FreeRTOS的源码如下;


    在这里插入图片描述


    对于相应的文件夹和源码文件做一下介绍;

    • CMSIS_RTOS_V2:这是API的版本,对应的还有CMSIS_RTOS_V1;
    • portable:这里主要是移植的时候需要修改的一些文件,针对不同的MCU以及不同的编译器,需要对这块进行修改;
      • portmacro.h:定义编译器相关的数据类型和中断处理的宏定义;
      • port.c:实现任务的堆栈初始化systick任务上下文切换
    • MemMang:内存管理的文件,目前主要用heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c
    • list.c:双向链表的实现,主要供给内核调度器使用;
    • queue.c: 队列的实现,主要支持中断环境和信号量控制;
    • croutine.c :任务使用同一个堆栈,这样使得减小RAM的使用,但是在使用上会受到相当大的限制;
    • task.c:每个任务都有各自的独立堆栈,支持完全的抢占式调度

    2 CubeMX 整合 RTOS

    1. 在配置工程的时候或者打开后缀名为.ioccubemx配置文件;
    2. 点击菜单栏Pinout&Configuration
    3. 点击Middleware选择FREERTOS

    具体如下图所示;

    在这里插入图片描述

    最终生成的文件结构如下图所示;
    在这里插入图片描述
    这时候已经将FreeRTOS集成到工程中了,*****,简直不能再方便了;

    3 新建RTOS任务

    直接生成的工程中,系统已经创建好了一个任务StartDefaultTask;
    整体的main.c代码如下;

    #include "main.h"
    #include "cmsis_os.h"
    
    /* Definitions for defaultTask */
    osThreadId_t defaultTaskHandle;
    const osThreadAttr_t defaultTask_attributes = {
      .name = "defaultTask",
      .priority = (osPriority_t) osPriorityNormal,
      .stack_size = 128 * 4
    };
    
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    void StartDefaultTask(void *argument);
    
    int main(void)
    {
      HAL_Init();
      
      SystemClock_Config();
    
      MX_GPIO_Init();
    
      osKernelInitialize();
    
      defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
      /* Start scheduler */
      osKernelStart();
      /* We should never get here as control is now taken by the scheduler */
      while (1)
      {
    
      }
    }
    
    void SystemClock_Config(void)
    {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
      /** Configure the main internal regulator output voltage 
      */
      __HAL_RCC_PWR_CLK_ENABLE();
      __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
      /** Initializes the CPU, AHB and APB busses clocks 
      */
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
      RCC_OscInitStruct.HSIState = RCC_HSI_ON;
      RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
      /** Initializes the CPU, AHB and APB busses clocks 
      */
      RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
      RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
      RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
      RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
      RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    
      if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
      {
        Error_Handler();
      }
    }
    static void MX_GPIO_Init(void)
    {
    
      /* GPIO Ports Clock Enable */
      __HAL_RCC_GPIOA_CLK_ENABLE();
    
    }
    void StartDefaultTask(void *argument)
    {
      for(;;)
      {
        osDelay(1);
      }
    }
    
    void Error_Handler(void)
    {
    
    }
    
    #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)
    { 
    
    }
    #endif /* USE_FULL_ASSERT */
    
    

    这里可以看到整体的流程为两部分HAL层和OS层;

    HAL

    • 硬件初始化 HAL_Init
    • 时钟配置 SystemClock_Config
    • BSP初始化 MX_GPIO_Init

    OS

    • 内核初始化 osKernelInitialize
    • 创建任务/线程 osThreadNew
    • 任务调度 osKernelStart

    整体流程图如下所示;

    Created with Raphaël 2.2.0开始HAL_InitSystemClock_ConfigBSP_InitosKernelInitializeosThreadNewosKernelStart结束

    从OS的设计上来讲,`Thread`拥有自己的`id`,并且可以分配`timer` 并且拥有一个类似线程的任务函数或者称为任务回调函数(可能不太严谨,下面统一称之为任务回调函数),因此这里定义一个FreeRTOS任务需要三点;
    1. osThreadId_t defaultTaskHandle;定义一个变量,后面创建任务会分配一个id作为该任务的唯一标识符/身份证;
    2. osThreadAttr_t defaultTask_attributes;作为定义一个任务时的参数,包括任务的优先级,所需要分配的栈空间大小.以及任务的名字等等;具体结构体osThreadAttr_t如下所示;
    typedef struct {
      const char                   *name;   ///< name of the thread
      uint32_t                 attr_bits;   ///< attribute bits
      void                      *cb_mem;    ///< memory for control block
      uint32_t                   cb_size;   ///< size of provided memory for control block
      void                   *stack_mem;    ///< memory for stack
      uint32_t                stack_size;   ///< size of stack
      osPriority_t              priority;   ///< initial thread priority (default: osPriorityNormal)
      TZ_ModuleId_t            tz_module;   ///< TrustZone module identifier
      uint32_t                  reserved;   ///< reserved (must be 0)
    } osThreadAttr_t;
    
    1. 创建任务回调函数,这里有一个生命周期的问题,通常在任务中放一个死循环,以便于任务保持存活,可以被一直调度;
    void StartDefaultTask(void *argument)
    {
      	for(;;)
      	{
        	osDelay(1);
      	}
    }
    

    最后,调用osThreadNew创建任务;该函数声明如下所示;

    osThreadId_t osThreadNew (	osThreadFunc_t func, 
    							void *argument, 
    							const osThreadAttr_t *attr);
    

    4 总结

    通过cube自动生成了一个FREERTOS工程,简单分析了一下如何创建一个任务,后续需要在实践中深入到源码中学习FreeRTOS

  • 相关阅读:
    【唯星宠物】——CSS/BootStrap/Jquery爬坑之响应式首页
    layui table数据渲染页面+筛选医生+在筛选日期一条龙2
    layui table数据渲染页面+筛选医生+在筛选日期一条龙
    拿到数组逗号分隔在循环拿到里面的数据,最后DOM插入页面
    解决跨域请求的几种方式
    MUI下拉刷新
    Java集合(6):理解Map
    Java集合(5):理解Collection
    Java集合(4):未获支持的操作及UnsupportedOperationException
    Java集合(3):使用Abstract类
  • 原文地址:https://www.cnblogs.com/unclemac/p/12783321.html
Copyright © 2011-2022 走看看