大家好,又见面了,我是你们的朋友全栈君。
何谓操作系统
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切换到任务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)新建文件并添加到工程中
系统滴答定时器初始化
代码语言:javascript复制//函数功能:系统滴答中断
//参数说明:待延时的毫秒
//返回值:无
//注意事项:
//时间: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复制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复制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