文章目录
ucos移植相关知识点
一、ucos系统
1、实时操作系统的概念
如果操作系统能使计算机系统及时响应外部事件的请求,并能及时控制所有实时设备与实时任务协调运行,且能在一个规定的时间内完成对事件的处理,则这个操作系统就是一个实时操作系统。
实时操作系统有两个基本要求:
第一,实时系统的计算必须产生正确的结果,称为逻辑或功能正确。
第二,实时系统的计算必须在预定的时间内完成称为时间正确。
按时间正确的程度来分,实时操作系统分为硬实时操作系统和软实时操作系统。
2、任务的相关概念
在设计一个较为复杂的应用程序时,通常把一个大型任务分解成多个小任务,然后在计算机中通过运行这些小任务,最终完成大人的目的。
-
在ucos-ii中,与上述小任务对应的程序实体就叫做”任务“,而ucos-ii就是一个能对这些小任务的运行进行管理和调度的多任务操作系统。
-
在代码上看,任务就是一个函数。
-
从任务的存储结构上看,分为:任务控制块、任务堆栈、任务代码,三个部分
-
任务的控制块
任务控制块是关联了任务代码的程序控制块,它记录了各个属性;
- 任务的堆栈
任务堆栈则用来保存任务的工作环境;
- 任务的链表
链表对任务进行管理;
-
ucos-ii的任务有两种:用户任务和系统任务
- 用户任务
由应用程序设计者编写的任务,且是为了解决应用问题而编写的。
- 系统任务(两个系统任务:空闲任务和统计任务)
为应用程序提供某种服务或为系统本身服务的。
3、任务调度的相关概念
任务调度为系统中处于就绪状态的任务分配CPU是多任务操作系统的核心工作。
这项工作设计两项技术:
一是判断哪些任务处于就绪状态(OSTimeTick(););
二是进行任务调度(PendSV_Handler)。
所谓任务调度,就是通过一个算法在就绪任务中确定应该马上运行的任务,操作系统用于负责这项工作的程序模块叫调度器。
-
任务就绪表
它登记了系统中所有处于就绪状态的任务,每个任务在位图中占据一个二进位,该位置的状态(0或1)就表示任务是否处于就绪状态。
-
登记
当某个任务处于就绪状态时,系统将该任务登记在任务就绪表中,即在就绪表中将该任务的对应位置1。
- 注销
当某个任务需要脱离就绪状态时,系统在就绪表中将该任务的位置0。
- 调度器
有两种:
一种是任务级的调度器,由函数OSSched()实现。
一种是中断级的调度器,由OSIntExt()实现。
- OS_TASK_SW() 任务切换宏
其实任务切换的工作是靠OSCtxsw()来完成的,它的工作:
-
把被中止的任务的断点指针保存到任务堆栈中;
-
把CPU通用寄存器的内容保存到任务堆栈中;
-
把被中止任务的任务堆栈指针当前值保存到该任务的任务控制块的OSTCBStkPtr中;
-
获得待运行任务的任务控制块;
-
使CPU通过任务控制块获得待运行任务的任务堆栈指针;
-
把待运行任务堆栈中通用寄存器的内容恢复到CPU的通用寄存器中;
-
使CPU获得待运行任务的断点指针
- 任务的切换
在多任务系统中,令CPU中止当前正在运行的任务转而取运行另一个任务的工作叫做任务切换。 - 任务的优先级
系统中的每个任务都按照其任务的重要性分配有一个唯一的优先级别,优先级别高的任务先运行,优先级别低的任务后运行。(系统默认把最低优先级的给空闲任务,最低优先级-1的给统计任务) - 任务的挂起
所谓挂起一个任务,就是停止这个任务的运行,从运行状态转为等待状态。 - 任务的恢复
从任务的等待状态,转为运行状态。
4、中断的相关概念
任务在运行过程中,应内部或外部异步事件的请求中止当前任务,而去处理异步事件所要求的任务的过程叫做中断。
- ucosii的中断过程
系统接收到中断请求后,如果这时CPU处于中断允许状态,系统就会中止正在运行的当前任务,而按照中断向量的指向转而去运行中断服务子程序;当中断服务子程序的运行结束后,系统将会根据情况返回到被中止的任务继续运行,或者转向运行另一个具有更高优先级别的就绪任务。
可剥夺性的特点在上面也有体现出来,中断服务子程序运行结束之后,系统就会进行任务调度去运行级别最高的就绪任务,而并不一定要接续运行被中断的任务。 - 为什么中断需要调用**OSIntEnter()和OSIntExit()**函数?
答:OSIntEnter的作用是把全局变量OSIntNesting加1,用它来记录中断嵌套的层数。
OSIntExit()将中断嵌套层数计数器减1,然后做判断如果嵌套层数不是0,那么就继续执行中断服务函数,如果嵌套层数是0,就要进行任务切换。
-
OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()
-
OS_ENTER_CRITICAL()就是关中断函数,而OS_EXIT_CRITICAL()就是开中断函数
-
OS_ENTER_CRITICAL()作用是让ucos无法再执行任务调度,ucos处于关闭状态
-
区域用于处理比较连续的事情,不希望被ucos打断。
-
OS_EXIT_CRITICAL()用于恢复ucos的任务调度功能,重新开启ucos。
-
SysTick_Handler的含义:里面调用了OSTimeTick()。
-
delay_init
通过OS_TICKS_PER_SEC来计算reload,通过reload数可以得出想要的uocs节拍时间。然后进入中断服务函数SysTick_Handler,中断里面一直在循环OSTimeTick():
-
PendSV_Handler是用来任务切换的,在滴答定时器执行完后调用任务切换和中断结束后中断切换;
中断主要做上下文切换,也就是任务切换。
5、进程通信
-
信号量
- 信号量是一类时间。使用信号量的最初目的,是为了给共享资源设立一个标志,该标志表示该共享资源被占用的情况。信号量用于同步,主要任务间和中断间同步;
- 常用函数
OSSemCreate(0); //创建信号量
OSSemPost(Fun_Semp); //发送信号量 将0置1
OSSemPend(Fun_Semp,0,&err); //等待信号量 检测是否大于1,是的话执行后面代码,自身减1
- 信号量释放时候怎么进行减一操作的(接收到信号量,然后有个计数器会减)
-
互斥信号量
-
互斥量用于互锁,在信号量的基础上添加一个限制,用于保护同时只能有一个任务访问的资源,为资源上一把锁。
-
高8位为要提升的优先级别,低8位表明信号量有没有被任务占用。
-
常用函数
Semaphore = OSMutexCreate(19,&err); //创建互斥量 默认为信号量没有被使用
OSMutexPend(Semaphore,0,&err); //请求互斥量
OSMutexPost(Semaphore); //释放互斥量
-
-
邮箱
-
在多任务操作系统中,常常需要在任务与任务之间通过传递一个数据(这种数据叫队列)的方式来进行通信。为了达到这个目的,可以在内存中创建一个存储空间作为该数据的缓冲区。如果把这个缓冲区叫做消息缓冲区,那么在任务间传递数据(消息)的一个最简单的方法就是传递消息缓冲区的指针。因此,用来传递消息缓冲区指针的数据结构就叫做消息邮箱。
-
缓存区里就是一个指针,发送邮箱就是把这个指针指向需要发送的消息
-
消息接收就是创建一个指针,然后指向缓存区的指针
-
常用函数
Str_Box = OSMboxCreate((void *)0); //创建消息邮箱
OSMboxPost(Str_Box,&exti_num); //发送给邮箱消息
ss = OSMboxPend(Str_Box,0,&err); //接收中断发来的邮箱消息
-
-
队列
-
包含邮箱消息,邮箱消息是特殊的一种消息队列,队列是将数组指针传进邮箱里。
-
队列可以直观的看作多个邮箱组成的
-
每一个邮箱里都有一个指针,发送一个消息就把队列对应的指针指向发送的消息,然后队列指针+1,如果再发在把这个指针指向要发的消息。
-
常用函数
Str_Q = OSQCreate(&MsgGrp[0],N_MESSAGES); //创建消息队列
OSQPostFront(Str_Q,ss); //发送消息给队列
sss = OSQPend(Str_Q,0,&err); //接收队列消息
-
6、内存管理
-
使用内存管理的原因
如果直接用用malloc()和free()两个函数动态地分配内存和释放内存,会把原来很大的一块连续内存区域,逐渐地分割成许多非常小而且彼此又不相邻的内存区域,也就是内存碎片。由于这些碎片的大量存在,使得程序到后来连非常小的内存也分配不到。在ucos中操作系统把连续的大块内存按分区来管理。每个分区中包含有整数个大小相同的内存块,利用这种机制,ucos对malloc()和free()函数进行了改进,使得它们可以分配和释放固定大小的内存块。这样一来,malloc()和free()函数的执行时间也是固定的了。
-
内存控制块与内存分区和内存块的关系
-
常用函数
建立一个内存分区,OSMemCreate();
分配一个内存块,OSMemGet();
释放一个内存块,OSMemPut();
请求一个内存块,OSMemGet();
二、 嵌入式平台
1、GPIO的驱动
- 开启时钟
- 配置IO口模式
2、定时器的驱动
- 中断优先级配置
- 定时器配置
- 中断服务函数
三、ucos移植
1、ucos的移植
- 源码移植
(1).OS_CPU.H :用#define设置一些常量的值,声明10个数据类型,用#define声明三个宏。
(2).OS_CPU_C.C : 用C语言编写六个简单的函数。
OSTaskStkInit()
//初始化任务堆栈结构
OSTaskCreateHook()
//允许用户或使用用户的移植实例的用户扩展μC/OS-Ⅱ的功能
OSTaskDelHook()
//该函数在把任务从μC/OS-Ⅱ的内部任务链表中解开之前被调用
OSTaskSwHook()
//当发生任务切换的时候调用OSTaskSwHook()
OSTaskStatHook()
//用户可以用OSTaskStatHook()来扩展统计功能
OSTimeTickHook()
OSTaskTimeHook()在每个时钟节拍都会被OSTaskTick()调用
(3).OS_CPU_A.ASM : 编写四个汇编语言函数。
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
- 文件夹的作用
- CONFIG文件夹下
- os_cfg.h:UCOSII的配置文件(例如:设置优先级的个数、时钟的频率、各种函数的裁剪)
- PROT文件夹下
- os_cpu.a.asm:写切换任务的一些相关代码
- os_cpu.h:定义了数据类型(INT8U、INT16U等、)
- os_cpu_c.c:堆栈初始化函数和定义了几钩子函数
- CORE文件夹下
- os_core.c:内核文件
- os_flag.c:事件标志组相关文件
- os_mbox.c:消息邮箱文件
- os_mem.c:内存管理文件
- os_mutex:互斥量信号文件
- os_q.c:消息队列文件
- os_sem.c:信号量文件
- os_task.c:软件定时器相关文件
- os_time.c:时间相关文件
- os_tmr.c:软件定时器相关文件
- CONFIG文件夹下
2、点灯
定义任务信息:
创建任务:
任务里内容: