准备工作 下载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版本足以。
注:本次自己测试使用的是官网中下载最新版本 FreeRTOSv202112.00
简单介绍FreeRTOS FreeRTOS包含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,肯定需要一个基础工程,基础工程越简单越好,这里我使用的是STM32F103RET6的板子,例程则选择最基础的OLED实验来作为基础工程 复制 stm32f103vet6_OLED_I2C 到工程位置即可
移植过程参考一(这个测试) 注意:这个方法是跑不起来,进不去任务,是因为配置文件不对,在使用了后边方法修改的配置文件后才实现!
2.1、添加FreeRTOS 源码
在基础工程中新建一个名为 FreeRTOS 的文件夹,如图所示
创建 FreeRTOS 文件夹以后就可以将 FreeRTOS 的源码添加到这个文件夹中,打开刚刚下载的FreeRTOS源码,找到路径为:FreeRTOSv202111.00\FreeRTOS\Source下的文件
将所有.c文件以及include、portable两个文件夹复制到基础工程下刚刚创建的FreeRTOS 文件夹中,如下图所示:
复制完成之后打开portable文件夹,我们只需要留下 keil、MemMang 和 RVDS 这三个文件夹,其他的都可以删除掉,删除完成之后如下图所示
接着打开FreeRTOS源码,找到路径为:FreeRTOSv202111.00\FreeRTOS\Demo\CORTEX_STM32F103_Keil下的 FreeRTOSConfig.h文件,将其复制到基础工程路径为:实验1 LED灯闪烁\FreeRTOS\include下,如下图所示:
2.2、向工程分组中添加文件
打开基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE
接着往这两个分组里添加文件,FreeRTOS_CORE 分组添加路径为:实验1 LED灯闪烁\FreeRTOS下的所有.c文件
FreeRTOS_PORTABLE分组添加路径为:实验1 LED灯闪烁\FreeRTOS\portable\MemMang下的heap_4.c文件以及路径为:实验1 LED灯闪烁\FreeRTOS\portable\RVDS\ARM_CM3下的port.c文件
添加完成如下图所示:
2.3、添加相对应的头文件路径 添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路径如下图所示:
2.4、修改BasicSYSTEM文件 SYSTEM 文件夹里面的文件一开始是针对UCOS 而编写的,所以如果使用 FreeRTOS 的话 就需要做相应的修改。本来打算让 SYSTEM 文件夹也支持 FreeRTOS,但是这样的话会导致 SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对 FreeRTOS 修改了 SYSTEM 里面的文件。 1、修改 sys.h 文件 sys.h 文件修改很简单,在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS, 我们使用了 FreeRTOS,所以应该将宏 SYSTEM_SUPPORT_OS 改为 1。
1 2 3 #define SYSTEM_SUPPORT_OS 1
2、修改 usart.c 文件 usart.c 文件修改也很简单,usart.c 文件有两部分要修改,一个是添加 FreeRTOS.h 头文件, 默认是添加的 UCOS 中的 includes.h 头文件,修改以后如下:
1 2 3 #define SYSTEM_SUPPORT_OS 1
另外一个就是 USART1 的中断服务函数,在使用 UCOS 的时候进出中断的时候需要添加 OSIntEnter()和 OSIntExit(),使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改以后如下:维持了原有,因为用不到
3、修改 delay.c 文件 delay.c 文件修改的就比较大了,因为涉及到 FreeRTOS 的系统时钟,delay.c 文件里面有 4 个函数,先来看一下函数 SysTick_Handler(),此函数是滴答定时器的中断服务函数,代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #if SYSTEM_SUPPORT_OS #include "FreeRTOS.h" #endif #include "task.h" #include "event_groups.h" extern void xPortSysTickHandler (void ) ;void SysTick_Handler (void ) { if (xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED) { xPortSysTickHandler(); } }
FreeRTOS 的心跳就是由滴答定时器产生的,根据 FreeRTOS 的系统时钟节拍设置好滴答定 时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用 FreeRTOS 的API函数 xPortSysTickHandler()。 delay_init()是用来初始化滴答定时器和延时函数,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void delay_init () { u32 reload; SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); fac_us=SystemCoreClock/1000000 ; reload=SystemCoreClock/1000000 ; reload*=1000000 /configTICK_RATE_HZ; fac_ms=1000 /configTICK_RATE_HZ; SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; SysTick->LOAD=reload; SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; }
前面我们说了 FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据 FreeRTOS 的 系统时钟节拍来初始化滴答定时器了,delay_init()就是来完成这个功能的。FreeRTOS 的系统时 钟节拍由宏 configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后我 们就要根据这个值来初始化滴答定时器,其实就是设置滴答定时器的中断周期。在基础例程中 滴答定时器的时钟频率设置的是 AHB 的 1/8,这里为了兼容 FreeRTOS 将滴答定时器的时钟频 率改为了 AHB,也就是 72MHz!这一点一定要注意! 接下来的三个函数都是延时的,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 void delay_us (u32 nus) { u32 ticks; u32 told,tnow,tcnt=0 ; u32 reload=SysTick->LOAD; ticks=nus*fac_us; told=SysTick->VAL; while (1 ) { tnow=SysTick->VAL; if (tnow!=told) { if (tnow<told)tcnt+=told-tnow; else tcnt+=reload-tnow+told; told=tnow; if (tcnt>=ticks)break ; } }; } void delay_ms (u16 nms) { if (xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED) { if (nms>=fac_ms) { vTaskDelay(nms/fac_ms); } nms%=fac_ms; } delay_us((u32)(nms*1000 )); } void delay_xms (u32 nms) { u32 i; for (i=0 ;i<nms;i++) delay_us(1000 ); }
delay_us()是 us 级延时函数,delay_ms 和 delay_xms()都是 ms 级的延时函数,delay_us()和 delay_xms()不会导致任务切换。delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的 简单封装,所以在使用 delay_ms()的时候就会导致任务切换。 delay.c 修改完成以后编译一下,会提示如下图所示错误:
上图的错误提示表示在 port.c、delay.c 和 stm32f10x_it.c 中三个重复定义的函数: SysTick_Handler()、SVC_Handler()和 PendSV_Handler(),这三个函数分别为滴答定时器中断服 务函数、SVC 中断服务函数和 PendSV 中断服务函数,将 stm32f10x_it.c 中的三个函数屏蔽掉, 如下图所示:
再次编译代码,应该没有错误了,如果还是错误的话自行根据错误类型修改!至此,SYSTEM 文件夹就修改完成了,接下来我们进行跑马灯测试,测试代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 #include "sys.h" #include "delay.h" #include "usart.h" #include "oled_bmp.h" #include "oled_font.h" #include "oled.h" #include "led.h" #include "FreeRTOS.h" #include "task.h" #define START_TASK_PRIO 1 #define START_STK_SIZE 128 TaskHandle_t StartTask_Handler; void start_task (void *pvParameters) ;#define LED0_TASK_PRIO 2 #define LED0_STK_SIZE 50 TaskHandle_t LED0Task_Handler; void led0_task (void *pvParameters) ;#define LED1_TASK_PRIO 3 #define LED1_STK_SIZE 50 TaskHandle_t LED1Task_Handler; void led1_task (void *pvParameters) ;int main (void ) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); delay_init(); LED_Init(); xTaskCreate((TaskFunction_t )start_task, (const char * )"start_task" , (uint16_t )START_STK_SIZE, (void * )NULL , (UBaseType_t )START_TASK_PRIO, (TaskHandle_t* )&StartTask_Handler); vTaskStartScheduler(); } void start_task (void *pvParameters) { taskENTER_CRITICAL(); xTaskCreate((TaskFunction_t )led0_task, (const char * )"led0_task" , (uint16_t )LED0_STK_SIZE, (void * )NULL , (UBaseType_t )LED0_TASK_PRIO, (TaskHandle_t* )&LED0Task_Handler); xTaskCreate((TaskFunction_t )led1_task, (const char * )"led1_task" , (uint16_t )LED1_STK_SIZE, (void * )NULL , (UBaseType_t )LED1_TASK_PRIO, (TaskHandle_t* )&LED1Task_Handler); vTaskDelete(StartTask_Handler); taskEXIT_CRITICAL(); } void led0_task (void *pvParameters) { vTaskDelay(800 ); while (1 ) { LED0=~LED0; vTaskDelay(500 ); } } void led1_task (void *pvParameters) { vTaskDelay(800 ); while (1 ) { LED1=0 ; vTaskDelay(200 ); LED1=1 ; vTaskDelay(800 ); } }
测试代码中创建了 3 个任务:LED0 测试任务、LED1 测试任务和浮点测试任务,它们的任务函数分别为:led0_task()、led1_task()。led0_task()和 led1_task()任务很简单,就是让 LED0 和 LED1 周期性闪烁 编译并下载代码到 STM32F103VET6 开发板中,下载进去以后会看到 LED0 和 LED1 开始闪烁, LED0 均匀闪烁,那是因为我们在 LED0 的任务代码中设置好的 LED0 亮 500ms,灭 500ms。 LED1 亮的时间短,灭的时间长,这是因为在 LED1 的任务代码中设置好的亮 200ms,灭 800ms。
设置配置文件
到这里还是没能跑起来,还需要参考后边设置的 FreeRTOSConfig.h 后才能工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H #include "stm32f10x.h" #define configUSE_PREEMPTION 1 #define configUSE_TIME_SLICING 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 #define configUSE_TICKLESS_IDLE 0 #define configCPU_CLOCK_HZ (SystemCoreClock) #define configTICK_RATE_HZ (( TickType_t )1000) #define configMAX_PRIORITIES (32) #define configMINIMAL_STACK_SIZE ((unsigned short)128) #define configMAX_TASK_NAME_LEN (16) #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_QUEUE_SETS 1 #define configUSE_TASK_NOTIFICATIONS 1 #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configQUEUE_REGISTRY_SIZE 10 #define configUSE_APPLICATION_TASK_TAG 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 0 #define configTOTAL_HEAP_SIZE ((size_t)(36*1024)) #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_TRACE_FACILITY 0 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) #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 1 #endif #define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 #endif
移植过程参考二(超详细说明) 提取源码
首先在我们的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.h FreeRTOSConfig.h是直接从demo文件夹下面拷贝过来的,该头文件对裁剪整个FreeRTOS所需的功能的宏均做了定义,有些宏定义被使能,有些宏定义被失能,一开始我们只需要配置最简单的功能即可。要想随心所欲的配置FreeRTOS的功能,我们必须对这些宏定义的功能有所掌握,下面我们先简单的介绍下这些宏定义的含义,然后再对这些宏定义进行修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 #ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H #include "stm32f10x.h" #include "bsp_usart.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__) #define configUSE_PREEMPTION 1 #define configUSE_TIME_SLICING 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 #define configUSE_TICKLESS_IDLE 0 #define configCPU_CLOCK_HZ (SystemCoreClock) #define configTICK_RATE_HZ (( TickType_t )1000) #define configMAX_PRIORITIES (32) #define configMINIMAL_STACK_SIZE ((unsigned short)128) #define configMAX_TASK_NAME_LEN (16) #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 #define configUSE_QUEUE_SETS 1 #define configUSE_TASK_NOTIFICATIONS 1 #define configUSE_MUTEXES 1 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configQUEUE_REGISTRY_SIZE 10 #define configUSE_APPLICATION_TASK_TAG 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define configSUPPORT_STATIC_ALLOCATION 0 #define configTOTAL_HEAP_SIZE ((size_t)(36*1024)) #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configUSE_MALLOC_FAILED_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_TRACE_FACILITY 0 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) #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 1 #endif #endif
修改stm32f10x_it.c SysTick中断服务函数是一个非常重要的函数,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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 extern void xPortSysTickHandler (void ) ;void SysTick_Handler (void ) { #if (INCLUDE_xTaskGetSchedulerState == 1 ) if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { #endif xPortSysTickHandler(); #if (INCLUDE_xTaskGetSchedulerState == 1 ) } #endif }
创建任务 这里,我们创建一个单任务,任务使用的栈和任务控制块是在创建任务的时候FreeRTOS动态分配的。 任务必须是一个死循环,否则任务将通过LR返回,如果LR指向了非法的内存就会产生HardFault_Handler,而FreeRTOS指向一个死循环,那么任务返回之后就在死循环中执行,这样子的任务是不安全的,所以避免这种情况,任务一般都是死循环并且无返回值的。并且每个任务循环主体中应该有阻塞任务的函数,否则就会饿死比它优先级更低的任务!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include "FreeRTOS.h" #include "task.h" #include "bsp_led.h" static void AppTaskCreate (void ) ; static TaskHandle_t AppTask_Handle = NULL ;int main (void ) { BaseType_t xReturn = pdPASS; BSP_Init(); xReturn = xTaskCreate((TaskFunction_t )AppTask, (const char * )"AppTask" , (uint16_t )512 , (void * )NULL , (UBaseType_t )1 , (TaskHandle_t* )&AppTask_Handle); if (pdPASS == xReturn) vTaskStartScheduler(); else return -1 ; while (1 ); } static void AppTask (void * parameter) { while (1 ) { LED1_ON; vTaskDelay(500 ); LED1_OFF; vTaskDelay(500 ); } }
相关链接(侵删)
超详细的FreeRTOS移植全教程——基于srm32
STM32F1+FreeRTOS系统移植例程
https://blog.csdn.net/cs953575/article/details/118443722
=================我是分割线=================
欢迎到公众号来唠嗑: