FreeRTOS源码分析-代码结构

2023-05-16

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内核调度所需的底层接口,相关代码十分精简。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS源码分析-代码结构 的相关文章

随机推荐

  • TX2核心板安装OpenCV3.2(在cuda9.0的环境下)

    今天新到的TX2 xff0c 还有点烫手 xff0c 买来要用在无人机上做视觉的目标识别 xff0c 所以自然要装上OpenCV喽 xff01 TX2核心板买来就自带了cuda9 0 xff0c 据说这个和opencv3不太搭 xff0c
  • c语言面试题 指针30个常错题型

    1 char const p char const p const char p 上述三个有什么区别 xff1f char const p 常量指针 xff0c p的值不可以修改 char const p xff1b 指向常量的指针 xff
  • c++ 笔试面试题 难题精选 持续更新

    第一题 问下面的输出结果是 什么 xff1f include lt stdio h gt include lt iostream gt using namespace std class A protected int m data pub
  • VS2010 添加OnInitDialog的方法

    OnInitDialog 在vs2010中实现为虚函数 所以在 项目 gt 类向导 gt 虚函数 gt 选中要添加的类 xff0c 找到对应虚函数添加即可 就这么简单
  • HBITMAP与BITMAP 的区别 BMP图像的格式

    HBITMAP 是句柄 xff1b BITMAP 是实例 xff1a typedef struct tagBITMAP bm int bmType 必须是BM int bmWidth 指定位图的宽度 xff08 以象素为单位 xff09 i
  • fatal error LNK1281: 无法生成 SAFESEH 映像。

    解决方法 xff1a 1 打开该项目的 属性页 对话框 2 单击 链接器 文件夹 3 单击 命令行 属性页 4 将 SAFESEH NO 键入 附加选项 框中 xff0c 然后点击应用
  • 如何实现科技论文里面的算法

    这是一篇关于如何实现科研论文中算法的简要指南 作者曾实现过很多书本上和科研论文中的复杂算法 xff0c 在这篇文章中作者总结他在研究 xff0c 阅读 xff0c 编码和调试时积累的大量经验 很显然 xff0c 这篇文章主要集中在和计算机科
  • 程序员专用经典语录—看完笑一阵可以,千万不要死循环哦!

    IT人表示屁股上还得纹一个 lt body gt 要不中间来个hello world 真正的程序员喜欢兼卖爆米花 xff0c 他们利用CPU散发出的热量做爆米花 xff0c 可以根据米花 爆裂的速度听出正在运行什么程序 十年生死两茫茫 xf
  • android 自学中的散乱笔记

    1 查看程序运行记录 要在LogCat中查看 其内可选择查看的信息级别 xff0c 比如info xff0c error xff0c debug等等 xff0c 信息可筛选显示 2 xff1a 安装好手机驱动 xff0c 将手机接入usb即
  • java lambda表达式 闭包学习笔记

    我们把这些只拥有一个方法的接口称为函数式接口 声明一个接口是函数式接口 xff1a 编译器会根据接口的结构自行判断 xff08 判断过程并非简单的对接口方法计数 xff1a 一个接口可能冗余的定义了一个Object已经提供的方法 xff0c
  • C++ STL set和multiset的使用 hunst_xiehonghao 总结

    C 43 43 STL set和multiset的使用 std set lt int gt s 那个s这个对象里面存贮的元素是从小到大 排序的 xff0c 因为用std less作为比较工具 1 xff0c set的含义是集合 xff0c
  • 数据结构之车厢调度 思路很重要

    问题描述 假设停在铁路调度站入口处的车厢序列的编号依次为1 xff0c 2 xff0c 3 N 设计一个程序 xff0c 求出所有由此输出的长度为N的车厢序列 要求3节车厢调度的方法 xff0c 1代表进栈 xff0c 0代表出栈 有几点要
  • 判断N 数码是否有解 牛人总结 归并排序

    作者 力的博客 先介绍八数码问题 xff1a 我们首先从经典的八数码问题入手 xff0c 即对于八数码问题的任意一个排列是否有解 xff1f 有解的条件是什么 xff1f 我在网上搜了半天 xff0c 找到一个十分简洁的结论 八数码问题原始
  • 爬虫技术概览

    爬虫介绍 简介 自动化 半自动化从互联网上采集数据的程序 爬虫框架 一个简单的爬虫框架 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 img 7ifdLU0V 1625273924704 http m qpic cn
  • 怎样在github上发布pre-release和release?

    请参考 xff1a https jingyan baidu com album ad310e80e0c1d11848f49e7f html picindex 61 2 以上是通过网页进行操作的 xff1b 能通过GUI进行操作吗 xff1f
  • git fatal: unrecognized input ^[

    原因是安装了一个vim的插件NREDTREE xff0c 另外需要退出当前用户然后重新登录
  • keil main之前执行过程

    硬件复位后 第一步是执行复位处理程序 这个程序的入口在启动代码里 默认 摘录一段cortex m3的复位处理入口代码 Reset Handler PROC PROC等同于FUNCTION 表示一个函数的开始 与ENDP相对 EXPORT R
  • 蓝牙物理链路类型:SCO和ACL链路与A2DP

    转载于https blog csdn net wenzongliang article details 84689377 蓝牙链路分两种同步链路 SCO 和异步链路 xff08 ACL xff09 A2DP xff08 Advanced A
  • FreeRTOS源码分析-异常处理

    asm void vPortSVCHandler void PRESERVE8 ldr r3 61 pxCurrentTCB Restore the context ldr r1 r3 Use pxCurrentTCBConst to ge
  • FreeRTOS源码分析-代码结构

    FreeRTOS作为开源的轻量级实时性操作系统 xff0c 不仅实现了基本的实时调度 信号量 队列和存储管理 xff0c 而且在商业应用上不需要授权费 FreeRTOS的实现主要由list c queue c croutine c和task