昨天在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()后会增加程序代码,也会使程序变慢。  |