昨天在KL26Z上移植了Freertos实时操作系统。整理了一下,把移植的过程及注意的地方共享一下。有需要的可以参考。
FreeRtos移植:版本 FreeRTOS V8.2.2 目前最新版本 1,源文件准备,可以到freertos官网上下载。 1)与FreeRTOS内核有关的文件数量仅为3个,分别是list.c queue.c tasks.c croutine.c timers.c该文件位于FreeRTOS\Source 2)与内存分配有关的文件共有4个,分别是heap_1.c,heap_2.c,heap_3.c,heap_4.c,heap_5.c最新版有5个文件只需选择其中的1个,STM32选择heap_2.c。新塘选择heap_2.c。KL26Zheap_2.c。我们也选择该文件位于Source\portable\MemMang。 3) 与移植相关的代码包括port.c,portmacro.h。这些代码不但和编译器有关还和平台(MCU)有关。FreeRTOS先以编译器为大类,然后再以平台(MCU)为小类。在这里选择Keil编译器,平台为ARM_CM4F。该文件位于Source\portable\RVDS\ARM_CM4F。 4)除了上述内容之外,还包括FreeRTOS内核相关的头文件。该文件FreeRTOS\Source\include。 2,在工程中加入Freertos文件。 加入的文件有list.c queue.c tasks.c timers.c heap_2.c,port.c 3,在工程中加入Freertos文件的头文件路径 4,编译,提示错误,没有FreeRTOSConfig.h文件 编写该文件对内核的配置,需要FreeRTOSConfig.h中的相关宏定义 5,需要在启动代码中修改这些中断向量的名称,并声明这些中断向量为外部函数。这也是初次使用FreeRTOS容易范的错误。我这里就在FreeRTOSConfig.h中定义: #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler 编译出现错误: .\obj\USBD_VCOM.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o). .\obj\USBD_VCOM.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o). .\obj\USBD_VCOM.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_2.o). 原因是没有定义这三个函数。 在main文件中定义这三个函数: void vApplicationIdleHook(void) { } void vApplicationTickHook(void) { #if ( mainCREATE_SIMPLE_BLINKY_DEMO_ONLY == 0 ) { // In this case the tick hook is used as part of the queue set test. } #endif // mainCREATE_SIMPLE_BLINKY_DEMO_ONLY } void vApplicationMallocFailedHook(void) { taskDISABLE_INTERRUPTS(); for(;;); } 再编译,还出现一个错误: .\obj\USBD_VCOM.axf: Error: L6218E: Undefined symbol taskDISABLE_INTERRUPTS (referred from main.o). 是因为main文件中没有添加内核头文件: // Kernel includes. #include "FreeRTOS.h" #include "task.h" #include "timers.h" #include "semphr.h" 这时编译通过. 6,到这里移植就完成了,是不是很简单?可以开始实现任务代码了,我在这里实现一个简单的任务,实现USB收发数据。 xTaskCreate(USBD_Task, "USBD", configMINIMAL_STACK_SIZE, NULL, mainKEY_TASK_PRIORITY, NULL); /* Start the scheduler. */ vTaskStartScheduler(); while(1) { } 注意,这时候运行起来,好像切换是有问题的,是因为我定义了 #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); },导致启动内核的时候有问题。解决办法:一是不要定义configASSERT,关于configASSERT后面会说到。二是在port.c中的BaseType_t xPortStartScheduler( void )的函数中去掉 configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); configASSERT( portCPUID != portCORTEX_M7_r0p1_ID ); configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); #if( configASSERT_DEFINED == 1 ) { volatile uint32_t ulOriginalPriority; volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); volatile uint8_t ucMaxPriorityValue; ulOriginalPriority = *pucFirstUserPriorityRegister; *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; ucMaxPriorityValue = *pucFirstUserPriorityRegister; configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) { ulMaxPRIGROUPValue--; ucMaxPriorityValue <<= ( uint8_t ) 0x01; } #endif 这个运行就一切正常了。 7,有人应该注意到了,我们还有一个文件需要配置些什么东西。这个文件是FreeRTOSConfig.h(这个文件自己编写),FreeRTOS 是高度可配置的。所有的可配置项都在FreeRTOSConfig.h 文件中。每一个Demo 程序中都包含了一个配置好的FreeRTOSConfig.h 文件,可以以Demo程序中的FreeRTOSConfig.h 文件作为模板,在其基础上加以修改。 关注portmacro.h。 1)在portmacro.h portmacro.h 主要包括两部分内容,第一部分定义了一系列内核代码中用到的数据类型。FreeRTOS 与 uC/OS-II 一样,并不直接使用char、int 等这些原生类型,而是将其重新定义为一系列以port开头的新类型。在uC/OS-II的移植代码中,通常采用 typedef 来定义新的类型,而FreeRTOS的作者似乎更喜欢用宏定义,相应代码: #define portCHAR char #define portFLOAT float #define portDOUBLE double #define portLONG long #define portSHORT short #define portSTACK_TYPE uint32_t #define portBASE_TYPE long typedef portSTACK_TYPE StackType_t; typedef long BaseType_t; typedef unsigned long UBaseType_t; #if( configUSE_16_BIT_TICKS == 1 ) typedef uint16_t TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffff #else typedef uint32_t TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffffffffUL /* 32-bit tick type on a 32-bit architecture, so reads of the tick count do not need to be guarded with a critical section. */ #define portTICK_TYPE_IS_ATOMIC 1 #endif 然后是一些硬件相关的定义。包括数据对其方式,堆栈增长方向,Tick Rate,还有任务切换的宏 #define portSTACK_GROWTH ( -1 ) #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) #define portBYTE_ALIGNMENT 8 /* Constants used with memory barrier intrinsics. */ #define portSY_FULL_READ_WRITE ( 15 ) portSTACK_GROWTH 定义为1 表示堆栈是正向生长的,-1为逆向生长的。一般来说堆栈都是倒生的。 多说一句在 uC/OS-II 中,对应的宏是OS_STK_GROWTH, 1 表示逆向生长,0表示正向生长。 portTICK_RATE_MS 只在应用代码中可能会用到,表示的是Tick 间间隔多少 ms。 portYIELD() 实现的是任务切换,相当于 uC/OS-II中的 OS_TASK_SW()。 #define portYIELD() { portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; __dsb( portSY_FULL_READ_WRITE ); __isb( portSY_FULL_READ_WRITE ); } 然后是有关临界区的处理代码: #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) 2)FreeRTOSConfig.h配置说明 //是否配置成抢先式多任务内核,是1的时候,优先级高的任务优先执行。 为0任务就没有优//先级之说,用时间片轮流执行 #define configUSE_PREEMPTION 1 // IDLE任务的HOOK函数,用于OS功能扩展,需要你自己编相应函数, 名字是void //vApplicationIdleHook( void ) #define configUSE_IDLE_HOOK 1 // SYSTEM TICK的HOOK函数,用于OS功能扩展,需要你自己编相应函数, 名字是 void //vApplicationTickHook( void ) #define configUSE_TICK_HOOK 1 #define configCPU_CLOCK_HZ ( SystemCoreClock ) // 系统CPU频率,单位是Hz // 系统SYSTEM TICK每秒钟的发生次数, 数值越大系统反应越快,但是CPU用在任务切换//的开销就越多 #define configTICK_RATE_HZ ( ( portTickType ) 1000 ) //系统任务优先级数。10 说明任务有10级优先度。这个数目越大耗费RAM越多 #define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 10 ) // 系统最小堆栈尺寸,注意128不是128字节,而是128个入栈。比如ARM32位,128个入//栈就是512字节 #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 100 ) // 系统可用内存。一般设成除了操作系统和你的程序所用RAM外的最大RAM。 比如20KRAM //你用了2K,系统用了3K,剩下15就是最大HEAP 尺寸。你可以先设小然后看编译结果往大//里加 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 8 * 1024 ) ) // 任务的PC名字最大长度,因为函数名编译完了就不见了,所以追踪时不知道哪个名字。10 //表示10个char #define configMAX_TASK_NAME_LEN ( 10 ) // 是否设定成追踪,由PC端TraceCon.exe记录,也可以转到系统显示屏上 #define configUSE_TRACE_FACILITY 1 // 就是SYSTEM TICK的长度,16是16位,如果是16位以下CPU, 一般选1;如果是32 //位系统,一般选0 #define configUSE_16_BIT_TICKS 0 // 简单理解一下就是和IDLE TASK同样优先级的任务执行情况。建议设成1,对系统影响不大 #define configIDLE_SHOULD_YIELD 1 //是否用MUTEXES。 MUTEXES是任务间通讯的一种方式,特别是用于任务共享资源的应//用,比如打印机,任务A用的时候就排斥别的任务应用,用完了别的任务才可以应用。 #define configUSE_MUTEXES 1 //用于DEBUG,登记SEMAPHORESQ和QUEUE的最大个数, 需要在任务用应用函数//vQueueAddToRegistry()和vQueueUnregisterQueue() #define configQUEUE_REGISTRY_SIZE 8 //此项用于DEBUG, 来看是否有栈溢出,需要你自己编相应检查函数void //vApplicationStackOverflowHook( xTaskHandle *pxTask, signed //portCHAR *pcTaskName ) #define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_RECURSIVE_MUTEXES 1// 确定是否用递归式的MUTEXES #define configUSE_MALLOC_FAILED_HOOK 1 #define configUSE_APPLICATION_TASK_TAG 0 #define configUSE_COUNTING_SEMAPHORES 1 #define configGENERATE_RUN_TIME_STATS 0 #define configUSE_QUEUE_SETS 1 // Co-routine definitions. //是用用协程。协程公用堆栈,节省RAM,但是没有任务优先级高,也无法和任务通讯 , 设置//为1则包含co-routines 功能,如果包含了co-routines功能,则编译时需包含croutine.c 文件 #define configUSE_CO_ROUTINES 0 //co-routines 可以使用的优先级的数量 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //Software timer definitions. #define configUSE_TIMERS 1//设置为1则包含软件定时器功能 #define configTIMER_TASK_PRIORITY ( 2 )//设置软件定时器任务的优先级 //设置软件定时器任务中用到的命令队列的长度 #define configTIMER_QUEUE_LENGTH 10 //设置软件定时器任务需要的任务堆栈大小。 #define configTIMER_TASK_STACK_DEPTH configMINIMAL_STACK_SIZE * 2 ) // Set the following definitions to 1 to include the API function, or zero //to exclude the API function. #define INCLUDE_vTaskPrioritySet 1// 设定可以改变任务优先度 #define INCLUDE_uxTaskPriorityGet 1// 设定可以查询任务优先度 #define INCLUDE_vTaskDelete 1// 设定可以删除任务 // 据说是可以回收删除任务后的资源(RAM等) #define INCLUDE_vTaskCleanUpResources 1 #define INCLUDE_vTaskSuspend 1 //设置可以把任务挂起 // 设置任务延迟的绝对时间,比如现在4:30,延迟到5:00。时间都是绝对时间 #define INCLUDE_vTaskDelayUntil 1 // 设置任务延时,比如延迟30分钟,相对的时间,现在什么时间,不需要知道 #define INCLUDE_vTaskDelay 1 #define INCLUDE_xTaskGetSchedulerState 1 //设置取得当前任务分配器的状态
#define INCLUDE_xTaskGetCurrentTaskHandle 1 //设置当前任务是由哪个任务开的 //是否使能这一函数,函数的目的是返回任务执行后任务堆栈的最小未用数量,同样是为防止//堆栈溢出
#define INCLUDE_uxTaskGetStackHighWaterMark 0
// 系统内核的中断优先级,中断优先级越低,越不会影响其他中断。一般设成最低
#define configKERNEL_INTERRUPT_PRIORITY [dependent of processor] // 系统SVC中断优先级,这两项都在在M3和PIC32上应用 #define configMAX_SYSCALL_INTERRUPT_PRIORITY [dependent on processor and application] configASSERT宏configASSERT()的作用类似C语言标准库中的宏assert(),configASSERT() 可以帮助调试,但是定义了configASSERT()后会增加程序代码,也会使程序变慢。 |