注:本文章参考朱有鹏单片机课程课件
一、uCOS2内核部分源码概述
1.1 主要包含如下三个文件夹
1)uC-CPU :主要是和CPU硬件相关的
2)uC-LIB:主要是一些可能会使用到的库,比如内存相关、字符相关
3)uCOS-II:这个是真正和内核相关的代码
1.2 注意点
(1).asm的汇编文件
(2)学习技巧:一边分析一边写注解
(3)一个有意思的疑问:从简单(边角)到难(核心)还是从难到简单
二、uC-LIB部分源码分析
lib_def.h : 文件中定义了很多宏,这些宏要么是对一些常数的宏定义(譬如0 1 0xff等),要么是对一些可能用到的和环境有关的数值的定义(譬如一个月多少天,一天多少小时,一小时多少分等等)
lib_mem.h :文件中包含了memory出错时的error code 以及一些配置宏,如:
LIB_MEM_CFG_ARG_CHK_EXT_EN 类似于这种宏在uCOS中叫做配置宏,这些宏的作用是用来配置uCOS内核的可选项目。uCOS本身设计的时候设计了很多属性,但是这些属性是可选使能或者不使能的,这样我们就可以通过一些配置宏来配置项目,在编译的时候(预处理)由编译器(预处理器)来帮我们把uCOS内核的这些特性加上或者去掉。
还有一些内存处理函数的声明,比如:复制、清除、设置等
lib_mem.c: 这个文件里面的就是对内存处理的各个函数定义,比如 Mem_Init 、Mem_Clr、Mem_Set、Mem_Copy等。
lib_str.c : 这个文件里面主要就是实现对字符的处理,比如:Str_Len、Str_Copy、Str_Cmp等。
lib_mem_a.asm: 这文件是在ports目录下,是一个汇编文件,主要实现的是一个Mem_Copy的功能。
1)该文件实现了一个函数mem_cpy,这个函数是用来进行内存的拷贝的。内存的拷贝是在RTOS中非常重要,内核实现也会需要用到内存拷贝,所以效率要求比较高,所以用汇编来实现。
(2)为什么uCOS2中有些东西要用汇编来实现?2个原因:一个是效率更高,另一个是不得不用汇编因为C实现不了。
知识点1:文件包含的三种方法
1、#include<xxx.h>
这种方式下,预处理器通常只会去系统指定目录下查找。如果需要包含标准库头文件或者实现版本所提供的头文件,可以使用这种方式。
2、#include"xxx.h"
这种方式下,预处理器通常优先在当前目录下寻找,即当前工程下的其他源文件的目录。如果在当前目录下没有找到,那么预处理器也会搜索系统的 include 路径。如果需要包含针对程序所开发的源文件,则可用这种方式。
3、#include"xx\xx.h"
文件名中包含了路径,则预处理器只会到该目录下寻找。
版权声明:本文为CSDN博主「蓝晴明」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012017396/article/details/104006112
知识点2: 内存池
内存池就是在程序启动时,预先向堆中申请一部分内存,交给一个管理对象。在程序运行中,需要时向管理对象“借”,不需要时“还”给管理对象。
更多详情请参考 内存池简单实现(一)
知识点3: 汇编中的EXPORT关键字
import,标识符表明要调用的函数为本模块外部定义的
export,标识符表示本模块中定时的符号可以为外部模块使用
总结
对于Lib这部分的源码分析,我们发现这部分代码的作用主要就是提供内存处理、字符处理和一些常用的宏定义的接口,方便我们其他地方进行调用。
三、uC-CPU部分源码分析
3.1 、cpu_def.h
该文件的作用主要是定义了CPU的字大小、大小端、以及临界区相关的宏。
这里主要是定义了临界区的几种方式
/* --------------- CPU CRITICAL SECTION METHODS --------------- */
#define CPU_CRITICAL_METHOD_NONE 0 /* */
#define CPU_CRITICAL_METHOD_INT_DIS_EN 1 /* DIS/EN ints. */
#define CPU_CRITICAL_METHOD_STATUS_STK 2 /* Push/Pop int status onto stk. */
#define CPU_CRITICAL_METHOD_STATUS_LOCAL 3 /* Save/Restore int status to local var. */
3.2 、cpu.h
该文件中主要作用:
1)定义了一些标准的数据类型
2)定义临界区进入退出宏
CPU_CRITICAL_ENTER()宏用来进入临界区,实现其实就是函数内部关闭中断,然后备份CPSR到局部变量cpu_sr中即可。
CPU_CRITICAL_EXIT()宏用来退出临界区,
#define CPU_CFG_CRITICAL_METHOD CPU_CRITICAL_METHOD_STATUS_LOCAL
#define CPU_CRITICAL_ENTER() {
cpu_sr = CPU_SR_Save(); }
#define CPU_CRITICAL_EXIT() {
CPU_SR_Restore(cpu_sr); }
所对应的汇编在cpu_a.asm中实现
这种方式就是通过Save/Restore 中断状态进行临界区进出,也就是3.1中最后的一种方式
3)进行函数声明
4)中断源的定义,这里目前有16个
5)一些特定寄存器的定义,比如:
volatile的作用: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
6)定义了CPU 寄存器一些特定的位
7)配置了一些error信息,就是在CPU中必须要配置的项,如果没有配置就会报错。
3.3 、cpu_a.asm
该文件的作用是一个用汇编实现了如下的功能,并导出供其他使用。
开关中断、保存恢复中断状态、计算第一个二进制数前0的个数、位反转、等待中断、等待异常。
3.4、cpu_c.c
这个文件中涉及位带访问的几个函数定义,如位带清除、位带设置、中断源关闭/使能、中断优先级设置/获取。
1)下面三行在宏定义配置判断成功时,其实相当于定义了一个函数内的局部变量,类型是CPU_SR,变量名是cpu_sr。定义这个局部变量的作用,是用来配合后面的2个宏。
#if (CPU_CFG_CRITICAL_METHOD == CPU_CRITICAL_METHOD_STATUS_LOCAL)
CPU_SR cpu_sr;
#endif
(2)RTOS中实现临界区常用的有三种方法,uCOS2本来是想要三种方式都支持的,所以在宏定义的时候定义了三种,但是实际上方式1和2都不靠谱,实际全是用的3,1和2名存实亡。所以CPU_CRITICAL_ENTER()和CPU_CRITICAL_EXIT()中直接使用了方式3,但是方式3的这2个宏依赖于一个名叫cpu_sr的局部变量。如果没有这个局部变量则会编译时报错,所以只要函数用到了这2个宏,就得在函数前面加上(1)中的三行。
知识点1: 临界区
临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。
当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机。
进程进入临界区的调度原则是:
1、如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
2、任何时候,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待。
3、进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区。
4、如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象。
知识点2: 为什么有些宏定义在头文件中,有些定义在源文件中
结论:主要看可能使用的范围决定,详细可参考C项目中变量、宏定义、结构体等声明定义位置问题(头文件、源文件)
知识点3: STM32的位带操作
结论: 就是将每一