学习笔记之FreeRTOS实时操作系统

        笔者在学习完常用的STM32内容后,听朋友推荐FreeRTOS具有很大操作空间,于是决定开始这一部分的探索。在学习完普中的FreeRTOS视频后,笔者总结了一些自己的看法。

1. 什么是RTOS

     实时操作系统(Real Time Operating System,简称RTOS)是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,并控制所有实时任务协调一致运行的操作系统。提供及时响应和高可靠性是其主要特点。这里要注意,RTOS 不是指某一个确定的系统,而是指一类系统。比如 UCOSFreeRTOSRTXRT-Thread 等这些都是 RTOS 类操作系统。本次所学的FreeROTS 就是一个免费的 RTOS 类系统,他十分小巧可以在资源有限的MCU中运行当然它也可以被扩展到其他的控制器中。  

        一般来说,我们需要的是能够在别人移植好的系统之上,利用RTOS里的API说明,然后使得这些API可以实现自己的功能即可。当然,如果有更高要求的读者也可以更加深入的了解其中的一款。 

2. Free RTOS

        FreeRTOS是是一个可裁剪的小型RTOS系统,其特点包括:
  • 内核配合支持抢占式、合作式和时间片调度;
  • 提供了一个用于低功耗的Tickless模式;
  • 系统的组件在创建时可以选择动态或静态RAM,如:任务、消息队列、信号量、软件定时器等;
  • 可移植性高,主要以C语言编写为主;
  • 支持实时任务和协程;
  • 系统简单小巧应用通常占用内核的4K到9K字节空间。

2.1 Free RTOS编程风格

数据类型

新定义的数据类型实际的数据类型(C标准类型)其他
portCHARchar
portSHORTshort16位
portLONGlong32位
portTickTypeunsigned short int用于定义系统时其计数器的值和阻塞时间的值,在头文件Free RTOSConfig.h头文件中的宏configUSE_16BIT_TICKS为1时则为16位
unsigned int用于定义系统时其计数器的值和阻塞时间的值 ,在头文件Free RTOSConfig.h头文件中的宏configUSE_16BIT_TICKS为0时则为32位
portBASE_TYPElong根据处理器的架构来决定多少位,如果是32/16/8bit的处理器则对应32/16/8bit的数据类型,一般用于定义函数的返回值或者布尔类型 

变量名

        例如char型变量的前缀是c,short型的变量前缀是s, long型变量的前缀是l, portBASE_TYPE类型的前缀是x,还有其他的数据类型例如数据结构、任务句柄、队列句柄等定义的变量名前缀也是x。如果是一个无符号型的前面还会有一个u的前缀,如果是一个指针型变量型则会有一个前缀p。

函数名

        如果是一个私有的函数,则会加一个prv(private)的前缀。如果在函数前加上一个v的前缀,则表示该函数的返回值为void的类型;若函数前缀为x,则返回值为上文所提到的portBASE_TYPE类型。

前缀所在宏定义文件
portportable.h
tasktask.h
pdprojdefsh
configFreeRTOSConfig.h
errprojdefsh

2.2 Free RTOS的配置文件

configASSERT(x)

        断言,类似 C 标准库中的 assert()函数,调试代码的时候可以检查传入的参数是否合理,FreeRTOS 内核中的关键点都会调用 configASSERT(x),当 x 0 的时候说明有错误发生。

configUSE_PREEMPTION

        使能抢占式调度器,为 1 时使用抢占式调度器,为 0 时使用协程。如果使用抢占式调度器的话内核会在每个时钟节拍中断中进行任务切换,在任务结束后CPU不会等待低优先级任务,直接由高优先级占取,而使用协程的话会在如下地方进行任务切换:

● 一个任务调用了函数 taskYIELD()

● 一个任务调用了可以使任务进入阻塞态的 API 函数。

● 应用程序明确定义了在中断中执行上下文切换。

configUSE_TIME_SLICING

        使能时间片调度器,默认情况下为高电平,此时FreeRTOS 使用抢占式调度器,这意味着调度器永远都在执行已经就绪了的最高优先级任务,优先级相同的任务在时钟节拍中断中进行切换,当宏 configUSE_TIME_SLICING为 0 的时候不会在时钟节拍中断中执行相同优先级任务的任务切换
        值得一提的是某些运行FreeRTOS的硬件有以下两种方法:通用方法和特殊方法(硬件)。
configUSE_PORT_OPTIMISED_TASK_SELECTION
        是否启用特殊方法来执行下一个任务,为高电平时使能tickless低功耗模式;低电平时保持系统节拍中断一直进行 。与此同时,若该宏为1,则采用上述的通用方式,若该宏为0,则采用特殊方法。
configUSE_TICKLESS_IDLE
        置1:使能低功耗模式;置0:关闭低功耗模式。

configCPU_CLOCK_HZ                        (SystemCoreClock)

        配置CPU时钟频率。

configTICK_RATE_HZ                        (1000)

        配置系统节拍中断的频率,即一秒钟中断的次数,每次中断RTOS都会进行任务调动。此频率也是滴答定时器的中断频率,需要使用此宏来配置滴答定时器的中断。

configMAX_PRIORITIES                        (32)

        配置优先级数字越大优先级越高,数字越小优先级越低。

configMINIMAL_STACK_SIZE                ((unsigned short)130)

        设置空闲任务的最小任务堆栈大小,以字为单位,不是字节。比如在STM32 上设置为100的话,那么真正的堆栈大小就是 100*4=400 字节

configMAX_TASK_NAME_LEN

        设置任务名最大长度。

configUSE_16_BIT_TICKS

        设置系统节拍计数器变量数据类型,系统节拍计数器变量类型为 TickType_t,当该宏为1的时候 TickType_t 就是 16 位的,当 configUSE_16_BIT_TICKS为 0 的话 TickType_t 就是 32 位。

configIDLE_SHOULD_YIELD 

        默认为高电平,此时空闲任务放弃CPU的使用权给其他同优先级的任务。 

configUSE_QUEUE_SETS

        默认为低电平,启动队列。

configUSE_TASK_NOTIFICATIONS 

        默认为高电平,开启任务通知功能。

configUSE_MUTEXES 

        默认为高电平,使用互斥信号。

configUSE_COUNTING_SEMAPHORES

        设置为 1 的时候启用计数型信号量,相关的 API 函数会被编译。

configQUEUE_REGISTRY_SIZE

        设置可以注册的队列和信号量的最大数量,在使用内核调试器查看信号量和队列的时候需要设置此宏,而且要先将消息队列和信号量进行注册,只有注册了的队列和信号量才会再内核 调试器中看到,如果不使用内核调试器的话此宏设置为 0 即可。

configSUPPORT_DYNAMIC_ALLOCATION

        默认为高电平,此时在创建 FreeRTOS 的内核对象的时候所需要的 RAM 就会从 FreeRTOS 的堆中 动态的获取内存,如果定义为 0 的话所需的 RAM 就需要用户自行提供,默认情况下宏configSUPPORT_DYNAMIC_ALLOCATION 为 1

 configTOTAL_HEAP_SIZE                    ((size_t)(20*1024)) 

        设置系统所有堆大小,如果使用了动态内存管理的话,FreeRTOS 在创建任务、信号量、队列等的时候 就 会 使 用 heap_x.c(x 1~5) 中 的 内 存 申 请 函 数 来 申 请 内 存 。 这 些 内 存 就 是 从 堆 ucHeap[configTOTAL_HEAP_SIZE]中申请的,堆的大小由 configTOTAL_HEAP_SIZE 来定义。

configUSE_IDLE_HOOK

        默认为低电平,使用空闲任务钩子函数,用户需要实现空闲任务钩子函数,函数的原型为:void vApplicationIdleHook( void )。这个函数在每个空闲任务周期都会被调用,对已经删除的任务空闲任务可以释放给他们堆栈内存,因此需要保证空闲任务可以被CPU所执行,不可调用的任务会引起空闲任务阻塞的API函数。

configUSE_TICK_HOOK

        默认为低电平,也是一个函数void vApplicationTickHook( void )。时间片中断可以周期调用,但必须十分短小,不可使用大量的堆栈,并且不能以调用"FromISR"或者"FROM_ISR"结尾的API函数。

configGENERATE_RUN_TIME_STATS

         默认为低电平,设置为 1 开启时间统计功能,相应的 API 函数会被编译,为 0 时关闭时间统计功能。如果宏 configGENERATE_RUN_TIME_STATS 1 的话还需要定义下表中的宏。

3.任务基础知识

        我们以前在使用 51、STM32 单片机的时候一般都是在main 函数里面用 while(1)做一个大循环来完成所有的处理,即应用程序是一个无限的循环,循环中调用相应的函数完成所需的处理。有时候我们也需要中断中完成一些处理。相对于多任务系统而言,这个就是单任务系统,也称作前后台系统,中断服务函数作为前台程序,大循环while(1)作为后台程序。但是这种系统的实时性差并且对于CPU资源的利用率不高,这时候我们就引出了任务系统的概念了。多任务系统的核心便是“分而治之”的思想,将许多大的问题分散为多个很小的问题,逐步将小问题解决掉,因此可以把每一个小问题当做是一个小任务来处理。

3.1 任务状态

        一般来说 FreeRTOS中的任务永远是处于运行态、就绪态、阻塞态和挂起态四种状态。其中阻塞态是指一个任务当前正在等待某个外部事件,比如说如果某个任务调用了函数 vTaskDelay()的话就会进入阻塞态,直到延时周期完成。任务在等待队列、信号量、事件组、通知或互斥信号量的时候也会进入阻塞态。任务进入阻塞态会有一个超时时间,当超过这个超时时间任务就会退出阻塞态,即使所等待的事件还没有来临!

        像阻塞态一样,挂起态也是当任务进入挂起态以后也不能被调度器调用,但是进入挂起态的任务没有超时时间。任务进入和退出挂起态通过调用函数 vTaskSuspend()xTaskResume()

3.2 任务的实现

  • 任务函数本质也是函数,所以肯定有任务名什么的,不过这里我们要注意:任务函数的返回类型一定要为 void 类型,也就是无返回值,而且任务的参数也是 void 指针类型的!任务函数名可以根据实际情况定义。
  • 任务的具体执行过程是一个大循环,for(; ; )就代表一个循环,作用和 while(1)一样,笔者习惯用 while(1)
  • 循环里面就是真正的任务代码了,此任务具体要干的活就在这里实现!
  • FreeRTOS 的延时函数,此处不一定要用延时函数,其他只要能让 FreeRTOS 发生任务切换的 API 函数都可以,比如请求信号量、队列等,甚至直接调用任务调度器。只不过最常用的就是 FreeRTOS 的延时函数。
  • 任务函数一般不允许跳出循环,如果一定要跳出循环的话在跳出循环以后一定要调用函数 vTaskDelete(NULL)删除此任务!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值