微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > FreeRTOS 在STM32上的移植 V1.0

FreeRTOS 在STM32上的移植 V1.0

时间:11-17 来源:互联网 点击:

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上刷新一个数字。

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top