05-FreeRTOS的移植与适配

05-FreeRTOS的移植与适配 获取FreeRTOS源码网站https://sourceforge.net/projects/freertos/files/FreeRTOS/在stm32裸机工程中移植FreeRTOSSource文件该文件夹下包含RTOS的源码针对不用芯片架构的底层代码存放在portable目录下其他部分的代码是通用的 我们需要关注的是RVDS/ARM_CM3目录在该目录下有两个文件分别是port.c和portmacro.h这两个文件是针对CortexM3架构的MCU设计的。接下来看一下port.c文件中的内容。/* 初始化任务栈 */StackType_t*pxPortInitialiseStack(StackType_t*pxTopOfStack,TaskFunction_t pxCode,void*pvParameters);/* 调度器函数 */BaseType_txPortStartScheduler(void);/* SVC 系统调用中断处理函数 */__asmvoidvPortSVCHandler(void);/* 临界区管理 */voidvPortEnterCritical(void);voidvPortExitCritical(void);/* pendSV中断函数处理函数实现上下文切换 */__asmvoidxPortPendSVHandler(void)/* 嘀嗒定时器中断处理函数实现基础时钟 */voidxPortSysTickHandler(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剪切配置文件分析#ifndefFREERTOS_CONFIG_H#defineFREERTOS_CONFIG_H/* 针对不用的编译器调用不用的stdint.h文件 */#ifdefined(__ICCARM__)||defined(__CC_ARM)||defined(__GNUC__)#includestdint.hexternuint32_tSystemCoreClock;#endif/* 断言 */#definevAssertCalled(char,int)printf(Error:%s,%d\r\n,char,int)#defineconfigASSERT(x)if((x)0)vAssertCalled(__FILE__,__LINE__)/* 调度器方式配置 0协作式 1抢占式 */#defineconfigUSE_PREEMPTION1/* 使能时间片调度 1使能 0关闭 */#defineconfigUSE_TIME_SLICING1/* 下一个任务的选择方法 0通用方法 1特殊方法计算前导0 */#defineconfigUSE_PORT_OPTIMISED_TASK_SELECTION1/* 1使能低功耗模式 0系统节拍中断一直运行 */#defineconfigUSE_TICKLESS_IDLE0/* CPU时钟配置 */#defineconfigCPU_CLOCK_HZ(SystemCoreClock)/* RTOS系统节拍的频率配置 */#defineconfigTICK_RATE_HZ((TickType_t)1000)/* 最大优先级配置 */#defineconfigMAX_PRIORITIES(32)/* 最小栈空间配置 */#defineconfigMINIMAL_STACK_SIZE((unsignedshort)128)/* 任务名字长度配置 */#defineconfigMAX_TASK_NAME_LE(16)/* 系统节拍计数变量的数据类型 116位 032位 */#defineconfigUSE_16_BIT_TICKS0/* 空闲任务配置 1放弃CPU使用权给其他同级别 0不放弃 */#defineconfigIDLE_SHOULD_YIELD1/* 队列设置 1启用队列 0关闭队列 */#defineconfigUSE_QUEUE_SETS0/* 任务通知配置 1开启 0关闭 */#defineconfigUSE_TASK_NOTIFICATIONS1/* 互斥信号量配置 1开启 0关闭 */#defineconfigUSE_MUTEXES0/* 递归互斥信号量配置 1开启 0关闭 */#defineconfigUSE_RECURSIVE_MUTEXES0/* 计数信号量配置 1开启 0关闭 */#defineconfigUSE_COUNTING_SEMAPHORES0/* 信号量和消息队列个数配置 */#defineconfigQUEUE_REGISTRY_SIZE10#defineconfigUSE_APPLICATION_TASK_TAG0/* 是否支持动态内存申请 1支持 0不支持*/#defineconfigSUPPORT_DYNAMIC_ALLOCATION1/* 是否支持静态内存申请 1支持 0不支持*/#defineconfigSUPPORT_STATIC_ALLOCATION0/* 堆大小配置 */#defineconfigTOTAL_HEAP_SIZE((size_t)(36*1024))/* 空闲钩子函数配置 1启动 0关闭 */#defineconfigUSE_IDLE_HOOK0/* 时间片钩子函数配置 1启动 0关闭 */#defineconfigUSE_TICK_HOOK0/* 内存申请失败钩子函数配置 1启动 0关闭 */#defineconfigUSE_MALLOC_FAILED_HOOK0/* 堆栈溢出检测配置 0关闭 1检测方法1 2检测方法2 */#defineconfigCHECK_FOR_STACK_OVERFLOW0/* 运行时间统计功能配置 1启动 0关闭 */#defineconfigGENERATE_RUN_TIME_STATS0/* 可视化跟踪调试配置 1启用 0关闭 */#defineconfigUSE_TRACE_FACILITY0#defineconfigUSE_STATS_FORMATTING_FUNCTIONS1/* 启动协程配置 1启动 0关闭 */#defineconfigUSE_CO_ROUTINES0/* 协程有效优先级数目 */#defineconfigMAX_CO_ROUTINE_PRIORITIES(2)/* 软件定时器配置 1开启 0关闭 */#defineconfigUSE_TIMERS1/* 软件定时器优先级 */#defineconfigTIMER_TASK_PRIORITY(configMAX_PRIORITIES-1)/* 软件定期器队列长度 */#defineconfigTIMER_QUEUE_LENGTH10/* 软件定时器任务堆大小 **/#defineconfigTIMER_TASK_STACK_DEPTH(configMINIMAL_STACK_SIZE*2)#defineINCLUDE_xTaskGetSchedulerState1#defineINCLUDE_vTaskPrioritySet1#defineINCLUDE_uxTaskPriorityGet1#defineINCLUDE_vTaskDelete1#defineINCLUDE_vTaskCleanUpResources1#defineINCLUDE_vTaskSuspend1#defineINCLUDE_vTaskDelayUntil1#defineINCLUDE_vTaskDelay1#defineINCLUDE_eTaskGetState1#defineINCLUDE_xTimerPendFunctionCall1#ifdef__NVIC_PRIO_BITS#defineconfigPRIO_BITS__NVIC_PRIO_BITS#else#defineconfigPRIO_BITS4#endif/* 中断最低优先级 */#defineconfigLIBRARY_LOWEST_INTERRUPT_PRIORITY15/* 系统可以管理的最高优先级 */#defineconfigLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY5#defineconfigKERNEL_INTERRUPT_PRIORITY(configLIBRARY_LOWEST_INTERRUPT_PRIORITY(8-configPRIO_BITS))#defineconfigMAX_SYSCALL_INTERRUPT_PRIORITY(configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(8-configPRIO_BITS))/* 中断服务函数配置 */#definexPortPendSVHandlerPendSV_Handler#definevPortSVCHandlerSVC_Handler#if(configUSE_TRACE_FACILITY1)#includetrcRecorder.h#defineINCLUDE_xTaskGetCurrentTaskHandle0#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,则xSwitchRequiredtrue进行时间片轮询调度。总结该函数的功能就是查找延时链表中的时间是否到达如果到达则从延时链表中删除加入到就绪链表中如果这个任务的优先级大于当前任务的优先级则直接返回需要调度的标志。如果当前就绪链表中任务是多个也是需要返回调度的标志进行时间片调度。抢占式时间片调度算法就绪态的高优先级先执行同优先级的就绪任务轮流执行判断xTaskIncrementTick的返回数值决定是否需要上下文切换上下文切换函数为vTaskSwitchContext上下文切换实质上是查找优先级最高的就绪任务将该任务TCB写入到pxCurrentTCB上下文切换后将pxCurrentTCB栈内容恢复到CPU寄存器中总结本文主要概述了FreeRTOS的移植和剪切过程其实在FreeRTOS中已经帮助我们适配了大部分处理器我们要做的就是拿来会用就行但是针对新的CPU和架构就需要自己移植了理论上讲无论CPU是大的SOC还是小的MCU只要处理器中有一个定时器有系统调用中断基本上都可以运行freeRTOS。