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

2025-07-19 22:29:33 5670

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

何谓操作系统1.什么是操作系统?

操作系统是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行。介于APP和硬件之间。

2. 为什么要用操作系统?

1)相比裸机,可以实现更加复杂的功能。

2)屏蔽硬件。使得上层应用APP的移植性更好。

常见操作系统常见操作系统

安卓、IOS、Windows、Linux、塞班、Vxworks、wince、RTT、UCOS、FreeRTOS等。常见操作系统分类实时操作系统(RTOS)

每一个任务的执行时间是不固定的,任务与任务之间的切换时以优先级为调度原则,优先级高的任务可以抢占优先级低的任务的CPU使用使用权,所以也叫抢占式内核。响应速度快。

RTT、UCOS、FreeRTOS分时操作系统

系统分配若干个时间片给每个任务,当前任务执行完自己的时间后会自动交出CPU使用权给下一个任务。

时间片:每个时间片都是一样的,系统会分配若干个时间片给每个任务

举例:1S平均分成1000份,每一份就是一个时间片–1ms。

给任务A分配100份,给任务B分配200份,给任务C分配300份…

Windows95/98/2000、LINUX2.6内核之前半分时半实时操作系统

有一些任务是实时的,有些任务是分时的。

Windows7/8/10、LINUX2.6内核之后UCOS操作系统概述UCOS操作系统的调度原则实时操作系统:以任务优先级作为调度原则

分时操作系统:以时间片作为调度原则

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

UCOS操作系统的程序结构裸机:有且只能有一个主函数,并且在主函数必须要有死循环(while(1)),把要实现的功能在主函数里实现。

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

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

任务控制块:当成功创建了一个任务之后,系统就会自动分配一段内存空间,这段内存空间就是所谓任务控制块,存放这该任务的相关信息,包括任务函数地址、任务栈、任务优先级、任务状态。

任务函数:可以看成是一个功能函数,把要该任务实现的功能写进该函数中,系统通过该任务函数名(地址)访问该任务函数。

任务栈:当任务与任务之间发生切换时,保存当前任务环境(寄存器配置,变量等)和恢复任务环境。

任务优先级:每个任务都有唯一的优先级,是系统调度和任务切换的依据。

任务状态:休眠/停止、等待/挂起、就绪、运行、中断

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

处理就绪状态中任务的优先级问题。

任务切换:CPU从一个任务切换到另一个任务。

什么时候发生系统调度?

满足两个条件中的一个即可发生。

时基的时间到了。时基又叫Tick,Tick一般情况下都是由系统滴答定时器提供,这个Tick时间长短可以由开发人员自己设定。执行到某些API函数( Osched() )发生系统调度一定会产生任务切换吗?

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

举例:

任务A优先级为10–挂起

任务B优先级为13–运行

任务C优先级为18–就绪

任务D优先级为20–就绪

当前任务B正在运行,突然Tick到了,就会发生一次系统调度,当前任务B从运行态转为就绪态,然后目前一定有任务B,任务C和任务D处于就绪态,并且任务B优先级最高,那么CPU的使用权仍然会给到任务B。

所以,这种情况虽然发生了系统调度,但是并没有产生任务切换。

任务间的切换过程是怎样的?A—>B—>A

任务A入栈任务B把内容从栈中弹出(出栈)CPU切换到任务B任务B入栈任务A出栈CPU切换到任务AUCOS操作系统的任务中断上了操作系统后,中断的写法跟以前裸机基本没有变化。

同样需要设置中断分组,中断优先级,使用中断等(配置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)新建文件并添加到工程中

系统滴答定时器初始化代码语言:javascript代码运行次数:0运行复制//函数功能:系统滴答中断

//参数说明:待延时的毫秒

//返回值:无

//注意事项:

//时间: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();//告诉操作系统已经处理完了中断服务函数

}主函数的编写代码语言:javascript代码运行次数:0运行复制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 //创建任务成功。

代码语言:javascript代码运行次数:0运行复制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);

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