• ucosii初认识==ucosii


    ucosii特点
    • uCOS至少要5K以上,
    • uCOSII 每个任务都只有一个独一无二的优先级
    • uCOS在商业上的应用是要付钱的。
    • uCOS只能管理64个。
    多任务优点
    • 多任务的最大好处是充分利用硬件资源,如在单任务时(大循环结构,如大部分 51程序)遇到 delay 函数时,CPU 在空转;而在多任务系统,遇到 delay 或需等待资源时系统会自动运行下一个任务,等条件满足再回来运行先前的任务,这样就充分利用了 CPU, 提高了效率。
    多任务状态

    在这里插入图片描述

    • 动态性。任务并不是随时都可以运行的,而一个已经运行的任务并不能保证一直占有 CPU 直到运行完。一般有就绪态,运行态,挂起态等。
      • 运行态。一个运行态的任务是一个正在使用 CPU 的任务。任何时刻有且只有一个运行着的任务。
      • 就绪态。一个就绪态任务是可运行的,等待占有 CPU 的任务释放 CPU。
      • 挂起态。某些条件不满足而挂起不能运行的状态。
    • 独立性。任务之间互相独立,不存在互相调用的关系。所有任务在逻辑上都是平等的。由于任务之间互相看不见,所以他们之间的信息传输就无法当面 完成。这就需要各种通信机制如信号量,消息邮箱,队列等来实现
    • 并发性。由同一个处理器轮换地运行多个程序。或者说是由多个程序轮班地占用处理器这个资源。且在占用这个资源期间,并不一定能够把程序运行完毕。
      在这里插入图片描述
    抢占式调度
    • 调度的概念,通俗的说就是系统在多个任务中选择合适的任务执行。系统如何知道何时该执行哪个任务?可以为每个任务安排一个唯一的优先级别,当同时有多个任务就 绪时,优先运行优先级较高的任务。
    • 同时,任务的优先级也作为任务的唯一标识号。代码中都是对标识号来完成对任务的操作的。如 OSDelPrioRdy(prio),OSSetPrioRdy(prio)等。
      不同的优先级对应就绪表中的每一位。低位对应高优先级。优先级 0 的优先权最高, 优先级 31 的优先权最低。
      在这里插入图片描述
    • 所谓“抢占式调度”是指:一旦就绪状态中出现优先权更高的任务,便立即剥夺当前任务的运行权,把 CPU 分配给更高优先级的任务。这样 CPU 总是执行处于就绪条件 下优先级最高的任务。
    • 如何实现抢占式调度呢?
      • 高优先级的任务因为需要某种资源或延时,主动请求挂起,让出处理器。触发方式(如任务执行 OSTimeDly()或 OSTaskSuspend()把自身挂起就属于这种。)==最常用的。
      • 高优先级的任务因为时钟节拍到来,或在中断处理结束后,内核发现更高优先级任务获得了执行条件(如延时的时钟到时),则在中断后直接切换到更高优先级任务执行。这 种调度也称为中断级的切换。
    多任务系统的时间管理
    • 与人一样,多任务系统也需要一个“心跳”来维持其正常运行,这个心跳叫做时钟节拍,通常由定时器产生一个固定周期的中断来充当,频率一般为 50-100Hz。(这里如你使用滴答定时器,你要重新初始化TIM定时器去替代)
    如何实现多任务?(独立)
    • 只有一个 CPU,如何在同一时间实现多个独立程序的运行?要实现多任务,条件是 每个任务互相独立。人如何才能独立,有自己的私有财产。任务也一样,如果一个任务有自己的 CPU,堆栈,程序代码,数据存储区,那这个任务就是一个独立的任务。
    • 先看代码。每个任务的程序代码与函数一样,与 51 的裸奔程序一样,每个任务都是一个大循环。
      void task ( ) 
      { 
       	//initialize 
      	while(1) 
      	{ 
      	//your code 
      	} 
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • 然后看数据存储区,由于全局变量是系统共用的,各个任务共享,不是任务私有,所以这里的数据存储区是指任务的私有变量,如何变成私有?局部变量也。编译器是把局 部变量保存在栈里的,所以好办,只要任务有个私有的栈就行。(同样这样会带来通信的问题)
    • 看看多任务的原理,借鉴了中断的思想。任务切换时,把当前任务的现场数据保存在自己的任务栈里面,再把待运行的任务的数据从自己的任务栈装载到 CPU 中,改变 CPU 的 PC,SP,寄存器等。可以说,任务的
      切换是任务运行环境的切换。而任务的运行环境保存在任务栈中,也就是说,任务切换的 关键是把任务的私有堆栈指针赋予处理器的堆栈指针 SP。
      在这里插入图片描述
    任务状态管理
    • 创建任务:
      OSTaskCreate(Task0,&StackTask0[StackSizeTask0 - 1],PrioTask0); // 创建一个任务
    • 挂起/恢复任务
      • 通过 OSTaskSuspend()可以主动挂起一个任务。OSTaskSuspend()会把任务从任务就绪表中移出,最后重新启动系统调度。这个函数可以挂起任务本身也可以挂起其他任务。
      • 恢复任务OSTaskResume(),可以让被 OSTaskSuspend或 OSTimeDly 挂起的任务恢复就绪态,然后进行任务调度
    • 删除任务
      删除任务前一定要向系统提出删除请求。OSTaskDelReq(BEEP_TASK_PRIO);
      OSTaskDel(OS_PRIO_SELF);
    简单ucosii模板

    上面进行简单的介绍,下面放一段简单典型的代码,大家可以直观的感受一下。

    #include "sys.h"
    #include "delay.h"
    #include "usart.h"
    #include "includes.h"
    
    //START 任务
    //设置任务优先级
    #define START_TASK_PRIO      			10 //开始任务的优先级设置为最低
    //设置任务堆栈大小
    #define START_STK_SIZE  				64
    //任务堆栈	
    OS_STK START_TASK_STK[START_STK_SIZE];
    //任务函数
    void start_task(void *pdata);	
     		  
    //设置任务优先级
    #define RGD0_TASK_PRIO       			7 
    //设置任务堆栈大小
    #define RGD0_STK_SIZE  		    		64
    //任务堆栈	
    OS_STK RGD0_TASK_STK[RGD0_STK_SIZE];
    //任务函数
    void rgd0_task(void *pdata);
    
    //设置任务优先级
    #define RGD1_TASK_PRIO       			6 
    //设置任务堆栈大小
    #define RGD1_STK_SIZE  					64
    //任务堆栈
    OS_STK RGD1_TASK_STK[RGD1_STK_SIZE];
    //任务函数
    void rgd1_task(void *pdata);
    
    int main(void)
    { 
    	delay_init(168);		  //初始化延时函数
    	//所有外设初始化地区
    	OSInit();   
     	OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
    	OSStart();	
    }
     //开始任务
    void start_task(void *pdata)
    {
        OS_CPU_SR cpu_sr=0;
    	pdata = pdata; 
    	//通信(消息,信号量。邮箱)初始化--核心
      	OS_ENTER_CRITICAL();			//进入临界区(无法被中断打断)    
     	OSTaskCreate(rgd0_task,(void *)0,(OS_STK*)&RGD0_TASK_STK[RGD0_STK_SIZE-1],RGD0_TASK_PRIO);						   
     	OSTaskCreate(rgd1_task,(void *)0,(OS_STK*)&RGD1_TASK_STK[RGD1_STK_SIZE-1],RGD1_TASK_PRIO);	 				   
    	OSTaskSuspend(START_TASK_PRIO);	//挂起起始任务.
    	OS_EXIT_CRITICAL();				//退出临界区(可以被中断打断)
    } 
    void rgd0_task(void *pdata)
    {	 	
    	while(1)
    	{
    	//=============
    	};
    }
    void rgd1_task(void *pdata)
    {	  
    	while(1)
    	{
    	//=============
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    由于抢占调度让任务独立运行的特点,我们不使用全局变量,让任务拥有自己栈。会导致我们任务间需要信息的沟通与传递,(这也是精华啦!下一篇见)

  • 相关阅读:
    用acme.sh给网站域名,申请免费SSL永久证书(自动续期)
    python:根据旋转平移矩阵求取原始点云或者转换后点云
    基于STM32设计的智能货架(华为云IOT)(225)
    PTA:字符串加密
    DRM系列(5)之vblank事件驱动
    微服务测试怎么做
    读书笔记:《探索大脑的内部世界》
    Ubuntu添加和删除用户
    LINUX 基础
    企业微信托管集成语聚AI,做AI智能客服助手,实现精准回答用户问题、创建群组自动化场景
  • 原文地址:https://blog.csdn.net/weixin_47397155/article/details/126677631