FreeRTOS-FreeRTOS概述

目录

FreeRTOS

FreeRTOS目录结构

移植过程

创建任务

FreeRTOS数据类型

FreeRTOS命名规范

内存管理

Heap_1

Heap_2

Heap_3

Heap_4

Heap_5

Heap相关函数

pvPortMalloc/vPortFree

xPortGetFreeHeapSize

xPortGetMinimunEverFreeHeapSize

malloc失败的钩子函数


FreeRTOS

FreeRTOS目录结构

移植过程

在工程中创建freertos文件夹,在freertos文件夹中创建src文件夹、inc文件夹、port文件夹。

freertos/src存放源码
freertos/inc存放头文件
freertos/port存放移植平台的相关文件

复制内存管理文件:复制FreeRTOS/Source/portable/MemMang/heap_4.c文件到freertos/port文件夹。

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

复制移植相关文件:复制FreeRTOS/Source/portable/RVDS/ARM_CM3文件下的port.c文件、portmacro.h文件到freertos/port文件夹。

复制通用核心文件:复制FreeRTOS/Source文件夹下的croutine.c文件、event_groups.c文件、list.c文件、queue.c文件、task.c文件、timers.c文件到freertos/src文件夹。

复制源码头文件:复制FreeRTOS/Source/include下的所有文件到freertos/inc文件夹。

复制配置文件:复制FreeRTOS/Demo/CORTEX_STM32F103_Keil文件夹下的FreeRTOSConfig.h文件到freertos文件夹。

到此配置完成,在IDE里把添加的文件导到工程中。

需要额外修改:

        在FreeRTOSConfig.h文件中末尾#endif前添加3个宏。

                #define xPortPendSVHandler PendSV_Handler

                #define xPortSVCHandler       SVC_Handler

                #define xPortSysTickHandler  SysTick_Handler

        这三个宏是中断服务函数的宏,在FreeRTOS里用到,并且FreeRTOS操作系统已经实现了这三个中断服务函数。

        因此在stm32f10x_it.c文件中注释掉3个相关的中断服务函数。

创建任务

#include "freertos.h"
#include "task.h"

TaskHandler_t myTaskHandler;

void myTask(void *arg)
{
    whiel(1)
    {
        vTaskDelay(500);
    }
}

int main(void)
{
    初始化工作

    // 任务函数名称,任务名称,分配的任务堆栈大小,任务传递参数,任务优先级,任务句柄
    xTaskCreate(myTask, "myTask", 512, NULL, 2, &myTaskHandler);

    // 开启调度
    vTaskStartScheduler();

    while(1)
    {}
}

FreeRTOS数据类型

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

TickType_t:

        FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt,每发生一次中断,中断次数tick count累加,tick count变量的类型就是TickType_t。

        TickType_t可以是16位或32位,具体是在FreeRTOSConfig.h文件中由configUSE_16_BIT_TICKS宏定义,宏条件成立就为uint16_t,否则为uint32_t。

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

BaseType_t:

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

        在32位架构,他就是uint32_t。在16位架构,他就是uint16_t。在8位架构,他就是uint8_t。

        BaseType_t通常用作简单的返回值类型和逻辑值(比如pdTRUE/pdFALSE)。

FreeRTOS命名规范

变量名前缀含义
cchar
sint16_t,short
lint32_t,long
xBaseType_t,其它非标准的类型:结构体、task handle、queue handle等
uunsigned
p指针
ucuint8_t,unsigned char
pcchar指针
函数名前缀含义
vTaskPrioritySet返回值类型:void
在task.c中定义
xQueueReceive返回值类型:BaseType_t
在queue.c中定义
pvTimerGetTimerID返回值类型:pointer to void
在timer.c中定义
宏的前缀含义(在哪个文件里定义)
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
通用宏
pdTRUE1
pdFALSE0
pdPASS1
pdFAIL0

内存管理

在c语言的库函数中,有malloc、free等函数,但是在FreeRTOS中,它们不适用,因为:

        malloc、free等函数实现过于复杂、占据的代码空间太大,不适合用在资源紧缺的嵌入式系统中;

        并非线程安全的(thread-safe),运行具有不确定性(每次调用malloc、free等函数时花费的时间可能都不相同);

        内存碎片化;

        使用不同的编译器时,需要进行复杂的配置,有时候难以调试。

堆(heap):一块空闲的内存,需要提供管理函数。

        malloc:从堆中划出一块空间给程序使用。

        free:用完后,再把它标记为空闲,供下次使用。

栈(stack):函数调用局部变量时保存在栈中,当前程序的环境也是保存在栈中。可以从堆中分配一块空间用作栈。

FreeRTOS中内存管理的接口函数为:pvPortMalloc、vPortFree,对应于C库的malloc、free。文件存在于FreeRTOS/Source/portable/MemMang。

Heap_1

只实现了pvPortMalloc,没有实现vPortFree。如果程序不需要删除内核对象,就可以使用。

实现最简单,没有碎片问题。在一些要求非常严格的系统里不允许使用动态内存,就可以使用heap_1。

实现原理很简单,首先定义一个大数组。

/* Allocate the memory for the heap. */
#if ( configAPPLICATION_ALLOCATED_HEAP == 1 )

/* The application writer has already defined the array used for the RTOS
* heap - probably so it can be placed in a special segment or address. */
    extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#else
    static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
#endif /* configAPPLICATION_ALLOCATED_HEAP */

然后pvPortMalloc()函数调用时,从这个数组中分配空间。

FreeRTOS在创建任务时,需要2个内核对象:task control block(TCB)、stack。

使用heap_1时,内存分配过程如下:

Heap_2

Heap_2之所以还保留,只是为了兼容以前的代码。新设计中不再推荐使用Heap_2(但效率仍然远高于malloc、free)。建议使用Heap_4来代替Heap_2,更高效。

Heap_2和Heap_1一样也是在数组上分配内存,区别在于Heap_2使用最佳匹配算法(best fit)来分配内存并且支持vPortFree

最佳匹配算法(best fit):假设heap有3块空闲内存:5字节、25字节、100字节。

pvPortMalloc想申请20字节,则需找出最小的、能满足pvPortMalloc的内存:25字节。把25字节划分为20字节、5字节。返回这20字节的地址给分配,剩下的5字节仍然是空闲的状态,留给后续的pvPortMalloc使用。

与Heap_4相比,Heap_2不会合并相邻的空闲内存,所以Heap_2会导致严重的碎片化问题。但是如果申请内存时大小总是相同的,Heap_2是没有碎片化的问题的,适合频繁地创建、删除任务,但是任务的栈大小都是相同的(创建任务时需要分配TCB和栈,TCB总是一样的)场景。

使用heap_2时,内存分配过程如下:

Heap_3

使用标准C库的malloc、free函数,所以堆大小由链接器的配置决定,配置项 configAPPLICATION_ALLOCATED_HEAP 不再起作用。

标准C库的malloc、free函数并非线程安全的。Heap_3中先暂停FreeRTOS的调度器,再去调用标准C库的malloc、free函数,使用这种方法实现了线程安全。

Heap_4

Heap_4和Heap_1、Heap_2一样也是使用大数组来分配内存。Heap_4使用首次适应算法(first fit)来分配内存,并且还会把相邻的空闲内存合并为一个更大的空闲内存,有助于较少内存的碎片问题。适用于频繁分配、释放不同大小的内存场景。Heap_4执行时间不确定,但效率高于标准库的malloc、free。

首次适应算法(first fit):假设heap有3块空闲内存:5字节、200字节、100字节。

pvPortMalloc想申请20字节,则需找出第一个能满足pvPortMalloc的内存:200字节。把它划分为20字节、180字节。返回这20字节的地址给分配,剩下的180字节仍然是空闲的状态,留给后续的pvPortMalloc使用。

使用heap_4时,内存分配过程如下:

Heap_5

Heap_5分配、释放内存的算法和Heap_4是一样的。区别是Heap_5并不局限于管理一个大数组,可以管理多个块、分隔开的内存。适用于内存地址不连续的场景。

既然内存是分隔开的,所以需要进行初始化来确定内存位置和大小。使用pvPortMalloc前必须使用vPortDefineHeapRegions指定内存块的信息;

// 把pxHeapRegions数组传给vPortDefineHeapRegions函数即可初始化Heap_5
void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions );

// 怎么指定一块内存?使用如下结构体
typedef struct HeapRegion
{
	uint8_t * pucStartAddress; 	// 起始地址
	size_t xSizeInBytes; 		// 大小
} HeapRegion_t;

// 怎么指定多块内存?使用一个HeapRegion_t数组,在这个数组中,低地址在前、高地址在后。
HeapRegion_t xHeapRegions[] =
{
	{ ( uint8_t * ) 0x80000000UL, 0x10000 }, 	// 起始地址0x80000000,大小0x10000
	{ ( uint8_t * ) 0x90000000UL, 0xa0000 }, 	// 起始地址0x90000000,大小0xa0000
	{ NULL, 0 } 								// 表示数组结束
};

Heap相关函数

pvPortMalloc/vPortFree
void * pvPortMalloc( size_t xWantedSize );
void vPortFree( void * pv );

分配、释放内存。如果分配内存不成功,返回NULL。

xPortGetFreeHeapSize
size_t xPortGetFreeHeapSize( void );

查看当前还有多少空闲内存,可以用来优化内存的使用情况,如当所有内核对象都分配好后,执行此函数返回2000,那么configTOTAL_HEAP_SIZE就可减小2000。

在heap_3中无法使用该函数。

xPortGetMinimunEverFreeHeapSize
size_t xPortGetMinimumEverFreeHeapSize( void );

返回程序运行过程中的空闲内存容量的最小值。

只有heap_4、heap_5可使用该函数。

malloc失败的钩子函数
void * pvPortMalloc( size_t xWantedSize )vPortDefineHeapRegions
{
    ......
#if ( configUSE_MALLOC_FAILED_HOOK == 1 )
{
    if( pvReturn == NULL )
    {
        extern void vApplicationMallocFailedHook( void );
        vApplicationMallocFailedHook();
    }
}
#endif

    return pvReturn;
}

所以如果想要使用这个钩子函数:

        在FreeRTOSConfig.h文件中,把configUSE_MALLOC_FAILED_HOOK宏定义为1。

        提供vApplicationMallocFailedHook函数。

        pvPortMalloc失败时才会调用此函数。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一篇 平台篇 第1章 ARM处理器简介 1.1 ARM内核处理器沿革 1.1.1 传统ARM处理器 1.1.2 Cortex内核处理器 1.2 Cortex内核系列处理器技术特点 1.2.1 ARM Cortex-M系列处理器 1.2.2 ARM Cortex-R系列处理器 1.2.3 ARM Cortex-A系列处理器 1.3 STM32互联型嵌入式控制器 1.4 微控制器选型 1.4.1 选型因素 1.4.2 选型示例 第2章 基于STM32F107的开发板 2.1 STM32F107开发板 2.2 主要板载资源 2.2.1 10/100M以太网接口 2.2.2 CAN总线接口 2.2.3 RS485总线接口 2.2.4 其他总线接口 2.3 硬件设计要点 2.3.1 电磁兼容问题 2.3.2 信号完整性 2.3.3 电源完整性 第3章 开发环境 3.1 开发环境及搭建 3.1.1 常见开发环境 3.1.2 IAR EWARM安装 3.1.3 RealView MDK安装 3.2 相关开发工具 3.3 创建工程 第4章 编程规范 4.1 ST固件库编程规范 4.1.1 缩写 4.1.2 命名规则 4.1.3 编码规则 4.2 基于C语言的嵌入式编程规范 4.2.1 源代码的排版 4.2.2 源代码的注释 4.2.3 标识符命名 4.2.4 代码可读性 4.2.5 变量、结构 4.2.6 函数、过程 4.2.7 可测性 4.2.8 程序效率 4.2.9 质量保证 4.2.10 代码编辑、编译、审查 4.2.11 测试与维护 4.2.12 宏定义 第5章 项目规划 5.1 概述 5.2 系统分析 5.3 系统设计 5.4 系统制造 5.5 系统运用及反馈 5.6 开发团队 5.6.1 团队负责人 5.6.2 调研人员 5.6.3 开发人员 第二篇 RTOS篇 第6章 操作系统原理基础知识 6.1 前后台模式应用程序 6.2 嵌入式操作系统 6.2.1 相关基本概念 6.2.2 系统调用 6.2.3 操作系统结构 6.2.4 进程与任务 6.2.5 进程间的通信 6.2.6 进程调度 6.2.7 存储管理 第7章 FreeRTOS嵌入式操作系统 7.1 FreeRTOS特色 7.2 任务管理 7.2.1 任务函数 7.2.2 基本任务状态 7.2.3 任务创建 7.2.4 任务的优先级 7.2.5 非运行状态 7.2.6 空闲任务及回调函数 7.2.7 改变任务优先级 7.2.8 删除任务 7.2.9 调度算法概述 7.3 队列管理 7.3.1 概述 7.3.2 使用队列 7.3.3 大型数据单元传输 7.4 中断管理 7.4.1 延迟中断处理 7.4.2 计数信号量 7.4.3 在中断服务例程中使用队列 7.4.4 中断嵌套 7.5 资源管理 7.5.1 基本概念 7.5.2 临界区与挂起调度器 7.5.3 互斥量 7.5.4 互斥的另一种实现 7.6 内存管理 7.6.1 概述 7.6.2 内存分配方案范例 7.7 常见错误 7.7.1 概述 7.7.2 栈溢出 7.7.3 其他常见错误 第8章 基于STM32F107的FreeRTOS移植 8.1 概述 8.2 FreeRTOS移植 8.2.1 portmacro.h头文件 8.2.2 port.c源文件 8.2.3 portasm.s汇编源文件 8.2.4 其他问题 8.3 创建测试任务 第三篇 LwIP篇 第9章 TCP/IP协议栈介绍 9.1 引言 9.2 网络分层 9.2.1 OSI七层参考模型 9.2.2 TCP/IP分层 9.2.3 TCP/IP协议簇的协议 9.3 IP协议 9.4 ARP协议与RARP协议 9.5 ICMP 9.6 TCP协议 9.7 UDP协议 9.8 FTP协议 第10章 LwIP轻量级TCP/IP协议栈 10.1 LwIP进程模型 10.2 LwIP缓冲与内存管理 10.2.1 LwIP动态内存管理机制 10.2.2 LwIP的缓冲管理机制 10.3 LwIP网络接口 10.4 LwIP的ARP处理 10.5 LwIP的IP处理 10.6 LwIP的ICMP处理 10.7 LwIP的UDP处理 10.8 LwIP的TCP处理 10.8.1 TCP处理流程概述 10.8.2 TCP控制块 10.8.3 LwIP的TCP滑动窗口 10.8.4 LwIP的TCP超时与重传 10.8.5 LwIP的TCP拥塞控制 10.8.6 LwIP的TCP定时器 10.9 LwIP的应用程序接口简介 10.9.1 RAW API接口 10.9.2 Sequential API接口 第11章 基于STM32F107的LwIP移植 11.1 ethernetif.c文件的移植 11.1.1 ethernetif_init函数 11.1.2 low_level_init函数 11.1.3 ethernetif_input函数 11.1.4 low_level_input函数 11.1.5 low_level_output函数 11.2 网络驱动移植 11.2.1 以太网控制器概述 11.2.2 以太网控制器硬件配置 11.2.3 以太网控制器硬件的引脚配置 11.2.4 以太网驱动之接收 11.2.5 以太网驱动之发送 11.2.6 其他注意事项 11.3 基于RAW API接口的HelloWorld例程 第四篇 移植篇 第12章 基于FreeRTOS的LwIP协议栈移植 12.1 概述 12.2 FreeRTOS下以太网驱动程序的移植 12.3 LwIP程序移植 12.3.1 以太网接口文件ethernetif.c的移植 12.3.2 操作系统模拟层文件sys_arch.c的移植 第13章 工业通信网关解析 13.1 概述 13.2 编码实现 13.3 通信测试 附录A 开发板原理图 附录B 专业术语 参考文献
【RT-Thread作品秀】智能路灯作者:杨满意 概述目前路灯大多采用定时功能,但由于四季交替变化,日出日落时间不一样,如果采用统一时间定时开关灯,将会浪费多余的电力资源,如果通过无线网控制和通过日出日落时间灯多种方式控制,将会灵活控制路灯,从而达到节能功能,是一个相当实用的设计开发环境硬件:art-pi RT-Thread版本:RT-Thread V 4.0.3 开发工具及版本:MDK 5.27 RT-Thread使用情况概述内核部分:调度器,信号量,消息队列。 调度器:创建多个线程来实现不同的工作。 信号量:用来同步线程。 消息队列:用来实现线程之间传递的数据。 组件部分:SPI框架,Sensor框架,SAL套接字抽象层 SPI框架:使用SPI框架来驱动温度传感器,上层代码可以提高代码的可重用性。 Sensor框架:为上层提供统一的操作接口,提高上层代码的可重用性;简化底层驱动开发的难度,只要实现简单的ops(operations:操作命令)就可以将传感器注册到系统上。 SAL套接字抽象层:组件完成对不同网络协议栈或网络实现接口的抽象并对上层提供一组标准的BSD Socket API,这样开发者只需要关心和使用网络应用层提供的网络接口,而无需关心底层具体网络协议栈类型和实现,极大的提高了系统的兼容性,方便开发者完成协议栈的适配和网络相关的开发 软件包部分: Webclient:提供设备与HTTP Server的通讯的基本功能。 pahomqtt,:本软件包是在Eclipse paho-mqtt源码包的基础上设计的一套MQTT客户端程序。 Onenet:是RT-Thread针对OneNET平台连接做的的适配,通过这个软件包,可以让设备在RT-Thread上非常方便的连接OneNet平台,完成数据的发送、接收、设备的注册和控制等功能。 cJSON:C语言实现的极简的解析JSON格式的软件包。 at_device:是由RT-Thread AT组件针对不同AT设备的移植文件和示例代码组成,目前支持的AT设备有:ESP8266、M26、MC20、RW007、MW31、SIM800C以及SIM76XX系列设备等。 wifi:是一个RT-Thread的软件包,该软件包提供了wifi模块的驱动 硬件框架软件框架说明软件框架说明,首先初始化硬件,通过wifi连接路由器,连接onenet服务器,然后判断工作模式是否为手动模式,如果是手动模式,通过onenet服务器下发开关灯指令,如果是自动模式,则通过事先保存的经纬度数据,通过算法,计算当地如初日落时间,然后转化为时间戳,,然后根据实时时钟的时间转化的时间戳进行对比,判断是否开关灯软件模块说明onenet_mqtt_init_entry:初始化onenet_mqtt, mqtt初始化成功之后,释放信号量告知onenet_upload_data_thread线程可以上传数据了,这里会做循环处理,周期性的检查onenet_mqtt的状态,如果已经断线,就进行再次连接。 onenet_upload_data_entry:take信号量的方式,获取到信号量之后,通过邮箱获取到的内存池首地址去拿到数据,然后就可以发数据到onenet云平台和PC端上位机。 led _entry:读取实时时钟,转化为时间戳,判断工作模式,判断是否开关灯 演示效果代码地址在附件。比赛感悟 纸上得来终觉浅,绝知此事要躬行。陆游的这首诗,完美的诠释了我在这次比赛中的收获。 首次接触RT-Thread,之前接触的是freertos,多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等大致相同,值得赞赏的是rtthread官方生态真的好,各种插件,都已经集成好,自己写的代码越来越少,更容易上手。 感谢主办方给我这次机会接触这么优秀的平台,为以后的开发者提供更大的便利,提供更多的选择,

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值