获取FreeRTOS源码
在stm32裸机工程中移植FreeRTOS
Source文件

该文件夹下包含RTOS的源码,针对不用芯片架构的底层代码存放在portable目录下,其他部分的代码是通用的; 我们需要关注的是RVDS/ARM_CM3目录,在该目录下有两个文件,分别是port.c和portmacro.h,这两个文件是针对CortexM3架构的MCU设计的。接下来看一下port.c文件中的内容。
c
/* 初始化任务栈 */
StackType_t *pxPortInitialiseStack(
StackType_t *pxTopOfStack,
TaskFunction_t pxCode,
void *pvParameters );
/* 调度器函数 */
BaseType_t xPortStartScheduler( void );
/* SVC 系统调用中断处理函数 */
__asm void vPortSVCHandler( void ) ;
/* 临界区管理 */
void vPortEnterCritical( void );
void vPortExitCritical( void );
/* pendSV中断函数处理函数实现上下文切换 */
__asm void xPortPendSVHandler( void )
/* 嘀嗒定时器中断处理函数,实现基础时钟 */
void xPortSysTickHandler( void );
还有一个需要关注的内容是内存管理相关的文件,在MemMang下有五种内存管理方式的源码,经常用的是heap_4.c;
移植过程
源码拷贝
-
在stm32的工程中新建freeRTOS目录,在该目录下新建下面的三个文件夹,将freeRTOS源码中Source目录下的.c文件拷贝到src目录,include文件拷贝include目录,port下存放port.c heap.c等文件;

-
将FreeRTOS\Demo\CORTEX_STM32F103_Keil文件下的FreeRTOSConfig.h拷贝至工程的user目录下;
Keil软件设置
-
在工程中创建目录,并添加文件,如下图所示;

-
头文件路径设置

到此结束,我们就将freeRTOS的源码在我们的stm32工程中完成了移植,下面是RTOS的剪切。
RTOS剪切
配置文件分析
c
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* 针对不用的编译器调用不用的stdint.h文件 */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
/* 断言 */
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)
/* 调度器方式配置 0:协作式 1:抢占式 */
#define configUSE_PREEMPTION 1
/* 使能时间片调度 1:使能 0:关闭 */
#define configUSE_TIME_SLICING 1
/* 下一个任务的选择方法 0:通用方法 1:特殊方法(计算前导0) */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 1:使能低功耗模式 0:系统节拍中断一直运行 */
#define configUSE_TICKLESS_IDLE 0
/* CPU时钟配置 */
#define configCPU_CLOCK_HZ (SystemCoreClock)
/* RTOS系统节拍的频率配置 */
#define configTICK_RATE_HZ (( TickType_t )1000)
/* 最大优先级配置 */
#define configMAX_PRIORITIES (32)
/* 最小栈空间配置 */
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
/* 任务名字长度配置 */
#define configMAX_TASK_NAME_LE (16)
/* 系统节拍计数变量的数据类型 1:16位 0:32位 */
#define configUSE_16_BIT_TICKS 0
/* 空闲任务配置 1:放弃CPU使用权给其他同级别 0:不放弃 */
#define configIDLE_SHOULD_YIELD 1
/* 队列设置 1:启用队列 0:关闭队列 */
#define configUSE_QUEUE_SETS 0
/* 任务通知配置 1:开启 0:关闭 */
#define configUSE_TASK_NOTIFICATIONS 1
/* 互斥信号量配置 1:开启 0:关闭 */
#define configUSE_MUTEXES 0
/* 递归互斥信号量配置 1:开启 0:关闭 */
#define configUSE_RECURSIVE_MUTEXES 0
/* 计数信号量配置 1:开启 0:关闭 */
#define configUSE_COUNTING_SEMAPHORES 0
/* 信号量和消息队列个数配置 */
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_APPLICATION_TASK_TAG 0
/* 是否支持动态内存申请 1:支持 0:不支持*/
#define configSUPPORT_DYNAMIC_ALLOCATION 1
/* 是否支持静态内存申请 1:支持 0:不支持*/
#define configSUPPORT_STATIC_ALLOCATION 0
/* 堆大小配置 */
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024))
/* 空闲钩子函数配置 1:启动 0:关闭 */
#define configUSE_IDLE_HOOK 0
/* 时间片钩子函数配置 1:启动 0:关闭 */
#define configUSE_TICK_HOOK 0
/* 内存申请失败钩子函数配置 1:启动 0:关闭 */
#define configUSE_MALLOC_FAILED_HOOK 0
/* 堆栈溢出检测配置 0:关闭 1:检测方法1 2:检测方法2 */
#define configCHECK_FOR_STACK_OVERFLOW 0
/* 运行时间统计功能配置 1:启动 0:关闭 */
#define configGENERATE_RUN_TIME_STATS 0
/* 可视化跟踪调试配置 1:启用 0:关闭 */
#define configUSE_TRACE_FACILITY 0
#define configUSE_STATS_FORMATTING_FUNCTIONS 1
/* 启动协程配置 1:启动 0:关闭 */
#define configUSE_CO_ROUTINES 0
/* 协程有效优先级数目 */
#define configMAX_CO_ROUTINE_PRIORITIES (2)
/* 软件定时器配置 1:开启 0:关闭 */
#define configUSE_TIMERS 1
/* 软件定时器优先级 */
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
/* 软件定期器队列长度 */
#define configTIMER_QUEUE_LENGTH 10
/* 软件定时器任务堆大小 **/
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2)
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/* 中断最低优先级 */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* 系统可以管理的最高优先级 */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* 中断服务函数配置 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#define INCLUDE_xTaskGetCurrentTaskHandle 0
#endif
#endif
工程修改
中断处理函数
- 修改PendSV_Handler中断处理函数,屏蔽默认的函数,在FreeRTOS的port.c文件中重新定义了该中断服务函数以次来实现上下文的切换;
- 修改PendSV_Handler中断处理函数,屏蔽此函数,在FreeRTOS的port.c文件中重新定义了该中断服务函数来实现任务的启动;
- 修改SysTick_Handler中断处理函数,在函数中调用xPortSysTickHandler接口,该接口是留给各个芯片时钟节拍的函数;
添加头文件
添加必要的头文件FreeRTOS.h;
在其他处理器下移植FreeRTOS
在一个新的处理器中如果要移植freeRTOS,主要操作的文件则是porttable下面的port.c文件。主要涉及三大块,分别是栈,中断和定时器。除此之外,还有临界区的管理,比如开关中断,系统调用方法等。
栈
栈是用来保存任务中的数据变量的,包括CPU相关寄存器的,每一种CPU中要保存的寄存器数量或者种类可能不同,所以在移植的过程中这一块是需要关注的,要想清楚栈中存储什么内容。
中断
每一种CPU的中断管理可能是不同的,中断在这里的作用有两个,一个是进行任务的切换 ,通常由系统调用实现,另外一个是基础时钟中断,也就是定时器中断,来保障RTOS的运行,在移植的过程中,中断管理是重要的部分。有的处理器中断和例外的入口是同一个,在进入中断后,还需要对中断进行分发。
定时器
定时器是很重要的一部分,要在CPU的资源中找到一个定时器,配置定时器的中断和中断时间。一般来讲,定时器中断时间为1ms左右。
定时器中断中断中一般的处理过程如下:
-
清除定时器中断的标志位;
-
调用xTaskIncrementTick函数;
-
xTickCount数值加1,在这个函数中还有一个重要的任务,就是判断当前状态是否需要调度,通过xSwitchRequired变量返回。
判断的逻辑为:
-
下一个任务的阻塞时间已到,判断延时链表是否为空,如果不为空,则获取延时链表的头结点,根据头结点获取延时的时间,如果延时的时间大于xConstTickCount时间,则代表延时还没有到,更新xNextTaskUnblockTime时间。如果时间到了,则从延时链表移除,也从事件链表移除,再将其加入到就绪链表中。如果这个任务的优先级大于当前任务的优先级,则直接xSwitchRequired等于true,
-
时间片轮询:当前优先级任务就绪链表中如果长度大于1,则xSwitchRequired=true,进行时间片轮询调度。
-
总结:该函数的功能就是查找延时链表中的时间是否到达,如果到达,则从延时链表中删除,加入到就绪链表中,如果这个任务的优先级大于当前任务的优先级,则直接返回需要调度的标志。如果当前就绪链表中任务是多个,也是需要返回调度的标志,进行时间片调度。
-
抢占式时间片调度算法:就绪态的高优先级先执行,同优先级的就绪任务轮流执行;
-
-
判断xTaskIncrementTick的返回数值决定是否需要上下文切换;
- 上下文切换函数为vTaskSwitchContext,上下文切换实质上是查找优先级最高的就绪任务,将该任务TCB写入到pxCurrentTCB;
-
上下文切换后,将pxCurrentTCB栈内容恢复到CPU寄存器中;
总结
本文主要概述了FreeRTOS的移植和剪切过程,其实在FreeRTOS中已经帮助我们适配了大部分处理器,我们要做的就是拿来会用就行,但是针对新的CPU和架构,就需要自己移植了,理论上讲无论CPU是大的SOC还是小的MCU,只要处理器中有一个定时器,有系统调用中断,基本上都可以运行freeRTOS。