freeRTOS 在arm926ejs上的移植

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的管理,还是不了解。

你可能感兴趣的:(rtos)