RTOS

RTOS系统

FreeRTOS,UCOS,RTX,RT-Thread,DJYOS

FreeRTOS

资料

FreeRTOS官网

http://www.freertos.org/

开源电子网

www.opendv.com

前后台系统和RTOS系统

前后台系统

image-20210930214550374

RTOS系统

img

img

img

img

运行就绪的高优先级任务

协程

(co-routines也有称为 合作 式、 协同程序, 本教程 均成为协程 )。

源码

image-20210928205031474

image-20210928205031474

image-20210928205031474

image-20210928205031474

移植

delay相关

delay_us()是 us级延时函数, delay_ms和 delay_xms()都是 ms级的延时函数, delay_us()和delay_xms()不会导致任务切换。

delay_ms()其实就是对 FreeRTOS中的延时函数 vTaskDelay()的简单封装,所以在使用 delay_ms()的时候就会导致任务切换。

FreeRTOSConfig.h配置

INCLUDE_开头的宏

0关闭,1开启,不用的可以取消,节省空间

config开头的宏

configSUPPORT_DYNAMIC_ALLOCATION

#define configSUPPORT_DYNAMIC_ALLOCATION 1

支持动态内存申请,可以从heap_1.c 到heap_5.c自行选择

configASSERT

FreeRTOS内核中的关键点都会调用 configASSERT( 当 x为 0的时候说明有错误发生,使用断言的话会导致开销加大,一般在调试阶段使用。 configASSERT()需要在 FreeRTOSConfig.h文件中定义, 如下实例:

#define configASSERT((x)) if((x)==0) vAssertCalled(__FILE_,__LINE__);
注意, vAssertCalled()函数需要用户自行去定义,可以是显示到 LCD上的 函数 ,也可以是通过串口打印出来的函数 ,本教程的所有例程采用如下的定义
//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)

当参数 x错误的时候就通过串口打印出发生错误的文件名和错误所在的 行号,调试代码的
可以使用断言,当调试完成以后尽量去掉断言, 防止 增加开销!

configCHECK_FOR_STACK_OVERFLOW

堆栈溢出检测

configCHECK_FOR_STACK_OVERFLOW不为 0 则开启需要用户提供一个钩子函数(回调函数)

void vApplicationStackOverflowHook( TaskHandle_t xTask, char * pcTaskName );  /*参数 xTask是任务句柄, pcTaskName是任务名字
如果发生这种情况的话可以直接查看变量 pxCurrentTCB来确定哪个任务发
生了堆栈溢出 。有些处理器可能在堆栈溢出的时候生成一个*/

两种堆栈检测方法

configCHECK_FOR_STACK_OVERFLOW==1 检测快,但检测不全

configCHECK_FOR_STACK_OVERFLOW==2 检测相对慢些,但几乎都能检测出来

configCPU_CLOCK_HZ

设置 CPU的频率。

onfigSUPPORT_DYNAMIC_ALLOCATION

的话在创建 FreeRTOS的内核对象的时候 所需要的 RAM就会从 FreeRTOS的堆中动态的获取内存,如果定义为0 ,用户需要自己提供

configENABLE_BACKWARD_COMPATIBILITY

configGENERATE_RUN_TIME_STATS

设置为 1开启时间统计功能,还需设置一下宏

描述
portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() 此宏用来初始化一个外设来作为时间统计的基准时钟。
portGET_RUN_TIME_COUNTER_VALUE()或portALT_GET_RUN_TIME_COUNTER_VALUE(Time) 此宏用来返回当前基准时钟的时钟值。

configIDLE_SHOULD_YIELD

此宏定义了与空闲任务(idle Task)处于同等优先级的其他用户任务的行为

0空闲任务不会为其他处于同优先级的任务让出CPU 使用权

1空闲任务就会为处于同等优先级的用户任务让出CPU 使用权,除非没有就绪的用户任务

建议关闭,因为会导致某个任务的运行时间变短

configKERNEL_INTERRUPT_PRIORITY、configMAX_SYSCALL_INTERRUPT_PRIORITY、

configMAX_API_CALL_INTERRUPT_PRIORITY

和FreeRTOS 的中断配置有关

configMAX_CO_ROUTINE_PRIORITIES

设置可以分配给协程的最大优先级,也就是协程的优先级数

设置好以后协程的优先级可
以从0 到configMAX_CO_ROUTINE_PRIORITIES-1 , 其中0 是最低的优先级,configMAX_CO_ROUTINE_PRIORITIES-1 为最高的优先级。

configMAX_PRIORITIES

设置任务的优先级数量

设置好以后任务就可以使用从0 到configMAX_PRIORITIES-1 的
优先级,其中0 是最低优先级,configMAX_PRIORITIES-1 是最高优先级。注意和UCOS 的区别,UCOS 中0 是最高优先级!

configMAX_TASK_NAME_LEN

设置任务名最大长度。

configMINIMAL_STACK_SIZE

设置空闲任务的最小任务堆栈大小,以字为单位,不是字节。

比如在STM32上设置为 100的话,那么真正的堆栈大小就是 100*4=400字节。

字(Word)代表计算机处理指令或数据的二进制数位数,是计算机进行数据存储和数据处理的运算的单位。

对于32位计算机与64位计算机,字的大小往往不同。

32位计算机:1字=32位=4字节,64位计算机:1字=64位=8字节

configNUM_THREAD_LOCAL_STORAGE_POINTERS

设置每个任务的本地存储指针数组大小,任务控制块中有本地存储数组指针,用户应用程序可以 在 这些本地存储中存入一些数据。

configUSE_PORT_OPTIMISED_TASK_SELECTION

FreeRTOS有两种方法来选择下一个要运行的任务,一个是通用的方法,另外一个 是特殊的方法,也就是硬件方法,使用MCU自带 的硬件指令来实现。
通用方法:
● 当宏 configUSE_PORT_OPTIMISED_TASK_SELECTION为 0,或者硬件不支持的时候。
● 希望所有硬件通用的时候。
● 全部用 C语言来实现,但是效率比特殊方法低。
● 不限制最大优先级 数目的时候 。
特殊方法:
● 不是所有的硬件都支持。
● 当宏 configUSE_PORT_OPTIMISED_TASK_SELECTION为 1的时候。
● 硬件拥有特殊的指令,比如计算前导零 (CLZ)指令。
● 比通用方法效率高。
● 会限制优先级数目,一般是 32个。
STM32有计算前导零的指令,所以我们可以使用特殊方法,即将宏
configUSE_PORT_OPTIMISED_TASK_SELECTION定义为 1。 计算前导零的指令在 UCOSIII也用到了,也是用来查找下一个要运行的任务的。

configUSE_PREEMPTION

为1时使用抢占式调度器,为 0时使用协程 。如果使用抢占式调度器的话内核会在每个时钟节拍中断中进行任务切换,当使用 协程 的话会在如下地方进行任务切换:
● 一个任务调用了函数 taskYIELD()。
● 一个任务调用了可以使任务进入阻塞态的 API函数。
● 应用程序明确定义了在中断中执行上下文切换。

FreeRTOS中断配置和临界段

Cortex-M中断

Cotex-M3的 NVIC最多支持 240个 IRQ(中断请求 )、 1 (NMI)、 1 Systick(滴
答定时器 )定时器中断和多个系统异常。

优先级地址,四个相邻的寄存器组成一个32位的寄存器,直接操作四个相邻中的第一个

FreeRTOS中断配置

FreeRTOS的中断配置没有处理亚优,配置为组 4(即NVIC_PriorityGroup_4),直接就 16个优先级

PRIMASK和 FAULTMASK寄存器

PRIMASK用于禁止除 NMI和 HardFalut外的所有异常和中断

FAULTMASK禁止除 NMI的所有异常和中断

与临界代码段有关->用于保护临界区内的代码运行

BASEPRI寄存器

可调节屏蔽中断的阈值

如果向 BASEPRI写 0的话就会停止屏蔽中断。

FreeRTOS的开关中断就是操作 BASEPRI寄存器来实现的!它可以关闭低于某个阈值的中断,高于这个阈值的中断就不会被关闭!

FreeRTOS中断配置宏

configPRIO_BITS

此宏用来设置MCU使用几位优先级, STM32使用的是4位,因此此宏为4

configLIBRARY_LOWEST_INTERRUPT_PRIORITY

此宏是用来设置最低优先级

STM32优先级使用了 4位,而且 STM32配置的使用组 4,也就是 4位都是抢占优先级。 因此 优先级 数 就是 16个,最低优先级那就是 15。 所以此宏就是 15

configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

设置FreeRTOS 系统可管理的最大优先级

设置为5的话,高于5 的优先级(优先级数小于5)不归FreeRTOS 管理!

configMAX_SYSCALL_INTERRUPT_PRIORITY

此宏设置好以后,低于此优先级的中断可以安全的调用FreeRTOS 的API 函数,高于此优先级的中断,FreeRTOS 是不能禁止的,中断服务函数也不能调用FreeRTOS 的API 函数!

例如:

● configMAX_SYSCALL_INTERRUPT_PRIORITY==5
● configKERNEL_INTERRUPT_PRIORITY==15

image-20220220211027273

image-20220220211027273

image-20220220211027273

image-20220220211027273

对于实时性要求严格的任务可以使用这些优先级

临界段代码

程序完整运行不被打断,中优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断会被屏蔽掉,高于的就不会

FreeRTOS与临界段代码保护有关的函数有 4个:

  • 任务级的临界段代码保护->用在任务中
void taskcritical_test(void) {
    while(1) {
        taskENTER_CRITICAL(); //(1)
        total_num+=0.01f; printf("total_num的值为 : %.4f\r\n",total_num);
        taskEXIT_CRITICAL(); //(2)
        vTaskDelay(1000);
    }
}

在内部使用delay_xms(5000);不会引起任务调度

该函数的活动会先暂停去执行其他函数,直到5s到了

  • 中断级的临界段代码保护->用在中断服务中的
//定时器 3中断服务函数
void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
    status_value=taskENTER_CRITICAL_FROM_ISR(); //(1)

    total_num+=1; printf("float_num的值为 : %d\r\n",total_num);

    taskEXIT_CRITICAL_FROM_ISR(status_value); //(2)
}

vTaskDelay()进入阻塞状态

任务

协程,用于资源很少的MCU(不更新了)

任务没有超时时间。任务进入和退出挂起态通过调用函数 vTaskSuspend()和 xTaskResume()。

阻塞态有有超过时间

挂起态没有超过时间

#define configMAX_PRIORITIES (32)

//可使用的最大优先级 按需设置

当宏configUSE_TIME_SLICING 定义为1 的时候多个任务可以共用一个优先级,数量不限。

。如果一定要从任务函数中退出的话那一定
要调用函数 vTaskDelete(NULL)来删除此任务。 */ vTaskDelete(NULL); (

的内存需要用户自行释放掉,比如某个任务中用户调用函数 pvPortMalloc()分配了 500字节的内
存,那么在此任务被删除以后用户也必须调用函数 vPortFree()将这 500字节的内存释放掉,否

vTaskStartScheduler(); //开启任务调度

任务挂起与恢复

vTaskSuspend()
挂起一个任务

vTaskResume()
恢复一个任务的运行

xTaskResumeFromISR()
中断服务函数中恢复一个任务的运行。

如果参数为 NULL的话表示挂起任务自己。

中断的时候就讲过,如果中断服务函数要使
用 FreeRTOS的 API函数的话那么中断优先级一定要低于
configMAX_SYSCALL_INTERRUPT_PRIORITY!这里设置为 6。


#rtos