返回列表 发帖

FreeRTOS 在STM32的移植

FreeRTOS 在STM32的移植

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 portCHAR  char
#define portFLOAT  float
#define portDOUBLE  double
#define portLONG  long
#define portSHORT  short
#define portSTACK_TYPE unsigned portLONG
#define portBASE_TYPE long
2)架构相关的定义
Cortex-M3的堆栈增长方向为高地址向低地址增长
#define portSTACK_GROWTH ( -1 )
每毫秒的心跳次数
#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ )
访问SRAM的字节对齐
#define portBYTE_ALIGNMENT 8
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  
ldr r3, =pxCurrentTCB 获取当前任务的任务控制块指针
ldr r2, [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;
ldr r3, =pxCurrentTCB
ldr r1, [r3]
ldr r0, [r1]
ldmia r0!, {r4-r11}
msr psp, r0
mov r0, #0
msr basepri, 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上刷新一个数字。如下图:

至此,FreeRTOS在STM32上的移植基本完成。与UC/OSII相比,FreeRTOS精简的实现更适合用来学习实时操作系统的工作原理,对其进行剖析也相对容易。

本帖隐藏的内容需要回复才可以浏览
附件: 您需要登录才可以下载或查看附件。没有帐号?本站只开放邀请码注册,QQ:82475491,索要邀请码
分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

站长同志,我想问一下这样讨论问题不累吗?
回复个贴都不容易,还要一大堆的认证

TOP

学习
同意loushang

TOP

原帖由 wenxusun 于 2009-1-12 23:25 发表
站长同志,我想问一下这样讨论问题不累吗?
回复个贴都不容易,还要一大堆的认证


恩,是挺累的,不过我们设置的是一定级别就不用这么认证了,因为如果不这样做,垃圾帖子就太多了,这样会影响环境。因为mcuos还不像那些前辈大牛网站,管理的人多,我们还不到完全开放的那个等级。

TOP

谢谢

谢谢正需要,正在学习分析RTOS代码

TOP

謝謝提供分享。下載學習研究。

TOP

谢谢,正在学习分析RTOS代码!!

TOP

呵呵,认证,吸引人气呗

TOP

谢谢正需要,正在学习分析RTOS代码

TOP

学习中!!!!!!!!!!

TOP

谢谢提供 学习一下!!!!!!!!

TOP

我是初學者
感謝提供

TOP

学习........................

TOP

看卡内容,哈哈哈,学习中。。。。。

TOP

看看不见的东东
看看不见的东东
看看不见的东东
看看不见的东东

TOP

哈哈

謝謝謝謝

謝謝謝謝
謝謝謝謝

謝謝謝謝

TOP

正在学习分析RTOS代码
謝謝!!!

TOP

看看看看看看看看看

TOP

正在学习分析RTOS代码

TOP

回复 1# osboy 的帖子

TOP

不错,学习了

TOP

不错,正在学习呢

TOP

标记下

TOP

学习下。
附件: 您需要登录才可以下载或查看附件。没有帐号?本站只开放邀请码注册,QQ:82475491,索要邀请码

TOP

good!顶下

TOP

學習依下

TOP

不错,学习了

TOP

怎样增加积分啊?

TOP

看看。注册就可以看,不要回复!

TOP

看看是什么?

TOP

返回列表
网页右侧QQ悬浮滚动在线客服
网页右侧QQ悬浮滚动在线客服