您的位置:首页 > Web前端 > JavaScript

freeRTOS 在arm926ejs上的移植

2011-09-14 11:19 295 查看
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的管理,还是不了解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: