嵌入式实时操作系统UCOSII[通俗易懂]

嵌入式实时操作系统UCOSII[通俗易懂]何谓操作系统1.什么是操作系统?操作系统是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行。介于APP和硬件之间。2. 为什么要用操作系统?1)相比裸机,可以实现更加复杂的功能。2)屏蔽硬件。使得上层应用APP的移植性更好。常见操作系统常见操作系统安卓、IOS、Windows、Linux、塞班、V…

大家好,又见面了,我是你们的朋友全栈君。

何谓操作系统

1.什么是操作系统?
操作系统是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行。介于APP和硬件之间。
在这里插入图片描述
2. 为什么要用操作系统?
1)相比裸机,可以实现更加复杂的功能。
2)屏蔽硬件。使得上层应用APP的移植性更好。

常见操作系统

  1. 常见操作系统
    安卓、IOS、Windows、Linux、塞班、Vxworks、wince、RTT、UCOS、FreeRTOS等。
  2. 常见操作系统分类
  1. 实时操作系统(RTOS)
    每一个任务的执行时间是不固定的,任务与任务之间的切换时以优先级为调度原则,优先级高的任务可以抢占优先级低的任务的CPU使用使用权,所以也叫抢占式内核。响应速度快。
    RTT、UCOS、FreeRTOS
  2. 分时操作系统
    系统分配若干个时间片给每个任务,当前任务执行完自己的时间后会自动交出CPU使用权给下一个任务。
    时间片:每个时间片都是一样的,系统会分配若干个时间片给每个任务
    举例:1S平均分成1000份,每一份就是一个时间片–1ms。
    给任务A分配100份,给任务B分配200份,给任务C分配300份…
    Windows95/98/2000、LINUX2.6内核之前
  3. 半分时半实时操作系统
    有一些任务是实时的,有些任务是分时的。
    Windows7/8/10、LINUX2.6内核之后

UCOS操作系统概述

UCOS操作系统的调度原则

实时操作系统:以任务优先级作为调度原则
分时操作系统:以时间片作为调度原则

UCOSII是实时操作系统,所以它是以任务优先级作为调度原则。

UCOS操作系统的程序结构

裸机:有且只能有一个主函数,并且在主函数必须要有死循环(while(1)),把要实现的功能在主函数里实现。

上了UCOSII操作系统后:有且只能有一个主函数,在主函数中可以不需要死循环(while(1)),在工程中有多个任务,每个任务都必须有个死循环,把要实现的功能写进各个任务中。
在这里插入图片描述

任务结构:任务控制块、任务函数地址、任务栈、任务优先级、任务状态

任务控制块:当成功创建了一个任务之后,系统就会自动分配一段内存空间,这段内存空间就是所谓任务控制块,存放这该任务的相关信息,包括任务函数地址、任务栈、任务优先级、任务状态。
在这里插入图片描述
任务函数:可以看成是一个功能函数,把要该任务实现的功能写进该函数中,系统通过该任务函数名(地址)访问该任务函数。
任务栈:当任务与任务之间发生切换时,保存当前任务环境(寄存器配置,变量等)和恢复任务环境。
任务优先级:每个任务都有唯一的优先级,是系统调度和任务切换的依据。
任务状态:休眠/停止、等待/挂起、就绪、运行、中断

UCOS操作系统的系统调度和任务切换

系统调度:当发生系统调度的时候,系统就会查询当前所有处于就绪状态中的任务的优先级,把CPU的使用权给到优先级最高的那个任务(就绪)。
处理就绪状态中任务的优先级问题。
任务切换:CPU从一个任务切换到另一个任务。

什么时候发生系统调度?
满足两个条件中的一个即可发生。

  1. 时基的时间到了。时基又叫Tick,Tick一般情况下都是由系统滴答定时器提供,这个Tick时间长短可以由开发人员自己设定。
  2. 执行到某些API函数( Osched() )

发生系统调度一定会产生任务切换吗?
发生系统调度不一定会产生任务切换

举例:
任务A优先级为10–挂起
任务B优先级为13–运行
任务C优先级为18–就绪
任务D优先级为20–就绪
当前任务B正在运行,突然Tick到了,就会发生一次系统调度,当前任务B从运行态转为就绪态,然后目前一定有任务B,任务C和任务D处于就绪态,并且任务B优先级最高,那么CPU的使用权仍然会给到任务B。
所以,这种情况虽然发生了系统调度,但是并没有产生任务切换。

任务间的切换过程是怎样的?

A—>B—>A

  1. 任务A入栈
  2. 任务B把内容从栈中弹出(出栈)
  3. CPU切换到任务B
  4. 任务B入栈
  5. 任务A出栈
  6. CPU切换到任务A

UCOS操作系统的任务中断

上了操作系统后,中断的写法跟以前裸机基本没有变化。
同样需要设置中断分组,中断优先级,使用中断等(配置NVIC)–没有变化
中断服务函数名也没有没变化
编写中断服务函数的内容需要增加两个UCOSII的API函数。—变化

在中断服务函数里的第一个行,必须加入“OSIntEnter()”,表示当前操作系统进入了中断服务函数
在中断服务函数里的最后一行,必须加入“OSIntExit()”,表示当前操作系统要退出中断服务函数
在这里插入图片描述
裸机:当发生了中断事件,会在当前运行的地方设定一个断点,执行完中断服务函数后,CPU会回到断点中继续执行。

上了UCOSII系统后:当发生了中断事件,同样会在当前运行的地方设定一个断点,执行完中断服务函数后,不一定会回到断点处。因为出中断前会执行“OSIntExit()”,执行这个API函数会产生一次系统调度,一旦发生了任务切换,CPU就不会回到断点处。

UCOS操作系统的任务状态

任务状态:休眠/停止、等待/挂起、就绪、运行、中断
在这里插入图片描述

创建UCOS版本工程

当前用到的是UCOSII版本。
UCOSII源码链接:www.micrium.com/downloadcenter/
在这里插入图片描述
选择对应的处理器
在这里插入图片描述
在这里插入图片描述

UCOSII文件说明

在这里插入图片描述
在这里插入图片描述
核心文件:
在这里插入图片描述

创建工程

1)新建一个空白文件夹
2)在上面创建的文件夹里再新建名为“CMSIS”、“USER”和“TASK”两个文件夹
3)在“USER”里再新建名为“inc”和“src”两个文件夹
4)在“TASK”里再新建名为“inc”和“src”两个文件夹
5)把芯片相关支持文件复制到“CMSIS”
6)把“UCOSII”整个文件夹复制到与“CMSIS”同目录下
7)打开KEIL软件创建新工程
8)创建虚拟工程树(在原来基础上增加“UCOSII_CONFIG”、“UCOSII_CORE”、“UCOSII_PORT”
9)配置工程属性
10)设定头文件路径(在原来基础上增加“./TASK/inc”、“./UCOSII/CONFIG”、“./UCOSII/CORE”、 “./UCOSII/PORT”
11)新建文件并添加到工程中

系统滴答定时器初始化

//函数功能:系统滴答中断
//参数说明:待延时的毫秒
//返回值:无
//注意事项:
//时间:2019.6

void Systick_Interrupt(u32 nms)
{
	SysTick->CTRL &=~(0x01<<2);//	选择时钟源(21M)
	SysTick->LOAD =21*nms*1000;//	设置重载值(LOAD)
  SysTick->VAL=0;//	写VAL(清除VAL,重装载,清标志)
	//使能中断(设置NVIC优先级,模块级中断使能)
	//设置优先级分组
	NVIC_SetPriority(SysTick_IRQn,NVIC_EncodePriority (7-2, 2,2));
	//NVIC中断使能---系统滴答中断NVIC是必须响应,不需要再使能
	//NVIC_EnableIRQ(SysTick_IRQn);
	SysTick->CTRL |=0x01<<1;
	
	SysTick->CTRL |=0x01<<0;//	开启(使能)定时器
}
void SysTick_Handler(void)
{
	OSIntEnter();//告诉操作系统当前进入了中断服务函数
	OSTimeTick();
	OSIntExit();//告诉操作系统已经处理完了中断服务函数
}

主函数的编写
nt main(void)
{
	//各种模块的初始化
  LED_Init( );//LED初始化
	Key_Init( );
	Uart1_Init(84,115200);
	Systick_Interrupt(1000/OS_TICKS_PER_SEC);//TICK=5ms
	
	
	
	OSInit();//初始化 UCOS-II 内核
	
	//至少要创建一个任务
	
	
	GPIO_ResetBits(GPIOF, GPIO_Pin_6);
	
	OSStart();//开启操作系统
						//一旦开启了操作系统,下面的代码就不会再执行了
	
	GPIO_SetBits(GPIOF, GPIO_Pin_6);
	GPIO_ResetBits(GPIOF, GPIO_Pin_9);
	
	while(1)
	{
			
	}
	
}

创建任务

调用 OSTaskCreate 创建一个任务,ucos会给这个任务分配一个任务控制块,任务控制块中记录了任务的任务函数地址,任务栈地址,任务的优先级,任务的状态。
如果成功创建一个任务,这个任务就处于了就绪状态。
不能在中断中创建任务,每个任务的优先级唯一,如果先后创建的两个任务的优先级一样,后一个任务创建会失败。任务的优先级不能低于最低优先级。
函数原型:
INT8U OSTaskCreate (
void (* task) (void *pd),
void *pdata,
OS_STK *ptos,
INT8U prio
)

参数说明:
1)task:函数指针,是“指向一个返回值类型为void,有一个形参为 void* 指针的函数的”指针。—–其实是就指向任务函数。
2)pdata:void* 指针,任务函数被调用需要传递实参,所传入参数就是这个参数,如果任务函数不需要使用这个参数,可以传递 0、NULL 或 (void*)0 。
3)ptos:任务栈(用户定义一个数组)顶部分,对于栈是递减方式,就是传递数组的最后一个元素地址,如果是递增,就是第0个元素地址。(ARM的CPU一般默认是递减的栈,所以看到的都是传入 最后一个 元素地址; 51单片的栈是递增的,所以51上使用ucOS传入的是第0个元素地址)。(一般不会在51上运行)
OS_STK 类型:实际上 unsigned int (对于32位平台),平台不同,长度不同,所以使用时候一般是使用 OS_STK 定义数组。
ptos空间最小不能小于17*4,如果任务函数局部变量比较多,还要更大;如果任务函数用到了浮点运算,一定要把栈设置成8字节对齐,否则出栈异常!
4)prio:任务的优先级。
ucOS2 范围是0~63,实际上是由 os_cfg.h 中宏 OS_LOWEST_PRIO 配置最大值(只能改小,不能改大)。
其中可用的优先级最低2个是内建任务使用,用户不能使用
最低优先级(等于 OS_LOWEST_PRIO):空闲任务 — 当所有用户任务都不运行的时候,CPU在执行空闲任务,通俗说:所有人都不要它时候,给空闲任务收留它。
次低优先级(等于 OS_LOWEST_PRIO -1):统计任务 — 统计CPU利用率。(该任务是可选择的,可裁剪的)。
ucOS 任务优先级不能相同,每个任务优先级都是惟一的。
(经验:建议最高前几个等级不要用,任务间优先级不要连续,考虑程序拓展性)

返回值说明:
1)成功:0,一般不直接使用数字,而使用ucOS提供宏判断执行结果。
OS_ERR_NONE //创建任务成功。

Returns    : OS_ERR_NONE             if the function was successful.
OS_ERR_PRIO_EXIST            if the task priority already exist
*                                      (each task MUST have a unique priority).
*          OS_ERR_PRIO_INVALID     if the priority you specify is higher that the maximum allowed
*                                      (i.e. >= OS_LOWEST_PRIO)
*          OS_ERR_TASK_CREATE_ISR  if you tried to create a task from an ISR.

具体代码请看示例

任务一个任务都必须有主动放弃CPU的动作,否则,比它优先级低的任务用于都得不到CPU。

任务函数: void Start_Task(void *pdata);

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/136141.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号