ck cpu freertos移植总结

举报
aiot_bigbear 发表于 2022/09/25 03:44:51 2022/09/25
【摘要】 摘要:介绍FreeRTOS在CK510 CPU上的移植方法 目录 第1章 FreeRTOS简介 第2章 移植FreeRTOS §2.1 FreeRTOS移植代码介绍 2.1.1 FreeR...

摘要:介绍FreeRTOS在CK510 CPU上的移植方法

目录

第1章 FreeRTOS简介

第2章 移植FreeRTOS

§2.1 FreeRTOS移植代码介绍

2.1.1 FreeRTOS目录介绍

2.1.2 portmacro.h移植文件

2.1.3 portISR.c移植文件

2.1.4 port.c移植文件

§2.2 FreeRTOS在CK510上的移植

2.2.1 CK510 CPU简介

2.2.2 移植思想

§2.3 FreeRTOS移植后记

第1章 FreeRTOS简介

嵌入式领域中,嵌入式实时操作系统正得到越来越广泛的应用。采用嵌入式实时操作系统(RTOS)可以更合理、更有效地利用CPU的资源,简化应用软件的设计,缩短系统开发时间,更好地保证系统的实时性和可靠性。由于RTOS需占用一定的系统资源(尤其是RAM资源),只有μC/OS-II、embOS、salvo、FreeRTOS等少数实时操作系统能在小RAM单片机上运行。相对于μC/OS-II、embOS等商业操作系统,FreeRTOS操作系统是完全免费的操作系统,具有源码公开、可移植、可裁减、调度策略灵活的特点,可以方便地移植到各种单片机上运行,其最新版本为2.6版。

作为一个轻量级的操作系统,FreeRTOS提供的功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能等,可基本满足较小系统的需要。FreeRTOS内核支持优先级调度算法,每个任务可根据重要程度的不同被赋予一定的优先级,CPU总是让处于就绪态的、优先级最高的任务先运行。FreeRT0S内核同时支持轮换调度算法,系统允许不同的任务使用相同的优先级,在没有更高优先级任务就绪的情况下,同一优先级的任务共享CPU的使用时间。
   FreeRTOS的内核可根据用户需要设置为可剥夺型内核或不可剥夺型内核。当FreeRTOS被设置为可剥夺型内核时,处于就绪态的高优先级任务能剥夺低优先级任务的CPU使用权,这样可保证系统满足实时性的要求;当FreeRTOS被设置为不可剥夺型内核时,处于就绪态的高优先级任务只有等当前运行任务主动释放CPU的使用权后才能获得运行,这样可提高CPU的运行效率。

在一个操作系统内部,内核[****kernel]****是最核心的部件。像Linux那样的操作系统使用的内核,从表面上看(seemingly),允许用户并发(simultaneously)访问 计算机。多个用户似乎(apparently)可以并行(concurrently)执行多个程序。

在操作系统的控制下,每个正在执行的程序就是一个任务[task]。如果一个操作系统能够以这种方法执行多个任务,这就叫做多任务[multitasking].

多任务操作系统的使用可以简化应用程序的设计:
1. 操作系统的多任务和任务间通信的机制允许复杂的应用程序被分成一系列更小的和更多的可以管理的任务。
2. (程序的)划分(partitioning)让软件测试更容易, 团队工作分解(work breakdown within teams),也有利于代码复用。
3. 复杂的定时和先后顺序的细节 可以从应用程序代码中 删除。(因为)这成为操作系统的职责。

传统的(conventional)的处理器同时只能执行一个任务。但通过快速的任务切换,一个多任务操作系统可以使它看起来(appear)好像每个任务并行执行一样。这可以下面的示意图来描述(depicted)。它显示了有关(with respect to)时间的3个任务的执行模式。任务名用颜色标注出来,写在左手边。时间从左到右增加,相应的颜色的线条 显示该任务在某个特殊时间正在执行。上面的图 演示的是用户所觉察到的并行执行模式,下面的图是实际的多任务执行模式。
在这里插入图片描述

所有可用的任务都好像在执行,但实际上在任何一个时刻都只有一个任务在执行

在这里插入图片描述

在这里插入图片描述

上图中提到的编号:
\1) Task1正在运行
\2) 内核挂起Task1
\3) 恢复任务Task2
\4) Task2正在执行,为独占访问(exclusive access),它锁定一个处理器外设
\5) 内核 挂起Task2
\6) 恢复Task3
\7) Task3试图访问同样的处理器外设,发现它被锁定,Task3不能继续,所以自己挂起自己。
\8) 内核恢复Task1
………….
\9) 接下来(the next time),Task2在9处执行。它完成了对处理器外设的访问,所以解锁它
\10) 再下来,Task3在10处执行。它发现 现在可以访问处理器外设了,于是开始执行,直到被内核挂起

第2章 移植FreeRTOS

§2.1 FreeRTOS移植代码介绍

2.1.1 FreeRTOS目录介绍

在FreeRTOS/Source/目录下,我们可以看到croutine.c、list.c、queue.c、tasks.c文件和portable、include文件夹。其中croutine.c、list.c、queue.c、tasks.c这四个文件是FreeRTOS的关键源代码文件,移植时不用作任何修改。

在FreeRTOS/Source/include/目录下为各关键源代码头文件,使用FreeRTOS插间时需包含相应头文件。

在FreeRTOS/Source/portable/目录下,我们可以看到有很多编译器的文件夹,这个目录下是在不同编译器下,不同CPU上的移植代码样例。每个样例目录下有三个关键的移植文件port.c、portISR.c、portmacro.h(不是所有的目录都必须严格地要有这三个文件)。

在FreeRTOS/Source/portable/MemMang/目录下,有heap_1.c、heap_2.c 、heap_3.c三个文件,这是内存管理代码,移植时只须使用其中一个即可。

在FreeRTOS/Demo/目录下是不同CPU上的移植工程,在每个目录下面有一个文件FreeRTOSConfig.h,这个文件是FreeRTOS的配置文件,如CPU时钟频率、系统Tick时钟频率、最大优先级、任务堆栈大小、任务名称长度等都由文件决定。所以,在配置系统时钟的驱动程序中,应按照FreeRTOSConfig.h文件中的配置来正确配置时钟。

2.1.2 portmacro.h****移植文件

上文说过,FreeRTOS/Source/portable/是移植相关的代码,所以要把FreeRTOS移植到一款新的CPU平台(FreeRTOS中没有现成的移植代码),要做的主要工作就是完成port.c、portISR.c、portmacro.h这三个文件中的功能函数,这三个文件中的功能函数与FreeRTOS源代码croutine.c、list.c、queue.c、tasks.c有调用和被调用关系,移植代码的好坏直接决定着系统的稳定性和功能的多样性。

打开FreeRTOS/Source/portable/GCC/ARM7_LPC2000/portmacro.h文件,这是ARM7TDMI CPU的一个移植文件(由FreeRTOS提供)。

#define portCHAR char

#define portFLOAT float

#define portDOUBLE double

#define portLONG ong

#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

以上是FreeRTOS所要用到的类型的定义。configUSE_16_BIT_TICKS在FreeRTOSConfig.h文件中定义的,若被移植的CPU是32位的,则configUSE_16_BIT_TICKS应该为0,为32位CPU则应该为1。

#define portSTACK_GROWTH ( -1 )

#define portTICK_RATE_MS ((portTickType) 1000 / configTICK_RATE_HZ )

#define portBYTE_ALIGNMENT 4

#define portNOP() asm volatile ( “NOP” );

portSTACK_GROWTH为堆栈的指针地址增长方式,为-1表示堆栈向低地址延伸,栈底在高地址,栈顶向低地址延伸。若堆栈栈低在低地址,栈顶向高地址延伸,则portSTACK_GROWTH应为1。至于该用哪种增长方式,由C编译器决定。CK510平台用的Ckcore-elf-gcc编译工具为满递减堆栈(堆栈指针总是指向栈顶,栈底在高地址),则portSTACK_GROWTH应为-1。

portBYTE_ALIGNMENT为代码对齐的字节数,对于16位代码长度的CK510平台,为2即可。

​ portSAVE_CONTEXT()和portRESTORE_CONTEXT()则是任务能够稳定切换的关键代码了。这两个用内嵌汇编写的宏分别实现当前任务CPU环境的保护和恢复当前任务的CPU环境,它们是互逆的过程。以下分别是portSAVE_CONTEXT()和portRESTORE_CONTEXT()的流程图:
在这里插入图片描述

​ portENTER_SWITCHING_ISR()和portEXIT_SWITCHING_ISR( SwitchRequired )是被FreeRTOS调用的系统通过调用这两段代码进入中断和退出中断。其中portEXIT_SWITCHING_ISR(SwitchRequired)带了参数,这个参数若非0,则表示在退出中断时执行任务的切换,否则,直接从中断中退出到原断点处继续执行。

portYIELD()代码是执行任务级切换的功能代码,由于程序一般运行在CPU的非特权模式下(在此模式下,有些CPU寄存器是不能操作的),所以一般采用一条软件中断指令(或称陷阱指令),在CPU进入特权模式的软件中断的服务程序中操作特权模式下的寄存器实现任务的切换。

portDISABLE_INTERRUPTS()和portENABLE_INTERRUPTS()分别是关闭CPU中断使能和打开CPU中断使能的代码。

portENTER_CRITICAL()和portEXIT_CRITICAL()分别为进入临界代码区和推出临界代码区的代码,可直接由portDISABLE_INTERRUPTS()和portENABLE_INTERRUPTS()分别替代。

2.1.3 portISR.c****移植文件

void vPortISRStartFirstTask( void )函数是在系统调用vTaskStartScheduler()时会执行此代码,它使用portRESTORE_CONTEXT()恢复当前任务。

void vPortYieldProcessor( void )实现任务级的切换,是portYIELD()的软件中断的服务程序。

void vPreemptiveTick( void )为系统Tick中断服务程序,在此中断服务程序中,会把系统时间计数器加1,然后依据调度算法,找出需要运行的任务,清除定时器中断标志,完成任务的切换。

2.1.4 port.c****移植文件

​ portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )函数在移植时需要修改一下,这是任务堆栈的初始化函数,这个函数返回值为初始化堆栈后的指向栈顶的指针,pxTopOfStack系统给该任务分配的堆栈栈低指针,pxCode为该任务的入口地址,pvParameters为系统传向任务的参数,

其中pxCode和pvParameters在创建任务时传进来的。

​ pxPortInitialiseStack函数内部初始化的堆栈方式与上文portSAVE_CONTEXT()一致,若堆栈为递增方式,则该函数中的pxTopOfStack–;应该改为pxTopOfStack++;若堆栈指针总是指向栈顶的最后一个有效字时,在“*pxTopOfStack = XXX;”之前应该执行“pxTopOfStack–;”或者“pxTopOfStack++;”,至于堆栈的这个方式,是由C编译器决定的。

​ 对于ADS1.2编译器,其参数传递遵循ATPCS传递规则,即CPU寄存器R0、R1、R2、R3分别用于传递函数的第一、二、三、四个参数。对于Ckcore-elf-gcc编译器,其规则如下:

在这里插入图片描述

所以,若要把FreeRTOS移植到ARM7TDMI平台上的并用ADS1.2编译器,则应把pvParameters传递给堆栈中存放R0的位置。若对于Ckcore-elf-gcc编译器和CK510平台,则应把pvParameters传递给堆栈中存放R2的位置。pxCode为该任务的入口地址,应该传递给存放程序计数器PC的位置。因为这些参数是最终由void vPortISRStartFirstTask( void )函数从堆栈中取出这些参数,然后传递给CPU寄存器的,所以存放顺序要与void vPortISRStartFirstTask( void )的取出顺序相对应,否则会导致系统崩溃。

§2.2 FreeRTOS在CK510上的移植

1.2.1 CK510 CPU简介

CK510是杭州中天微系统有限公司推出的一款面向嵌入式系统和SoC应用领域的32位高性能低功耗嵌入式CPU。该处理器有两种编程模型:

在这里插入图片描述

​ 普通用户模型下,程序只能访问的CPU寄存器有限,在超级用户模型下,程序可以访问所有的CPU寄存器。CPU可以通过中断进入超级用户模式,在CPU复位后,CPU也是在超级用户模式下。FreeRTOS在执行任务的切换,CPU中断的使能和禁止,应该在CPU的超级用户模式下操作,而在任务正常运行时,CPU是在普通用户模式下工作的。CK510有两组R0-R15寄存器,通过操作状态寄存器PSR中的AF位来选择CPU用哪一组寄存器。

在这里插入图片描述

S-超级用户模式设置位:

​ 当S为0时,处理器工作在普通用户模式。

​ 当S为1时,处理器工作在超级用户模式。

EE-异常有效控制位:

​ 当EE为0时,除了普通中断和快速中断外的所有异常一旦发生,都会被CK510认为为不可恢复异常。

​ 当EE为1时,异常有效,所有异常都会正常响应和使用EPSR和EPC。

IC-中断控制位:

​ 当IC为0时,中断只能在指令执行完成后被响应。

​ 当IC为1时,中断可在指令执行时被响应。

FE-快速中断有效控制位:

​ 当FE为0时,禁止快速中断。

​ 当FE为1时,使能快速中断,当发生快速中断时,短点处PC和PSR的值备分到FPC和FPSR中。

IE-普通中断有效控制位:

​ 当IE为0时,禁止普通中断。

​ 当IE为1时,使能普通中断,当发生普通中断时,短点处的PC和PSR的值会备分到EPC和EPSR中。

1.2.2 移植思想

系统复位后,进入Start.s文件中代码,完成中断向量表基地址、堆栈、CACHE和CPU时钟的初始化后,调用main函数,控制权交给了操作系统。在中断向量入口处,采用跳转指令,直接跳到相应处理程序中。

​ 为了能更好的操作寄存器,移植文件完全用汇编,所以我把portISR.c文件名改为portISR.S。

​ 要使系统能够在任务切换时不出差错,寄存器的入栈顺序要统一,本堆栈方式为满递减方式,压栈的顺序依次为R15、R14、R13、R12、R11、R10、R9、R8、R7、R6、R5、R4、R3、R2、R1、EPSR、EPC。并把堆栈指针R0存放在pxCurrentTCB->pxTopOfStack中。

请注意,我要没有保护PC和PSR而是保护EPSR和EPC,这是因为在保护CPU寄存器时,是在软中断中进行的,短点处的PC和PSR都被备分到EPSR和EPC中。

​ 操作系统只能使用普通中断和陷阱中断。

​ portYIELD()、portENTER_CRITICAL()、portEXIT_CRITICAL()使用不同的陷阱指令。

每个陷阱都有相应陷阱中断服务程序。

​ /保存当前任务CPU各寄存器/

​ .macro __portSAVE_CONTEXT

​ PSRSET EE

​ SUBI R0, 4*8

​ SUBI R0, 4*7

​ STM R1-R15, (R0) /当前任务的R1-R15压入当前任务堆栈/

​ SUBI R0 , 4*2

​ MFCR R15, EPSR

​ MFCR R14, EPC

​ STM R14-R15, (R0) /EPSR,EPC压入堆栈/

​ LRW R14, pxCurrentTCB

​ LD R14, (R14, 0)

​ ST R0 , (R14, 0) /存放当前任务堆栈指针R0至当前任务控制块中/

​ .endm

​ /恢复任务的CPU各寄存器/

​ .macro __portRESTORE_CONTEXT

​ PSRSET EE

​ LRW R1, pxCurrentTCB

​ LD R1, (R1,0)

​ LD R0, (R1,0) /从pxCurrentTCB中取出任务的堆栈指针R0/

​ LDM R14-R15, (R0)

​ MTCR R15, EPSR

​ MTCR R14, EPC /恢复EPC和EPSR,这是断点处的PC和PSR/

​ ADDI R0 , 4*2

​ LDM R1-R15, (R0) /恢复R1-R15/

​ ADDI R0 , 4*7

​ ADDI R0 , 4*8 /调整堆栈指针R0/

​ RTE /从陷阱指令中返回,完成后PC = EPC,PSR=EPSR/

​ .endm

​ /普通中断服务程序/

vNormalIsr:

​ __portSAVE_CONTEXT /保护当前任务的CPU环境至当前任务堆栈中/

​ LRW R2, OSIntNesting

​ LD R3, (R2, 0)

​ LRW R4, MAX_OS_INT_NESTING /若OSIntNesting小于MAX_OS_INT_NESTING,则OSIntNesting加1/

​ CMPHS R3, R4

​ BT 1f

​ ADDI R3, 1

​ ST R3, (R2, 0)

1:

​ LRW R1,NORMAL_STACK

​ MOV R0, R1 /使用普通中断的堆栈/

​ LRW R12, rINTC_NINT

​ LD R12,(R12, 0)

​ FF1 R12

​ MOVI R13,31

​ RSUB R12,R13

​ MOV R14, R12

​ LSLI R14, 2

​ LRW R10, NORMAL_ISR_TABLE

​ ADD R10, R14

​ LD R10, (R10,0) /依据rINTC_NINT算出地址偏移,从中断向量表中取出相应ISR入口地址/

​ JSR R10 /调用中断服务子程序/

​ LRW R10,vTaskSwitchContext

​ JSR R10 /调用void vTaskSwitchContext(void)函数,获得即将执行的任务的TCB/

​ LRW R2, OSIntNesting

​ LD R3, (R2, 0)

​ CMPNEI R3, 0

​ BF 2f /若OSIntNesting不等于0,则OSIntNesting减1/

​ SUBI R3, 1

​ ST R3, (R2, 0)

2:

​ __portRESTORE_CONTEXT /切换至新任务/

​ /任务的切换/

vPortYieldProcessor:

​ PSRSET EE /允许对EPC,EPSR重写,不产生不可恢复错误/

​ SUBI R0 , 4

​ ST R1 , (R0, 0)

​ MFCR R1 , EPC

​ ADDI R1 , 2

​ MTCR R1 , EPC /调整断点PC/

​ LD R1 , (R0, 0)

​ ADDI R0 , 4

​ __portSAVE_CONTEXT /保护当前任务的CPU环境/

​ LRW R10,vTaskSwitchContext

​ JSR R10 /调用void vTaskSwitchContext(void)函数,获得即将执行的任务的TCB/

​ __portRESTORE_CONTEXT /切换至新任务/

​ /快速中断服务程序/

vFastIsr:

​ PSRSET EE

​ PSRCLR IE

​ LRW R1 , FAST_STACK /由于快速中断使用可选择寄存器,所以不必要保存个寄存器/

​ MOV R0 , R1 /使用快速中断的堆栈/

​ LRW R12, rINTC_FINT

​ LD R12,(R12, 0)

​ FF1 R12

​ MOVI R13,31

​ RSUB R12,R13

​ MOV R14, R12

​ LSLI R14, 2

​ LRW R10, FAST_ISR_TABLE

​ ADD R10, R14

​ LD R10, (R10,0)

​ JSR R10 /调用相应中断服务程序/

​ RFI /从快速中断中返回/

​ /进入临界代码区,关闭普通中断和快速中断/

vPortEnterCritical:

​ PSRSET EE

​ SUBI R0 , 4*2

​ ST R1 , (R0, 0)

​ ST R2 , (R0, 4)

​ MFCR R1 , EPSR

​ MOVI R2 , 0x50

​ ANDN R1 , R2

​ MTCR R1 , EPSR

​ MFCR R1 , EPC

​ ADDI R1 , 2

​ MTCR R1 , EPC /调整PC/

​ LD R2 , (R0, 4)

​ LD R1 , (R0, 0)

​ ADDI R0 , 4*2

​ RTE

​ /退出临界代码区,打开普通中断和快速中断/

vPortExitCritical:

​ SUBI R0, 4*4

​ STM R12-R15, (R0)

​ PSRSET EE

​ MFCR R12, EPSR

​ MOVI R13, 0x50

​ OR R12, R13

​ MTCR R12, EPSR

​ MFCR R12, EPC

​ ADDI R12, 2

​ MTCR R12, EPC /调整PC/

​ LDM R12-R15, (R0)

​ ADDI R0, 4*4

​ RTE

§2.3 FreeRTOS移植后记

移植一款RTOS并没有我们想象的那么难,我们若学会一种RTOS的移植,其他的都是大同小异的,移植起来也就很顺手。若要移植一款OS到一个新的CPU上,需要具备以下条件:

1.对要移植的OS有一定的了解,尤其是底层与任务切换、中断处理相关的代码要非常清楚。

2.对CPU寄存器、CPU工作模式、编译器也要有一定的了解。如C编译器的堆栈是采用什么压栈方式,参数是怎样传递的,汇编与C的的接口等。另外对CPU汇编要有一定的了解,最好能熟记,它直接影响你写移植代码和调试的速度。

3.信心。操作系统也是人写的,别人能把他给写出来,自然你也有能力移植。

​ STM R12-R15, (R0)

​ PSRSET EE

​ MFCR R12, EPSR

​ MOVI R13, 0x50

​ OR R12, R13

​ MTCR R12, EPSR

​ MFCR R12, EPC

​ ADDI R12, 2

​ MTCR R12, EPC /调整PC/

​ LDM R12-R15, (R0)

​ ADDI R0, 4*4

​ RTE

§2.3 FreeRTOS移植后记

移植一款RTOS并没有我们想象的那么难,我们若学会一种RTOS的移植,其他的都是大同小异的,移植起来也就很顺手。若要移植一款OS到一个新的CPU上,需要具备以下条件:

1.对要移植的OS有一定的了解,尤其是底层与任务切换、中断处理相关的代码要非常清楚。

2.对CPU寄存器、CPU工作模式、编译器也要有一定的了解。如C编译器的堆栈是采用什么压栈方式,参数是怎样传递的,汇编与C的的接口等。另外对CPU汇编要有一定的了解,最好能熟记,它直接影响你写移植代码和调试的速度。

3.信心。操作系统也是人写的,别人能把他给写出来,自然你也有能力移植。

文章来源: blog.csdn.net,作者:悟空胆好小,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/xushx_bigbear/article/details/126910569

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。