01 FreeRTOS 初识

1、freeRTOS

1.1 什么是FreeRTOS

        Free就是免费的,RTOS全称是real time operating system,即实时操作系统。FreeRTOS是一个迷你的实时操作系统内核,作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可满足较小系统的需要。

        严格上来说,FreeRTOS并不是一个实时操作系统,因为它是分时复用的,系统将时间分割成很多时间片,然后轮流执行各个任务。每个任务都是独立运行的,互不影响,由于切块的频率很快,感觉就像是同时运行的一样。

1.2 为什么选择FreeRTOS

        第一是其是免费的,源码公开,具有可移植。可裁剪。调度灵活等特点,可以方便地移植到各种单片机上运行;其二是很多半导体厂商产品的SDk(Software Development Kit)软件开发工具包,就是使用FreeRTOS作为其操作系统,尤其是WIFI、蓝牙这些带有协议栈的芯片或模块;其三是因为FreeRTOS的文件数量很少,移植简单。

1.3 FreeRTOS资料与源码下载

        最好的地方就是官网:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions

2、堆/栈

2.1 堆

        堆就是一块空闲的内存,我们可以管理这块内存,从这块内存中取出一部分使用,使用完再释放回去。

//heap_buf就是我们开辟的堆
char heap_buf[1024];
int pos = 0;

void *my_malloc(int size)
{
	int old_pos = pos;
	pos += size;
	return &heap_buf[old_pos];
}

void my_free(void *buf)
{
	/* err */
}

int main(void)
{
	char ch = 65; // char ch = 'A';
	int i;
	char *buf = my_malloc(100);
	
	unsigned char uch = 200;
	
	for (i = 0; i < 26; i++)
		buf[i] = 'A' + i;
	
	
	return 0;
}

2.2 栈

        在编写代码中,我们可能感觉不到栈的存在,但是它确实一直在默默存在。

void c_fun(void)
{
}


void b_fun(void)
{
}


int a_fun(int val)
{
	int a = 8;
	a += val;
	
	b_fun();
	
	c_fun();
	
	return a;
}


int main(void)
{
	
	a_fun(46);
	
	return 0;
}

        在上面的程序中,main函数调用了a_fun,在a_fun中调用了b_fun和c_fun。main函数在调用a_fun之前,会将返回的地址保存在LR(Link Register)中,然后调用a_fun,在main中返回的地址就是return 0语句所在的位置。

        a_fun函数在调用b_fun和c_fun时也是类似的,在调用之前要先将返回的地址保存在LR(Link Register)中,然后去调用,而在a_fun中调用b_fun的返回地址是c_fun所在的语句。那么此时main和a_fun函数的返回值的地址都保存在LR中,LR会不会被覆盖了?

        在a_fun函数内部,它会将LR的值存入栈中,然后再来保存本身的返回值地址,这样就不怕LR会被覆盖了,因此函数返回值的地址是被存入栈中的。栈也是一块空闲的内存。在main函数执行时,它会给main函数开辟一部分的栈空间,里面存放LR等寄存器和局部变量的值。

2.3 堆和栈的区别

        管理方式不同:栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏。

        分配方式不同:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。

        空间大小不同:栈的大小要远远小于堆的大小。

        生长方向不同:堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。

        数据结构不同:堆可以看成一棵树,如堆排序;栈是一种先进后出的结构。

        作用域的不同:堆的作用域是在全局,栈的作用域是在函数局部。

3、从官方源码精简出第一个freeRTOS

3.1 从官网下载

FreeRTOS - Free RTOS Source Code Downloads, the official FreeRTOS zip file release download

解压之后打开的目录

        FreeRTOS-Plus里面是FreeRTOS生态的文件,非必需,tools里面是亚马逊相关的文件,也不需要。文件夹只需要保留FreeRTOS。

        进入FreeRTOS-Demo文件夹,选择我们的目标文件夹,例如CORTEX_STM32F103_Keil,其余的就可以删掉了,在Demo中的文件夹的命名规则是指令集+芯片+编译器或者芯片+编译器。

        在FreeRTOS-Source文件夹中,里面的目录结构如下所示,在portable文件夹中,我们保留RVDS和MemMang,这里的命名规则是编译器/架构,在RVDS文件夹中,我们可以只保存ARM_CM3。

        至此,从官网下载的文件中,我们暂时不需要的文件都已经清除,文件大小从455MB降到了8.28MB。

3.2 打开工程

        这个工程是用Keil4来编写的,我们要将其更新成Keil5

        点击 Migrate to Device Pack 即可,更新完之后关掉再重新打开。

        打开之后会出现一些错误,然后根据提示更改。嫌麻烦可以将FreeRTOS-Demo中的Common文件夹保留,就不会报错了。

3.3 添加串口打印功能

3.3.1 去掉无关的代码

        System中的lcd.c,Demo files中的ParTest.c timertest.c spi_test.c semtest.c BlockQ.c blocktim.c comtest.c death.c flash.c integer.c  PollQ.c

        然后编译,根据错误提示,来进行修改。

3.3.2 添加串口打印功能

        第一步要初始化串口

        第二步要实现fputc功能,因为printf函数是标准库写好的,里面用到了fputc函数,我们要用串口将其打印出来就要对其进行改写。

3.3.3 使用Keil自带模拟器运行

        点击魔法棒-Debug,勾选Use Simulator

        然后点击debug,运行查看结果。

4、自己的第一个freeRTOS程序

4.1 创建两个打印任务

//参数:
    //函数指针,任务函数
    //任务的名字
    //栈大小,单位为word,10代表40字节
    //调用任务函数时传入的参数
    //优先级
    //任务句柄,以后使用它来操作这个函数
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, 
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )
//关键代码
void Task1Function( void * param)
{
	while(1){
		printf("1");
	}
}

void Task2Function( void * param)
{
	while(1){
		printf("2");
	}
}


xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);    //这样写也可以

        该程序运行结果会发现1和2交替打印。       

        既可以仿真查看结果,也可以上传到开发板查看结果。

        在开发板上直接调试,要保证开发板上电,开发板要连接调试器,并且在Keil中要选择真实的调试器。

        如果在keil中无法在源文件中设置断点,要确保调试器接好,并且将(魔术棒-Debug-调试器Settings-Debug)Debug中的“Verify Code Download”和“Download to flash”去掉勾选。

        如果要在keil中重新使用模拟器运行代码,要在Device中选择模拟的设备,Debug中选择“Use Simulator”,指定DLL:DARMSTM.DLL,指定参数:-STM32Fxxxx

4.2 freeRTOS源码结构

        以Keil工具下STM32F103芯片为例,它的FreeRTOS的目录如下:

        主要涉及2个目录:

                Demo:

                        Demo目录下是工程文件,以"芯片和编译器"组合成一个名字,比如:CORTEX_STM32F103_Keil

                Source:

                        根目录下是核心文件,这些文件是通用的

                        portable目录下是移植时需要实现的文件

                                目录名为:[compiler]/[architecture]

                                比如:RVDS/ARM_CM3,这表示cortexM3架构在RVDS工具上的移植文件

4.2.1 核心文件

        freeRTOS的最核心文件只有两个:

                FreeRTOS/Source/task.c

                FreeRTOS/Source/list.c

                其它文件的作用:

FreeRTOS/Source/下的文件作用
task.c必需,任务操作
list.c必需,列表操作
queue.c基本必需,提供队列操作、信号量(semaphore)操作
timer.c可选,software timer
event_groups.c可选,提供event group功能
croutine.c可选,过时了

4.2.2 移植时涉及的文件

        移植FreeRTOS时涉及的文件放在FreeRToS/Source/portable/[compiler]/[architecture]目录下,比如:RVDS/ARM_CM3,这表示cortexM3架构在RVDS或Keil工具上的移植文件。

        里面有两个文件:port.c和portmacro.h

4.2.3 头文件相关

        FreeRTOS需要3个头文件目录:

                FreeRTOS本身的头文件:FreeRTOS/Source/linclude

                移植时用到的头文件:FreeRTOS/Source/portable/[compiler]/[architecture]

                含有配置文件FreeRTOSConfig.h的目录(Demo/目标文件夹)

        头文件列表:

头文件作用
FreeRTOSConfig.h
FreeRTOS 的配置文件,比如选择调度算法: configUSE_PREEMPTION
每个 demo 都必定含有 FreeRTOSConfig.h
建议去修改 demo 中的 FreeRTOSConfig.h ,而不是从头写一个
FreeRTOS.h
使用 FreeRTOS API 函数时,必须包含此文件。
FreeRTOS.h 之后,再去包含其他头文件,比如:
task.h queue.h semphr.h event_group.h

4.2.4 内存管理

        文件在FreeRTOS/Source/portable/MemMang下,它也是放在portable目录下,表示你可以提供自己的函数。

        源码中默认提供了5个文件,对应内存管理的5种方法:

文件优点缺点
heap_1.c
分配简单,时间确定
只分配、不回收
heap_2.c
动态分配、最佳匹配
碎片、时间不定
heap_3.c
调用标准库函数
速度慢、时间不定
heap_4.c
相邻空闲内存可合并
可解决碎片问题、时间不定
heap_5.c
heap_4 基础上支持分隔的内存块
可解决碎片问题、时间不定

4.2.5 Demo

        Demo目录下是预先配置好的、没有编译错误的工程。目的是让你可以基于它进行修改,以适配你的单板。

        这些Demo还可以继续精简:

                Demo/ common中的文件可以完全删除

                main函数中只需要保留2个函数:

                        prvSetupHardware()

                        vTaskStartScheduler()

                        如下图所示:


4.3 freeRTOS数据类型和编程规范

4.3.1 数据类型

        每个移植的版本都含有自己的portmacro.h头文件,里面定义了2个数据类型:

        TickType_t:

                FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt

                每发生一次中断,中断次数累加,这被称为tick counto

                tick count这个变量的类型就是TickType_t

                TickType_t可以是16位的,也可以是32位的

                FreeRTOSConfig.h中定义configUSE_16_BIT_TICKS时,TickType_t就是uint16_t

                否则TickType_t就是uint32_t

                对于32位架构,建议把TickType_t配置为uint32_t

        BaseType_t:

                这是该架构最高效的数据类型

                32位架构中,它就是uint32_t

                16位架构中,它就是uint16_t

                8位架构中,它就是uint8_t

                BaseType_t通常用作简单的返回值的类型,还有逻辑值,比如pdTRUE/pdFALSE

4.3.2 变量名

        在FreeRTOS的源码中,其变量名通常带有小写字母的前缀:

变量名前缀
含义
c
char
s
int16_t short
l
int32_t long
x
BaseType_t
其他非标准的类型:结构体、 task handle queue handle
u
unsigned
p
指针
uc
uint8_t unsigned char
pc
char 指针

4.3.3 函数名

        在FreeRTOS的源码中,函数名的前缀有2部分:返回值类型、在哪个文件定义。

函数名前缀
含义
vTaskPrioritySet
返回值类型: void
task.c 中定义
xQueueReceive
返回值类型: BaseType_t
queue.c 中定义
pvTimerGetTimerID
返回值类型: pointer to void
tmer.c 中定义

4.3.2 宏的名

        在FreeRTOS的源码中,宏的名字是大小,可以添加小写的前缀。前缀是用来表示:宏在哪个文件中定义。

宏的前缀
含义:在哪个文件里定义
port ( 比如 portMAX_DELAY)
portable.h portmacro.h
task ( 比如 taskENTER_CRITICAL())
task.h
pd ( 比如 pdTRUE)
projdefs.h
config ( 比如 configUSE_PREEMPTION)
FreeRTOSConfig.h
err ( 比如 errQUEUE_FULL)
projdefs.h

        通用的宏定义:

pdTRUE
1
pdFALSE
0
pdPASS
1
pdFAIL
0



 

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值