完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
本帖最后由 时飞大师兄 于 2019-10-20 17:09 编辑
嵌入式实时操作系统tiniOS设计与实现 名称:TiniOS - Tiny and efficient IoT operating system for microcontrollers (RTOS) 芯片MCU:ARM Coterx-M3内核与MCS-51 8051内核,具体型号选用STM32F1系列与STC8系列。 开发平台:Keil v5 与 Code::Blocks+SDCC 代码许可:遵循MIT开源许可协议,可以免费在商业产品中使用,不需要公布应用源码,没有任何潜在商业风险。 源码托管网址:tinios.com 以微型嵌入式实时操作系统TiniOS V2.1.0为基础,逐步展开对系统的讲解,揭开嵌入式实时操作系统的神秘面纱。在讲解的过程中,我们尽量做到避免芯片型号之间的差异性,力争做到同系列芯片下的普适性。 欢迎论坛的朋友们加入,一起开发、完善。当然,也欢迎朋友们来撕、来踩……
|
|
相关推荐
|
|
本帖最后由 时飞大师兄 于 2017-2-25 15:20 编辑
为了方便嵌入式操作系统的跨平台移植,我们先为操作系统定义一些必要的数据类型,同时这些数据类型要尽量避免与用户应用程序的数据类型相冲突。 在此我们给系统定义基本的数据类型如下: typedef unsigned char uOS8_t; typedef char sOS8_t; typedef unsigned short uOS16_t; typedef signed short sOS16_t; typedef unsigned int uOS32_t; typedef signed int sOS32_t; typedef uOS32_t uOSStack_t; typedef sOS32_t sOSBase_t; typedef uOS32_t uOSBase_t; typedef uOS32_t uOSTick_t; typedef enum {OS_FALSE = 0, OS_TRUE = !OS_FALSE} uOSBool_t; typedef enum {OS_SUCESS = 0, OS_ERROR = !OS_SUCESS} uOSStatus_t; 数据类型中的下划线 _t代表Type 下面我们定义一些和具体平台相关的一些参数,也就是和STM32F1相关的参数 #define FITSTACK_GROWTH ( -1 ) #define FITBYTE_ALIGNMENT ( 8 ) 因为是和平台相关的,我们定义相关参数的前缀为FIT,代表需要根据不同的芯片类型进行调整。用此前缀也便于提醒用户,在进行系统移植时,一定要注意带有前缀fit相关的参数、变量和函数; 上述宏定义的参数中FITSTACK_GROWTH 代表栈增长的方向, 我们用-1代表从高位置向低位置增长,用1代表从低位置向高位值增长;宏定义参数FITBYTE_ALIGNMENT代表数据对齐方式;在Cortex-M3系列的STM32F1芯片中,栈增长方向为从高到低,我们定义为-1,芯片为32位宽的,我们定义8字节对齐;这两个参数对嵌入式操作系统的内存布局影响非常大,后面我们会具体分析! |
|
|
|
|
|
嵌入式实时操作系统一般都运行在资源非常有限的芯片上,对内存管理要求比较严格,若使用方式比较粗放,很可能导致内存紧张。在此,我们先提出对内存管理的几点要求,后面再按照这个要求逐步实现。有兴趣的朋友也可以一起思考,共同实现!
对内存管理的相关要求如下: 1、在嵌入式操作系统未使用之前,为其分配的为一块连续的内存空间; 2、该内存空间可以由用户自由指定,即内存的起始位置可以进行配置,方便应用开发,也便于对内存的安全管理; 3、在用户使用时,可以从连续的内存块中自由取出指定大小的内存区域,以供专门的使用,比如为系统任务、消息队列、信号量、互斥锁等分配内存空间; 4、在用户释放内存空间后(如任务生命期结束,释放该任务占用的内存空间),可以由系统进行回收。若出现内存碎片,则相邻的内存碎片可以合并成一个较大的内存区域,供下次内存分配使用; 内存管理永远是一个操作系统中比较核心的内容,即使考虑的再周到也难以避免内存碎片的问题。尤其是动态内存的频繁分配与释放,往往会导致某些“破碎”的内存不可再次使用,尤其是嵌入式实时操作系统,内存资源更加宝贵。因此,在使用时,我们尽量把内存使用方式静态化——内存专用,即分给某一任务、消息队列、信号量或者互斥锁等的内存尽量固定下来,避免对内存的频繁分配与释放; |
|
|
|
|
|
感谢 王栋春 前来捧场
|
|
|
|
|
|
嵌入式实时操作系统一般都运行在资源非常有限的芯片上,对内存管理要求比较严格,若使用方式比较粗放,很可能导致内存紧张。在此,我们先提出对内存管理的几点要求,后面再按照这个要求逐步实现。有兴趣的朋友也可以一起思考,共同实现!
对内存管理的相关要求如下: 1、在嵌入式操作系统未使用之前,为其分配的为一块连续的内存空间; 2、该内存空间可以由用户自由指定,即内存的起始位置可以进行配置,方便应用开发,也便于对内存的安全管理; 3、在用户使用时,可以从连续的内存块中自由取出指定大小的内存区域,以供专门的使用,比如为系统任务、消息队列、信号量、互斥锁等分配内存空间; 4、在用户释放内存空间后(如任务生命期结束,释放该任务占用的内存空间),可以由系统进行回收。若出现内存碎片,则相邻的内存碎片可以合并成一个较大的内存区域,供下次内存分配使用; 内存管理永远是一个操作系统中比较核心的内容,即使考虑的再周到也难以避免内存碎片的问题。尤其是动态内存的频繁分配与释放,往往会导致某些“破碎”的内存不可再次使用,尤其是嵌入式实时操作系统,内存资源更加宝贵。因此,在使用时,我们尽量把内存使用方式静态化——内存专用,即分给某一任务、消息队列、信号量或者互斥锁等的内存尽量固定下来,避免对内存的频繁分配与释放; |
|
|
|
|
|
为什么有些回复还需要审核呢?而且回复过之后好久都没有出现在帖子下面呢?
|
|
|
|
|
|
嵌入式实时操作系统一般都运行在资源非常有限的芯片上,对内存管理要求比较严格,若使用方式比较粗放,很可能导致内存紧张。在此,我们先提出对内存管理的几点要求,后面再按照这个要求逐步实现。有兴趣的朋友也可以一起思考,共同实现!
对内存管理的相关要求如下: 1、在嵌入式操作系统未使用之前,为其分配的为一块连续的内存空间; 2、该内存空间可以由用户自由指定,即内存的起始位置可以进行配置,方便应用开发,也便于对内存的安全管理; 3、在用户使用时,可以从连续的内存块中自由取出指定大小的内存区域,以供专门的使用,比如为系统任务、消息队列、信号量、互斥锁等分配内存空间; 4、在用户释放内存空间后(如任务生命期结束,释放该任务占用的内存空间),可以由系统进行回收。若出现内存碎片,则相邻的内存碎片可以合并成一个较大的内存区域,供下次内存分配使用; 内存管理永远是一个操作系统中比较核心的内容,即使考虑的再周到也难以避免内存碎片的问题。尤其是动态内存的频繁分配与释放,往往会导致某些“破碎”的内存不可再次使用,尤其是嵌入式实时操作系统,内存资源更加宝贵。因此,在使用时,我们尽量把内存使用方式静态化——内存专用,即分给某一任务、消息队列、信号量或者互斥锁等的内存尽量固定下来,避免对内存的频繁分配与释放; |
|
|
|
|
|
先贴个图吧,图中的内容回复后,怎么也显示不出来
|
|
|
|
|
|
一些朋友看到我上面的描述可能会头晕目眩,不知所云。犹如苏轼那首经典古诗——题西林壁描述的一样:横看成岭侧成峰,远近高低各不同。不识庐山真面目,只缘身在此山中。这时,朋友们需要跳出这个圈子,从整体上对系统的内存管理进行重新认识。下面我们从整体上给内存管理功能“画一个轮廓”。 在刚刚为系统分配内存区域之后,这块内存区域一定是一个连续的空间。为了便于管理,我们在这块连续的内存空间中插入内存块信息头,记录内存块的基本信息:即前面提到的与该内存块相邻的前一块内存地址,后一块内存地址,当前内存块是否已经被分配使用等等;下图即为刚刚分配完的内存分布示意图; 图示:刚刚分配完毕时的内存分布示意图 为了便于管理,我们采用全局变量gpOSMemBegin记录第一块内存地址,用全局变量gpOSMemEnd记录最后一块的内存地址;同时为了方便更快速的分配内存,我们用全局变量gpOSMemLFree记录第一块未被分配使用的内存地址; 从上图中我们可以看出,对于刚刚分配好的内存区域,我们对其初始化为两个内存块;第一个内存块标记为未被使用的内存块,信息参数Used设置为0。其信息参数NextMem初始化为OSMEM_SIZE_ALIGNED,即指向最后一个内存块信息头。因为其自身就是第一个内存块,前面已经没有可用内存块了,我们把其参数PrevMem初始化为0。我们把第二个内存块设置为最后一个内存块,永远不能被系统分配使用,仅仅用其记录内存区域的尾部,因此其信息参数Used设置为1,表示已经使用,无法被系统再次分配使用。该内存块的信息参数NextMem与PrevMem均初始化为OSMEM_SIZE_ALIGNED,即永远指向自己。 至此,系统的内存区域已经初始化完毕了,下面请朋友们猜想一下,系统具体是如何分配使用内存的呢?没错,从第一个内存块切分!是的,系统若需要使用某一大小的内存块,则需要从上述初始化完毕的第一个内存块中切分出一块使用。为了方便管理,系统需要把切分出的内存块信息记录下来,并标记上已经在使用了;若系统运行一段时间后,某一块具体内存块不再需要了,就需要把该内存块释放掉。所谓内存释放也比较简单,只需要把内存块的使用标志清除掉即可。为了便于检测,在清除内存时,也可以把内存区设置为默认值。 图示:系统运行一段时间之后的内存分布示意图 上图表示运行一段时间之后系统的内存分布示意图;上图为了画图方便,指针的指示位置有所出入;在实际使用中,内存块信息参数中的NextMem指向下一个内存块信息的首地址,内存块信息参数PrevMem指向前一个内存块信息的首地址。 为了防止内存出现碎片的情况,我们在内存释放时还需要对相邻的前后内存使用情况进行检测,若相邻区域的内存块未使用,则需要把这两个相邻的未被使用的内存块合并起来; |
|
|
|
|
|
描述完毕“内存管理”的整个轮廓之后,我们来查看具体的内存管理函数是怎么实现的;
首先我们查看一下内存初始化函数OSMemInit,这个函数很简单,主要为全局变量gpOSMemBegin,gpOSMemEnd与gpOSMemLFree分配初始数值;
接着是内存分配函数。
内存释放函数。
代码已经更新到Github上了,感兴趣的朋友请移步到Github查看更多源代码。 到现在,我们已经实现了上面提到的内存管理方面的第3和第4两项要求了。这样,嵌入式实时操作系统AIOS的内存管理已经基本实现了; 由于大部分的微控制器芯片资源非常有限,部分嵌入式操作系统在设计时没有单独的内存管理模块,而是在使用时临时分配。例如ucos嵌入式操作系统,在创建任务时,总是通过一个数组的形式分配一段内存空间,然后把数组的首地址及长度传递给待创建的任务使用。这样相当于把内存管理分散化,但是如果要整体设置系统的内存位置就不那么方便了。 |
|
|
|
|
|
内存管理已经基本完成了,在进行下一步开发之前,我们先熟悉一下ARM汇编编程规则:
1. 基本概念 • ATPCS (ARM-Thumb Procedure Call Standard) 规定了一些子程序间调用的基本规则,这些规则包括子程序调用过程中寄存器的使用规则,数据栈的使用规则,参数的传递规则。有了这些规则之后,单独编译的C语言程序就可以和汇编程序相互调用。 使用ADS的C语言编译器编译的C语言子程序满足用户指定的ATPCS类型。而对于汇编语言来说,则需要用户来保证各个子程序满足ATPCS的要求。 • AAPCS (ARM Archtecture Procedure Call Standard) 2007年ARM公司正式推出了AAPCS标准,AAPCS是ATPCS的改进版,目前, AAPCS和ATPCS都是可用的标准。 2. 寄存器使用规则 • 子程序间通过寄存器R0~R3来传递参数。这时,寄存器R0~R3可记作a0~a3。被调用的子程序在返回前无需恢复寄存器R0~R3的内容。 • 在子程序中,使用寄存器R4~R11来保存局部变量。这时,寄存器R4~R11可以记作v1~v8。如果在子程序中使用了寄存器v1~v8中的某些寄存器,则子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值。在Thumb程序中,通常只能使用寄存器R4~R7来保存局部变量。 • 寄存器R12用作过程调用中间临时寄存器,记作IP。在子程序之间的连接代码段中常常有这种使用规则。 • 寄存器R13用作堆栈指针,记作SP。在子程序中寄存器R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序时的值必须相等。 • 寄存器R14称为连接寄存器,记作LR。它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器R14则可以用作其他用途。 • 寄存器R15是程序计数器,记作PC。它不能用作其它用途。 3. 堆栈使用规则 • ATPCS规定堆栈为FD(Full Descending: sp指向最后一个压入的值,数据栈由高地址向低地址生长)类型,即满递减堆栈,并且对堆栈的操作是8字节对齐。所以经常使用的指令就有STMFD和LDMFD。 •对于汇编程序来说,如果目标文件中包含了外部调用,则必须满足下列条件: (1)外部接口的堆栈必须是8字节对齐的。 (2)在汇编程序中使用PRESERVE8伪指令告诉连接器,本汇编程序数据是8字节对齐的。 4. 参数传递规则 • 根据参数个数是否固定,可以将子程序分为参数个数固定的子程序和参数个数可变化的子程序。 • 这两种子程序的参数传递规则是不一样的。 4.1 参数个数可变子程序参数传递规则 • 对于参数个数可变的子程序,当参数个数不超过4个时,可以使用寄存器R0~R3来传递参数;当参数超过4个时,还可以使用堆栈来传递参数。 • 在传递参数时,将所有参数看作是存放在连续的内存字单元的字数据。然后,依次将各字数据传递到寄存器R0,R1,R2和R3中。如果参数多于4个,则将剩余的字数据传递到堆栈中。入栈的顺序与参数传递顺序相反,即最后一个字数据先入栈。 4.2 参数个数固定子程序参数传递规则 • 如果系统不包含浮点运算的硬件部件,浮点参数会通过相应的规则转换成整数参数(若没有浮点参数,此步省略),然后依次将各字数据传送到寄存器R0~R3中。如果参数多于4个,将剩余的字数据传送堆栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。在参数传递时,将所有参数看作是存放在连续的内存字单元的字数据。 5. 子程序结果返回规则 子程序中结果返回的规则如下: • 结果为一个32位整数时,可以通过寄存器R0返回; • 结果为一个64位整数时,可以通过寄存器R0和R1返回; • 结果为一个浮点数时,可以通过浮点运算部件的寄存器f0、d0或s0来返回; • 结果为复合型浮点数(如复数)时,可以通过寄存器f0~fn或d0~dn来返回; • 对于位数更多的结果,需要通过内存来传递。 |
|
|
|
|
|
794 浏览 0 评论
4650 浏览 0 评论
如何使用python调起UDE STK5.2进行下载自动化下载呢?
2588 浏览 0 评论
开启全新AI时代 智能嵌入式系统快速发展——“第六届国产嵌入式操作系统技术与产业发展论坛”圆满结束
2945 浏览 0 评论
获奖公布!2024 RT-Thread全球巡回线下培训火热来袭!报名提问有奖!
31650 浏览 11 评论
73029 浏览 21 评论
浏览过的版块 |
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-24 12:19 , Processed in 0.939413 second(s), Total 91, Slave 71 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号