网站ui设计用什么软件做,最好的免费logo设计网站,上海公司注册地址有什么要求,网站做关键词首页由于正在学习韦东山老师的RTOS课程#xff0c;结合了网上的一些资料#xff0c;整理记录了下自己的感悟#xff0c;用于以后自己的回顾。如有不对的地方请各位大佬纠正。 文章目录 一、RTOS的优势二、RTOS的核心功能2.1 任务管理2.1.1 任务的创建2.1.2 任务的删除*2.1.3 任… 由于正在学习韦东山老师的RTOS课程结合了网上的一些资料整理记录了下自己的感悟用于以后自己的回顾。如有不对的地方请各位大佬纠正。 文章目录 一、RTOS的优势二、RTOS的核心功能2.1 任务管理2.1.1 任务的创建2.1.2 任务的删除*2.1.3 任务优先级和Tick一、优先级二、Tick滴答三、优先级的实验三、优先级设定的实验 2.1.4 任务状态一、阻塞状态Blocked二、就绪状态Ready三、暂停状态Suspended四、完整的状态转移图 2.1.5 相对延时和绝对延时一、相对延时函数二、绝对延时函数三、延时实验 2.1.6 空闲任务及钩子函数一、钩子函数 2.1.7 调度算法一、调度算法的配置 一、RTOS的优势
①确定性和实时性 RTOS的最大特点是能够在严格的时间约束内完成任务。这种确定性对于时间敏感的应用如工业控制、医疗设备等至关重要。 ②优先级调度 RTOS通常支持优先级调度机制确保高优先级的任务可以抢占低优先级的任务执行。这种机制保证了关键任务能够在最短时间内得到处理。 ③低延迟和高响应性 RTOS设计的目标是最小化任务切换时间和中断延迟从而实现高响应性。这在需要快速反应的嵌入式系统中非常重要。 ④资源管理和内存控制 RTOS通常提供精细的资源管理工具允许开发者更好地控制内存和CPU资源的使用。这种控制对于嵌入式系统中的资源有限环境尤其重要。 ⑤模块化和灵活性 RTOS通常具有模块化设计允许开发者根据具体需求启用或禁用特定的功能模块。这种灵活性有助于优化系统性能和减少系统开销。 ⑥可靠性和稳定性 RTOS被广泛应用于需要高可靠性和稳定性的系统中例如自动驾驶、军事系统等。RTOS通过严格的测试和验证确保其在各种边界情况下都能稳定运行。 ⑦较小的内存占用 RTOS通常占用的内存和资源较少这使得它非常适合嵌入式系统或其他资源受限的环境。
二、RTOS的核心功能
RTOS的核心功能块主要分为任务管理、内核管理、时间管理以及通信管理4部分框架图如下所示 1任务管理负责管理和调度任务的执行确保系统中的任务能够按照预期运行。 2内核管理负责系统核心功能的管理包括内存、中断、异常处理和系统启动等。 3时间管理负责所有与时间相关的操作包括系统时钟、定时器、任务延迟和周期性任务的执行。 4通信管理提供任务之间的通信机制确保任务能够有效地协作和共享资源。
2.1 任务管理
2.1.1 任务的创建 任务就是一个无返回的函数Void。由于函数传参的不同一个函数可以创建多个任务然后每个任务都有对应自身的栈也就是说一个函数可以有多个栈当然一个函数对应一个栈也是可以的。使用下面的函数用于创建任务
void TaskAFunction(void *param)
{int* tmp (int*) param;//首先将void *指针类型的param转为int *类型的指针 int value *tmp; //然后解引用来获取指针指向的值while(1){printf(%d,value);}
}尽管是同一个函数但是创建的多个任务主要不同还是在于传参而不是名字下面的代码使用了相同的名字“TaskA”创建了三个参数不同的任务。
int x11;int x22;int x33;
int main( void )
{TaskHandle_t xHandleTask1;#ifdef DEBUGdebug();
#endifprvSetupHardware();printf(Hello, world!\r\n);xTaskCreate(TaskAFunction,TaskA,100,x1,1,NULL);xTaskCreate(TaskAFunction,TaskA,100,x2,1,NULL);xTaskCreate(TaskAFunction,TaskA,100,x3,1,NULL);/* Start the scheduler. */vTaskStartScheduler();/* Will only get here if there was not enough heap space to create theidle task. */return 0;
}2.1.1 xTaskCreate 上面使用的xTaskCreate是动态创建任务的当然还有静态创建任务的函数xTaskCreateStatic后面再提静态创建。下图为xTaskCreate函数的参数及介绍 下图摘自韦东山的FreeRTOS完全开发手册3.2.2节
2.1.2 任务的删除* 任务的删除使用如下函数其中填入的参数如果是NULL表示自杀如果是自己的句柄则是被杀别人的句柄就是杀人。
void vTaskDelete( TaskHandle_t xTaskToDelete );实验是在vTask1任务中嵌套vTask2任务的创建而vTask2任务中执行删除自身任务的操作而相对延时函数vTaskDelay( xDelay100ms );在Task1中的存在与否会有影响么呢? 代码如下所示肯定先创建并运行Task1执行完自身的printf后创建并优先调用Task2Task2也会printf自身信息并删除自己的任务内存还未释放。此时就要注意了vTaskDelay函数会起了一个很重要的作用。因为vTaskDelay的存在使得Task1进入了阻塞状态此时没有其他任务Task2也被删除啦需要执行导致系统会执行优先级最低的IDLE任务这个任务会把Task2所占用栈的内存给释放。代码如下
TaskHandle_t xTask2Handle NULL;
int main( void )
{...xTaskCreate(vTask1, Task 1, 1000, NULL, 1, NULL);...
}
void vTask1( void *pvParameters )
{const TickType_t xDelay100ms pdMS_TO_TICKS( 100UL );//100ms的延时BaseType_t ret;/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务1的信息 */printf(Task1 is running\r\n);ret xTaskCreate( vTask2, Task 2, 1000, NULL, 2, xTask2Handle );if (ret ! pdPASS)//判断vTask2是否创建成功一般pdPASS默认为1printf(Create Task2 Failed\r\n);vTaskDelay( xDelay100ms );}
}void vTask2( void *pvParameters )
{/* 打印任务的信息 */printf(Task2 is running and about to delete itself\r\n);// 可以直接传入参数NULL进行“自杀”vTaskDelete(xTask2Handle);
} 实验结果如下Task1带有相对延时函数后能够正常释放被删除的Task2所占用的内存空间所以能够如下打印 通过上文我们知道vTaskDelay函数会起一个很重要的作用。此刻若是删除这个函数的话Task1自然不会进入阻塞状态而系统更没机会调用IDLE任务多次被删除的Task2任务所占用的内存一直无法释放而导致最后内存的耗尽结果如下。
2.1.3 任务优先级和Tick
一、优先级 优先级在上文中提过优先级的值大的优先执行相同优先级的则交替执行这个函数xTaskCreate(vTask1, Task 1, 1000, NULL, 1, NULL);的第5个参数则是表示优先级。 如何找到优先级最高的任务RTOS的调度器会根据configMAX_PRIORITIES的值来判断采用C函数还是汇编指令的方法来实现调度。
二、Tick滴答 函数vTaskDelay可以用于指定任务休眠的时间一般有以下两种表示方式 方式一vTaskDelay5【存在延时不准的问题】 该方式直接设置5个Tick根据下面公式可以算出时间T为 T(1/configTICK_RATE_HZ)*50.05s50ms 方式二vTaskDelaypdMS_TO_TICKS(50UL)【存在延时不准的问题】 该方式采用pdMS_TO_TICKS宏直接将ms转换为tick上式表示为等待50ms。
三、优先级的实验 参考韦东山FreeRTOS手册创建了3个任务其中Task1和Task2的优先级为1Task3的优先级为2。我们知道Task3任务优先级明显高于Task1和Task2的但是如果不对Task3进行进行vTaskDelay的话高优先级的会一直占用CPU那么Task1和Task2的则不会有机会执行就像备胎一样一直在当女神的备胎但是在女神眼里就是没正主优先级高备胎就算等着舔不到女神说明不要当舔狗不过这也对应了任务的阻塞状态。Task1~3的代码和main代码如下
xTaskCreate(vTask1,Task1,1000,NULL,1,NULL);
xTaskCreate(vTask2,Task2,1000,NULL,1,NULL);
xTaskCreate(vTask3,Task3,1000,NULL,2,NULL);void vTask1( void *pvParameters )
{/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务1的信息 */printf(T1\r\n);}
}
void vTask2( void *pvParameters )
{/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务2的信息 */printf(T2\r\n);}
}
void vTask3( void *pvParameters )
{const TickType_t xDelay3000mspdMS_TO_TICKS(1000UL);/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务3的信息 */printf(T3\r\n);
// vTaskDelay(xDelay3000ms);}结果如下只执行了Task3 当解开vTask3函数中vTaskDelay(xDelay3000ms);代码的注释后结果如下。Task3只执行1次后就不执行了后面是Task1和Task2两个优先级为1的相互执行。那是因为Task3执行到vTaskDelay这个函数后会进入休眠状态尽管优先级高于Task1和2但是休眠状态不占用CPU资源于是让给了两个优先级相同的Task1和Task2而Task3休眠结束后Task1和Task2没有休眠机制于是疯狂不断运行从而导致Task3的打印只出现了一次。
三、优先级设定的实验 本实验主要是通过vTaskPrioritySet函数实现对任务优先级的设定。该函数具体如下
void vTaskPrioritySet(TaskHandle_t xTask, UBaseType_t uxNewPriority);其中第一个参数是也就是对应Task的handle即每个任务在xTaskCreate创建任务时所传入的第6个参数xTask2Handle。而第二个参数uxNewPriority是通过函数uxTaskPriorityGet进行获取。 完整的实验如下创建Task1和Task2。在Task1中print并提高Task2的任务优先级来保证高于Task1。在Task2中同样print自己内容并降低Task2的任务优先级来保证低于Task1。这样很明显两者通过调整任务优先级来实现一个来回执行的效果代码如下
void vTask1( void *pvParameters )
{UBaseType_t uxPriority;//获取Task1的优先级其中NULL表示获取自身的优先级uxPriority uxTaskPriorityGet(NULL);/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务1的信息 */printf(Task1 is runing\r\n);printf(About to raise the Task 2 priority\r\n);/*通过使用vTask1的优先级再1来保证vTask2具有更高的优先级*/vTaskPrioritySet( xTask2Handle, ( uxPriority 1 ) );}
}void vTask2( void *pvParameters )
{UBaseType_t uxPriority;//获取Task2的优先级其中NULL表示获取自身的优先级uxPriority uxTaskPriorityGet(NULL);/* 任务函数的主体一般都是无限循环 */for( ;; ){/* 打印任务2的信息 */printf(Task2 is runing\r\n);printf(About to lower the Task 2 priority\r\n);/*通过使用vTask2的优先级再-2来保证vTask1具有更低的优先级*/vTaskPrioritySet(NULL,(uxPriority - 2));}
}经代码验证Task1与Task2的效果如下
2.1.4 任务状态 任务一般可以分为运行Runing和非运行不 Runing两类。但是非运行的状态还能分成①阻塞状态②暂停状态③就绪状态。
一、阻塞状态Blocked 阻塞状态指的是任务因为等待某个事件或条件发生而无法继续执行的状态。如1相对/绝对延时函数这类时间等待2队列或信号量等待3事件标志等待。等等。这个状态下任务不会占用CPU资源一旦满足某个事件的条件就能转为就绪状态了。
二、就绪状态Ready 就绪状态即随时准备响应调度器的号召可以由阻塞状态转换而成。就像女神会择优选择好的备胎来处一样调度器也会选择优先级最高且就绪Ready的任务来运行。 优先级最高好理解就绪状态是怎么由阻塞状态转过来的呢这个就涉及到了事件的概念时间一般包含两类1时间相关事件2同步事件。/* 同步事件的具体概念后面学习内容会涉及 */ 1时间相关事件即设定一定的时间这个时间内会处于阻塞状态时间满足了就会转成就绪状态就像延时函数vTaskDelay一样能够用来实现周期性/超时功能。 2同步事件某个任务在等待别的任务或者中断服务程序发来的信息来唤醒它。这些同步方式包括①任务通知;②队列③事件组④信号量semaphoe⑤互斥量mutex等
三、暂停状态Suspended emsp暂停状态一般很少用唯一使用的方法就是通过void vTaskSuspend( TaskHandle_t xTaskToSuspend );来使用。
四、完整的状态转移图 2.1.5 相对延时和绝对延时 FreeRTOS中两个延时函数分别是相对延时函数vTaskDelay和绝对延时函数vTaskDelayUntil尽管两个函数都能使任务进入堵塞状态但是由于延时方式的差异也会导致应用也有所不同。
一、相对延时函数 相对延时函数vTaskDelay的开始时间是从任务中执行到这个函数开始计算的上面提到过这个函数的时间并不准确是因为容易受到其他任务和中断活动的影响导致的。以当前任务遇到更高优先级的任务为例当前任务执行到这个相对延时函数后会进入阻塞状态系统会调度其他任务运行。如果有更高的优先级任务处于就绪状态那么调度器会优先运行高优先级任务。当高优先级任务占用了CPU资源后当前这个调用了vTaskDelay函数的低优先级任务则需等待高优先级任务结束或者进入阻塞状态才能再次运行这也会导致延迟时间的不准确。此外遇到中断处理时间较长或者频繁发生导致占用过多的CPU时间也会导致原计划中任务被推迟在中断结束后调度器才会重新调度任务因此vTaskDelay延迟时间可能会比预期要长。
void vTaskDelay(TickType_t xTicksToDelay);二、绝对延时函数 绝对延时函数vTaskDelayUntil的开始时间。如下所示参数pxPreviousWakeTime用于存储上次任务唤醒的时刻而参数xTimeIncrement用于表示每次任务被唤醒后所要延时的时间。正是由于有存储上轮任务唤醒时刻的机制这个绝对延时函数更适合用于实现周期性的延时操作。
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement )三、延时实验 相对延时的实验结果如下图所示flag1为1表示Task1任务运行中flag1为0表示Task2运行中Task1处于堵塞状态。相对延时函数的开始时间是从调用vTaskDelay这个函数开始即flag1从1跳变到0时计算的50ms。 绝对延时的实验结果如下图所示flag1为1表示Task1任务运行中flag1为0表示Task2运行中Task1处于堵塞状态。绝对延时函数的开始时间是从Task1记录的上轮任务调用时间开始计算的的50ms。 具体实验代码如下所示
void vTask1( void *pvParameters )
{const TickType_t xDelay50ms pdMS_TO_TICKS(50UL);TickType_t xLastWakeTime;int i;//获取获取当前的Tick CountxLastWakeTime xTaskGetTickCount();/* 任务函数的主体一般都是无限循环 */for( ;; ){flag1;for(i0;i5;i){printf(Task 1 is running\r\n);}
#if 1vTaskDelay(xDelay50ms);
#elsevTaskDelayUntil(xLastWakeTime,xDelay50ms);
#endif}
}void vTask2( void *pvParameters )
{for( ;; ){flag0;printf(Task 2 is running\r\n);}
}2.1.6 空闲任务及钩子函数 空闲任务也就是IDLE任务在本文的 “ 2.1.2 任务的删除 ”这个实验例子中有体现。在任务的删除中一般离不开IDLE任务可以回看下我个人感觉还是写的比较清晰的。 IDLE任务的比较特殊永远不会堵塞优先级为0。一般在系统没有任务或任务处于堵塞状态下IDLE任务会被调出来。
一、钩子函数 空闲任务的钩子函数是FreeRTOS提供的一种机制允许用户在系统进入空闲任务时执行一些特定的操作。可以通过定义一个空闲任务钩子函数vApplicationIdleHook()来扩展 IDLE 任务的功能比如在系统空闲时进入低功耗模式、执行后台任务等具体作用如下
执行一些低优先级的、后台的、需要连续执行的函数测量系统的空闲时间空闲任务能被执行就意味着所有的高优先级任务都停止了所以测量空闲任务占据的时间就可以算出处理器占用率。让系统进入省电模式空闲任务能被执行就意味着没有重要的事情要做当然可以进入省电模式了。
2.1.7 调度算法
一、调度算法的配置 调度算法不仅要保证高优先级的任务先运行还要确保同优先级的就绪态任务以“轮转调度”的策略来轮流执行。当然轮流调度存在的不保证任务运行时间的公平分配因此可以细化运行时间的分配。 从3个角度理解多种调度算法 1可否抢占高优先级的任务能否优先执行(配置项: configUSE_PREEMPTION) √: 可以被称作可抢占调度(Pre-emptive)高优先级的就绪任务马上执行下面再细化。 ×: 不可以不能抢就只能协商了被称作合作调度模式(Co-operative Scheduling) ①当前任务执行时更高优先级的任务就绪了也不能马上运行只能等待当前任务主动让出CPU资源。 ②其他同优先级的任务也只能等待更高优先级的任务都不能抢占平级的更应该老实点 2可抢占的前提下同优先级的任务是否轮流执行(配置项configUSE_TIME_SLICING) √: 轮流执行被称为时间片轮转(Time Slicing)同优先级的任务轮流执行你执行一个时间片、我再执行一个时间片 ×: 不轮流执行英文为without Time Slicing当前任务会一直执行直到主动放弃、或者被高优先级任务抢占 3在可抢占时间片轮转的前提下进一步细化空闲任务是否让步于用户任务(配置项configIDLE_SHOULD_YIELD) √: 空闲任务低人一等每执行一次循环就看看是否主动让位给用户任务 ×: 空闲任务跟用户任务一样大家轮流执行没有谁更特殊 下表用于配置调度算法一共包含三个配置项分别是1用于可抢占调度的配置项configUSE_PREEMPTION2用于时间片轮转的配置项configUSE_TIME_SLICING3用于关闭Tick中断来实现省电的配置项onfigUSE_TICKLESS_IDLE。 1配置项configUSE_PREEMPTION的影响 实验共有两个优先级为0的Task1和Task2一个优先级为2的Task3。每个任务都有自己对应的flag1表示该任务运行中若系统所有任务都未执行则将IDLE任务的标志位置1。改变FreeRTOSConfig.c中配置项configUSE_PREEMPTION的值来判断影响
此时FreeRTOSConfig.c里面配置项USE_PREEMPTION为1表示高优先级抢占
int main(void)
{......xTaskCreate(vTask1, Task 1, 1000, NULL, 0, NULL);xTaskCreate(vTask2, Task 2, 1000, NULL, 0, NULL);xTaskCreate(vTask3, Task 3, 1000, NULL, 2, NULL);......
}
void vTask1( void *pvParameters )
{for( ;; ){flagIdleTaskrun 0;flagTask1run 1;flagTask2run 0;flagTask3run 0;/* 打印任务的信息 */printf(T1\r\n); }
}
void vTask2( void *pvParameters )
{ for( ;; ){flagIdleTaskrun 0;flagTask1run 0;flagTask2run 1;flagTask3run 0;/* 打印任务的信息 */printf(T2\r\n); }
}
void vTask3( void *pvParameters )
{ const TickType_t xDelay5ms pdMS_TO_TICKS( 5UL ); for( ;; ){flagIdleTaskrun 0;flagTask1run 0;flagTask2run 0;flagTask3run 1;/* 打印任务的信息 */printf(T3\r\n); // 如果不休眠的话, 其他任务无法得到执行vTaskDelay( xDelay5ms );}
}
void vApplicationIdleHook(void)//空闲状态下的钩子函数在task.c里面掉用
{flagIdleTaskrun 1;flagTask1run 0;flagTask2run 0;flagTask3run 0; /* 故意加入打印让flagIdleTaskrun变为1的时间维持长一点 */printf(Id\r\n);
}#if ( configUSE_IDLE_HOOK 1 )//钩子函数的调用
{extern void vApplicationIdleHook( void );/* Call the user defined function from within the idle task. This* allows the application designer to add background functionality* without the overhead of a separate task.* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,* CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();
}实验结果如下所示Task3优先级最高优先执行了该任务并在红线1的时刻调用延时函数进入了阻塞状态。此时Task1和Task2两个同优先级的任务开始交叉执行。等到Task3延时结束后由于优先级最高则会立马抢占重新开始Task3任务的执行。而在这三个任务都不执行的时候系统则会执行IDLE状态对应红线。 相应的如果FreeRTOSConfig.c中配置项configUSE_PREEMPTION的值为0表示不抢占的话结果如下所示。可以看到在红线前半部分正常当Task3因为延时进入阻塞状态后开始就混乱了。没有抢占更没有协商好即使Task3延时超时后优先级更高的它也没机会执行了。 2配置项configUSE_TIME_SLICING的影响 实验代码如上只不过这里是对时间片是否轮转来判断影响的。因此这里只改变configUSE_TIME_SLICING的值另外两个配置项都为1。 下图为时间片轮转即配置项configUSE_TIME_SLICING值为1。 下图为时间片不轮转即配置项configUSE_TIME_SLICING值为0。不同于时间片轮转会在高优先级任务Task3阻塞的时候flag3为0的时候轮流执行相同优先级的Task1和Task2。时间片不轮转的情况下在高优先级任务阻塞时只引起了一个任务的执行Task1/Task2。而只有高优先级任务就绪或者不再运行时才会引起任务的切换。
3配置项configIDLE_SHOULD_YIELD的影响 实验代码如上只不过这里是对空闲任务是否让步来进行。因此这里只改变configIDLE_SHOULD_YIELD的值另外两个配置项都为1。 下图为空闲任务让步即配置项configIDLE_SHOULD_YIELD值为1。 下图为空闲任务不让步即配置项configIDLE_SHOULD_YIELD值为0。可以看到配置为空闲任务为不让步后三者的优先级是相同的。在高优先级任务阻塞的时候Task1、Task2以及IDLE任务都是相同优先级因此他们会采用轮流执行。