您的位置 首页 编程

FreeRTOS 在STM32上的移植 V1.0

FreeRTOS作为开源的轻量级实时性操作系统,不仅实现了基本的实时调度、信号量、队列和存储管理,而且在商业应用上不需要授权费。FreeRTOS…

FreeRTOS作为开源的轻量级实时性操作体系,不只完结了根本的实时调度、信号量、行列和存储办理,而且在商业应用上不需求授权费。

FreeRTOS的完结首要由list.c、queue.c、croutine.c和tasks.c 4个文件组成。list.c 是一个链表的完结,首要供应内核调度器运用;queue.c 是一个行列的完结,支撑中止环境和信号量操控;croutine.c 和task.c是两种使命的安排完结。关于croutine,各使命同享同一个仓库,使RAM的需求进一步缩小,但也正因如此,他的运用遭到相对严厉的约束。而task则是传统的完结,各使命运用各自的仓库,支撑彻底的抢占式调度。

FreeRTOS的首要功能能够归结为以下几点:
1)优先级调度、相同优先级使命的轮转调度,一起可设成可掠夺内核或不行掠夺内核
2)使命可选择是否同享仓库(co-routines & tasks),而且没有使命数约束
3)音讯行列,二值信号量,计数信号量,递归互斥体
4)时刻办理
5)内存办理

与UC/OSII相同,FreeRTOS在STM32移植大致由3个文件完结,一个.h文件界说编译器相关的数据类型和中止处理的宏界说;一个.c文件完结使命的仓库初始化、体系心跳的办理和使命切换的恳求;一个.s文件完结详细的使命切换。

在本次移植中,运用的编译软件为IAR EWARM 5.2。
一、各文件要害部分的完结:

1、PORTMACRO.H 宏界说部分
1)界说编译器相关的各种数据类型
#define portCHARchar
#define portFLOATfloat
#define portDOUBLEdouble
#define portLONGlong
#define portSHORTshort
#define portSTACK_TYPEunsigned portLONG
#define portBASE_TYPElong

2)架构相关的界说
Cortex-M3的仓库增加方向为高地址向低地址增加
#define portSTACK_GROWTH( -1 )
每毫秒的心跳次数
#define portTICK_RATE_MS( ( portTickType ) 1000 / configTICK_RATE_HZ )
拜访SRAM的字节对齐
#define portBYTE_ALIGNMENT8

3)界说用户自动引起内核调度的2个函数
强制上下文切换,用在使命环境中调用
#define portYIELD()vPortYieldFromISR()
强制上下文切换,用在中止处理环境中调用
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) vPortYieldFromISR()

4)界说临界区的办理函数
中止答应和封闭
#define portDISABLE_INTERRUPTS()vPortSetInterruptMask()
#define portENABLE_INTERRUPTS()vPortClearInterruptMask()
临界区进入和退出
#define portENTER_CRITICAL()vPortEnterCritical()
#define portEXIT_CRITICAL()vPortExitCritical()
用于在中止环境的中止答应和封闭
#define portSET_INTERRUPT_MASK_FROM_ISR()0;vPortSetInterruptMask()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)vPortClearInterruptMask();(void)x

2、PORT.C C接口部分
1)仓库初始化
portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )
{
*pxTopOfStack = portINITIAL_XPSR;/* 程序状况寄存器 */
pxTopOfStack–;
*pxTopOfStack = ( portSTACK_TYPE ) pxCode;/* 使命的进口点 */
pxTopOfStack–;
*pxTopOfStack = 0;/* LR */
pxTopOfStack -= 5;/* R12, R3, R2 and R1. */
*pxTopOfStack = ( portSTACK_TYPE ) pvParameters;/* 使命的参数 */
pxTopOfStack -= 8;/* R11, R10, R9, R8, R7, R6, R5 and R4. */
return pxTopOfStack;
}

2)发动使命调度
portBASE_TYPE xPortStartScheduler( void )
{
让使命切换中止和心跳中止坐落最低的优先级,使更高优先级能够抢占mcu
*(portNVIC_SYSPRI2) |= portNVIC_PENDSV_PRI;
*(portNVIC_SYSPRI2) |= portNVIC_SYSTICK_PRI;

设置并发动体系的心跳时钟
prvSetupTimerInterrupt();

初始化临界区的嵌套的个数
uxCriticalNesting = 0;

发动第一个使命
vPortStartFirstTask();

履行到vPortStartFirstTask函数,内核现已开端正常的调度
return 0;
}

3)自动开释mcu运用权
void vPortYieldFromISR( void )
{
触发PendSV体系服务中止,中止到来时由汇编函数xPortPendSVHandler()处理
*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;
}
进入临界区时,首要封闭中止;当退出所以嵌套的临界区后再使能中止
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
}
void vPortExitCritical( void )
{
uxCriticalNesting–;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
4)心跳时钟处理函数
void xPortSysTickHandler( void )
{
unsigned portLONG ulDummy;

如果是抢占式调度,首要看一下有没有需求调度的使命
#if configUSE_PREEMPTION == 1
*(portNVIC_INT_CTRL) = portNVIC_PENDSVSET;
#endif

ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();
{ 经过task.c的心跳处理函数vTaskIncrementTick(),进行时钟计数和延时使命的处理
vTaskIncrementTick();
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );
}

3、PORTASM.S 汇编处理部分
1)恳求切换使命
xPortPendSVHandler:
保存当前使命的上下文到其使命操控块
mrs r0, psp
ldrr3, =pxCurrentTCB获取当前使命的使命操控块指针
ldrr2, [r3]

stmdb r0!, {r4-r11}保存R4-R11到该使命的仓库
str r0, [r2]将最终的仓库指针保存到使命操控块的pxTopOfStack

stmdb sp!, {r3, r14}
封闭中止
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0
切换使命的上下文,pxCurrentTCB已指向新的使命

bl vTaskSwitchContext
mov r0, #0
msr basepri, r0
ldmia sp!, {r3, r14}
康复新使命的上下文到各寄存器
ldr r1, [r3]
ldr r0, [r1]/* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0!, {r4-r11}/* Pop the registers. */
msr psp, r0
bx r14
使命切换的示意图如下:

2.)中止答应和封闭的完结,经过BASEPRI屏蔽相应优先级的中止源
vPortSetInterruptMask:
push { r0 }
mov R0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr BASEPRI, R0
pop { R0 }

bx r14

vPortClearInterruptMask:
PUSH { r0 }
MOV R0, #0
MSR BASEPRI, R0
POP { R0 }

bx r14

3)直接切换使命,用于vPortStartFirstTask第一次发动使命时初始化仓库和各寄存器
vPortSVCHandler;
ldrr3, =pxCurrentTCB
ldr r1, [r3]
ldr r0, [r1]
ldmia r0!, {r4-r11}
msr psp, r0
mov r0, #0
msrbasepri, r0
orr r14, r14, #13
bx r14

4)发动第一个使命的汇编完结
vPortStartFirstTask
经过中止向量表的定位仓库的地址
ldr r0, =0xE000ED08向量表偏移量寄存器 (VTOR)
ldr r0, [r0]
ldr r0, [r0]
msr msp, r0将仓库地址保存到主仓库指针msp中
触发SVC软中止,由vPortSVCHandler()完结第一个使命的详细切换作业
svc 0

FreeRTOS内核调度器发动的流程如下:

以上3个文件完结了FreeRTOS内核调度所需的底层接口,相关代码非常精简。

二、创立测验使命:
下面创立第一个测验使命,LED测验
int main( void )
{
设置体系时钟,中止向量表和LED运用的GPIO
运用stm32的固件包供给的初始化函数,详细阐明见相关手册
prvSetupHardware();

经过xTaskCreate()创立4个LED使命vLEDFlashTask(),
每个使命依据各自的频率闪耀,别离对应开发板上的4个LED
vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY );

•创立一个IDLE使命后,经过xPortStartScheduler发动调度器
vTaskStartScheduler();

调度器作业不正常时回来
return 0;
}

portTASK_FUNCTION()是FreeRTOS界说的函数声明,没特殊作用
static portTASK_FUNCTION( vLEDFlashTask, pvParameters )
{
……省掉……,详细为核算各LED的闪耀频率
for(;;)
{
vTaskDelayUntil( &xLastFlashTime, xFlashRate );
vParTestToggleLED( uxLED );

vTaskDelayUntil()的延时时刻xFlashRate,是从上一次的延时时刻xLastFlashTime算起的,
相对vTaskDelay()的直接延时更为精准。
vTaskDelayUntil( &xLastFlashTime, xFlashRate );
vParTestToggleLED( uxLED );
}
}

FreeRTOS的使命创立与UC/OSII差异不大,首要参数为使命函数,仓库巨细和使命的优先级。如:
xTaskCreate( vLEDFlashTask, ( signed portCHAR * ) “LEDx”, ledSTACK_SIZE, NULL, uxPriority, ( xTaskHandle * ) NULL );

下面再创立一个LCD显现使命,以最低优先级运转:
xTaskCreate( vLCDTask, ( signed portCHAR * ) “LCD”, configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );

void vLCDTask( void *pvParameters )
{
……省掉……
for( ;; )
{
vTaskDelay(1000);
printf(“%c “, usDisplayChar);
}
}
该使命很简单,每隔1000个ticks(便是1000ms),从LCD上改写一个数字。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/fangan/biancheng/268265.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部