FreeRTOS历史

 RTOS 已经有超过30年的历史
 比较著名的商业产品有;(按照时间顺序)
 VRTX Microtec (Mentor 公司收购)
 pSOS Wind RiverSystem wrs.com (WRS 公司收购)
 OS-9 Microware Microware.com (Metorworks 收购)
 SMX Micro Digtal www.smxrtos.com
 VxWorks Wind RiverSystem wrs.com (Intel 公司收购)
 LynxOS lynuxwork ynuxworks.com
 QNX QNX www.qnx.com (黑莓收购)
 CMX CMX system www.cmx.com
 Nucleus ATI www.mentor.com/esd (Mentor收购)
 THREADX Expresslogic www.rtos.com
 uC/OS –II/III Micrium www.micrium.com
 INTEGRITY Gree Hill www.ghs.com
 全球超过100多种,中国几种,更有许多用户自己设计RTOS

RTOS应用编程接口
 CMSIS-RTOS-ARM 制定Cortex MCU RTOS接口标准

FreeRTOS 资料比较少,官方只有英文的PDF 手册
 芯片公司的BSP 比较多
 视频(ATMEL 工程师)
 1 RTOS 介绍
 http://v.youku.com/v_show/id_XNTgyMTEzOTU2.html 
 2 RTOS 特性和API
 http://v.youku.com/v_show/id_XNTgyMTE4NjQw.html 
 3 FreeRTOS 使用
 http://v.youku.com/v_show/id_XNTgyMTE4MDg4.html 
 4 深入了解FreeRTOS
 http://v.youku.com/v_show/id_XNTgyMTIyODgw.html 

PDF 手册链接
http://www.freertos.org/Documentation/RTOS_book.html

目录

1、FreeRTOS介绍

2、FreeRTOS开发应用

3、FreeRTOS如何工作

4、STM32Cube FreeRTOS例程

—————————————————-

1、FreeRTOS介绍

(1.win8

(2.Blackberry4.6

 

(3.Android 4.0

 

 (4.ubuntu 12-linux

(5.HP WebOS

 

(6.Mac OS X Snow-Leopard

 

(7.Symbian Belle

 

从层次来看,操作系统位于计算
机硬件之上,应用软件之下.
•OS是一种为应用程序提供服务的
系统软件.

1.1常见两种编程方式

前后台模式的特点:
• 后台
• 应用程序通常是一个无限的循环,在循环中,通过调用相应的处理函数
完成相应的操作,这部分可以看做为后台行为
• 前台
• 中断服务程序接收异步中断,来通知后台,后台收到中断请求后
进行处理

1.2嵌入式操作系统的特点

• 嵌入式操作系统是一种用途广泛的系统软件,通常包括与硬件相关
的底层驱动软件、系统内核、设备驱动接口、通信协议、图形界面、
标准化浏览器等。
• 负责嵌入式系统的全部软、硬件资源的分配、任务调度,控制、协
调并发活动。
• 目前在嵌入式领域广泛使用的操作系统有:嵌入式实时操作系统
FreeRTOS、µC/OS-II、RThread、WindowsCE、VxWorks 等等

1.3前后台程序基本结构

运行环境包括了两部分:处理器中的运行环境和内存中的运行环境

处理器通过两个指针寄存器(PC和SP)来与应用代码和应用堆栈建立联系并运行它;

1.4两种开发模式对比

前后台模式

资源使用,不需要额外分配空间给OS

学习曲线,开发者不需要学习OS的API

实时性,难以确保每个操作能够
实时相应,如果一个函
数花费过长时间,将使
整个系统的实时性下降
系统结构,耦合度较高

嵌入式操作系统
资源使用 
需要分配资源给OS(内核资源使
用情况取决于使用何种OS)
学习曲线
开发者需要熟悉OS的基本操作
(任务建立/删除、任务间通信、
优先级处理、中断处理…)
实时性 
实时嵌入式OS的调度算法可以
最大程度保证系统的实时性
系统结构 耦合度较高 模块化、结构清晰

可协作/可扩展/可维护

—————————————————

2、FreeRTOS开发应用

FreeRTOS是一个轻量级嵌入式操作系统,具有源码公开、可移植、可裁剪、
调度策略灵活的特点,可以方便地移植到各种嵌入式控制器上实现满足用户需
求的应用.
• FreeRTOS作为一个轻量级嵌入式操作系统,提供一个高层次的可信任代码
• 源代码以C开发,系统实现的任务没有数量的限制.
• FreeRTOS内核支持优先级调度算法,每个任务课根据重要程度的不同赋予一定
的优先级,CPU总是让处于就绪态的、优先级最高的任务先运行.
• FreeRTOS内核同时支持轮换调度算法,系统允许不同的任务使用相同的优先
级,在没有更高优先级任务就绪的情况下,同一优先级的任务共享CPU使用时
间.
• FreeRTOS官方支持 30 种架构 (ARM7 和 ARM Cortex M3 每个算一种架
构)。

可通过队列、二进制信号量、计数信号量、递归信号量、互斥量在任务间、任
务与中断 间通信和同步
• 互斥量有优先级继承。
• 支持高效的软件定时器
• 强大的执行跟踪功能,栈溢出检测选项。
• 免费论坛支持,或可选择商业支持和授权。
• 可创建的任务数无软件限制,可使用的优先级数无软件限制。
• 优先级指定无限制 – 可为多个任务指定同一优先级。
• 免费的嵌入式软件源代码,免版税。
• 可从标准的 Windows 主机交叉开发。
• 无论商业应用还是个人学习,都无需商业授权,是完全免费的操作系统.

• 授权 FreeRTOS 源代码使用修正的 GNU 通用公开许可来授权。

—————————————————-

3、FreeRTOS开发应用

 V字形模型

项目启动-项目定义-需求分析-概要设计-详细设计-程序编码-单体测试-集成测试-系统测试-发布-维护

—————————————————-

4、FreeRTOS如何工作

4.1 FreeRTOS与STM32Cube
4.2 FreeRTOS任务管理
4.3 FreeRTOS内存管理
4.4 FreeRTOS任务间通信
4.5 FreeRTOS软件定时器
4.6 FreeRTOS延时
4.7 FreeRTOS临界区与挂起调度器
4.8 FreeRTOS中断嵌套

4.1

首先,介绍STM32Cube

STM32Cube包括两部分组件:

(1.STM32CubeMX,即PC端的图形化配置工具

(2.STM32CubeFWLib,即基于STM32的嵌入式软件库

 

 MCU选择

• 通过型号,外设,封装进行过滤
• MCU配置
• 管脚配置
• 时钟树配置
• 外设和Middleware配置
• 功率评估
• 代码生成
• 直接生成工程
• 支持IAR,Keil,TrueStudio
• 两种版本
• 独立的版本
• Eclipse插件

 

FreeRTOS参数设置
• 内核参数设定
• 内存管理设定
• 任务
• 队列
• 信号量
• 定时器

内核设置

调度方式选择:
• 使能抢占式调度方式

• 使能协同式调度方式

 

4.2 FreeRTOS任务管理

 

 

 

就绪状态 Ready任务准备好但由于有更高的优先级任务执行所以没有执行

运行状态 Running任务处在运行状态

阻塞状态 Blocked任务等待某个事件

挂起状态 Suspended非运行状态的子状态,处于挂起状态的任务

对调度器而言不可见。让一个任务进入挂起状态的唯一办法就是调用vTaskSuspend()

API 函数;而把一个挂起状态的任务唤醒的唯一途径就是调用vTaskResume() 或vTaskResumeFromISR() API 函数。

• SVC 中断作用:
• 用于产生系统函数的调用请求

优先级抢占式调度

• 每个任务都赋予了一个优先级。• 每个任务都可以存在于一个或多个状态。

• 在任何时候都只有一个任务可以处于运行状态。

• 调度器总是在所有处于就绪态的任务中选择具有最高优先级的任务执行。

举例说明:
图示某个应用程序的执行流程展现了抢占式调度的行为方式

• 采用一个纯粹的协作式调度器,只可能在运行态任务进入阻塞态或是
运行态任务显式调用taskYIELD()时,才会进行上下文切换。任务永远
不会被抢占,而具有相同优先级的任务也不会自动共享处理器时间。
协作式调度的这作工作方式虽然比较简单,但可能会导致系统响应不
够快。

• 当任务大部份时间都处于阻塞态。这种状态下所有的任务都不可运行,
• 所以也不能被调度器选中。但处理器总是需要代码来执行——所以至
少要有一个任务处于运行态。为了保证这一点,当调用
vTaskStartScheduler()时,调度器会自动创建一个空闲任务。

• 空闲任务是一个非常短小的循环——和最早的示例任务十分相似,总
是可以运行。空闲任务拥有最低优先级(优先级0)以保证其不会妨碍具
有更高优先级的应用任务
• 进入运行态——当然,没有任何限制说是不能把应用任务创建在与空
闲任务相同的优先级上;如果需要的话,你一样可以和空闲任务一起
共享优先级。
• 运行在最低优先级可以保证一旦有更高优先级的任务进入就绪态,空
闲任务就会立即切出运行态。

• 通过空闲任务钩子函数(或称回调,hook, or call-back),可以直接在空
闲任务中添加应用程序相关的功能。空闲任务钩子函数会被空闲任务
每循环一次就自动调用一次。
• 通常空闲任务钩子函数被用于执行低优先级,后台或需要不停处理的
功能代码。

任务API

• 新建task
• Task handle 定义:
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)

• 删除task

osStatus osThreadTerminate (osThreadId thread_id)

• 获取task ID
osThreadId osThreadGetId (void)
/* Private variables ———————————————————*/
osThreadId Task1Handle;

• 产生Task
/* Create the thread(s) */
/* definition and creation of Task1 */
osThreadDef(Task1, StartTask1, osPriorityNormal, 0, 128);
Task1Handle = osThreadCreate(osThread(Task1), NULL);

• 检查task是否被挂起

osStatus osThreadIsSuspended(osThreadId thread_id)

• 检查task状态
osThreadState osThreadGetState(osThreadId thread_id)

• 恢复task

osStatus osThreadIsSuspended(osThreadId thread_id)

• 挂起task

osStatus osThreadSuspend (osThreadId thread_id)

• 恢复所有tasks

osStatus osThreadResume (osThreadId thread_id)

• 挂起所有tasks
osStatus osThreadSuspendAll (void)

 

新建任务实验

 task1(任务必须是一个无限的循环)

void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOG,GPIO_PIN_14);
osDelay(1000);
}
/* USER CODE END 5 */
}

Task1 优先级高于Task2

在task1中创建一个task2

void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
printf(“Create task2”);
osThreadDef(Task2, StartTask2, osPriorityNormal, 0, 128);
Task2Handle = osThreadCreate(osThread(Task2), NULL);
osDelay(1000);
}
/* USER CODE END 5 */
}

 Task2运行并删除自己

/* StartTask2 function */
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
printf(“Delete Task2\n”);
osThreadTerminate(Task2Handle);
}
/* USER CODE END StartTask2 */
}

 

4.3 FreeRTOS内存管理

FreeRTOS允许用户选择不同的内存管理方式,Total heap size for FreeRTOS和 How is memory allocated

and dealocated,如下图:

 

 

 

Heap_1.c
• 这是所有方案里最简单的。当内存分配后,它不允许释放内存,但除了这
点,它适合于大量 的应用。 该算法仅在请求 RAM 时,将一个数组分为更
小的块。数组总大小通过定义 configTOTAL_HEAP_SIZE 设置 – 定义于
FreeRTOSConfig.h 中。此方案特点为: 若您的应用永远不会删除任务或
队列 (永远不会调用 vTaskDelete () 或 vQueueDelete ()), 则可使用。

 

Heap_2.c
• 此方案使用了最佳适用算法,与方案 1 不同,它允许释放之前分配的块。然而,它
不会将相 邻的自由块合并为一个大块。 同样,可用的RAM总量通过定义
configTOTAL_HEAP_SIZE设置 – 定义于FreeRTOSConfig.h 中。 此方案特点为: •
即使应用反复调用 vTaskCreate ()/vTaskDelete () 或 vQueueCreate
()/vQueueDelete () (导致多次调用 pvPortMalloc() 和 vPortFree()),仍可使用
此方案。 • 若分配和释放的内存为随机大小 —— 仅在每个被删除的任务都有不同的
栈深度,或被删 除的队列有不同的长度时才会如此—— 则不应使用此方案。 • 若您
的应用创建队列或任务块的顺序不可预测,则可能导致内存碎片问题。可能所有应
用都不会这样,但是应对此了解。 • 它不是确定性的 – 但它也不是效率特别低的。
heap_2.c 适合于很多必须动态创建任务的小实时系统。

Heap_3.c
它仅是标准 malloc() 和 free() 函数的封装。它可确保线程安全。此方案特
点为: • 需要链接器建立堆,编译器库提供 malloc() 和 free() 的实现。 •
它不是确定性的。 • 可能会大幅增加内核代码量。 • 它被 PC (x86 单板
电脑)演示应用所使用。

Heap_4.c
• 此方案使用了首先适用算法,与方案 2 不同,它不会将相邻的自由内存块
合并为一个大块(它 不包含合并算法)。 可用的堆空间总量通过
configTOTAL_HEAP_SIZE 设置 – 定义于 FreeRTOSConfig.h 中。
xPortGetFreeHeapSize() API 函数返回还未分配的堆空间总量(令
configTOTAL_HEAP_SIZE 设置优化),但它不会提供未分配内存如何分
片为更小块的信息。 此实现的特点为: • 即使应用反复删除任务、队列、
信号量、互斥量等等,仍可使用此实现。 • 相比于 heap_2 实现,它不容
易产生分片为多个小块的堆空间 – 即使在分配和释放的内存 大小随机时也
是如此。 • 它不是确定性的 – 但它比多数标准 C 库的 malloc 实现的效率
高得多。 对于需要在应用代码中直接使用移植层内存分配方案 (而不是通
过调用 API 函数,间接调用 pvPortMalloc() and vPortFree())的应用而
言, heap_4.c 尤其有用。

使用heap_4.c
• Memory Handler 定义
• Memory 分配
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
osPoolDef(Memory,0x100,uint8_t);
PoolHandle = osPoolCreate(osPool(Memory));  //Create memory pool
uint8_t* buffer=osPoolAlloc(PoolHandle);  //Allocate memory from pool
/* Infinite loop */
for(;;)
{
osDelay(5000);
}
/* USER CODE END 5 */
}

 

4.4 FreeRTOS任务间通信

队列

信号量

互斥量

4.4.1 队列的作用
• 基于FreeRTOS的应用程序是由一系列独立或交互的任务构成的,每个任务都拥有自
己相对独立的资源。
• FreeRTOS中大多数通信和同步都是基于队列实现的。
• 通常队列被作为FIFO使用,即数据由队列尾写入,从队列首读出

• 创建队列:
osMessageQId osMessageCreate (const osMessageQDef_t *queue_def, osThreadId thread_id)

• 发送数据给队列

osStatus osMessagePut (osMessageQId queue_id, uint32_t info, uint32_t millisec)

• 从队列接收数据

osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec)

其中osEvent 是指Structure with status and with received item,如下:

typedef struct {
osStatus status; ///< status code: event or error information
union {
uint32_t v; ///< message as 32-bit value
void *p; ///< message or mail as void pointer
int32_t signals; ///< signal flags
} value; ///< event value
union {
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
} def; ///< event definition
} osEvent;

4.4.1.1队列实验

• 创建2个任务
• 设置队列的大小为256
• 队列类型为uint8_t

• 定义队列handler
/* Private variables ———————————————————*/
osThreadId Sender1Handle;
osThreadId ReceiverHandle;
osMessageQId Queue1Handle;

• 队列大小=256、类型 =uint8_t已经被定义在这里
/* Create the queue(s) */
/* definition and creation of Queue1 */
osMessageQDef(Queue1, 256, uint8_t);
Queue1Handle = osMessageCreate(osMessageQ(Queue1), NULL);

• Sender1任务
87
void StartSender1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,SET);
osMessagePut(Queue1Handle,0x1,200);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,RESET);
osDelay(1000);
}
/* USER CODE END 5 */
}

对上面代码注释:

//Put value ‘1’ into queue
//Item to send =0x1

 

• Receiver 任务
88
/* StartReceiver function */
void StartReceiver(void const * argument)
{
/* USER CODE BEGIN StartReceiver */
osEvent retvalue;
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,SET);
retvalue=osMessageGet(Queue1Handle,4000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,RESET);
}
/* USER CODE END StartReceiver */
}

我们可以在watch窗口检查retvalue.value.p的值

• 调用osMessageGet之后
• 如果队列中没有数据,则Receiver阻塞
• 如果队列中有数据,则Reciever task继续运行

 

4.4.1 信号量

• 信号量的主要作用
    •用于做任务间的同步
    • 用于做中断与任务之间的同步
• 信号量的类型
    • 二进制信号量
        • 仅有一个 ‘token’
        • 用于同步一个操作
    • 计数信号量
        • 拥有多个‘tokens’
        • 同步多个操作

二进制信号量API:

• 创建二进制信号量
osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count)

• 等待信号量释放

int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec)

• 释放信号量

osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)

二进制信号量实验

•创建二进制信号量
• 设置名称

• 定义信号量
/* Private variables ———————————————————*/
osThreadId Task1Handle;
osThreadId Task2Handle;
osSemaphoreId myBinarySem01Handle;

• 创建二进制信号量
/* Create the semaphores(s) */
/* definition and creation of myBinarySem01 */
osSemaphoreDef(myBinarySem01);
myBinarySem01Handle = osSemaphoreCreate(osSemaphore(myBinarySem01), 1);

• 使用信号量
• 如果任务/中断完成,信号量被释放
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
osDelay(2000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,SET);
osSemaphoreRelease(myBinarySem01Handle);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,RESET);
}
/* USER CODE END 5 */
}

 

• 等待信号量
• Task2等待信号量释放
• 释放后task2得以继续工作
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,SET);
osSemaphoreWait(myBinarySem01Handle,4000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,RESET);//Task2 synchronized
}
/* USER CODE END StartTask2 */
}

 

计数信号量API

API 与二进制信号量类似
• 创建信号量
osSemaphoreId osSemaphoreCreate (const osSemaphoreDef_t *semaphore_def, int32_t count)

• 等待信号量释放

int32_t osSemaphoreWait (osSemaphoreId semaphore_id, uint32_t millisec)

• 释放信号量

osStatus osSemaphoreRelease (osSemaphoreId semaphore_id)

计数信号量实验
1

创建3个任务
• 设置相同的优先级
• 设置参数

创建计数信号量
• 设置计数tokens
• 设置名称

实验代码

• 创建计数信号量
• Task1 和Task2 相同
/* Create the semaphores(s) */
/* definition and creation of myCountingSem01 */
osSemaphoreDef(myCountingSem01);
myCountingSem01Handle = osSemaphoreCreate(osSemaphore(myCountingSem01), 2);
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
osDelay(2000);
//Task1 Release counting semaphore
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,SET);
osSemaphoreRelease(myCountingSem01Handle);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,RESET);
}
/* USER CODE END 5 */
}

 

void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
osDelay(2000);
//Task2 Release counting semaphore
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,SET);
osSemaphoreRelease(myCountingSem01Handle);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,RESET);
}
/* USER CODE END StartTask2 */
}

• Task3 持续等待直到信号量被2次释放
void StartTask3(void const * argument)
{
/* USER CODE BEGIN StartTask3 */
/* Infinite loop */
for(;;)
{
osSemaphoreWait(myCountingSem01Handle, 4000);
osSemaphoreWait(myCountingSem01Handle, 4000);
//Task3 synchronized
}
/* USER CODE END StartTask3 */
}

 

4.4.3 互斥量

• 产生互斥量
osMutexId osMutexCreate (const osMutexDef_t *mutex_def)

• 等待互斥量释放

osStatus osMutexWait (osMutexId mutex_id, uint32_t millisec)

• 释放互斥量

osStatus osMutexRelease (osMutexId mutex_id)

互斥量实验

创建两个任务
• 优先级相同
• 设置参数

创建两个任务
• 优先级相同
• 设置参数

• 创建互斥量
• 命名互斥量

两个task都使用printf函数
• 互斥量的使用避免相互冲突

Only one task can have semaphore

• 互斥量handle 定义
• 创建互斥量
/* Private variables ———————————————————*/
osThreadId Task1Handle;
osThreadId Task2Handle;
osMutexId myMutex01Handle;
/* Create the mutex(es) */
/* definition and creation of myMutex01 */
osMutexDef(myMutex01);
myMutex01Handle = osMutexCreate(osMutex(myMutex01));

• 使用信号量
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
osDelay(2000);
printf(“Task1 Release semaphore\n”);
osSemaphoreRelease(myBinarySem01Handle);
}
/* USER CODE END 5 */
}

• Task1 和Task2 使用互斥量进行同步

void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
for(;;)
{
osDelay(2000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,SET);
osMutexWait(myMutex01Handle,1000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,RSET);
osMutexRelease(myMutex01Handle);
}
/* USER CODE END 5 */
}

void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
osDelay(2000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,SET);
osMutexWait(myMutex01Handle,1000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,RESET);
osMutexRelease(myMutex01Handle);
}
/* USER CODE END StartTask2 */
}

4.5 FreeRTOS软件定时器

• 软件定时器特性
• 软件定时器是RTOS的一个组件
• 可以扩展STM32定时器的数目
• 虽不是那么精确但是可以处理周期性的动作

软件定时器API

• 软件定时器创建
osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument)

• 启动软件定时器

osStatus osTimerStart (osTimerId timer_id, uint32_t millisec)

• 停止软件定时器

osStatus osTimerStop (osTimerId timer_id)

软件定时器的两种模式:

周期性

一次运行

软件定时器实验

 

• 创建定时器
• 设置定时器
• 设置定时器回调函数

• 软件定时器handle定义
• 创建软件定时器

• 启动软件定时器
/* Private variables ———————————————————*/
osThreadId Task1Handle;
osTimerId myTimer01Handle;
/* Create the timer(s) */
/* definition and creation of myTimer01 */
osTimerDef(myTimer01, Callback01);
myTimer01Handle = osTimerCreate(osTimer(myTimer01), osTimerPeriodic, NULL);
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
osTimerStart(myTimer01Handle,1000);
/* Infinite loop */
for(;;)
{
osDelay(2000);
printf(“Task1 Print\n”);
}
/* USER CODE END 5 */
}

• 软件定时器回调函数

/* Callback01 function */

void Callback01(void const * argument)
{
/* USER CODE BEGIN Callback01 */
HAL_GPIO_Toggle(GPIOG,GPIO_PIN_13);
/* USER CODE END Callback01 */
}

4.6 FreeRTOS延时

• osDelay函数
• osDelayUntil 函数
osStatus osDelay (uint32_t millisec)  //• osDelay延时时间从调用osDelay开始计算
osStatus osDelayUntil (uint32_t PreviousWakeTime, uint32_t millisec)  //• osDelayUntil延时的计算时间从我们选择的时间点开始计算

//osDelay实验

oid StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
uint32_t i = 0;
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,SET);
HAL_Delay(1000);
osDelay(2000);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_13,RESET);
}
/* USER CODE END 5 */
}
/* StartTask2 function */
void StartTask2(void const * argument)
{
/* USER CODE BEGIN StartTask2 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,SET);
HAL_Delay(200);
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_14,RESET);
}
/* USER CODE END StartTask2 */
}

osDelayUntil实验

• 使能 vTaskDelayUntil
• 重新生成工程:
140
void StartTask1(void const * argument)
{
/* USER CODE BEGIN 5 */
uint32_t wakeuptime;
/* Infinite loop */
for(;;)
{
wakeuptime=osKernelSysTick();
printf(“Task 1\n”);
HAL_Delay(1000);
osDelayUntil(wakeuptime,2000);
}
/* USER CODE END 5 */

• 如果没有延时,系统将在Running状态和Ready状态切换

4.7 FreeRTOS临界区与挂起调度器

• 基本临界区
• 基本临界区(Critical Section)是指虹taskENTER_CRITICAL()与taskEXIT_CRITICAL()之间的代码

/* 为了保证对PORTA寄存器的访问不被中断,将访问操作放入临界区*/

/*进入临界区*/
taskENTER_CRITICAL();
/* 在taskENTER_CRITICAL()与taskEXIT_CRITICAL()之间不会切换到其他任务。中断可以执行,允许嵌套,但是只
针对优先级高于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断,而且这些中断不允许访问FreeRTOS API函
数 */
PORTA|=0x01;
/* 已经完成了对PORTA的访问,因此可以安全地离开临界区了 */
taskEXIT_CRITAL();

• 临界区是提供互斥功能的一种非常原始的实现方法,临界区的工作仅仅是简单地全部关闭中断,
或是关掉优先级在config_SYSCALL_INTERRUPT_PRIORITY及以下的中断。抢占式上下文切换
只可能在某个中断中完成,所以调用taskENTER_CRITICAL()的任务可以在中断关闭的时段一直保
持运行态,直到退出临界区.
• 临界区必须只具有很短的时间,否则会反过来影响中断的响应时间。在每次调用
taskENTER_CRITICAL()之后,必须尽快调用一个taskEXIT_CRITICAL().从这个角度来看,对标
准输出的保护不应当采用临界区,因为串口输出需要一段时间。
• 临界区嵌套是安全的,因为内核维护了一个嵌套深度计数。临界区指挥在嵌套深度为0时才会真
正退出,即在为每个之前调用taskENTER_CRITICAL()都配置调用了taskEXIT_CRITICAL()之后.

 

挂起(锁定)调度器
• 也可以通过挂起调度器来创建临界区。
• 基本临界区保护一段代码区间不被其他任务或中断打断。
• 由挂起调度器实现的临界区只可以保护一段代码区间不被其他任务打断,因为在这种方式下,中
断是使能的。
• 如果一个临界区太长而不适合简单地关中断来实现,那么可以考虑采用挂起调度器,但是唤醒调
度器却是一个时间相当长的操作。所以需要评估哪种是最佳的方式.
• 在调度器处于挂起状态时,不能调用FreeRTOS API函数.
• 嵌套使用vTaskSuspendAll和xTaskResumeAll()是安全的,因为内核维护了一个嵌套深度计数,
调度器只会在嵌套深度为0时才会被唤醒,即在为每个之前调用vTaskSuspendAll ()都配置调用:

了xTaskResumeAll ()之后.

4.8 FreeRTOS中断嵌套

最新的FreeRTOS允许中断嵌套,中断嵌套需要在FreeRTOSConfig.h中定义两个常量

configKERNEL_INTERRUPT_PRIORITY
 设置系统心跳时钟的中断优先级
 如果在设置中没有使用常量

么需要调用中断安全版本FreeRTOSAPI的中断都必
须运行在此优先级上

configMAX_SYSCALL_INTERRUPT_PRIORITY
 设置中断安全版本FreeRTOSAPI可以运行的最高中
断优先级

• 建立一个全面的中断嵌套模型需要设置configMAX_SYSCALL_INTERRUPT_PRIORITY为比
configKERNEL_INTERRUPT_PRIORITY高的优先级。
• 假定configMAX_SYSCALL_INTERRUPT_PRIORITY设置为3,
configKERNEL_INTERRUPT_PRIORITY设置为1.假定控制器具有7级不同的优先级
• 任务优先级和MCU中断优先级是相反的

• 处于中断优先级1~3的中断会被内核或处于临界区的应用程序阻塞执行,但是他们可以调用中断
安全版本的FreeRTOS API函数.
• 处于中断优先级4及以上的中断不会受临界区影响,所以其不会被内核的任何行为阻塞,可以立
即得到执行—这是由微控制器本身对中断优先级的限定所决定的。通常需要严格时间精度的功能
(如电机控制)会使用高于configMAX_SYSCALL_INTERRUPT_PRIRITY的优先级,以保证调度
器不会对中断响应时间造成抖动。
• 不需要调用FreeRTOS API函数的中断,可以自由地使用任意优先级.

—————————————————-

4、STM32Cube FreeRTOS例程

API category     CMSIS_RTOS     API FreeRTOS API
Kernel control     osKernelStart     vTaskStartScheduler
Thread management     osThreadCreate     xTaskCreate
Semaphore     osSemaphoreCreate     vSemaphoreCreateBinary
                       xSemaphoreCreateCounting
Mutex     osMutexWait     xSemaphoreTake
Message queue     osMessagePut     xQueueSend
                       xQueueSendFromISR
Timer     osTimerCreate     xTimerCreate

CMSIS-RTOS 是实时操作系统的通用 API。它提供了标准化的编程接口,可移植到很多
RTOS,使软件模板、中间件、库及其它组件能工作于支持的 RTOS 系统。

—————————————————-

最后粘贴一张ST的海报

 

版权声明:本文为aiziyoudexinxin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/aiziyoudexinxin/p/STM32FREERTOS.html