FreeRtOs(1)-基础知识

这是FreeRTOS系列学习文章的第一节,主要是关于FreeRTOS的前置知识点。

1 基础知识

首先我们了解freertos中的变量类型.在这个实时操作系统中使用的基本变量类型虽然是标准的,但是都被更改为了其他命令,具有显著特征.

1.1 变量类型

在这个参数系统中int没有被使用, 取而代之的是unsigned intlong.
并且由于C标准中没有规定char默认是有符号的还是无符号的.所以,在FreeRTOS中,我们都需要明确指定变量char是有符号的还是无符号的。

在FreeRTOS中,使用的数据类型虽然都是标准C里面的数据类型,但是针对不同的处理器,对标准C的数据类型又进行了重定义,给它们设置了一个新的名字,比如为char重新定义了一个名字portCHAR,这里的port表示接口,在将FreeRTOS移植到处理器上时,需要用这些接口文件把它们连接在一起。但是用户在写程序时并非一定要遵循FreeRTOS的风格,仍可以直接用C语言的标准类型。在FreeRTOS中,int型从不使用,只使用short型和long型。在Cortex-M内核的MCU中,short为16位,long为32位。

具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define portCHAR        char
#define portFLOAT float
#define portDOUBLE double
#define portLONG long
#define portSHORT short
#define portSTACK_TYPE uint32_t
#define portBASE_TYPE long

typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;

#if( config USE_16_BIT_TICKS == 1 )

typedef uint16_t TickType_t;

#define portMAX_DELAY ( TickType_t ) 0xffff

#else
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL

1.1.1 具体变量名

FreeRTOS中,定义变量时往往会把变量的类型当作前缀加在变量上,这样做的好处是让用户一看到这个变量就知道该变量的类型。比如char型变量的前缀是c,short型变量的前缀是s,long型变量的前缀是l,portBASE_TYPE类型变量的前缀是x。还有其他的数据类型,比如数据结构、任务句柄、队列句柄等定义的变量名的前缀也是x。

如果一个变量是无符号型的,那么会有一个前缀u,如果是一个指针变量,则会有一个前缀p。因此,当我们定义一个无符号的char型变量时会加一个uc前缀,当定义一个char型的指针变量时会加一个pc前缀。

FreeRTOS都会将标准的C数据类型用typedef重新定义一个类型名。这些经过重定义的数据类型放在portmacro.h

1.1.2 函数名

在FreeRTOS中函数的命令非常特别,其将返回类型、定义在哪里、干什么的都体现在函数名中:

  • vTaskPrioritySet()函数的返回值为void型,在task.c文件中定义。
  • xQueueReceive()函数的返回值为portBASE_TYPE型,在queue.c文件中定义。
  • vSemaphoreCreateBinary()函数的返回值为void型,在semphr.h文件中定义。

1.2 系统模式

在单片机中,由于结构简单、性能较低所以使用都是一些简单的系统管理方式:

  • 裸机系统
  • 轮询系统
  • 前后台系统
  • 多任务系统

关于裸机系统和轮询系统不在多说,主要是前后台系统和任务系统。其实这两个系统也是非常类似的。

前后台系统中,将整个系统划分为前台和后台,前台其实就是main函数;而后台就是对应的中断处理函数。一个事件的响应和处理都是在中断处理函数中进行,这就到这了效率低下。

在多任务系统中,只是多了一种任务处理方式。事件的响应仍然是在中断处理函数中,但是具体的执行被转换到了任务处理函数。提高了效率

各个系统的主要区别:

2.1 名词解释

2.1.1 任务

在FreeRTOS中提出了任务这个概念。在裸机系统中,系统的主体就是main()函数里面顺序执行的无限循环,在这个无限循环中,CPU按照顺序完成各种操作。在多任务系统中,我们根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这种函数我们称为任务。

“任务”的主要特点是:

  • 每一个任务都有自己的栈,它们之间互不干扰
  • 在任何时间点,应用程序中只能执行一个任务,实时 RTOS 调度器负责决定所要执行的任务。
  • 使用优先级进行cpu资源的分配
  • 支持完全抢占机制
  • 独立、死循环、不可返回

2.1.2 协程

协程和任务是非常类似的,但是仍然有着一下不同:

  • 所有的协程共享一个栈,所以协程对于内存的需求是很小的,这使得协程就主要应用于内存非常小的单片机中。
  • 协程间使用优先级协同调度,但可以包含在使用抢占式任务的应用程序中。但是如果同时使用了任务和协程,则协程总是被任务抢占。

请注意,可以仅使用任务,仅使用协程,或结合两者设计应用程序。然而,任务和协程使用不同的 API 函数,因此不能通过队列(或信号量)将数据从任务传递到协程,反之亦然。

3.1 源码结构

FreeRTOS是开源的实时操作系统, 其核心代码仅仅是task.clist.c这两个文件. 其他的源文件的作用如下:

  • queue.c 这个文件提供了队列和信号量支持,这个文件几乎总是需要的。
  • timers.c 这个文件提供了软件定时器功能,只有当要用到软件定时器的使用才需要包含它。
  • evnet-group.c 这个文件提供了任务组的功能,只有当需要使用任务组是才需要将其包含构建。
  • crotine.c 这个文件提供了协程的代码,只有需要使用协程的使用才是需要将它包含进来。

3.1.1 其他目录

源码中还有一个portable的目录,这个目录主要是编译器的接口文件。

3.1.2 构建系统

想要构建这个操作系统,一下的文件是不可少的。

  • 上面讲到的几个.c文件,最后都加上
  • include/目录下的所有头文件+FreeRTOSConfig.h 这个文件在例子中有。

4.0 临界区

每个进程中访问临界资源的那段代码称为临界区(Critical Section) (临界资源是一次仅允许一个进程使用的共享资源)。

每次只准许一个进程进入临界区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临界资源,多个进程必须互斥地对它进行访问。多个进程中涉及到同一个临界资源的临界区称为相关临界区。

具体的概念:

考虑一个由n个进程{P 0 , P 1 ,..., P n−1 }组成的系统。每个进程都有一段称为临界区(critical section)的代码,进程会在其中更改公共变量、更新表、编写文件等等。该系统的重要特性是,当一个进程在其临界区(critical section)执行时,不允许其他进程在其临界区(critical section)执行。也就是说,没有两个进程可以同时在它们的临界区(critical section)执行。临界区问题(critical-section problem)是设计一个进程可以用来进行协作的协议。每个进程必须请求允许进入其临界区(critical section)。实现此请求的代码段是入口区(entry section)。临界区(critical section)之后则是出口区(exit section)。其余的代码是剩余区(remainder section)。

4.1 各种区的总结

  • 临界区:每个进程有一个代码段叫临界区,没有两个进程可同时在临界区内执行
  • 进入区:实现请求进入临界区的代码段叫做进入区
  • 退出区:退出临界区后进行一些状态处理的代码段叫做退出区
  • 剩余区:其他代码称为剩余区
  • 临界区问题:设计一个协议,每个进程必须请求允许进入其临界区

4.2 临界区解答的三项要求

  • 互斥:若一个进程在临界区内,其他进程不得进入临界区
  • 前进:如果没有进程在其临界区内执行且有进程需要进入临界区,那么只有那些想要进入临界区的进程参加选择以可以进入临界区
  • 有限等待:从一个进程做出进入临界区的请求,直到该请求允许为止,其他进程允许进入其临界区的次数有上限

4.3 操作系统内临界区问题的处理方式:

  • 非抢占内核
    • 不允许处于内核模式的进程被抢占, 从根本上不会导致竞争条件
  • 抢占内核
    • 允许处于内核模式的进程被抢占
      优点
      更适合实时编程
      抢占内核响应更快

5.0 杂项

  1. 在FreeRTOS的代码中经常会看到诸如traceTIMER_COMMAND_SENDtraceEVENT_GROUP_CREATE这类的函数宏,但是点进去看又发现没有定义如何东西,其实这是FreeRTOS和Trace调试工具的联合使用所需要的代码。

官网写道:

FreeRTOS 内核在关键位置包含 100 多个“跟踪钩子”

所以我们看到的哪些没有被定义的函数宏实际上就是钩子函数,用于后续的Trace调试。


FreeRtOs(1)-基础知识
https://ysc2.github.io/ysc2.github.io/2024/01/06/FreeRtOs-1-基础知识/
作者
Ysc
发布于
2024年1月6日
许可协议