freeRTOS 是一个实时的内核,完全免费,即使你用做商用,并且可以配置成抢占式或者支持时间片的抢占式,不像ucosii,开源但是收费,而且只支持抢占式。
目标硬件平台平台:基于arm926ejs的SOC
IDE:CodeWarrior 5.7.0
debug:realdebug + jlink
freeRTOS 版本:V7.0.1
说明:此文档只关注OS的移植,不涉及目标板的boot的初始化配置(PLL MMU CACHE MEMORY...),以及demo\common 下驱动的实现。

1.)下载freeRTOS 源码
   https://freertos.svn.sourceforge.net/svnroot/freertos/tags/V7.0.1 下载最新版
   
   https://freertos.svn.sourceforge.net/svnroot/freertos 下载所有的版本,很大,不推荐。
   
2.)freeRTOS 目录结构
   freeRTOS-
           -Demo
                -许多porting实例
                -common
           -License
           -Source
                  -include 所有头文件
                  -portable 包含硬件相关的代码
           -TraceCon
           
   Demo 下面每个子目录对应每个porting的实例,子目录里包含了开发环境的工程目录,task 的实现,概括来说此目录存放APPLICATION 相关的文件。
         子目录命名遵循一定的规则:CPUCORENAME_SOCNAME_COMPILERNAME.
         common 目录下包含硬件驱动,文件系统,网络驱动等在具体平台上的实现。此文章不涉及。
   Source 此目录下包含真正的OS kernel的source code.
          include 下的头文件和 croutine.c list.c queue.c timers.c tasks.c 为硬件无关的代码,所有硬件平台都共享这些文件。
          portable 下包含众多以compiler 命名的文件夹 比如codewarrior rvds等,以及MemMang目录,此目录下包含heap_1.c heap_2.c heap_3.c
          实现了MEMORY 管理,具体porting的时候你只需要选择一个文件到IDE,否则会报重复定义的错误。
          在以compiler命名的目录下又有一些以SOC名字命名的文件夹。 每个[compiler]目录下的[soc]目录对应着一个具体的porting实例。这些目录下都包含着
          类似的文件,port.c portasm.s portmacro.h 有多有少,但是都是实现了硬件平台相关的代码。
          
 3.) 开始移植。

     在Demo目录下创建ARM9_XXXX_CodeWarrior目录,添加了boot 代码,scatter 文件 以及FreeRTOSConfig.h和main.c

    其中FreeRTOSConfig.h 来自demo中的arm7的实例,文件里重要配置了一些宏,代码如下:

             #define configUSE_PREEMPTION        1 ///1配置成抢占式的,0配置成支持时间片


                    #define configUSE_IDLE_HOOK            1 ///使用idle hook 函数,需要application 实现


                    #define configUSE_TICK_HOOK            1 ///使用tick hook 函数,需要application 实现


                    #define configCPU_CLOCK_HZ            ( ( unsigned long ) 240000000 )    ///cpu clock 240M


                    #define configTICK_RATE_HZ            ( ( portTickType ) 1 ) ///每秒tick数


                    #define configMAX_PRIORITIES        ( ( unsigned portBASE_TYPE ) 4 )///优先级级数


                    #define configMINIMAL_STACK_SIZE    ( ( unsigned short ) 90 )     ///栈大小


                    #define configTOTAL_HEAP_SIZE        ( ( size_t ) 13 * 1024 )        ///堆大小


                    #define configMAX_TASK_NAME_LEN        ( 8 )                         ///task name的长度


                    #define configUSE_TRACE_FACILITY    0


                    #define configUSE_16_BIT_TICKS        0                             ///对于32的cpu 都配置成0


                    #define configIDLE_SHOULD_YIELD        1                             ///待研究:(


                    


                    #define configQUEUE_REGISTRY_SIZE     0                           ///待研究:(


                    


                    /* Co-routine definitions. */                                   ///待研究:(


                    #define configUSE_CO_ROUTINES         0


                    #define configMAX_CO_ROUTINE_PRIORITIES ( 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


                    #define INCLUDE_vTaskCleanUpResources    0


                    #define INCLUDE_vTaskSuspend            1


                    #define INCLUDE_vTaskDelayUntil            1


                    #define INCLUDE_vTaskDelay                1


                    


                    


            main.c 则实现了几个任务函数,hook函数,以及入口函数,每个任务都有下面的格式


            static portTASK_FUNCTION( vTask1, pvParameters )


                    {


                        /* Calculate the LED and flash rate. */


                        for(;;)


                        {


                            portTickType xTickCount = xTaskGetTickCount();


                            printf("task1:%d\n",xTickCount);


                            /* Delay for half the flash period then turn the LED off. */


                            vTaskDelay( 4 );


                    #if configUSE_PREEMPTION == 0


                            taskYIELD();


                    #endif


                        }


                    }


     在 source\portalbe 目录下创建CodeWarrior目录(如果已经有了,则不需要创建),在codeWarrior目录下创建xxxx(SOC名字)  在此目录下


     创建了port.c portasm.s portmacro.h


           


           portmacro.h: 


            #define portCHAR        char


                        #define portFLOAT        float


                        #define portDOUBLE        double


                        #define portLONG        long


                        #define portSHORT        short


                        #define portSTACK_TYPE    unsigned portLONG


                        #define portBASE_TYPE    portLONG


                        


                        #if( configUSE_16_BIT_TICKS == 1 )


                            typedef unsigned portSHORT portTickType;


                            #define portMAX_DELAY ( portTickType ) 0xffff


                        #else


                            typedef unsigned portLONG portTickType;


                            #define portMAX_DELAY ( portTickType ) 0xffffffff


                        #endif


                        /*-----------------------------------------------------------*/    


                        


                        /* Hardware specifics. */


                        #define portSTACK_GROWTH            ( -1 )


                        #define portTICK_RATE_MS            ( ( portTickType ) 1000 / configTICK_RATE_HZ )        


                        #define portBYTE_ALIGNMENT            8


                        /*-----------------------------------------------------------*/    


                        


                        


                        #define portEXIT_SWITCHING_ISR(SwitchRequired)                 \


                        {                                                             \


                        extern void vTaskSwitchContext(void);                         \


                                                                                     \


                                if(SwitchRequired)                                     \


                                {                                                     \


                                    vTaskSwitchContext();                             \


                                }                                                     \


                        }                                                             \


                        


                        extern void vPortYield( void );


                        #define portYIELD() vPortYield()


                        


                        


                        /* Critical section management. */


                        


                        #define portDISABLE_INTERRUPTS()    __disable_irq()                    


                        #define portENABLE_INTERRUPTS()        __enable_irq()


                    


                        extern void vPortEnterCritical( void );


                        extern void vPortExitCritical( void );


                        


                        #define portENTER_CRITICAL()        vPortEnterCritical();


                        #define portEXIT_CRITICAL()            vPortExitCritical();


                        /*-----------------------------------------------------------*/    


                        


                        /* Compiler specifics. */


                        #define inline


                        #define register


                        #define portNOP()    __asm{ NOP }


                        /*-----------------------------------------------------------*/    


                        


                        /* Task function macros as described on the FreeRTOS.org WEB site. */


                        #define portTASK_FUNCTION_PROTO( vFunction, pvParameters )    void vFunction( void *pvParameters )


                        #define portTASK_FUNCTION( vFunction, pvParameters )    void vFunction( void *pvParameters )


                        


                        代码中大部分都是声明了一些函数,以及定义了数据类型


                        真正重要有三个地方


                        #define portYIELD() vPortYield() ///这个宏告诉os, task如何放弃CPU. vPortYield会在portasm.s 中实现。


                        #define portDISABLE_INTERRUPTS()    __disable_irq()                     实现了关中断 __disable_irq() 和 __enable_irq() 支持的库函数


                        #define portENABLE_INTERRUPTS()        __enable_irq()           实现了开中断


                        #define portNOP()    __asm{ NOP }                             实现了nop


                        


                        以上都是跟硬件相关的。


                        


                        port.c


                        主要实现了几个重要函数。


                        1. 


                        portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )


                        {


                                portSTACK_TYPE *pxOriginalTOS;


                                


                                    /* Setup the initial stack of the task.  The stack is set exactly as


                                    expected by the portRESTORE_CONTEXT() macro.


                                


                                    Remember where the top of the (simulated) stack is before we place


                                    anything on it. */


                                    pxOriginalTOS = pxTopOfStack;


                                    


                                    /* To ensure asserts in tasks.c don't fail, although in this case the assert


                                    is not really required. */


                                    pxTopOfStack--;


                                


                                    /* First on the stack is the return address - which in this case is the


                                    start of the task.  The offset is added to make the return address appear


                                    as it would within an IRQ ISR. */


                                    *pxTopOfStack = ( portSTACK_TYPE ) pxCode + portINSTRUCTION_SIZE;        


                                    pxTopOfStack--;


                                


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0xaaaaaaaa;    /* R14 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) pxOriginalTOS; /* Stack used when task starts goes in R13. */


                                    pxTopOfStack--;


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x12121212;    /* R12 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x11111111;    /* R11 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x10101010;    /* R10 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x09090909;    /* R9 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x08080808;    /* R8 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x07070707;    /* R7 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x06060606;    /* R6 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x05050505;    /* R5 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x04040404;    /* R4 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x03030303;    /* R3 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x02020202;    /* R2 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x01010101;    /* R1 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */


                                    pxTopOfStack--;


                                


                                    /* The last thing onto the stack is the status register, which is set for


                                    system mode, with interrupts enabled. */


                                    *pxTopOfStack = ( portSTACK_TYPE ) portINITIAL_SPSR;


                                


                                    if( ( ( unsigned long ) pxCode & 0x01UL ) != 0x00UL )


                                    {


                                        /* We want the task to start in thumb mode. */


                                        *pxTopOfStack |= portTHUMB_MODE_BIT;


                                    }


                                


                                    pxTopOfStack--;


                                


                                    return pxTopOfStack;


                            }


                            初始化任务的栈,使他的栈就像是刚发生中断一样。


                            


                            2.


                            static void prvSetupTimerInterrupt( void )


                            {


                                //add code here


                                //平台相关


                            }


                            初始化一个timer,提供系统tick。



                            3.


                            portBASE_TYPE xPortStartScheduler( void )


                            {


                                /* Start the timer that generates the tick ISR. */


                                prvSetupTimerInterrupt();


                            


                                /* Start the first task.  This is done from portISR.c as ARM mode must be


                                used. */


                                vPortStartFirstTask();


                            


                                /* Should not get here! */


                                return 0;


                            }


                            启动scheduler,只调用一次。vPortStartFirstTask在portasm.s 中实现。


                            


                            4.


                            void freeRtosTickIrqHandler( void )


                            {


                                // Increment the tick counter.


                                vTaskIncrementTick();


                            


                                #if configUSE_PREEMPTION == 1


                                {


                                    // The new tick value might unblock a task.  Ensure the highest task that


                                    // is ready to execute is the task that will execute when the tick ISR


                                    // exits.


                                    vTaskSwitchContext();


                                }


                                #endif


                            }


                            实现了timer的中断处理函数。application也可以自己再实现一个isr,增加功能,但是一定要调用这个函数。


                            


                            5


                            void vPortEnterCritical( void )


                            {


                                /* Disable interrupts as per portDISABLE_INTERRUPTS();                             */


                                portDISABLE_INTERRUPTS();


                            


                                /* Now interrupts are disabled ulCriticalNesting can be accessed


                                directly.  Increment ulCriticalNesting to keep a count of how many times


                                portENTER_CRITICAL() has been called. */


                                ulCriticalNesting++;


                            }


                            /*-----------------------------------------------------------*/


                            


                            void vPortExitCritical( void )


                            {


                                if( ulCriticalNesting > portNO_CRITICAL_NESTING )


                                {


                                    /* Decrement the nesting count as we are leaving a critical section. */


                                    ulCriticalNesting--;


                            


                                    /* If the nesting level has reached zero then interrupts should be


                                    re-enabled. */


                                    if( ulCriticalNesting == portNO_CRITICAL_NESTING )


                                    {


                                        /* Enable interrupts as per portEXIT_CRITICAL(). */


                                        portENABLE_INTERRUPTS();


                                    }


                                }


                            }


                            


                            上面两个函数实现了临界代码函数


                            


                            portasm.s


                            IMPORT    vTaskSwitchContext


                            IMPORT    vTaskIncrementTick


                            IMPORT  top_level_int_handler


                        


                            EXPORT    vPortYieldProcessor


                            EXPORT    vPortStartFirstTask


                            EXPORT    vPreemptiveTick


                            EXPORT    vPortYield


                            EXPORT  irqHandler


                            


                            IMPORT  ulCriticalNesting        ;


                            IMPORT    pxCurrentTCB            ;


                        


                        


                            MACRO


                            portRESTORE_CONTEXT


                        


                        


                            LDR        R0, =pxCurrentTCB        ; Set the LR to the task stack.  The location was...


                            LDR        R0, [R0]                ; ... stored in pxCurrentTCB


                            LDR        LR, [R0]


                        


                            LDR        R0, =ulCriticalNesting    ; The critical nesting depth is the first item on...


                            LDMFD    LR!, {R1}                ; ...the stack.  Load it into the ulCriticalNesting var.


                            STR        R1, [R0]                ;


                        


                            LDMFD    LR!, {R0}                ; Get the SPSR from the stack.


                            MSR        SPSR_cxsf, R0            ;


                        


                            LDMFD    LR, {R0-R14}^            ; Restore all system mode registers for the task.


                            NOP                                ;


                        


                            LDR        LR, [LR, #+60]            ; Restore the return address


                        


                                                            ; And return - correcting the offset in the LR to obtain ...


                            SUBS    PC, LR, #4                ; ...the correct address.


                        


                            MEND


                        


                        ; /**********************************************************************/


                        


                            MACRO


                            portSAVE_CONTEXT


                        


                        


                            STMDB     SP!, {R0}                ; Store R0 first as we need to use it.


                        


                            STMDB    SP,{SP}^                ; Set R0 to point to the task stack pointer.


                            NOP                                ;


                            SUB        SP, SP, #4                ;


                            LDMIA    SP!,{R0}                ;


                        


                            STMDB    R0!, {LR}                ; Push the return address onto the stack.


                            MOV        LR, R0                    ; Now we have saved LR we can use it instead of R0.


                            LDMIA    SP!, {R0}                ; Pop R0 so we can save it onto the system mode stack.


                        


                            STMDB    LR,{R0-LR}^                ; Push all the system mode registers onto the task stack.


                            NOP                                ;


                            SUB        LR, LR, #60                ;


                        


                            MRS        R0, SPSR                ; Push the SPSR onto the task stack.


                            STMDB    LR!, {R0}                ;


                        


                            LDR        R0, =ulCriticalNesting    ;


                            LDR        R0, [R0]                ;


                            STMDB    LR!, {R0}                ;


                        


                            LDR        R0, =pxCurrentTCB        ; Store the new top of stack for the task.


                            LDR        R1, [R0]                ;          


                            STR        LR, [R1]                ;


                            


                            MEND


                            


                        


                        


                            ARM


                            AREA    PORT_ASM, CODE, READONLY


                        


                        


                        


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        ; Starting the first task is done by just restoring the context


                        ; setup by pxPortInitialiseStack


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        vPortStartFirstTask


                            PRESERVE8


                            portRESTORE_CONTEXT


                        


                        


                        vPortYield


                            PRESERVE8


                            SVC 0


                            bx lr


                        


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        ; Interrupt service routine for the SWI interrupt.  The vector table is


                        ; configured in the startup.s file.


                        ;


                        ; vPortYieldProcessor() is used to manually force a context switch.  The


                        ; SWI interrupt is generated by a call to taskYIELD() or portYIELD().


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        


                        vPortYieldProcessor


                            PRESERVE8


                            ; Within an IRQ ISR the link register has an offset from the true return


                            ; address, but an SWI ISR does not.  Add the offset manually so the same


                            ; ISR return code can be used in both cases.


                            ADD    LR, LR, #4


                        


                            ; Perform the context switch.


                            portSAVE_CONTEXT                    ; Save current task context                


                            LDR R0, =vTaskSwitchContext            ; Get the address of the context switch function


                            MOV LR, PC                            ; Store the return address


                            BX    R0                                ; Call the contedxt switch function


                            portRESTORE_CONTEXT                    ; restore the context of the selected task    


                        


                                                


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        ; IRQ handler


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        irqHandler


                            portSAVE_CONTEXT                        ; Save the context of the current task...


                        


                            BL     top_level_int_handler


                        


                            portRESTORE_CONTEXT                     ; Restore the context of the selected task.


                        


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                            END


                            


                            


                vPortStartFirstTask 实现了开始第一个就绪的任务。


                vPortYield          实现了产生软件中断


                


                ///以下两个函数在平台的启动代码中的vector.s中,分别为swi和irq 异常的入口函数。


                vPortYieldProcessor 实现的软中断的中断处理函数


                irqHandler          实现了irq异常的处理函数


                


                top_level_int_handler 读者可以自己实现,概括来说,就是读取中断寄存器,找出是谁产生的了中断,然后调用相关的isr。伪代码如下


                


                void top_level_int_handler()


                {


                      get current interrupt bit position in the register.


                      switch(position)


                      {


                      case timer0:timer0isr();break;


                      ...


                      


                      }


                }


    4. 编译调试


       把源代码加到工程中,去掉错误信息,生成axf文件。 用realdebug通过jlink连上目标板,加载axf文件,如愿出现print信息。


       


    5. 小结


       如果对板子的boot很熟悉,再参考demo中arm7的一些例子,还是很快就能完成porting的。


       但是还是对任务的调度,memory的管理,还是不了解。




0

freeRTOS 是一个实时的内核,完全免费,即使你用做商用,并且可以配置成抢占式或者支持时间片的抢占式,不像ucosii,开源但是收费,而且只支持抢占式。

目标硬件平台平台:基于arm926ejs的SOC


IDE:CodeWarrior 5.7.0


debug:realdebug + jlink


freeRTOS 版本:V7.0.1


说明:此文档只关注OS的移植,不涉及目标板的boot的初始化配置(PLL MMU CACHE MEMORY...),以及demo\common 下驱动的实现。



1.)下载freeRTOS 源码


   https://freertos.svn.sourceforge.net/svnroot/freertos/tags/V7.0.1 下载最新版


   


   https://freertos.svn.sourceforge.net/svnroot/freertos 下载所有的版本,很大,不推荐。


   


2.)freeRTOS 目录结构


   freeRTOS-


           -Demo


                -许多porting实例


                -common


           -License


           -Source


                  -include 所有头文件


                  -portable 包含硬件相关的代码


           -TraceCon


           


   Demo 下面每个子目录对应每个porting的实例,子目录里包含了开发环境的工程目录,task 的实现,概括来说此目录存放APPLICATION 相关的文件。


         子目录命名遵循一定的规则:CPUCORENAME_SOCNAME_COMPILERNAME.


         common 目录下包含硬件驱动,文件系统,网络驱动等在具体平台上的实现。此文章不涉及。


   Source 此目录下包含真正的OS kernel的source code.


          include 下的头文件和 croutine.c list.c queue.c timers.c tasks.c 为硬件无关的代码,所有硬件平台都共享这些文件。


          portable 下包含众多以compiler 命名的文件夹 比如codewarrior rvds等,以及MemMang目录,此目录下包含heap_1.c heap_2.c heap_3.c


          实现了MEMORY 管理,具体porting的时候你只需要选择一个文件到IDE,否则会报重复定义的错误。


          在以compiler命名的目录下又有一些以SOC名字命名的文件夹。 每个[compiler]目录下的[soc]目录对应着一个具体的porting实例。这些目录下都包含着


          类似的文件,port.c portasm.s portmacro.h 有多有少,但是都是实现了硬件平台相关的代码。


          


 3.) 开始移植。


     在Demo目录下创建ARM9_XXXX_CodeWarrior目录,添加了boot 代码,scatter 文件 以及FreeRTOSConfig.h和main.c

    其中FreeRTOSConfig.h 来自demo中的arm7的实例,文件里重要配置了一些宏,代码如下:

             #define configUSE_PREEMPTION        1 ///1配置成抢占式的,0配置成支持时间片


                    #define configUSE_IDLE_HOOK            1 ///使用idle hook 函数,需要application 实现


                    #define configUSE_TICK_HOOK            1 ///使用tick hook 函数,需要application 实现


                    #define configCPU_CLOCK_HZ            ( ( unsigned long ) 240000000 )    ///cpu clock 240M


                    #define configTICK_RATE_HZ            ( ( portTickType ) 1 ) ///每秒tick数


                    #define configMAX_PRIORITIES        ( ( unsigned portBASE_TYPE ) 4 )///优先级级数


                    #define configMINIMAL_STACK_SIZE    ( ( unsigned short ) 90 )     ///栈大小


                    #define configTOTAL_HEAP_SIZE        ( ( size_t ) 13 * 1024 )        ///堆大小


                    #define configMAX_TASK_NAME_LEN        ( 8 )                         ///task name的长度


                    #define configUSE_TRACE_FACILITY    0


                    #define configUSE_16_BIT_TICKS        0                             ///对于32的cpu 都配置成0


                    #define configIDLE_SHOULD_YIELD        1                             ///待研究:(


                    


                    #define configQUEUE_REGISTRY_SIZE     0                           ///待研究:(


                    


                    /* Co-routine definitions. */                                   ///待研究:(


                    #define configUSE_CO_ROUTINES         0


                    #define configMAX_CO_ROUTINE_PRIORITIES ( 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


                    #define INCLUDE_vTaskCleanUpResources    0


                    #define INCLUDE_vTaskSuspend            1


                    #define INCLUDE_vTaskDelayUntil            1


                    #define INCLUDE_vTaskDelay                1


                    


                    


            main.c 则实现了几个任务函数,hook函数,以及入口函数,每个任务都有下面的格式


            static portTASK_FUNCTION( vTask1, pvParameters )


                    {


                        /* Calculate the LED and flash rate. */


                        for(;;)


                        {


                            portTickType xTickCount = xTaskGetTickCount();


                            printf("task1:%d\n",xTickCount);


                            /* Delay for half the flash period then turn the LED off. */


                            vTaskDelay( 4 );


                    #if configUSE_PREEMPTION == 0


                            taskYIELD();


                    #endif


                        }


                    }


     在 source\portalbe 目录下创建CodeWarrior目录(如果已经有了,则不需要创建),在codeWarrior目录下创建xxxx(SOC名字)  在此目录下


     创建了port.c portasm.s portmacro.h


           


           portmacro.h: 


            #define portCHAR        char


                        #define portFLOAT        float


                        #define portDOUBLE        double


                        #define portLONG        long


                        #define portSHORT        short


                        #define portSTACK_TYPE    unsigned portLONG


                        #define portBASE_TYPE    portLONG


                        


                        #if( configUSE_16_BIT_TICKS == 1 )


                            typedef unsigned portSHORT portTickType;


                            #define portMAX_DELAY ( portTickType ) 0xffff


                        #else


                            typedef unsigned portLONG portTickType;


                            #define portMAX_DELAY ( portTickType ) 0xffffffff


                        #endif


                        /*-----------------------------------------------------------*/    


                        


                        /* Hardware specifics. */


                        #define portSTACK_GROWTH            ( -1 )


                        #define portTICK_RATE_MS            ( ( portTickType ) 1000 / configTICK_RATE_HZ )        


                        #define portBYTE_ALIGNMENT            8


                        /*-----------------------------------------------------------*/    


                        


                        


                        #define portEXIT_SWITCHING_ISR(SwitchRequired)                 \


                        {                                                             \


                        extern void vTaskSwitchContext(void);                         \


                                                                                     \


                                if(SwitchRequired)                                     \


                                {                                                     \


                                    vTaskSwitchContext();                             \


                                }                                                     \


                        }                                                             \


                        


                        extern void vPortYield( void );


                        #define portYIELD() vPortYield()


                        


                        


                        /* Critical section management. */


                        


                        #define portDISABLE_INTERRUPTS()    __disable_irq()                    


                        #define portENABLE_INTERRUPTS()        __enable_irq()


                    


                        extern void vPortEnterCritical( void );


                        extern void vPortExitCritical( void );


                        


                        #define portENTER_CRITICAL()        vPortEnterCritical();


                        #define portEXIT_CRITICAL()            vPortExitCritical();


                        /*-----------------------------------------------------------*/    


                        


                        /* Compiler specifics. */


                        #define inline


                        #define register


                        #define portNOP()    __asm{ NOP }


                        /*-----------------------------------------------------------*/    


                        


                        /* Task function macros as described on the FreeRTOS.org WEB site. */


                        #define portTASK_FUNCTION_PROTO( vFunction, pvParameters )    void vFunction( void *pvParameters )


                        #define portTASK_FUNCTION( vFunction, pvParameters )    void vFunction( void *pvParameters )


                        


                        代码中大部分都是声明了一些函数,以及定义了数据类型


                        真正重要有三个地方


                        #define portYIELD() vPortYield() ///这个宏告诉os, task如何放弃CPU. vPortYield会在portasm.s 中实现。


                        #define portDISABLE_INTERRUPTS()    __disable_irq()                     实现了关中断 __disable_irq() 和 __enable_irq() 支持的库函数


                        #define portENABLE_INTERRUPTS()        __enable_irq()           实现了开中断


                        #define portNOP()    __asm{ NOP }                             实现了nop


                        


                        以上都是跟硬件相关的。


                        


                        port.c


                        主要实现了几个重要函数。


                        1. 


                        portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )


                        {


                                portSTACK_TYPE *pxOriginalTOS;


                                


                                    /* Setup the initial stack of the task.  The stack is set exactly as


                                    expected by the portRESTORE_CONTEXT() macro.


                                


                                    Remember where the top of the (simulated) stack is before we place


                                    anything on it. */


                                    pxOriginalTOS = pxTopOfStack;


                                    


                                    /* To ensure asserts in tasks.c don't fail, although in this case the assert


                                    is not really required. */


                                    pxTopOfStack--;


                                


                                    /* First on the stack is the return address - which in this case is the


                                    start of the task.  The offset is added to make the return address appear


                                    as it would within an IRQ ISR. */


                                    *pxTopOfStack = ( portSTACK_TYPE ) pxCode + portINSTRUCTION_SIZE;        


                                    pxTopOfStack--;


                                


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0xaaaaaaaa;    /* R14 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) pxOriginalTOS; /* Stack used when task starts goes in R13. */


                                    pxTopOfStack--;


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x12121212;    /* R12 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x11111111;    /* R11 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x10101010;    /* R10 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x09090909;    /* R9 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x08080808;    /* R8 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x07070707;    /* R7 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x06060606;    /* R6 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x05050505;    /* R5 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x04040404;    /* R4 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x03030303;    /* R3 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x02020202;    /* R2 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) 0x01010101;    /* R1 */


                                    pxTopOfStack--;    


                                    *pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */


                                    pxTopOfStack--;


                                


                                    /* The last thing onto the stack is the status register, which is set for


                                    system mode, with interrupts enabled. */


                                    *pxTopOfStack = ( portSTACK_TYPE ) portINITIAL_SPSR;


                                


                                    if( ( ( unsigned long ) pxCode & 0x01UL ) != 0x00UL )


                                    {


                                        /* We want the task to start in thumb mode. */


                                        *pxTopOfStack |= portTHUMB_MODE_BIT;


                                    }


                                


                                    pxTopOfStack--;


                                


                                    return pxTopOfStack;


                            }


                            初始化任务的栈,使他的栈就像是刚发生中断一样。


                            


                            2.


                            static void prvSetupTimerInterrupt( void )


                            {


                                //add code here


                                //平台相关


                            }


                            初始化一个timer,提供系统tick。



                            3.


                            portBASE_TYPE xPortStartScheduler( void )


                            {


                                /* Start the timer that generates the tick ISR. */


                                prvSetupTimerInterrupt();


                            


                                /* Start the first task.  This is done from portISR.c as ARM mode must be


                                used. */


                                vPortStartFirstTask();


                            


                                /* Should not get here! */


                                return 0;


                            }


                            启动scheduler,只调用一次。vPortStartFirstTask在portasm.s 中实现。


                            


                            4.


                            void freeRtosTickIrqHandler( void )


                            {


                                // Increment the tick counter.


                                vTaskIncrementTick();


                            


                                #if configUSE_PREEMPTION == 1


                                {


                                    // The new tick value might unblock a task.  Ensure the highest task that


                                    // is ready to execute is the task that will execute when the tick ISR


                                    // exits.


                                    vTaskSwitchContext();


                                }


                                #endif


                            }


                            实现了timer的中断处理函数。application也可以自己再实现一个isr,增加功能,但是一定要调用这个函数。


                            


                            5


                            void vPortEnterCritical( void )


                            {


                                /* Disable interrupts as per portDISABLE_INTERRUPTS();                             */


                                portDISABLE_INTERRUPTS();


                            


                                /* Now interrupts are disabled ulCriticalNesting can be accessed


                                directly.  Increment ulCriticalNesting to keep a count of how many times


                                portENTER_CRITICAL() has been called. */


                                ulCriticalNesting++;


                            }


                            /*-----------------------------------------------------------*/


                            


                            void vPortExitCritical( void )


                            {


                                if( ulCriticalNesting > portNO_CRITICAL_NESTING )


                                {


                                    /* Decrement the nesting count as we are leaving a critical section. */


                                    ulCriticalNesting--;


                            


                                    /* If the nesting level has reached zero then interrupts should be


                                    re-enabled. */


                                    if( ulCriticalNesting == portNO_CRITICAL_NESTING )


                                    {


                                        /* Enable interrupts as per portEXIT_CRITICAL(). */


                                        portENABLE_INTERRUPTS();


                                    }


                                }


                            }


                            


                            上面两个函数实现了临界代码函数


                            


                            portasm.s


                            IMPORT    vTaskSwitchContext


                            IMPORT    vTaskIncrementTick


                            IMPORT  top_level_int_handler


                        


                            EXPORT    vPortYieldProcessor


                            EXPORT    vPortStartFirstTask


                            EXPORT    vPreemptiveTick


                            EXPORT    vPortYield


                            EXPORT  irqHandler


                            


                            IMPORT  ulCriticalNesting        ;


                            IMPORT    pxCurrentTCB            ;


                        


                        


                            MACRO


                            portRESTORE_CONTEXT


                        


                        


                            LDR        R0, =pxCurrentTCB        ; Set the LR to the task stack.  The location was...


                            LDR        R0, [R0]                ; ... stored in pxCurrentTCB


                            LDR        LR, [R0]


                        


                            LDR        R0, =ulCriticalNesting    ; The critical nesting depth is the first item on...


                            LDMFD    LR!, {R1}                ; ...the stack.  Load it into the ulCriticalNesting var.


                            STR        R1, [R0]                ;


                        


                            LDMFD    LR!, {R0}                ; Get the SPSR from the stack.


                            MSR        SPSR_cxsf, R0            ;


                        


                            LDMFD    LR, {R0-R14}^            ; Restore all system mode registers for the task.


                            NOP                                ;


                        


                            LDR        LR, [LR, #+60]            ; Restore the return address


                        


                                                            ; And return - correcting the offset in the LR to obtain ...


                            SUBS    PC, LR, #4                ; ...the correct address.


                        


                            MEND


                        


                        ; /**********************************************************************/


                        


                            MACRO


                            portSAVE_CONTEXT


                        


                        


                            STMDB     SP!, {R0}                ; Store R0 first as we need to use it.


                        


                            STMDB    SP,{SP}^                ; Set R0 to point to the task stack pointer.


                            NOP                                ;


                            SUB        SP, SP, #4                ;


                            LDMIA    SP!,{R0}                ;


                        


                            STMDB    R0!, {LR}                ; Push the return address onto the stack.


                            MOV        LR, R0                    ; Now we have saved LR we can use it instead of R0.


                            LDMIA    SP!, {R0}                ; Pop R0 so we can save it onto the system mode stack.


                        


                            STMDB    LR,{R0-LR}^                ; Push all the system mode registers onto the task stack.


                            NOP                                ;


                            SUB        LR, LR, #60                ;


                        


                            MRS        R0, SPSR                ; Push the SPSR onto the task stack.


                            STMDB    LR!, {R0}                ;


                        


                            LDR        R0, =ulCriticalNesting    ;


                            LDR        R0, [R0]                ;


                            STMDB    LR!, {R0}                ;


                        


                            LDR        R0, =pxCurrentTCB        ; Store the new top of stack for the task.


                            LDR        R1, [R0]                ;          


                            STR        LR, [R1]                ;


                            


                            MEND


                            


                        


                        


                            ARM


                            AREA    PORT_ASM, CODE, READONLY


                        


                        


                        


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        ; Starting the first task is done by just restoring the context


                        ; setup by pxPortInitialiseStack


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        vPortStartFirstTask


                            PRESERVE8


                            portRESTORE_CONTEXT


                        


                        


                        vPortYield


                            PRESERVE8


                            SVC 0


                            bx lr


                        


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        ; Interrupt service routine for the SWI interrupt.  The vector table is


                        ; configured in the startup.s file.


                        ;


                        ; vPortYieldProcessor() is used to manually force a context switch.  The


                        ; SWI interrupt is generated by a call to taskYIELD() or portYIELD().


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        


                        vPortYieldProcessor


                            PRESERVE8


                            ; Within an IRQ ISR the link register has an offset from the true return


                            ; address, but an SWI ISR does not.  Add the offset manually so the same


                            ; ISR return code can be used in both cases.


                            ADD    LR, LR, #4


                        


                            ; Perform the context switch.


                            portSAVE_CONTEXT                    ; Save current task context                


                            LDR R0, =vTaskSwitchContext            ; Get the address of the context switch function


                            MOV LR, PC                            ; Store the return address


                            BX    R0                                ; Call the contedxt switch function


                            portRESTORE_CONTEXT                    ; restore the context of the selected task    


                        


                                                


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        ; IRQ handler


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                        irqHandler


                            portSAVE_CONTEXT                        ; Save the context of the current task...


                        


                            BL     top_level_int_handler


                        


                            portRESTORE_CONTEXT                     ; Restore the context of the selected task.


                        


                        ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


                            END


                            


                            


                vPortStartFirstTask 实现了开始第一个就绪的任务。


                vPortYield          实现了产生软件中断


                


                ///以下两个函数在平台的启动代码中的vector.s中,分别为swi和irq 异常的入口函数。


                vPortYieldProcessor 实现的软中断的中断处理函数


                irqHandler          实现了irq异常的处理函数


                


                top_level_int_handler 读者可以自己实现,概括来说,就是读取中断寄存器,找出是谁产生的了中断,然后调用相关的isr。伪代码如下


                


                void top_level_int_handler()


                {


                      get current interrupt bit position in the register.


                      switch(position)


                      {


                      case timer0:timer0isr();break;


                      ...


                      


                      }


                }


    4. 编译调试


       把源代码加到工程中,去掉错误信息,生成axf文件。 用realdebug通过jlink连上目标板,加载axf文件,如愿出现print信息。


       


    5. 小结


       如果对板子的boot很熟悉,再参考demo中arm7的一些例子,还是很快就能完成porting的。


       但是还是对任务的调度,memory的管理,还是不了解。