STM32 FreeRTOS CMSIS—OS

这篇具有很好参考价值的文章主要介绍了STM32 FreeRTOS CMSIS—OS。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

使用STM32CubeMX代码生成,在 STM32Cube 固件中,通过 ARM 提供的通用 CMSIS-OS 封装层,将 FreeRTOS 用作实时操作系统。也就是说在一套代码里有着两套标准,在阅读源码时需要注意区分。

1.关于 osThreadNew ()

1.1 FreeRTOS线程与任务的关系

osThreadId_t osThreadNew (osThreadFunc_t func, 
						  void *argument, 
						  const osThreadAttr_t *attr)
/*******************************没有感情的分界线********************/
BaseType_t xTaskCreate(	TaskFunction_t pxTaskCode,
						const char * const pcName,
						const configSTACK_DEPTH_TYPE usStackDepth,
						void * const pvParameters,
						UBaseType_t uxPriority,
						TaskHandle_t * const pxCreatedTask )
TaskHandle_t xTaskCreateStatic(	TaskFunction_t pxTaskCode,
								const char * const pcName,		
								const uint32_t ulStackDepth,
								void * const pvParameters,
								UBaseType_t uxPriority,
								StackType_t * const puxStackBuffer,
								StaticTask_t * const pxTaskBuffer )

先后分别是CMSIS-OS和FreeRTOS提供的任务创建API,函数名给我带来了一个疑问线程和任务之间的关系,翻阅资料后找到如下解释。

在多数通用(分时)操作系统(如Linux,Windows)中,进程是系统资源分配的最小单位,线程为CPU调度的最小单元。而在实时操作系统中,多数情况下不区分线程与进程进行独立管理,为了减小系统资源占用以及提高实时性,往往将线程与进程合二为一,采用任务(一个个独立且无法返回的函数)作为应用程序的最小调度运行单元,使用TCB(任务控制块)对任务进行管理,FreeRTOS即是如此。
结论:像FreeRTOS这样的轻量级OS,其中的任务与线程的含义一致,即在FreeRTOS上跑的整个程序即为一个进程,该进程中又包含着多个线程(任务),这些线程有些是系统自动创建的,有些为用户手动创建。
原文链接

翻阅ST提供的用户手册得到如下信息。

本用户手册的目标读者为在 STM32 微控制器上使用 STM32Cube 固件的开发者。它完整描述了如何使用具有实时操作系统 (RTOS)的 STM32Cube 固件组件;本用户手册还提供了一组示例说明,它们基于 FreeRTOS,使用 CMSIS-OS 封装层提供的通用 API。
在 STM32Cube 固件中,通过 ARM 提供的通用 CMSIS-OS 封装层,FreeRTOS 用作实时操作系统。使用 FreeRTOS 的样例和应用可直接移植到其它任何 RTOS 而不需要修改高层API,在此情况下仅需更改 CMSIS-OS 封装。

  • 结论:osThreadNew()是ARM的CMSIS-OS的封装层API,内部包含了FreeRTOS的
    动态任务创建xTaskCreate()
    静态任务创建xTaskCreateStatic()
    根据参数选择选择调用动态还是静态创建,具体实现后文给出。

1.2 osThreadNew ()

1.2.1 osThreadNew ()如何实现动态创建与静态创建的选择

从前文已知osThreadNew()是ARM提供的上层封装,那么具体是如何实现动态与静态的选择。
翻阅源码发现原理在于其参数的选择,一下给出源码

osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr) {
  char empty;
  const char *name;
  uint32_t stack;
  TaskHandle_t hTask;
  UBaseType_t prio;
  int32_t mem;

  hTask = NULL;

  if (!IS_IRQ() && (func != NULL)) {
    stack = configMINIMAL_STACK_SIZE;
    prio  = (UBaseType_t)osPriorityNormal;

    empty = '\0';
    name  = ∅
    mem   = -1;

    if (attr != NULL) {
      if (attr->name != NULL) {
        name = attr->name;
      }
      if (attr->priority != osPriorityNone) {
        prio = (UBaseType_t)attr->priority;
      }

      if ((prio < osPriorityIdle) || (prio > osPriorityISR) || ((attr->attr_bits & osThreadJoinable) == osThreadJoinable)) {
        return (NULL);
      }

      if (attr->stack_size > 0U) {
        /* In FreeRTOS stack is not in bytes, but in sizeof(StackType_t) which is 4 on ARM ports.       */
        /* Stack size should be therefore 4 byte aligned in order to avoid division caused side effects */
        stack = attr->stack_size / sizeof(StackType_t);
      }

      if ((attr->cb_mem    != NULL) && (attr->cb_size    >= sizeof(StaticTask_t)) &&
          (attr->stack_mem != NULL) && (attr->stack_size >  0U)) {
        mem = 1;
      }
      else {
        if ((attr->cb_mem == NULL) && (attr->cb_size == 0U) && (attr->stack_mem == NULL)) {
          mem = 0;
        }
      }
    }
    else {
      mem = 0;
    }

    if (mem == 1) {
      hTask = xTaskCreateStatic ((TaskFunction_t)func, name, stack, argument, prio, (StackType_t  *)attr->stack_mem,
                                                                                    (StaticTask_t *)attr->cb_mem);
    }
    else {
      if (mem == 0) {
        if (xTaskCreate ((TaskFunction_t)func, name, (uint16_t)stack, argument, prio, &hTask) != pdPASS) {
          hTask = NULL;
        }
      }
    }
  }

  return ((osThreadId_t)hTask);
}
  • 在末尾可以看出使用哪一种创建方式由参数mem决定,阅读前文发现只有
    • attr->stack_mem有值且指定用户数组以及任务控制块地址时的时候会使得mem置1,此时调用的时静态创建任务函数。
    • attr->stack_mem没有值的时候会使得mem置0,此时调用的时动态创建任务函数。
  • attr->stack_mem的值是由const osThreadAttr_t *attr形参结构体中取得,在创建任务的时候需要定义一个结构体作为参数传入osThreadNew()中,CubeMX给出的默认任务参数如下。
osThreadId_t startTaskHandle;
const osThreadAttr_t startTask_attributes = {
  .name = "startTask",
  .stack_size = 128 * 4,  //0.5KB
  .priority = (osPriority_t) osPriorityLow3,
};
  • 必须同时给出任务栈大小,任务堆栈,任务控制块地址才能调用静态创建任务函数,否则只取attr->stack_mem的值作为任务栈大小动态创建任务。若连attr->stack_mem都未给出,则取系统默认设置为任务栈大小动态创建任务。
1.2.2 xTaskCreate()任务栈空间理解

在阅读动态创建函数传参时候发现,居然也有任务栈空间这一项参数,而我记得我阅读操作手册的时候写的是动态任务创建任务栈空间由操作系统分配,归操作系统管理,那么为什么还要传这一个参数呢。

xTaskCreate ((TaskFunction_t)func, name, (uint16_t)stack, argument, prio, &hTask) 

回去重新阅读了操作手册发现自己理解错误,以下贴出操作手册原文。

每个任务都需要 RAM 来保存任务状态,并由任务用作其堆栈。 如果使用 xTaskCreate() 创建任务,则所需的 RAM 将自动 从 FreeRTOS 堆中分配。 如果创建任务 使用了 xTaskCreateStatic(),则 RAM 由应用程序编写者提供,因此可以在编译时进行静态分配。

  • 动态创建任务时也是需要指定任务栈空间的,但此时任务栈空间是由操作系统从自己管理的堆中分配的,管理权归操作系统。
  • 静态创建任务时候,指定的任务栈空间是由用户自己提供的,而占用的空间是在RAM中除去RTOS所管理的堆剩下的空间中提供的,所以不由RTOS管理。
  • 这里给出FreeRTOS官网开发者手册关于xTaskCreate描述的地址
  • 这里给出FreeRTOS官网开发者手册关于静态与动态创建任务描述的地址

所以实际上动态创建任务也是需要指定任务栈空间的,但是我们创建时并没有给出参数,他又是传了什么进去呢?

阅读上文源码发现,该参数一开始就被赋值了
stack = configMINIMAL_STACK_SIZE
其中
configMINIMAL_STACK_SIZE
是个宏定义,我们在FreeRTOSConfig.h文件中找到了定义
#define configMINIMAL_STACK_SIZE ((uint16_t)128)

  • 也就是说当我们不指定任务栈空间,CMSIS-OS提供的封装帮我自动设置了任务栈空间,并且调用动态创建任务函数。
1.2.3 CMSIS-OS与FreeRTOS任务栈空间单位不同!

接下来我遇到了最坑的一个点,如题所说,CMSIS-OS与FreeRTOS任务栈空间单位不同。

  • 在学习FreeRTOS时我观看了正点原子的视频,包括在网上搜索的资料都是面向FreeRTOS的。而FreeRTOS提供的创建任务函数中指定任务栈大小的参数stack其单位是

在stm32中,1字=4字节 1字节=8位

所以,我理所当然(我大意了)的认为osThreadNew()参数中的stack_size 其单位也是字,所以在计算的时候一直当作字来计算。

const osThreadAttr_t startTask_attributes = {
  .name = "startTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityLow3,
};

并且,在这个函数中的注释写了

/* In FreeRTOS stack is not in bytes, but in sizeof(StackType_t) which is 4 on ARM ports.       */
/* Stack size should be therefore 4 byte aligned in order to avoid division caused side effects */
翻译过来就是
/*在FreeRTOS中,堆栈不是以字节为单位,而是以sizeof(StackType_t)为单位,在ARM端口上为4*/
/*因此,堆栈大小应为4字节对齐,以避免除法造成的副作用*/

但当我调试时候计算栈空间时发现不太对劲,具体怎么发现的我就不说了,反正不是很聪明的样子。贴个调试图。
cmsis os,学习日志,stm32,单片机,嵌入式硬件
在和老学长一番争论,并且重新翻阅源码以及用户手册后我突然发现在注释的前后写了一段代码

    if (attr->stack_size > 0U) {
      /* In FreeRTOS stack is not in bytes, but in sizeof(StackType_t) which is 4 on ARM ports.       */
      /* Stack size should be therefore 4 byte aligned in order to avoid division caused side effects */
      stack = attr->stack_size / sizeof(StackType_t);
    }

他把我传进去的参数除了一个StackType_t大小。
查阅其定义发现
typedef portSTACK_TYPE StackType_t;
#define portSTACK_TYPE uint32_t

这个类型大小32位,8位1字节,也就4个字节,1个字。
sizeof()用于返回运算对象的内存大小,单位是字节。
所以sizeof(StackType_t)=4

  • 也就是说在osThreadNew()的任务栈空间参数在经过ARM封装后实际上是以“字节”为单位的,所以也就是说我我传进来的数字实际上是 128 * 4=512字节/4=128字赋给了stack

这里贴出官网对参数usStackDepth 也就是stack 赋给的形参值的介绍与地址。用户手册参数介绍地址

要分配用于 任务堆栈的 字数(不是字节)。例如,如果堆栈的宽度为 16 位,usStackDepth 为 100,则将分配 200 字节用作该任务的堆栈。 再举一例,如果堆栈的宽度为 32 位,usStackDepth 为 400,则将分配 1600 字节用作该任务的堆栈。
堆栈深度与堆栈宽度的乘积不得超过 size_t 类型变量所能包含的最大值。

1.2.4 选择动态分配时osThreadNew()分配的任务栈空间

这个比较好找,直接阅读源码

osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr) {
  char empty;
  const char *name;
  uint32_t stack;
  TaskHandle_t hTask;
  UBaseType_t prio;
  int32_t mem;

  hTask = NULL;

  if (!IS_IRQ() && (func != NULL)) {
    stack = configMINIMAL_STACK_SIZE;
    prio  = (UBaseType_t)osPriorityNormal;

    empty = '\0';
    name  = &empty;
    mem   = -1;

    if (attr != NULL) {
      if (attr->name != NULL) {
        name = attr->name;
      }
      if (attr->priority != osPriorityNone) {
        prio = (UBaseType_t)attr->priority;
      }

      if ((prio < osPriorityIdle) || (prio > osPriorityISR) || ((attr->attr_bits & osThreadJoinable) == osThreadJoinable)) {
        return (NULL);
      }

      if (attr->stack_size > 0U) {
        /* In FreeRTOS stack is not in bytes, but in sizeof(StackType_t) which is 4 on ARM ports.       */
        /* Stack size should be therefore 4 byte aligned in order to avoid division caused side effects */
        stack = attr->stack_size / sizeof(StackType_t);
      }

      if ((attr->cb_mem    != NULL) && (attr->cb_size    >= sizeof(StaticTask_t)) &&
          (attr->stack_mem != NULL) && (attr->stack_size >  0U)) {
        mem = 1;
      }
      else {
        if ((attr->cb_mem == NULL) && (attr->cb_size == 0U) && (attr->stack_mem == NULL)) {
          mem = 0;
        }
      }
    }
    else {
      mem = 0;
    }

    if (mem == 1) {
      hTask = xTaskCreateStatic ((TaskFunction_t)func, name, stack, argument, prio, (StackType_t  *)attr->stack_mem,
                                                                                    (StaticTask_t *)attr->cb_mem);
    }
    else {
      if (mem == 0) {
        if (xTaskCreate ((TaskFunction_t)func, name, (uint16_t)stack, argument, prio, &hTask) != pdPASS) {
          hTask = NULL;
        }
      }
    }
  }

  return ((osThreadId_t)hTask);
}

在函数开头 stack = configMINIMAL_STACK_SIZE;

参数stackosThreadNew()函数一开始就被赋值了,若参数attr->stack_mem缺省,则stack值不会被改变。翻找configMINIMAL_STACK_SIZE定义。
#define configMINIMAL_STACK_SIZE ((uint16_t)128)

  • 也就是说当用户不指定任务栈空间,CMSIS-OS会自动给用户分配128的任务栈。而只有attr->stack_size 的值大于0的时候,才会被除4。
  • 所以此时的128是以为单位的。
      if (attr->stack_size > 0U) {
        /* In FreeRTOS stack is not in bytes, but in sizeof(StackType_t) which is 4 on ARM ports.       */
        /* Stack size should be therefore 4 byte aligned in order to avoid division caused side effects */
        stack = attr->stack_size / sizeof(StackType_t);
      }
结论:
1.使用静态任务创建需要用户自己指定内存空间大小,参数单位是“字节”,并调用xTaskCreateStatic()。
2.使用动态任务创建CMSIS-OS指定了任务栈大小128字,并调用xTaskCreate()。
3.用户可以根据需要修改configMINIMAL_STACK_SIZE宏定义的值,来修改attr->stack_size缺省时任务栈大小的默认值。

更新中…文章来源地址https://www.toymoban.com/news/detail-525404.html

到了这里,关于STM32 FreeRTOS CMSIS—OS的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】FreeRTOS事件组学习

    事件组(Event Group) 一个任务执行之前需要经过多个条件进行判断,当条件全部满足或多个条件中的某一个条件满足才执行。 实验:创建两个任务,一个事件组,当按键一二三都按过一遍才打印。 实现:FreeRTOS在CubeMX中使用V1版本的时候,没法设置Events,必须手动添加,或者

    2024年02月12日
    浏览(33)
  • 【STM32】FreeRTOS软件定时器学习

      软件定时器 FreeRTOS提供了现成的软件定时器功能,可以一定程度上替代硬件定时器,但精度不高。 实验:创建一个任务,两个定时器,按键开启定时器,一个500ms打印一次,一个1000ms打印一次。 实现:在【STM32】FreeRTOS事件组学习基础上修改。 上面的代码是CubeMX封装好的。

    2024年02月12日
    浏览(36)
  • (第48-59讲)STM32F4单片机,FreeRTOS【事件标志、任务通知、软件定时器、Tickless低功耗】【纯文字讲解】【】

    【吐血总结】FreeRTOS难点、Systick中断-滴答定时器、PendSV中断-任务切换、SVC中断-系统底层、时间片调度-时钟节拍【已完结】 (第1-8讲)STM32F4单片机,FreeRTOS基础知识总结【视频笔记、代码讲解】【正点原子】【原创】 (第9-10讲)STM32F4单片机,FreeRTOS任务创建和删除(动态方

    2024年02月01日
    浏览(61)
  • 【STM32】FreeRTOS消息队列和信号量学习

    一、消息队列(queue) 队列是一种用于实现任务与任务之间,任务与中断之间消息交流的机制。 注意:1.数据的操作是FIFO模式。 2.队列需要明确数据的大小和队列的长度。 3.写和读都会出现堵塞。 实验:创建一个消息队列,两个发送任务,一个接收任务。 其中任务一任务三

    2024年02月13日
    浏览(39)
  • 【学习日记】【FreeRTOS】FreeRTOS 移植到 STM32F103C8

    本文基于野火 FreeRTOS 教程,内容是关于 FreeRTOS 官方代码的移植的注意事项,并将野火例程中 STM32F103RC 代码移植到 STM32F103C8。 两个下载链接: 官 网 代码托管 Source:内核源码,我们需要的主要内容 根目录:这里的 C 文件都是由 C 语言编写的,所以在各种单片机上是通用的

    2024年02月11日
    浏览(45)
  • STM32操作系统FreeRTOS学习——基于hal库

    目录 一、基础概念 1、FreeRTOS 2、单片机编程的系统概念 a、裸机系统,包括轮询系统(不包括中断)和前后台系统(中断为前台,轮询为后台) b、多任务系统 3、FreeRTOS编程风格 a、数据类型 b、变量名的定义 c、函数名 d、宏定义 二、Cubemx创建工程 1、创建任务 2、创建队列

    2024年02月10日
    浏览(38)
  • STM32CubeMx学习FreeRTOS的绝对延时和相对延时

    在阻塞状态中 可以空闲出时间 来让低优先级的任务可以进行 有两种阻塞延时 一个是相对延时 也就是  这样的osDelay可以让在到这里的时候,延时500ms 也就是程序到这里才500ms 不记程序前面所用的时间 而还有一个绝对延时 绝对延时指的是 加上程序自己跑的时间 全部的一起

    2024年02月14日
    浏览(41)
  • STM32单片机学习3--STM32控制键盘

    单片机型号:STM32F103C8T6 开发环境:Keil5 4种输入模式 上拉输入模式:在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为1,高电平(与Vdd相连的为上拉电阻); 下拉输入模式:在默认状态下(GPIO引脚无输入),读取得的GPIO引脚数据为0,低电平(与Vss相连的为下拉电

    2024年02月10日
    浏览(55)
  • STM32单片机学习4--STM32控制八段码

    数码管:实际上是多个LED按照一定顺序排列,并加上遮罩所构成的元件。 八段码一般会引出9个引脚,其中7个引脚显示数字(或某些字母),1个显示小数点,1个作为片选端。 根据连接方式的不同,数码管分为 共阳 和 共阴 。 共阳在这端输出低电平时点亮,高电平时会熄灭

    2024年01月23日
    浏览(46)
  • 【32单片机学习】(11)STM32启动过程详解

    目录 前言 一、系统架构 二、启动配置 三、启动流程 1.首先复位MCU,获取栈顶指针MSP和PC指针的内容 2.根据PC的值找到复位中断处理函数Reset_Handler 1.进入中断处理函数Reset_Handler 2.进入SystemInit函数 3._main函数 3.进入main函数 总结          通过查阅官方手册和对实际代码进行

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包