FreeRTOS
1. FreeRTOS简介
RTOS指一类系统:
RTOS类操作系统:UCOS,μ-cos II,μ-cos III FreeRTOS RT-thread
PC端:win,linux,安卓
文件操作系统:FATFS
界面系统GUI:emWin
FreeRTOS( Real Time Operating System 实时操作系统):FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。
轮询系统:有规律性
自上而下一行一行代码运行。
前后台系统:处理突发事件,
中断:时间片
多任务系统:实现多任务同时进行
函数1— While1----串口打印
函数2— While1----led闪烁
函数3— While1----按键检测
FreeRTOS特点:
1.1学习目的
- 项目需要 :实现的功能越来越多 ,裸机系统不能够完美地解决问题,降低编程的难度,实现多任务管理 。
- 学习需要 :学习更高级的东西,实现更好的职业规划,为以后做准备
2. FreeRTOS移植
FreeRTOS 的源码和相应的官方书籍均可从官网 www.freertos.org 获得
1.找一个轻便的工程:
2.在工程内部建立文件夹:
3.在新建的文件夹内部创建三个子文件夹:
4.移植.h文件:(全复制)
5.移植.h:
6.移植port:(系统内部管理):
7.移植heap4.c文件:
8.关闭SYStick系统滴答定时器:(屏蔽三个函数):
9.关闭主程序的sysytick_Config():
10创建钩子函数:(空闲时刻,程序进入钩子函数):
11.主频:72000000:
3.任务创建
3.1 变量名
定义变量时加上前缀:
char----c
short----s
long-----l
portBASE_TYPE,数据结构,任务句柄,队列句柄-----x
3.2 函数名
包含返回值类型,所在文件名,函数功能
prv(私有的private)
eg:
vTaskPrioritySet():返回值位void,在task.c中定义
3.3 创建、删除、启动任务
3.4 进入(退出)临界区
3.5 任务状态迁移
任务状态迁移图:
任务1—优先级3
任务2—优先级2
任务3—优先级5
优先级:优先级高的任务可以打断优先级的任务(3正在执行,5来到)
任务调度器:根据优先级的大小----抢占CPU资源
就绪态(Ready):任务创建完成后进入就绪态,表明任务已准备就绪,随时可以运行,只等待调度器进行调度(3、2、5同时运行)
运行态(Running):经过调度器按照优先级高低调度任务,从就绪态进入运行态。(3、2、5同时运行)
阻塞态( Blocked):正在运行的任务发生阻塞(挂起、延时、读信号量等待)时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。(5执行完,进入阻塞态,3和2有机会运行)
挂起态( Suspended):任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU 的使用权,也不会参与调度,除非它从挂起态中解除。(把一个挂起状态的任务恢复的唯一途径就是调用vTaskResume()或vTaskResumeFromISR() API 函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。)
3.6 挂起/恢复任务
实现功能: 按下按键1,进入挂起态
按下按键2,进入恢复任务
void Task_Key( void *p)
{
uint8_t keyval = 0;
while(1)
{
keyval = key_getval();
if(keyval == 1)
{
printf("按键 1 按下\r\n");
vTaskSuspend(TaskHandle_Test);//把测试函数挂起
}
else if(keyval == 2)
{
printf("按键 2 按下\r\n");
vTaskResume(TaskHandle_Test);
}
vTaskDelay(1000);
}
}
4. 任务间通信
4.1 消息队列
队列:先进先出原则(FIFO)—管道
堆栈:后进先出原则(LIFO)–电梯门
4.1.2 创建消息队列
4.2 计数信号量
实现目的:停车场道闸显示的当前剩余车位,按键1按下,信号量+1,按键2按下信号量-1;(先获取在释放)
4.2.1 创建计数信号量
4.2.2 获取信号量
4.2.2 释放信号量
void Task_Key( void *p)
{
BaseType_t queueret = 0;
uint8_t keyval = 0;
uint8_t i = 0;
char queuebuf[10]= "\0";
while(1)
{
keyval = key_getval();
if(keyval == 1)
{
printf("按键 1 按下车辆入库\r\n");
queueret = xSemaphoreGive(TaskHandle_Counting);//释放信号量
if(queueret == pdPASS)
{
printf("车辆入库成功\r\n");
printf("\r\n");
}
else
{
printf("车辆入库失败\r\n");
}
}
else if(keyval == 2)
{
printf("按键 2 按下车辆出库\r\n");
queueret = xSemaphoreTake(TaskHandle_Counting,0);//获取信号量
if(queueret == pdPASS)
{
printf("车辆出库成功");
printf("\r\n");
}
else
{
printf("车辆出库失败");
}
}
vTaskDelay(10);
}
}
5.二值信号量
应用案例:两人公用一个电话,一方接打电话,挂断后。另一方才能打电话。
//创建二值信号量句柄
SemaphoreHandle_t SemaphoreHandle_Binary = NULL;
void Task_Create( void *p)
{
while(1)
{
taskENTER_CRITICAL(); //进入临界区
//创建计数信号量
// TaskHandle_Counting = xSemaphoreCreateCounting(10,10);//共有10个车位,现有10
// if(TaskHandle_Counting != NULL)
// {
// printf("计数信号量创建成功\r\n");
// }
//创建二值信号量
SemaphoreHandle_Binary = xSemaphoreCreateBinary();
if(NULL != SemaphoreHandle_Binary)
{
printf("二值信号量创建成功\r\n");
}
xSemaphoreGive(SemaphoreHandle_Binary);
xTaskCreate(Task_Printf,"task_Printf",64,NULL,3,&TaskHandle_printf);
xTaskCreate(Task_Test,"task_Test",64,NULL,4,&TaskHandle_Test);
// xTaskCreate(Task_LED,"task_LED",64,NULL,2,&TaskHandle_LED);
// xTaskCreate(Task_Key,"task_Key",64,NULL,5,&TaskHandle_Key);
// xTaskCreate(Task_give,"task_give",64,NULL,1,&TaskHandle_give);
// xTaskCreate(Task_Take,"task_Take",64,NULL,1,&TaskHandle_Take);
vTaskDelete(p);
taskEXIT_CRITICAL(); //退出临界区
}
}
void Task_Printf( void *p)
{
while(1)
{
xSemaphoreTake(SemaphoreHandle_Binary,portMAX_DELAY);//获取信号量
printf("----打印\r\n");
printf("\r\n");
vTaskDelay(1000);
xSemaphoreGive(SemaphoreHandle_Binary);
vTaskDelay(10);
}
}
void Task_Test( void *p)
{
while(1)
{
xSemaphoreTake(SemaphoreHandle_Binary,portMAX_DELAY);//获取信号量
printf("测试函数----\r\n");
vTaskDelay(500);
xSemaphoreGive(SemaphoreHandle_Binary);
vTaskDelay(10);
}
}
6. 互斥信号量
互斥信号量其实是特殊的二值信号量,由于其特有的优先级继承机制从而使它更适用于简单互锁,也就是保护临界资源 。
优先级翻转:high 7
low 2
mid 4
high \ low抢占二值信号量
low运行完后,high抢占到信号进入运行态
mid来到,此时先运行mid
怎么避免优先级翻转现象?
互斥信号量:优先级继承(不能完全避免优先级翻转,只是缩短了高优先级等待的时间)
6.1 创建互斥信号量
释放、获取函数(和二值信号量运用的函数相同)
//创建互斥信号量
SemaphoreHandle_t SemaphoreHandle_Mutex = NULL;
void Task_Create( void *p)
{
while(1)
{
taskENTER_CRITICAL(); //进入临界区
//创建计数信号量
// TaskHandle_Counting = xSemaphoreCreateCounting(10,10);//共有10个车位,现有10
// if(TaskHandle_Counting != NULL)
// {
// printf("计数信号量创建成功\r\n");
// }
//创建二值信号量
// SemaphoreHandle_Binary = xSemaphoreCreateBinary();
// if(NULL != SemaphoreHandle_Binary)
// {
// printf("二值信号量创建成功\r\n");
// }
// xSemaphoreGive(SemaphoreHandle_Binary);
//创建互斥信号量
SemaphoreHandle_Mutex = xSemaphoreCreateMutex();
if(SemaphoreHandle_Mutex != NULL)
{
printf("互斥信号量创建成功\r\n");
}
xSemaphoreGive(SemaphoreHandle_Mutex);
xTaskCreate(Task_Low,"task_Low",64,NULL,2,&TaskHandle_Low);
xTaskCreate(Task_High,"task_High",64,NULL,6,&TaskHandle_High);
xTaskCreate(Task_Mid,"task_Mid",64,NULL,4,&TaskHandle_Mid);
// xTaskCreate(Task_LED,"task_LED",64,NULL,2,&TaskHandle_LED);
// xTaskCreate(Task_Key,"task_Key",64,NULL,5,&TaskHandle_Key);
// xTaskCreate(Task_give,"task_give",64,NULL,1,&TaskHandle_give);
// xTaskCreate(Task_Take,"task_Take",64,NULL,1,&TaskHandle_Take);
vTaskDelete(p);
taskEXIT_CRITICAL(); //退出临界区
}
}
void Task_High( void *p)
{
BaseType_t xReturn;
while(1)
{
xReturn = xSemaphoreTake(SemaphoreHandle_Mutex,portMAX_DELAY);//获取信号量
if(xReturn == pdPASS)
{
printf("High runing\r\n");
}
printf("\r\n");
delay_nms(3000);
xSemaphoreGive(SemaphoreHandle_Mutex);
vTaskDelay(10);
}
}
void Task_Mid( void *p)
{
while(1)
{
printf("mid runing\r\n");
vTaskDelay(1000);
}
}
void Task_Low( void *p)
{
BaseType_t xReturn;
while(1)
{
xReturn = xSemaphoreTake(SemaphoreHandle_Mutex,portMAX_DELAY);//获取信号量
if(xReturn == pdPASS)
{
printf("low runing\r\n");
}
delay_nms(2000);
xSemaphoreGive(SemaphoreHandle_Mutex);
vTaskDelay(10);
}
}
7. 事件
事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。与信号量不同的是,它可以实现一对多,多对多的同步。
一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事件处理;也可以是几个事件都发生后才唤醒任务进行事件处理。同样,也可以是多个任务同步多个事件。
事件特点:
1.事件只与任务相关联,事件相互独立,一个 32 位的事件集合( EventBits_t 类型的变量, 实际可用与表示事件的只有 24 位),用于标识该任务发生的事件类型,其中每一位表示一种事件类型( 0 表示该事件类型未发生、1 表示该事件类型已经发生),一共 24 种事件类型。
2.事件仅用于同步,不提供数据传输功能。
3.事件无排队性,即多次向任务设置同一事件(如果任务还未来得及读走), 等效于只设置一次。
4.允许多个任务对同一事件进行读写操作。
5.支持事件等待超时机制。
7.1 事件应用场景
事件可使用于多种场合,它能够在一定程度上替代信号量,用于任务与任务间,中断与任务间的同步。一个任务或中断服务例程发送一个事件给事件对象,而后等待的任务被唤醒并对相应的事件进行处理。
各个事件可分别发送或一起发送给事件对象,而任务可以等待多个事件
7.2 事件运作机制
事件不与任务相关联,事件相互独立
事件唤醒机制,当任务因为等待某个或者多个事件发生而进入阻塞态,当事件发生的时候会被唤醒 。
任务 1 对事件 3 或事件 5 感兴趣(逻辑或),当发生其中的某一个事件都会被唤醒,并且执行相应操作。而任务 2 对事件 3 与事件 5 感兴趣(逻辑与),当且仅当事件 3 与事件 5 都发生的时候, 任务 2 才会被唤醒,如果只有一个其中一个事件发生,那么任务还是会继续等待事件发生。如果接在收事件函数中设置了清除事件位 xClearOnExit,那么当任务唤醒后将把事件 3 和事件 5 的事件标志清零,否则事件标志将依然存在 。
7.3 事件创建函数 xEventGroupCreate()
xEventGroupCreate()用于创建一个事件组,并返回对应的句柄。 要想使用该函数必须在头文件 FreeRTOSConfig.h 定义宏.
FreeRTOS 给我们提供了一个创建事件的函数 xEventGroupCreate(),当创建一个事件时, 系统会首先给我们分配事件控制块的内存空间,然后对该事件控制块进行基本的初始化,创建成功返回事件句柄;创建失败返回 NULL。
7.4 事件删除函数 vEventGroupDelete()
FreeRTOS 给我们提供了一个删除事件的函数——vEventGroupDelete(),使用它就能将事件进行删除了。当系统不再使用事件对象时,可以通过删除事件对象控制块来释放系统资源 。
8. 软件定时器
8.1 软件定时器的基本概念
定时器,是指从指定的时刻开始,经过一个指定时间,然后触发一个超时事件,用户可以自定义定时器的周期与频率。
定时器有硬件定时器和软件定时器之分:
硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
软件定时器,软件定时器是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受硬件定时器资源限制的定时器服务,它实现的功能与硬件定时器也是类似的。
FreeRTOS 软件定时器功能上支持:
裁剪:能通过宏关闭软件定时器功能。
软件定时器创建、启动、软停止、复位、删除 。
8.2 软件定时器应用场景
硬件定时器受硬件的限制,数量上不足以满足用户的实际需求,无法提供更多的定时器,那么可以采用软件定时器来完成,由软件定时器代替硬件定时器任务。
8.3 软件定时器创建函数 xTimerCreate()
动态创建软件定时器 xTimerCreate()
静态创建方式 xTimerCreateStatic()
Swtmr2_Handle=xTimerCreate((const char* )"OneShotTimer", (TickType_t)5000,/* 定时器周期 5000(tick) */
(UBaseType_t )pdFALSE,/* 单次模式 */
(void*)2,/* 为每个计时器分配一个索引的唯一 ID */
(TimerCallbackFunction_t)Swtmr2_Callback);
8.4 软件定时器启动函数 xTimerStart()
xTimerStart( xTimer, xTicksToWait )
xTimerGenericCommand( ( xTimer )//要操作的软件定时器句柄,
tmrCOMMAND_START// xCommandID 参数可以指定多个命令
( xTaskGetTickCount() )//获取当前系统时间,
NULL//参数在中断中发送命令才起作用,
( xTicksToWait ) )//用户指定超时阻塞时间
xTimerStart()函数就是一个宏定义,真正起作用的是 xTimerGenericCommand()函数。
8.5 软件定时器停止函数xTimerStop()
xTimerStop() 用于停止一个已经启动的软件定时器, 该函数的实现也是通过“定时器命令队列”发送一个停止命令给软件定时器任务,从而唤醒软件定时器任务去将定时器停止。