FreeRTOS的移植

前言我为什么会写移植呢,因为是公众号有一个兄弟想要我写一份移植的教程,所以应它要求我就写了一篇。
准备在移植之前,我们首先要获取到freertos的官方的源码包。这里我们提供两个下载链接:
一个是官网:http://www.freertos.org/
另外一个是代码托管网站:https://sourceforge.net/projects/freertos/files/freertos/
打开网站链接之后,我们选择freertos的最新版本v9.0.0(2016年),尽管现在freertos的版本已经更新到v10.0.1了,但是我们还是选择v9.0.0,因为内核很稳定,并且网上资料很多,因为v10.0.0版本之后是亚马逊收购了freertos之后才出来的版本,主要添加了一些云端组件,我们本书所讲的freertos是实时内核,采用v9.0.0版本足以。
简单介绍freertosfreertos包含demo例程和内核源码(比较重要,我们就需要提取该目录下的大部分文件)。
source文件夹里面包含的是freertos内核的源代码,我们移植freertos的时候就需要这部分源代码;
demo 文件夹里面包含了freertos官方为各个单片机移植好的工程代码,freertos为了推广自己,会给各种半导体厂商的评估板写好完整的工程程序,这些程序就放在demo这个目录下,这部分demo非常有参考价值。
在这里插入图片描述
source文件夹这里我们再重点分析下freertos/ source文件夹下的文件,①和③包含的是freertos的通用的头文件和c文件,这两部分的文件试用于各种编译器和处理器,是通用的。需要移植的头文件和c文件放在②portblle这个文件夹。
在这里插入图片描述
portblle文件夹,是与编译器相关的文件夹,在不同的编译器中使用不同的支持文件。①中的keil就是我们就是我们使用的编译器,其实keil里面的内容跟rvds里面的内容一样,所以我们只需要③rvds文件夹里面的内容即可,里面包含了各种处理器相关的文件夹,从文件夹的名字我们就非常熟悉了,我们学习的stm32有m0、m3、m4等各种系列,freertos是一个软件,单片机是一个硬件,freertos要想运行在一个单片机上面,它们就必须关联在一起。memmang文件夹下存放的是跟内存管理相关的源文件。
在这里插入图片描述
移植过程提取源码首先在我们的stm32裸机工程模板根目录下新建一个文件夹,命名为“freertos”,并且在freertos文件夹下新建两个空文件夹,分别命名为“src”与“port”,src文件夹用于保存freertos中的核心源文件,也就是我们常说的‘.c文件’,port文件夹用于保存内存管理以及处理器架构相关代码,这些代码freertos官方已经提供给我们的,直接使用即可,在前面已经说了,freertos是软件,我们的开发版是硬件,软硬件必须有桥梁来连接,这些与处理器架构相关的代码,可以称之为rtos硬件接口层,它们位于freertos/source/portable文件夹下。
打开freertos v9.0.0源码,在“freertosv9.0.0\\freertos\\source”目录下找到所有的‘.c文件’,将它们拷贝到我们新建的src文件夹中,
在这里插入图片描述
打开freertos v9.0.0源码,在“freertosv9.0.0\\freertos\\source\\portable”目录下找到“memmang”文件夹与“rvds”文件夹,将它们拷贝到我们新建的port文件夹中
在这里插入图片描述
打开freertos v9.0.0源码,在“freertosv9.0.0\\ freertos\\source”目录下找到“include”文件夹,它是我们需要用到freertos的一些头文件,将它直接拷贝到我们新建的freertos文件夹中,完成这一步之后就可以看到我们新建的freertos文件夹已经有3个文件夹,这3个文件夹就包含freertos的核心文件,至此,freertos的源码就提取完成。
在这里插入图片描述
添加到工程添加freertosconfig.h文件
freertosconfig.h文件是freertos的工程配置文件,因为freertos是可以裁剪的实时操作内核,应用于不同的处理器平台,用户可以通过修改这个freertos内核的配置头文件来裁剪freertos的功能,所以我们把它拷贝一份放在user这个文件夹下面。
打开freertosv9.0.0源码,在“freertosv9.0.0\\freertos\\demo”文件夹下面找到“cortex_stm32f103_keil”这个文件夹,双击打开,在其根目录下找到这个“freertosconfig.h”文件,然后拷贝到我们工程的user文件夹下即可,等下我们需要对这个文件进行修改。
创建工程分组
接下来我们在mdk里面新建freertos/src和freertos/port两个组文件夹,其中freertos/src用于存放src文件夹的内容,freertos/port用于存放port\\memmang文件夹 与port\\rvds\\arm_cm3文件夹的内容。
然后我们将工程文件中freertos的内容添加到工程中去,按照已经新建的分组添加我们的freertos工程源码。
在freertos/port分组中添加memmang文件夹中的文件只需选择其中一个即可,我们选择“heap_4.c”,这是freertos的一个内存管理源码文件。
添加完成后:
在这里插入图片描述
** 添加头文件路径**
freertos的源码已经添加到开发环境的组文件夹下面,编译的时候需要为这些源文件指定头文件的路径,不然编译会报错。freertos的源码里面只有freertos\\include和freertos\\port\\rvds\\arm_cm3这两个文件夹下面有头文件,只需要将这两个头文件的路径在开发环境里面指定即可。同时我们还将freertosconfig.h这个头文件拷贝到了工程根目录下的user文件夹下,所以user的路径也要加到开发环境里面。
在这里插入图片描述
修改freertosconfig.hfreertosconfig.h是直接从demo文件夹下面拷贝过来的,该头文件对裁剪整个freertos所需的功能的宏均做了定义,有些宏定义被使能,有些宏定义被失能,一开始我们只需要配置最简单的功能即可。要想随心所欲的配置freertos的功能,我们必须对这些宏定义的功能有所掌握,下面我们先简单的介绍下这些宏定义的含义,然后再对这些宏定义进行修改。
1#ifndef freertos_config_h 2#define freertos_config_h 3 4#include stm32f10x.h 5#include bsp_usart.h 6 7 8//针对不同的编译器调用不同的stdint.h文件 9#if defined(__iccarm__) || defined(__cc_arm) || defined(__gnuc__) 10 #include 11 extern uint32_t systemcoreclock; 12#endif 13 14//断言 15#define vassertcalled(char,int) printf(error:%s,%d\\r\\n,char,int) 16#define configassert(x) if((x)==0) vassertcalled(__file__,__line__) 17 18/************************************************************************ 19 * freertos基础配置配置选项 20 *********************************************************************/ 21/* 置1:rtos使用抢占式调度器;置0:rtos使用协作式调度器(时间片) 22 * 23 * 注:在多任务管理机制上,操作系统可以分为抢占式和协作式两种。 24 * 协作式操作系统是任务主动释放cpu后,切换到下一个任务。 25 * 任务切换的时机完全取决于正在运行的任务。 26 */ 27#define configuse_preemption 1 28 29//1使能时间片调度(默认式使能的) 30#define configuse_time_slicing 1 31 32/* 某些运行freertos的硬件有两种方法选择下一个要执行的任务: 33 * 通用方法和特定于硬件的方法(以下简称“特殊方法”)。 34 * 35 * 通用方法: 36 * 1.configuse_port_optimised_task_selection 为 0 或者硬件不支持这种特殊方法。 37 * 2.可以用于所有freertos支持的硬件 38 * 3.完全用c实现,效率略低于特殊方法。 39 * 4.不强制要求限制最大可用优先级数目 40 * 特殊方法: 41 * 1.必须将configuse_port_optimised_task_selection设置为1。 42 * 2.依赖一个或多个特定架构的汇编指令(一般是类似计算前导零[clz]指令)。 43 * 3.比通用方法更高效 44 * 4.一般强制限定最大可用优先级数目为32 45 * 一般是硬件计算前导零指令,如果所使用的,mcu没有这些硬件指令的话此宏应该设置为0! 46 */ 47#define configuse_port_optimised_task_selection 1 48 49/* 置1:使能低功耗tickless模式;置0:保持系统节拍(tick)中断一直运行 50 * 假设开启低功耗的话可能会导致下载出现问题,因为程序在睡眠中,可用以下办法解决 51 * 52 * 下载方法: 53 * 1.将开发版正常连接好 54 * 2.按住复位按键,点击下载瞬间松开复位按键 55 * 56 * 1.通过跳线帽将 boot 0 接高电平(3.3v) 57 * 2.重新上电,下载 58 * 59 * 1.使用flymcu擦除一下芯片,然后进行下载 60 * stmisp -> 清除芯片(z) 61 */ 62#define configuse_tickless_idle 0 63 64/* 65 * 写入实际的cpu内核时钟频率,也就是cpu指令执行频率,通常称为fclk 66 * fclk为供给cpu内核的时钟信号,我们所说的cpu主频为 xx mhz, 67 * 就是指的这个时钟信号,相应的,1/fclk即为cpu时钟周期; 68 */ 69#define configcpu_clock_hz (systemcoreclock) 70 71//rtos系统节拍中断的频率。即一秒中断的次数,每次中断rtos都会进行任务调度 72#define configtick_rate_hz (( ticktype_t )1000) 73 74//可使用的最大优先级 75#define configmax_priorities (32) 76 77//空闲任务使用的堆栈大小 78#define configminimal_stack_size ((unsigned short)128) 79 80//任务名字字符串长度 81#define configmax_task_name_len (16) 82 83 //系统节拍计数器变量数据类型,1表示为16位无符号整形,0表示为32位无符号整形 84#define configuse_16_bit_ticks 0 85 86//空闲任务放弃cpu使用权给其他同优先级的用户任务 87#define configidle_should_yield 1 88 89//启用队列 90#define configuse_queue_sets 1 91 92//开启任务通知功能,默认开启 93#define configuse_task_notifications 1 94 95//使用互斥信号量 96#define configuse_mutexes 1 97 98//使用递归互斥信号量 99#define configuse_recursive_mutexes 1 100101//为1时使用计数信号量102#define configuse_counting_semaphores 1103104/* 设置可以注册的信号量和消息队列个数 */105#define configqueue_registry_size 10 106107#define configuse_application_task_tag 0 108109110/*****************************************************************111 freertos与内存申请有关配置选项 112*****************************************************************/113//支持动态内存申请114#define configsupport_dynamic_allocation 1 115//支持静态内存116#define configsupport_static_allocation 0 117//系统所有总的堆大小118#define configtotal_heap_size ((size_t)(36*1024)) 119120121/***************************************************************122 freertos与钩子函数有关的配置选项 123**************************************************************/124/* 置1:使用空闲钩子(idle hook类似于回调函数);置0:忽略空闲钩子125 * 126 * 空闲任务钩子是一个函数,这个函数由用户来实现,127 * freertos规定了函数的名字和参数:void vapplicationidlehook(void ),128 * 这个函数在每个空闲任务周期都会被调用129 * 对于已经删除的rtos任务,空闲任务可以释放分配给它们的堆栈内存。130 * 因此必须保证空闲任务可以被cpu执行131 * 使用空闲钩子函数设置cpu进入省电模式是很常见的132 * 不可以调用会引起空闲任务阻塞的api函数133 */134#define configuse_idle_hook 0 135136/* 置1:使用时间片钩子(tick hook);置0:忽略时间片钩子137 * 138 * 139 * 时间片钩子是一个函数,这个函数由用户来实现,140 * freertos规定了函数的名字和参数:void vapplicationtickhook(void )141 * 时间片中断可以周期性的调用142 * 函数必须非常短小,不能大量使用堆栈,143 * 不能调用以”fromisr 或 from_isr”结尾的api函数144 */145 /*xtaskincrementtick函数是在xportsystickhandler中断函数中被调用的。因此,vapplicationtickhook()函数执行的时间必须很短才行*/146#define configuse_tick_hook 0 147148//使用内存申请失败钩子函数149#define configuse_malloc_failed_hook 0 150151/*152 * 大于0时启用堆栈溢出检测功能,如果使用此功能 153 * 用户必须提供一个栈溢出钩子函数,如果使用的话154 * 此值可以为1或者2,因为有两种栈溢出检测方法 */155#define configcheck_for_stack_overflow 0 156157158/********************************************************************159 freertos与运行时间和任务状态收集有关的配置选项 160**********************************************************************/161//启用运行时间统计功能162#define configgenerate_run_time_stats 0 163 //启用可视化跟踪调试164#define configuse_trace_facility 0 165/* 与宏configuse_trace_facility同时为1时会编译下面3个函数166 * prvwritenametobuffer()167 * vtasklist(),168 * vtaskgetruntimestats()169*/170#define configuse_stats_formatting_functions 1 171172173/********************************************************************174 freertos与协程有关的配置选项 175*********************************************************************/176//启用协程,启用协程以后必须添加文件croutine.c177#define configuse_co_routines 0 178//协程的有效优先级数目179#define configmax_co_routine_priorities ( 2 ) 180181182/***********************************************************************183 freertos与软件定时器有关的配置选项 184**********************************************************************/185 //启用软件定时器186#define configuse_timers 1 187//软件定时器优先级188#define configtimer_task_priority (configmax_priorities-1) 189//软件定时器队列长度190#define configtimer_queue_length 10 191//软件定时器任务堆栈大小192#define configtimer_task_stack_depth (configminimal_stack_size*2) 193194/************************************************************195 freertos可选函数配置选项 196************************************************************/197#define include_xtaskgetschedulerstate 1 198#define include_vtaskpriorityset 1199#define include_uxtaskpriorityget 1200#define include_vtaskdelete 1201#define include_vtaskcleanupresources 1202#define include_vtasksuspend 1203#define include_vtaskdelayuntil 1204#define include_vtaskdelay 1205#define include_etaskgetstate 1206#define include_xtimerpendfunctioncall 1207//#define include_xtaskgetcurrenttaskhandle 1208//#define include_uxtaskgetstackhighwatermark 0209//#define include_xtaskgetidletaskhandle 0210211212/******************************************************************213 freertos与中断有关的配置选项 214******************************************************************/215#ifdef __nvic_prio_bits216 #define configprio_bits __nvic_prio_bits217#else218 #define configprio_bits 4 219#endif220//中断最低优先级221#define configlibrary_lowest_interrupt_priority 15 222223//系统可管理的最高中断优先级224#define configlibrary_max_syscall_interrupt_priority 5 225226#define configkernel_interrupt_priority ( configlibrary_lowest_interrupt_priority << (8 - configprio_bits) ) /* 240 */227228#define configmax_syscall_interrupt_priority ( configlibrary_max_syscall_interrupt_priority << (8 - configprio_bits) )229230231/****************************************************************232 freertos与中断服务函数有关的配置选项 233****************************************************************/234#define xportpendsvhandler pendsv_handler235#define vportsvchandler svc_handler236237238/* 以下为使用percepio tracealyzer需要的东西,不需要时将 configuse_trace_facility 定义为 0 */239#if ( configuse_trace_facility == 1 )240#include trcrecorder.h241#define include_xtaskgetcurrenttaskhandle 1 // 启用一个可选函数(该函数被 trace源码使用,默认该值为0 表示不用)242#endif243244245#endif /* freertos_config_h */修改stm32f10x_it.csystick中断服务函数是一个非常重要的函数,freertos所有跟时间相关的事情都在里面处理,systick就是freertos的一个心跳时钟,驱动着freertos的运行,就像人的心跳一样,假如没有心跳,我们就相当于“死了”,同样的,freertos没有了心跳,那么它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现一个freertos的心跳时钟,freertos帮我们实现了systick的启动的配置:在port.c文件中已经实现vportsetuptimerinterrupt()函数,并且freertos通用的systick中断服务函数也实现了:在port.c文件中已经实现xportsystickhandler()函数,所以移植的时候只需要我们在stm32f10x_it.c文件中实现我们对应(stm32)平台上的systick_handler()函数即可。freertos为开发者考虑得特别多,pendsv_handler()与svc_handler()这两个很重要的函数都帮我们实现了,在在port.c文件中已经实现xportpendsvhandler()与vportsvchandler()函数,防止我们自己实现不了,那么在stm32f10x_it.c中就需要我们注释掉pendsv_handler()与svc_handler()这两个函数了。
1//void svc_handler(void) 2//{ 3//} 4 5//void pendsv_handler(void) 6//{ 7//} 8 9extern void xportsystickhandler(void);1011//systick中断服务函数12void systick_handler(void)13{ 14 #if (include_xtaskgetschedulerstate == 1 )15 if (xtaskgetschedulerstate() != taskscheduler_not_started)16 {17 #endif /* include_xtaskgetschedulerstate */ 18 xportsystickhandler();19 #if (include_xtaskgetschedulerstate == 1 )20 }21 #endif /* include_xtaskgetschedulerstate */22}创建任务这里,我们创建一个单任务,任务使用的栈和任务控制块是在创建任务的时候freertos动态分配的。
任务必须是一个死循环,否则任务将通过lr返回,如果lr指向了非法的内存就会产生hardfault_handler,而freertos指向一个死循环,那么任务返回之后就在死循环中执行,这样子的任务是不安全的,所以避免这种情况,任务一般都是死循环并且无返回值的。
并且每个任务循环主体中应该有阻塞任务的函数,否则就会饿死比它优先级更低的任务!!!
1/* freertos头文件 */ 2#include freertos.h 3#include task.h 4/* 开发板硬件bsp头文件 */ 5#include bsp_led.h 6 7static void apptaskcreate(void);/* apptask任务 */ 8 9 /* 创建任务句柄 */10static taskhandle_t apptask_handle = null;1112int main(void)13{ 14 basetype_t xreturn = pdpass;/* 定义一个创建信息返回值,默认为pdpass */1516 /* 开发板硬件初始化 */17 bsp_init();1819 /* 创建apptaskcreate任务 */20 xreturn = xtaskcreate((taskfunction_t )apptask, /* 任务入口函数 */21 (const char* )apptask,/* 任务名字 */22 (uint16_t )512, /* 任务栈大小 */23 (void* )null,/* 任务入口函数参数 */24 (ubasetype_t )1, /* 任务的优先级 */25 (taskhandle_t* )&apptask_handle);/* 任务控制块指针 */ 26 /* 启动任务调度 */ 27 if(pdpass == xreturn)28 vtaskstartscheduler(); /* 启动任务,开启调度 */29 else30 return -1; 3132 while(1); /* 正常不会执行到这里 */ 33}3435static void apptask(void* parameter)36{ 37 while (1)38 {39 led1_on;40 vtaskdelay(500); /* 延时500个tick */41 led1_off; 42 vtaskdelay(500); /* 延时500个tick */ 43 }44}

前端开发之函数式编程实践
半导体“黑科技”:氮化镓
英伟达宣布与腾讯合作,助力START云游戏平台
华庄科技创业板IPO迎新进展!第一大客户比亚迪,募资4.5亿建设生产基地等
Ozone的3.30版本——J-Link调试器发布
FreeRTOS的移植
新西兰称从未将华为排除在新西兰5G网络建设之外
什么是射频?为什么离不开射频?
亚运在深圳系列之八:一穷二白做销售
NVIDIA 校招 | 芯片架构团队正在热招!
雷曼荣获“中国LED首创奖”重磅奖项
荣耀MagicWatch 2手表首度曝光,将与荣耀V30共同发布
无刷电机空载起步震动过大的原因 浅谈电机故障处理方法
运算放大器介绍
iQOO Pro在5G和快充在同类中无人匹敌?
澳科学家开发全新可穿戴技术,可连续测量患者血压
俄罗斯第五代战斗机发动机产品30正在处于第二阶段试验
关于工业机器人的真正应用普及的分析和介绍
Arduino湿度传感器的制作
大疆开始探索无人驾驶技术,无人车市场又迎新对手