STM32开发(FreeRTOS实时操作系统)

目录

一、FreeRTOS实时操作系统介绍

1、FreeRTOS实时操作系统是什么

2、裸机开发和系统开发的区别

3、FreeRTOS实时操作系统的特点

1)多任务并发处理(实时性)

(1)抢占式调度机制(任务管理)

(2)通讯机制(同步互斥)

2)内存管理

3)时间管理

4)记录功能

4、FreeRTOS实时操作系统的任务调度机制

5、线程状态

6、FreeRTOS操作系统和Linux操作系统的区别

二、STM32CubeMX在线下载FreeRTOS内核

三、使用STM32CubeMX配置FreeRTOS实时操作系统

1、安装实时操作系统

2、CubeMX配置实时操作系统

四、CMSIS_RTOS2和FreeRTOS提供的库函数

1、CMSIS_RTOS2和FreeRTOS的区别

2、CMSIS_RTOS2提供的函数

3、FreeRTOS提供的函数

五、创建线程

1、静态创建线程分配任务资源

1)CubeMX创建线程

2)CubeMX初始化的代码

(1)主函数

(2)定义结构体并填充

(3)定义任务入口函数(线程执行函数)

(4)向内核申请线程资源

2、动态创建线程(手动创建线程)

1)osThreadNew(新建线程函数)

2)定义线程对象和参数结构体

3)定义任务的入口函数

4)初始化新建线程向内核申请资源

六、任务/线程间的同步互斥

1、二值信号量

1)xSemaphoreCreateBinary(创建二值信号量)

2)xSemaphoreTake(申请信号量)

3)xSemaporeGive(释放信号量)

4)示例代码

(1)定义信号量对象

(2)创建二值信号量

(3)使用二值信号量实现同步

(4)程序现象

2、计数型信号量

1)xSemaporeCounting(创建计数型信号量)

2)xSemaporeTake(申请信号量)

3)xSemaporeGive(释放信号量)

4)示例代码

(1)定义信号量对象

(2)创建信号量

(3)使用信号量实现操作

(4)程序现象

3、事件组(事件标志位)

1)事件组的使用情景

2)(创建事件组)

3)(将标志位置一)

4)(阻塞等待标志位置一)

5)(将标志位清零)

6)示例代码

(1)定义事件组对象

(2)定义宏体代表事件组某一位

(3)创建事件组

(4)操作事件标志位完成功能

(5)程序现象

7)示例代码

4、消息队列

1)(创建消息队列)

2)(向消息队列写入数据)

3)(从消息队列读取数据)

4)示例代码

(1)定义消息队列对象

(2)创建消息队列

(3)操作消息队列完成功能

(4)程序现象


1、FreeRTOS实时操作系统是什么

FreeRTOS实时操作系统,是一种轻量级的实时操作系统,为了实现快速响应、快速处理
FreeRTOS实时操作系统,是RTOS实时操作系统下的一个版本/子集
FreeRTOS实时操作系统 = 裸机开发 + 多线程(多任务)并发处理

RTOS实时操作系统具备很多版本/子集:FreeRTOS、RT-Thread、Thread-X ······

基于操作系统开发的是系统开发,不基于操作系统开发的是裸机开发

2、裸机开发和系统开发的区别

裸机开发:不使用操作系统开发,所有的驱动代码都按照main函数中的执行顺序执行
系统开发:使用操作系统开发,所有的驱动代码支持多进程/多线程的并发处理机制,实现快速响应、快速处理

多进程和多线程的区别:
    1. 资源量(进程是资源分配的最小单位、线程是任务调度的最小单位)
    2. 安全性(进程间用户空间相互独立[IPC通讯机制]、同一进程下的线程间共享同一用户资源[同步互斥])
    3. 高效性(进程间切换涉及到进程的上下文切换、线程间可以直接切换,固然线程的并发效率更高)

3、FreeRTOS实时操作系统的特点

1)多任务并发处理(实时性)

具有严格的任务调度机制,确保任务按照优先级和时间约束、以预定的先后顺序执行
(1)抢占式调度机制(任务管理)
抢占式任务调度机制:
    给每个任务/线程分配对应的优先级等级,
    优先级等级高的任务/线程先执行、优先级等级低的任务/线程后执行

FreeRTOS实时操作系统默认使用抢占式调度机制:
    优先级不同时:系统内核会优先执行线程等待列表中优先等级最高的线程,
    优先级相同时:系统内核会按照队列思想逐个执行

注意:
    1.只有当线程变为阻塞态时,才会被抢夺内核资源
    2.任务的优先级等级就是一个数字,数字越大,优先级等级越高
      中断的优先级等级就是一个数字,数字越小,优先级等级越高

linux操作系统的任务调度机制:默认使用时间片轮询机制,也可以使用抢占式任务调度机制
FreeRTOS实时操作系统的任务调度机制:默认使用抢占式任务调度机制,也可以使用时间片轮询机制
(2)通讯机制(同步互斥)
多种通讯机制,实现同步互斥,使任务在相同优先级下,也可以按照规定有序执行

1. 互斥锁
2. 消息队列
3. 信号量
4. 事件组

2)内存管理

可以动态分配内存空间,静态分配内存空间

3)时间管理

有软件定时器,严格管理任务时间

4)记录功能

有内核,文件信息,设备信息,内核链表/内核数组

4、FreeRTOS实时操作系统的任务调度机制

linux操作系统的任务调度机制:默认使用时间片轮询机制,也可以使用抢占式任务调度机制
FreeRTOS实时操作系统的任务调度机制:默认使用抢占式任务调度机制,也可以使用时间片轮询机制

抢占式任务调度机制:给每个任务/线程分配对应的优先级等级,优先级等级高的任务/线程先执行、优先级等级低的任务/线程后执行
注意:
    任务的优先级等级就是一个数字,数字越大,优先级等级越高
    中断的优先级等级就是一个数字,数字越小,优先级等级越高

5、线程状态

就绪态:任务的资源分配成功,等待被运行
运行态:被分配资源的任务使用系统的时间片,成功开始运行
阻塞态:任务中存在耗时、延时操作(Delay函数、超时检测)时,任务处于阻塞状态
       当某个任务处于阻塞态时,别的任务可以抢占系统的资源,进而运行
挂起态:任务1中使用挂起函数,挂起任务2,任务2处于挂起态,不会被系统调用
        需要在别的任务/任务1中使用解除挂起函数,解除任务2的挂起,此时任务2处于就绪态

6、FreeRTOS操作系统和Linux操作系统的区别

二、STM32CubeMX在线下载FreeRTOS内核



三、使用STM32CubeMX配置FreeRTOS实时操作系统

1、安装实时操作系统


2、CubeMX配置实时操作系统

四、CMSIS_RTOS2和FreeRTOS提供的库函数

        os开头的函数:是由ARM定义的所有操作系统都支持的CMSIS_RTOS2通用接口(相当于标准IO)
        x开头的函数:FreeRTOS内核自身提供的函数(相当于文件IO)

1、CMSIS_RTOS2和FreeRTOS的区别

CMSIS_RTOS2是由ARM公司定义的,专门作用于Cortex-M内核的操作系统的通用API标准接口,优点是可移植性强

FreeRTOS是RTOS操作系统中的一个子集,一个具体的操作系统内核

2、CMSIS_RTOS2提供的函数

在cmsis_os2.c这个文件中的所有函数都是由CMSIS_RTOS2所提供的API函数接口
这些API接口在所有Cortex-M内核的实时操作系统中都可以使用
由CMSIS_RTOS2所提供的API接口本质上是由FreeRTOS所提供的API接口包装而成

3、FreeRTOS提供的函数

五、创建线程

1、静态创建线程分配任务资源

1)CubeMX创建线程


2)CubeMX初始化的代码

(1)主函数

        在内核启动前(调度管理),不允许出现任何延时函数

(2)定义结构体并填充

(3)定义任务入口函数(线程执行函数)

(4)向内核申请线程资源

2、动态创建线程(手动创建线程)

1)osThreadNew(新建线程函数)

osThreadId_t osThreadNew (osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
功能:
    CMSIS_RTOS2通用接口下提供的用于创建任务的函数
参数:
    func:当前创建的任务对应的入口函数名(入口函数地址)
    argument:给当前创建的任务对应的入口函数的传参
    attr:当前创建的任务的信息结构体
返回值:
    返回线程标识符,线程对象,创建失败返回NULL

// 线程/任务的信息结构体,他需要被传输给内核进行识别
typedef struct {
  const char                   *name;       // 当前被创建的任务的名字,内核通过这个名字识别他
  uint32_t                 attr_bits;       // 当前被创建的任务的特殊属性位
  void                      *cb_mem;        // 当前被创建的任务需要创建在堆区的哪片地址上
  uint32_t                   cb_size;       // 当前被创建的任务需要创建在堆区的空间大小
  void                   *stack_mem;        // 当前被创建的任务需要创建在栈区的哪片地址上
  uint32_t                stack_size;       // 当前被创建的任务需要创建在栈区的空间大小
  osPriority_t              priority;       // 当前被创建的任务的优先级等级
  TZ_ModuleId_t            tz_module;       // 使用MON模式时,受信模块的属性
  uint32_t                  reserved;       // 保留信息
} osThreadAttr_t;

2)定义线程对象和参数结构体

3)定义任务的入口函数

4)初始化新建线程向内核申请资源

六、任务/线程间的同步互斥

由于FreeRTOS操作系统中提供的任务的优先等级个数有限
如果任务很多,数量超过优先级个数,就会被操作系统按照任务调取机制随机分配
此时可以引入同步互斥,使相同优先级的任务按照规定的顺序执行

1、二值信号量

信号量本质就是一个数字,一个计数单位,用于记录空间资源的个数
当信号量 > 0 时,代表存在空闲资源,可以申请空闲资源使用
当信号量 = 0 时,代表没有空闲资源,无法申请空闲资源使用

二值信号量(二进制信号量),就是信号量的值只有 0 和 1

信号量的核心操作(PV操作)
P操作:申请信号量(信号量 -1)
V操作:释放信号量(信号量 +1)

1)xSemaphoreCreateBinary(创建二值信号量)

SemaphoreHandle_t xSemaphoreCreateBinary(void);
功能:
    FreeRTOS内核提供的用于创建二值信号量的函数(初始信号量的值为0,也就是无法申请到信号量)
参数:
    无
返回值:
    函数执行成功,二值信号量创建成功,返回创建成功的二值信号量对象
    函数执行失败,返回NULL
osSemaphoreNew函数也能实现创建信号量的效果

2)xSemaphoreTake(申请信号量)

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
功能:
    FreeRTOS内核提供的用于申请信号量的函数(不管什么类型的信号量都可以申请,信号量-1)
参数:
    xSemaphore:需要申请的信号量对象
    xBlockTime:超时检测时间,当前函数的最大阻塞时间
返回值:
    函数执行成功,返回pdTRUE(1)
    函数执行失败,返回pdFALSE(0)
osSemaphoreAcquire函数也能实现申请信号量的效果

3)xSemaporeGive(释放信号量)

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)
功能:
    FreeRTOS内核提供的用于释放信号量的函数(不管什么类型的信号量都可以释放,信号量+1)
参数:
    xSemaphore:需要释放的信号量对象
返回值:
    函数执行成功,释放信号量成功,返回pdTRUE(1)
    函数执行失败,释放信号量失败,返回pdFALSE(0)
osSemaphoreRelease函数也能实现释放信号量的效果

4)示例代码

        使用二进制信号量,实现默认任务、任务1、任务2、按顺序执行

(1)定义信号量对象

(2)创建二值信号量

(3)使用二值信号量实现同步

(4)程序现象

2、计数型信号量

1)xSemaporeCounting(创建计数型信号量)

SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);
功能:
    FreeRTOS内核提供的用于创建计数型信号量的函数
    计数型信号量:就是信号量的个数有很多
参数:
    uxMaxCount:创建出来的计数型信号量个数的最大值
    uxInitialCount:创建出来的计数型信号量初始个数
返回值:
    函数执行成功,计数型信号量创建成功,函数返回创建成功的计数型信号量对象
    函数执行失败,返回NULL

2)xSemaporeTake(申请信号量)

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)
功能:
    FreeRTOS内核提供的用于申请信号量的函数(不管什么类型的信号量都可以申请,信号量-1)
参数:
    xSemaphore:需要申请的信号量对象
    xBlockTime:超时检测时间,当前函数的最大阻塞时间
返回值:
    函数执行成功,返回pdTRUE(1)
    函数执行失败,返回pdFALSE(0)
osSemaphoreAcquire函数也能实现申请信号量的效果

3)xSemaporeGive(释放信号量)

BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore)
功能:
    FreeRTOS内核提供的用于释放信号量的函数(不管什么类型的信号量都可以释放,信号量+1)
参数:
    xSemaphore:需要释放的信号量对象
返回值:
    函数执行成功,释放信号量成功,返回pdTRUE(1)
    函数执行失败,释放信号量失败,返回pdFALSE(0)
osSemaphoreRelease函数也能实现释放信号量的效果

4)示例代码

        使用二进制信号量和计数信号量,实现任务1、任务2,交替执行5次,再执行默认任务

(1)定义信号量对象

(2)创建信号量

(3)使用信号量实现操作

(4)程序现象

3、事件组(事件标志位)

事件组又称为事件标志位
事件组就是多个事件标志位的集合

事件组可以理解为一个寄存器,这个寄存器的每一位都代表一个事件
每一位的值都代表一个事件是否执行成功
如果 某一位的值 = 0 ,则代表这一位对应的事件没有执行
如果 某一位的值 = 1 ,则代表这一位对应的之间执行成功

1)事件组的使用情景

通过多个任务管理一个任务
1. 当所有任务均执行成功后,执行主任务
2. 当诸多任务中有一个执行成功后,执行主任务

2)(创建事件组)

osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr);
功能:
    适用于CMSIS_RTOS V2通用接口下用于创建事件组的函数
参数:
    attr:需要创建出来的事件组的信息结构体(可以填充NULL默认属性信息,由FreeRTOS自动分配)
          如果自己填充填充name即可,有特殊需求可以全部自行填充
返回值:
    函数执行成功,代表事件组创建成功,返回创建成功的事件组对象
    函数执行失败,返回NULL

// 参数attr的结构体变量
// Attributes structure for event flags.
typedef struct {
  const char                   *name;   ///< name of the event flags
  uint32_t                 attr_bits;   ///< attribute bits
  void                      *cb_mem;    ///< memory for control block
  uint32_t                   cb_size;   ///< size of provided memory for control block
} osEventFlagsAttr_t;

3)(将标志位置一)

uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags);
功能:
    适用于CMSIS_RTOS V2通用接口下用于对事件组中某一位/某些位置1的函数
    置1:代表当前事件执行成功,也就是当前任务执行成功
参数:
    ef_id:需要操作的事件组对象
    flags:需要被置1的事件标志位(需要被置1的对应任务的事件标志位)
返回值:
    函数执行成功,返回osOK(0)
    函数执行失败,返回错误码

// os函数使用的返回值
typedef enum {
  osOK                      =  0,         ///< Operation completed successfully.
  osError                   = -1,         ///< Unspecified RTOS error: run-time error but no other error message fits.
  osErrorTimeout            = -2,         ///< Operation not completed within the timeout period.
  osErrorResource           = -3,         ///< Resource not available.
  osErrorParameter          = -4,         ///< Parameter error.
  osErrorNoMemory           = -5,         ///< System is out of memory: it was impossible to allocate or reserve memory for the operation.
  osErrorISR                = -6,         ///< Not allowed in ISR context: the function cannot be called from interrupt service routines.
  osStatusReserved          = 0x7FFFFFFF  ///< Prevents enum down-size compiler optimization.
} osStatus_t;

4)(阻塞等待标志位置一)

uint32_t osEventFlagsWait (osEventFlagsId_t ef_id,
                           uint32_t flags,
                           uint32_t options,
                           uint32_t timeout);
功能:
    适用于CMSIS_RTOS V2通用接口下用于阻塞等待对应事件标志位被置1的函数
参数:
    ef_id:需要操作的事件组对象
    flags:需要等待被置1的事件标志位
    options:
            osFlagsWaitAny:等待第二个参数flags中任意一个标志位被置1
            osFlagsWaitAll:等待第二个参数flags中所有标志位被置1
    timeout:超时检测时间,当前函数的最大阻塞时间
            // Timeout value.
            #define osWaitForever         0xFFFFFFFFU ///< Wait forever timeout value.
返回值:
    函数执行成功,返回osOK(0)
    函数执行失败,返回错误码

//选择阻塞的状态
// Flags options (\ref osThreadFlagsWait and \ref osEventFlagsWait).
#define osFlagsWaitAny        0x00000000U //等待其中1位被置1
#define osFlagsWaitAll        0x00000001U //等待所有位被置1
#define osFlagsNoClear        0x00000002U //不清除那些已经被设置为等待的标志位
 
//当前函数的错误码
// Flags errors (returned by osThreadFlagsXxxx and osEventFlagsXxxx).
#define osFlagsError          0x80000000U ///< Error indicator.
#define osFlagsErrorUnknown   0xFFFFFFFFU ///< osError (-1).
#define osFlagsErrorTimeout   0xFFFFFFFEU ///< osErrorTimeout (-2).
#define osFlagsErrorResource  0xFFFFFFFDU ///< osErrorResource (-3).
#define osFlagsErrorParameter 0xFFFFFFFCU ///< osErrorParameter (-4).
#define osFlagsErrorISR       0xFFFFFFFAU ///< osErrorISR (-6).


任务1:对应事件组中的第0位        0x1 << 0    
任务2:对应事件组中的第1位        0x1 << 1
任务3:等待任务1和任务2都被执行完,任务3才能执行
       也就是任务1的事件标志位被置1并且任务2的事件标志位也要被置1,任务3才能执行
       flags = (0x1 << 0)| (0x1 << 1)

5)(将标志位清零)

uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags)
功能:
    适用于CMSIS_RTOS V2通用接口下用于清除对应标志位的函数
参数:
    ef_id:需要操作的事件组对象
    flags:需要清除的事件标志位
返回值:
    函数执行成功,返回osOK
    函数执行失败,返回错误码

6)示例代码

        实现所有任务都完成后,执行默认任务

(1)定义事件组对象

(2)定义宏体代表事件组某一位

(3)创建事件组

(4)操作事件标志位完成功能

(5)程序现象

7)示例代码

        任务1、任务2、任务3中随便一个任务执行成功,都能让默认任务执行

4、消息队列

消息队列:
    1.默认使用FIFO(先进先出,队列)
    2.可以使用优先级划分消息处理等级(优先级高的消息先被处理,优先级低的消息后被处理)
    3.相同优先级下,遵循FIFO规则,先进先出
    4.消息队列中的消息是一次性的,读取后就不存在于消息队列中了

1)(创建消息队列)

osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr) 
功能:
    适用于CMSIS_RTOS V2通用接口用于创建一个消息队列的函数
参数:
    msg_count:当前创建的消息队列支持的消息最大个数
    msg_size:当前创建的消息队列中的每个消息的大小,单位为字节
    attr:当前创建的消息队列的信息结构体,需要传递给内核(NULL默认信息)
返回值:
    函数执行成功,代表消息队列创建成功,返回创建成功的消息队列对象
    函数执行失败,返回NULL

2)(向消息队列写入数据)

osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout) 
功能:
    适用于CMSIS_RTOS V2通用接口用于向消息队列中写入数据的函数
参数:
    mq_id:需要操作的消息队列对象
    msg_ptr:需要写入到消息队列中的消息
    msg_prio:当前写入到消息队列中的消息的优先级等级
    timeout:超时检测时间,也就是当前函数的最大阻塞时间
返回值:
    函数执行成功,返回osOK(0)
    函数执行失败,返回错误码

3)(从消息队列读取数据)

osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout) 
功能:
    适用于CMSIS_RTOS V2通用接口用于从消息队列中读取数据的函数
参数:
    mq_id:需要操作的消息队列对象
    msg_ptr:从消息队列中读取的消息存储的位置
    msg_prio:需要读取的消息队列中消息的优先级等级
    timeout:超时检测时间,也就是当前函数的最大阻塞时间
返回值:
    函数执行成功,返回osOK(0)
    函数执行失败,返回错误码

消息队列的消息优先级等级(0-9级,数字越大,优先级等级越高)
0:普通日志消息
9:紧急消息

4)示例代码

(1)定义消息队列对象

(2)创建消息队列

(3)操作消息队列完成功能

(4)程序现象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值