找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1792|回复: 14
打印 上一主题 下一主题
收起左侧

uCOS-III学习笔记

  [复制链接]
跳转到指定楼层
楼主
ID:7209 发表于 2021-2-19 20:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
为什么要学习这个ucos ii  
在单片机的世界里翱翔了好多年,
一直觉得无所不能、无所不会鄙视一些应用层的开发,
轻示一些软硬都好搞一点的过去,
直到遇见你 ucosii 或你的好兄弟iii 我从没有如此卑微的如此靠近过。
你是来团结这些”地方武装势力的”真真为社会主义事业而奋斗终身的。
崇拜了无数大婶,还是俺叔给我说 “ 算法” 才是 ----葵花宝典
   但有天碰到一个“事情”需要两个或三个或更多个家庭来合力完成一项工作的情况下;
 比如“苦逼的程序员终于要结婚了”这就需要二个最最重要的两个高级任务你和你老婆、
 两个稍低一点的有可能存在“翻转任务优先级”的情况影响你和你老婆的任务执行的”任务” ,
 (其实我想说的是彩礼可能把好好的任务给搅黄,相当于最高级别的优先级丈母娘,一票否决权
  --- 如果最早你在进行任务留有debug 的话,这些都是小菜---你懂的)
 还需要一些处理各种额外的功能任务。
   如果是单片机的“世界中”这样的情况下需要先初始化了
  老婆的“包括找到老婆+熟悉老婆各种配件+熟悉她的各种关系”,
  初始化娶老婆的各种条件”买车+买房+买家具+++++”,
  初始化婚礼的各种琐事,初始化她父母+你父母的各种要求;
  最后的循环执行就是你和你老婆结婚+重复琐事+赚钱+养育孩子+照顾父母+孩子结婚+自己老去,
  最后你的主函数终于运行完毕。
  这里可能有些初始化条件不合适还有可能会出现#if………#else……….   //重新选择老婆
也可能会出现
Do{………..}while(条件) //试婚
也可能会出现
Delay(10year);  //  //延时
也可能会出现
While(各种可能情况)
{…………..};  //这里就是一个牢笼直至…………
最危险的不能进入“迭代的” 的循环中,
一旦进入后果无法设想
思想指针可以来回游走
可别忘记自己的类型和安全返回
否则很容易带回的是错误的数据和丢失自己,
损坏主函数的生命周期
“忒复杂了为了结一个婚这大工程还是给了系统好把”
系统是如何来处理
建立几个任务
任务一负责各种婚前准备
  任务二负责老婆家各种需要准备工作
  任务三负责各种意外情况处理
  任务四负责……..
  任务五负责……….
  任务六…………
这样是不是稍微轻松一下,但是也不是最轻松的
系统给设定好了各种格式
现在的社会你只需要有钱就可以了

Ucos  ii  -----   iii    也一样

感觉好幸福
有中马上见到美丽贤惠聪明可爱的 新娘子
流会儿口水了

评分

参与人数 2黑币 +62 收起 理由
gracture + 12 赞一个!
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏4 分享淘帖 顶2 踩
回复

使用道具 举报

沙发
ID:7209 发表于 2021-2-19 21:57 | 只看该作者
首先,第一个要解决的问题是,
为什么我们需要uCos?就像最开始学C编程时,师傅告诉我们,指针很重要,那时你肯定有一个大的疑问,指针到底有什么好?
心里一直犯嘀咕着:不用指针不一样把程序编出来了?  现在想想看c语言没了指针,是不是寸步难行呢。回到正题,我们到底为什么需要uCos?

一般的简单的嵌入式设备的编程思路是下面这样的:
int  main ()
{     //初始化各种外设
    //初始化各种时钟
    //初始化IO 口
   
  while(1)
  {
    {处理事务1};
    {处理事务2};
    {处理事务3};
        .......
    {处理事务N};
   }
}

void  shijian1 ()
{}
void shijian2()
{}
void shijian3()
{}

isr_server
{
    {处理中断};
}

这也是我们在51、 STM32、AVR、stm8 中最常用的形式
这是最一般的思路,对于简单的系统当然是够用了,
但这样的系统实时性是很差的,
比如“事务1”如果是一个用户输入的检测(扫描按键是否有按下),
当用户输入时,
如果程序正在处理事务1下面的那些事务,
也就是在处理任务2 后任务3 后任务4。
那么这次用户输入将失效,
用户的体验是“这个按键不灵敏,
这个机器很慢”,
而我们如果把事务放到中断里去处理(我们把按键扫描放在中断函数里面),
虽然改善了实时性但会导致另外一个问题,
有可能会引发中断丢失,
(这里我没有想明白 按键扫描在中断里丢失 是不是顺序扫描0-9个按键
当扫描到按键8 ,而这个时候 我 按得失2,
如果中断扫描足够快的话,应该也很快就可以扫描到
)
这个后果有时候比“慢一点”更加严重和恶劣!
又比如事务2是一个只需要1s钟处理一次的任务,
那么显然事务2会白白浪费CPU的时间。
通篇想说就是 “实时性不够好”





评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

板凳
ID:7209 发表于 2021-2-20 16:28 | 只看该作者
下面不是重点
  有些书会介绍(前后台系统   +  实时内核)都是用于处理实际工业具体问题的软件编程方法,
  前后台系统结构: 就是一个死循环和若干个中断服务程序
中断用于处理系统的异步事件(前台)
相应函数完成相应的操作(后台)
这其实和我们平常写的一样,主函数+应用函数+中断,只不过我们没有给他起名字而已。
这样做的有一个弊端也是书上说的,后来我想想也对啊
如果中断时前台――― 
 为了保证任务得到及时处理,
 哪些本该在任务中执行的关键代码必须放在,中断里执行。
 (就是为了得到及时的响应)
 想想中断到可以及时处理特定的数据并且更新
而任务或应用程序是不是能立刻更新呐???
这个真未必能够及时执行。
比如:
一个项目是这样的定时开灯或关灯+WIFI 通信 + 时间屏幕显示
更新时间功能我们放在中断里
当进行WIFI 通讯 时
即时我读取到了时间改变了,但是现在没有切换到
屏幕时间显示,是不是也不会更新显示时间数组
(这个说法有些涩)
生活的例子:
现在需要烧水(设定一个时间)+看闺女
看闺女就是中断里的代码
烧水开了没有开,只有看2分闺女――― 去看看时间或水―――
烧水时间是一值在走并且水一直在升温
而作为主循环中每走几分钟才查看一回,而且这个查看时间还不固定
闺女哭了、闺女不让走、闺女要吃奶粉
这些在中断中都需要立即执行的
中断里――只有听见水壶鸣叫或时间到
才去处理烧开水灌进水壶里的动作
有个说法 “任务级响应延迟”
实时内核:(ucos)
用于管理控制器、微处理器的时间及硬件资源的软件代码。
忒重要了这句话,就是通过管理控制器的时间来控制硬件资源的软件代码;
通过把一个大的项目分解成为几个小的任务或叫线程
分配单片机的时间来间隔运行任务 1、2、3、4、5
每个任务都是的
while(1) { ;;;}这样的死循环
实时内核--- 相当于家长来负责管理这些任务的运行时间、运行顺序;

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

地板
ID:7209 发表于 2021-2-20 16:54 | 只看该作者
重点一句话:    ucos是用约70多个新函数形式为应用程序代码提供服务的。   这句话也是书上的,相当于学习“c 语言中的 头文件函数”;   我们想 用 vc6.0  在屏幕上输出 字符 是不是要用到printf() ,   而用到这个函数是不是需要 #include <stdio.h>   想输出时间 是不是需要 #include <ctime.h>   学习细节---( 时刻把握整体  我们 wifi.c + LED.C + KEY.C               都写好了,并且放在一个工程中了, 调用一下系统函数              来协调外设给我老实工作否则,              在现在的 while(1) {} 死循环中,一样也能干掉你这个线程)   不畏惧困难。 还有这个线程就是任务就是一个函数实现一个功能。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

5#
ID:7209 发表于 2021-2-25 16:23 | 只看该作者
从总体上理解一个事物
从总体上看待一个人
从总体上学习一个技能
这个总体很重要,比如学习ucos  iii 或 ii
其实如果从总体上说没啥大的区别
可也不能说没有区别  
先学会一个在说 III 和 II 都是个浮云
先白话几句  下面都是聊天
如果计划作一件事情是不是先在脑子里思考一下这个事情的大概,
如果有老婆的话是不是需要和老婆商量一下,如果商量好了或自己决定了这件事情;
就相当于 建立了 “任务” 现在建立了任务
    这个任务可能是一个 “看电视的闲任务”
也可能会遇到更紧急的任务 “只好删除这个任务” ( 更紧急的任务是优先级)
还有可能 “在开始操作这个任务的时候先挂起这个任务”(就是先记下来不执行)
顶下来的无论学习任务还是找老婆的任务一旦定下来就必须要有回应
哪怕是 个“空任务”什么也不做; 也要对他有定义有设置。

人生就和这个很类似
china main()  //中国出生  也就是建立了任务
{
   father_init(); // 父亲初始化
   mother_init(); // 母亲初始化
   house_init();  //房子初始化
   while(1)
   {
     幼儿() ;  //
     小学 ();  //
     中学 ();  //
     高中 () ; //
     大学 ();  //
     工作 ();  //
     组合家庭 () ; //
     生活 ();  //
     孕育新生命 ();  //  ------------------   china main()
     50_year(); //                            {  ;;;;;;;;
     60_year(); //                              }
     70_year(); //
     80_year(); //
     90_year(); //
     over_game(); //      
    // 删除任务本身 (); //
     // 每个人没有 return
     //
   }
}
-----------------------------------------------
china main()
{
   //
   OSInit(&err);    //  各种适龄婚前的必要条件

   OSTaskCreate("new_family");  // 创建一个新任务 创建新的家庭

   OSStart(&err);   //这个开始任务有些 “农村分家的意思”   

  }

static void new_family()
  {
      //各种初始化两个人新家的准备
     //各种初始化条件

     OS_CPU_SystickInit();   //时钟 控制一个家庭的节奏
     Mem_Init();    //初始化家庭的内存空间

    CPU_IntDisMeasMaxCurReset();  //复位清 零当前最大 夫妻吵架互相不搭理的 最大隔阂时间
    //其实这里 不写函数名字好一些
    //提前给大家提高了  困难  指数

// ( 只看注释也可以)

     OSTaskCreate( " father_taks");    //

     OSTaskCreate( " mother_task");    //

     OSTaskCreate("  girl_task");     //

     OSTaskCreate("  son_task");      //


     OSTaskDel("&newfamily ,&err");  //删除开始任务
   }



void father_task1()
  {          }
void mother_task2()
  {          }
void girl_task()
   {              }
void son_task()
  {                }

主要任务就是这四个方面还有别的比如 :

  给父母养老_task ();   // 其实这个也是应该添加在任务里的
在往后的 几十年里 这四个任务会 按照一定的 "cnts" 或 Systick  系统时钟稳定运行

  我们是生活在一个连环的任务中,我们是父母的任务也是自己的孩子的任务,
  这要在C++ 上是继承和被继承的关系是类和派生类的关系
  在 代码中我们就是一个任务,一个家庭是一个大任务;
  丈夫_apptask();
  妻子_apptask();
  孩子_apptask();
  闺女_apptask();
  分工不同,需要完成的任务也不同
  但是在 社会这个大循环中个体所赋予的任务大同小异
  分工不同造成实际需要完成的工作也不同
  比如:father_init() ; 这个初始化函数中就必须表现出挣钱的特征,
     或者说以这个为主要任务
  //其实也就是下面的 括号里的 具体名字 ,叫法不同 我觉得来表示人生还是很像的
(  III 将学习 任务 --- 任务时间片运行 --- 闲任务和阻塞延时 --- 时间戳 --- 临界段
  --- 就绪列表 --- 优先级 --- 时基列表 --- 时间片 --- 任务挂起和恢复 --- 任务删除



评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

6#
ID:7209 发表于 2021-3-1 21:40 | 只看该作者
下面继续闲聊,
  经过几个片断聊天理解ucos  也是不现实的。
  如果是赵大爷的话就是 “话疗”,
  通过谈话的 方式来理解明白我们需要掌握的东西.

  刚开始学习ucos 时也是胆战心惊苦闷无比,
  搞不懂为什么有些通俗易懂的知识非要写的那么高大上
  难以理解。
  现实社会这个大循环中,人们是围绕 “钱” 在转,
  无论多少个家庭、个人都无法离开这个 最基本的 生存需求,
  不围绕钱来 转的 我只能想到 “佛” “道” 界,
  这些如果在 ucos 系统中,我理解应该就属于 "空闲任务" 没有这些任务是不行的。
  ( 社会需要这些精神上的知识)

  既然 社会 是围绕 "钱" 这个最基本的目标在运作,
  那么 在ucos 中 有没有 也有一个也围绕 “靶点”而运行?
  通过学习 知道是有的;
  这个 点就是 “时钟”--- Systick --- 类似---心跳--
  在ucos 系统中也是围绕这个和钱类似的  "点" ,
  让任务来有条不紊的运行,  没有他行不行,万万不行。
  在学习单片机的时候也有 51、avr 、stm8、stm32、PIC。
  这些都是需要时钟的;
  现在32 特别火 就已32 来说吧!

下面不是闲聊需要理解的  
  stm32 中有一个SysTick定时器 他是一个24位倒计定时器,当计数到 0 时,
  则从reload 寄存器中自动重新装载定时器的初值,并开始新一轮的计数。
  这个定时器是24位
  这个定时器是倒计时(比如 从1000减到0 , 1000 就是初值 )
  当递减到0 时就有从 reload 寄存器中重新加载初值
  这个初值最大是多少 24位 = 0xffffff = 0b1111 1111 1111 1111 1111 1111
  2^24 = 等于多大的数值 =  16777215
  一般我们也没有用这么大的,这是可以加载的最大数值
  也就是数 数字来 cpu 也要倒序 减 - - ;
  在 ucos 中使用systick 来计数,作为 “系统时钟节拍”
  一般这个节拍是固定的用 OS_TICKS_PER_SEC 这个标志来定义了
  OS_TICKS_PER_SEC = 200 也有的 = 500
  理解大概意思就行,知道有这个标志
  为什么 ???


  下面是 delay_init();
         delay_ms();
   两个函数 ,第一个初始化 系统时钟 开启SYSTICK
   第二就是在有个别任务中需要用到的延时函数


//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟



/***********************
要说清楚还需要进入这个延时函数初始化中来
这个函数最重要的功能是给
fac_ms fac_us赋值
如果是ucos 话进入这个函数
而且这个函数还必须在没有进入中断中来初始化
void delay_init()
{
#if SYSTEM_SUPPORT_OS                                                          //如果需要支持OS.
        u32 reload;
#endif
                //选择外部时钟  HCLK/8
        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//这里选择外部时钟原 也就是外部的 8Mhz
//经过倍频后 时钟 = 72mhz = 72000 000

        fac_us=SystemCoreClock/8000000;       
//这里计算 出 延时 微秒的倍乘数值
        //为系统时钟的1/8  
// fac_us = 72000 000 /8000 000
        //fac_us = 9       
        #if SYSTEM_SUPPORT_OS                                                          //如果需要支持OS.
        //72000000/8000000 = 9
        reload=SystemCoreClock/8000000;                                //每秒钟的计数次数 单位为K          
        //      = 72000 000 /8000 000
        //  reload = 9;
        //--------------------------------------------
       
        //      = 1000 000 /200
        //      = 1000 0 /2 =5000
        reload*=1000000/delay_ostickspersec;                //根据delay_ostickspersec设定溢出时间
        //reload = reload * 1000 000 /200
        // reload = 9 * 5000
        // reload = 45000     
        // 设定LOAD 寄存器的最大值
        //就是从45000 开始递减
       
        //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右       
       
        //--------------------------------------------------------
       
        fac_ms=1000/delay_ostickspersec;                        //代表OS可以延时的最少单位          
  // fac_ms = 1000 /200
  // fac_ms = 5       
        //设定 延时毫秒 倍 乘值
       
        //+++++++++++++++++++++++++++++++++++++++++++++++
       
        SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;           //开启SYSTICK中断
        SysTick->LOAD=reload;                                                 //每1/delay_ostickspersec秒中断一次       
        //这里 LOAD = 45000
       

       
        SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;           //开启SYSTICK   
          // 启动嘀嗒定时器

#else
       
        fac_ms=(u16)fac_us*1000;                                        //非OS下,代表每个ms需要的systick时钟数   
         // 9*1000 = 9000

#endif
}                                                                    

********************/


我贴过来这个delay_Init(); 延时函数初始化是因为在有些任务中,
需要用到 delay_ms(); delay_us();
//延时nus
//nus为要延时的us数.
//比如                                                                                       
void delay_us(u32 nus)
{               
        int ticks;
        int told,tnow,tcnt=0;
        int reload=SysTick->LOAD;                                        //LOAD的值                     
        // reload = 45000
       
        ticks=nus*fac_us;                                                         //需要的节拍数                           
    //这里假设 nus = 10
        //ticks = 10*9
        //这就是需要减 90 次
        //也就是 要从45000减到
        //        44910 就到了延时时间
        // 90 = 0x5A;
        // 90 就是 从 45000 减 到 44910

       
        tcnt=0;
        delay_osschedlock();                                                //阻止OS调度,防止打断us延时
        told=SysTick->VAL;                                                //刚进入时的计数器值
        // 这是刚进入ucos 的最大值
        //
        while(1)
        {
                tnow=SysTick->VAL;       
                //得到现在的的嘀嗒时钟的计数 值
                //如果现在的值不等于 最初进入的计数值
                //
               
                if(tnow!=told)   //进行比较
                {            
                        //这里注意一下SYSTICK是一个递减的计数器就可以了.
                        if(tnow<told)   
                                //如果现在的计数值 < 刚进入的计数值
                                  tcnt+=told-tnow;  //微秒延时使用这个来计数判断
                          //tcnt = tcnt+told-tnow
                          // 1000 = 1000 +2000-2000
                          // 7998 < 8000
                          // 0 = 0 + 8000 - 7998
                          
                        else
                                tcnt+=reload-tnow+told;          //ms 延时使用这个来计数
     // tcnt = tcnt +(reload-tnow+told)
     // 0 = 0 + ( 45000 - 1000 +2000 )
     told=tnow;
                        if(tcnt>=ticks)break;                                //时间超过/等于要延迟的时间,则退出.
                }  
        };
        //这个 while(); 等待函数
        //就是经过 不停的
        //tnow = tnow -0x14
        //每次循环一次就减20 个计数
        //直道tcnt = 6D
        //这个时候 大于 6D> 5A
        //就直接退出了定时
        //这里还有个发现就是 实际上定时 us= 10
        //多计数了 19 个计数
        delay_osschedunlock();                                                //恢复OS调度                                                                            
}
//通过不断的循环仿真还是有好处的
//特别对初始化函数 ucos 启动可以很好的理解
//启动是如何运行的
//这个是有些复杂的启动嘀嗒时钟情况
//在有的代码中只是启动了 systicks 使能了一下
//这里复杂就是有 延时函数 并且是不允许中断的延时
//还有就是 延时是有范围
//以前可能没有注意到这些
//在就是 野火的代码在 启动嘀嗒定时器的函数中是有错误的
//SystICK->CTRL| = 0X07; 在实际代码中不是这样写的
//但是 他计算后的情况和上面类似
//这就造成有些位设置不对
//会出现设置过后不能起到作用
//我也是看到有网友碰到了这种情况
//



评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

7#
ID:839835 发表于 2021-3-2 15:22 | 只看该作者
很好的文章
回复

使用道具 举报

8#
ID:7209 发表于 2021-3-4 20:21 | 只看该作者

谢谢鼓励
  delay_ms();
   delay_us();
  delay_Init();
  这三个函数 感觉没有 说明白
  这几天一直在 想如何在 补充一下
  我的想说的 是
  1  ucos  中的时钟节拍 是如何 算出来的
  2  在任务中如果要使用 delay_ms(); 是如何使用的 他是如何工作的
      2 -1   还有就是 本来 备案中有 程序一步一步是如何 运行的  可 想 不好 最好的表达方式
  
回复

使用道具 举报

9#
ID:7209 发表于 2021-3-16 21:22 | 只看该作者
ucos 的任务想了好几天
   也写了些稿子,总觉的都不够能清楚简单明了的说明这个任务
   任务在书上说了很多,包括一些视频分了好几段来说明
   而我又想不依靠书上了语言来说书上的知识
   这就造成好些“歪理邪说”很有可能误导一些知识点的解释
   
还是开始吧
   任务可以理解 --- 生活中的一项任务比如烧水
                                    比如洗菜
                   比如 炒菜、比如洗衣服、比如吃饭、比如收拾屋子都是在家庭内
                   围绕特定固定地址 的活动
   这些活动 可以允许 颠倒 顺序
   
   这些活动 也可以 根据需要切换 先后
   
   这些活动 也 有 必须立即执行的 中断任务  
     比如 水开了
     比如 12点了 必须中断一切别的活动 先 吃饭在说
         
   
   这些活动也 可以 同步一起做
       比如洗衣机洗衣服的时间内可以做饭
       洗衣服的时间内可以拖地
       这好像任务同步了是不是???

   这些活动是谁来安排先后顺序?
    一般有一个当家的老婆媳妇女朋友来安排
    如果是一个没有返回的单身汉就好比一个
    一家总要有一个说话算数的
    一家总要有一个来协调安排先做啥在做啥的
    一家总要有一个 “总管” 的
    void main("有继承没有子类的函数")
     {
      //0
      //1
      //20
      ......
      //110
      ......
     //活够了                        
     }                                       

    //想想就悲哀
   这些活动 还有一些 比如需要烧水 但是现在水瓶内还是满的
                     比如每天需要拖地 但是现在地板还很干净
                     比如需要做饭 但是你或你和媳妇都不饿
                     这是不是就相当于 有需求 但是先挂了起来不执行

  先说 一家的 “ 总管” 吧!
  她   --- 是如何管理你和你的家的
  她   --- 是如何给你分配干活的工具的
  她   --- 是如何给你分配先干啥后干啥的
  她   --- 是如何给你划的道道的

  在实际应用程序设计中,通常需要把握完成的工作分成多个任务
  每个任务只负责一项工作
  一个任务是一个 “线程”(计算机离不开叫线程的)
  实际上早期只有一个CPU
  某刻只能做一个任务
  ucos 支持多任务管理
  允许有无数个任务
  但是一般受限CPu 和存储器的容量
  
  多任务其实就是多个任务间的切换和调度
  在任务相继执行过程中
  CPU 的使用权在任务间切换
  
  创建后的任务函数是这样的
  
  void Task_task1(void* p_arg)
  这里我想说的是
  void *p_arg
  这和在普通函数的形参一样
  这个参数是 void 型指针
  该指针是一个通用的指针变量
  可以向任务传递一个 “变量地址”
  传递一个结构体
  甚至 传递一个函数入口地址


   生活中我们处理一个大问题的时候通常都是将这个问题“分而治之”,
   把大问题分成多个小问题,
   小问题被逐步的解决掉,
   大问题也就随之解决了。
   那么这些小问题就可以看成是很多个小任务。
   
   在我们设计复杂、大型程序的时候也是一样的,
   将这些负责的程序分割成许多个简单的小程序,
   这些小程序就是单个的任务,
   所有的小任务融洽的工作,
   最终完成复杂的功能。
   在操作系统中这些小任务可以并发执行,
   从而提高CPU的使用效率。
   
   UCOSIII就是一个可剥夺的多任务系统,
   我们使用UCOSIII的一个重要 的原因就是它的多任务处理能力。  

(
任务说白了就是一个大的工作分若干个人来共同完成
每个人就相当于与一个独立的任务
最后在系统的协调下
共同完成这项工作
)

评分

参与人数 1黑币 +20 收起 理由
admin + 20 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

10#
ID:86450 发表于 2021-3-17 08:40 | 只看该作者
这么好的文章 。。
回复

使用道具 举报

11#
ID:7209 发表于 2021-3-18 20:15 | 只看该作者
/***********************************************
感谢版主不辞辛苦的给予的“黑币”奖金鼓励
感谢各位老师给予的夸奖评价
在学习的路上不敢骄傲
在项目的路上老板
表扬的好似只有 0 回和 1回
更多的是

来 小X 这个需求在改改
   小X 这个功能能不能在添加一个新的创意
   小X 这个逻辑有些菜
   小X 这个项目进展有些慢
   小X 这个设备怎么设计的有bug
   ...........
   ...........
   第一年我羡慕大佬
   第二年我羡慕老板
   第三年我羡慕搬砖
   第四年 ......搬砖
  ........     搬砖
  ........搬砖
  ........ 搬砖
  .........搬砖
  (还想写,但是咱是来学习的)

后来想想就是个菜鸟
适合搬砖
其实想想
无论多么牛逼
都是个打工的
在别人眼里都是个工具

除非达到前几年
航天行业的那个兄弟
自己走了
那帮人玩不转
************************************/
//一直想从整体上说任务
//分开了说
//老忘记前面学的啥 (比较笨)
  
   任务是以何种面貌存在的呢?
   在UCOSIII中任务就是程序实体,
   每个任务就是一个 void main(); 函数
   UCOSIII能够管理和调度这些小任务(程序)。
   任务的设计也是整个软件代码的基础
   其他设计工作都是围绕一个个小任务来展开的
   
   在不看书本的知识情况下想一想
   做饭吃饭这类任务
   是不是 ----是个重复的或说周期类执行的任务
   
下馆子这类任务
是不是 ----是个事件触发类事物
   {
    //女朋友生日、老婆生日、朋友想你了陪他喝会儿
    //都是在受到外部刺激了
    //或受到外部触发了
    //才产生一次)  
   }



老婆给你买衣服这类任务
   是不是 ----是不是个单次执行类任务
   {
     //小姑娘可以一月买两件或三件衣服
     //大老爷们你一月买三件试试
     //大部分是一对鞋破了直接扔掉买对新的
     //衣服烂了小了在买一套替换
    }  

   
周期类或重复任务---- 比如做饭
   {
   准备好菜
   洗好米
   和好面
   --------这都是准备的 状态
   炒菜、蒸米、下面
   ------- 这是执行的状态
   蒸米需要10分钟
   炒菜需要5分钟
   面熟需要8分钟
   --------这是等待状态
   
  (这里一定别小看 等待的 状态这个状态忒有必要了)
   /***
   为什么这里等待很有必要
   其实等待的时间就是让CPU来
   切换不同的任务
   否则 一个任务写个 while(1) {   ......}
   一直运行不出来?
   咋运行别的任务
   这里也是系统的最好的地方
   系统内怎么切换的
   暂时不管它
   ****/
   }   
   
//下面继续伪代码
  
/**单次执行类任务-----就是女朋友给你买衣服******/
  
   鞋破了衣服烂了
   给当家的汇报---------这是创建任务

   通过了同意了
   给钱了,并且买到了----这是运行了任务
   {
    这也有可能
    给钱了同意了
    但是现在不同意去购买去执行
   }
   
   买到了试试合适
   还想要一个“机械键盘”的可爱要求
        ---- 直接删除这个任务3
  

   void my_Task_clothing(void *pdata)
   {
    //进行准备工作 定义和初始化变量
    //任务实体 完成衣服具体购买
    //任务删除 将这月买衣服的任务删除
   
    }   


/***周期类任务 做饭吃饭************************/   


   void my_Task_have_a_meal(void *pdata)
   {
    //进行准备工作
    //食材
    while(1)
     {
      //做饭
      //吃饭
      //等待延时 没到饭点,没有饿的需求。
      }
   }

/***触发类任务 有约去外吃饭**********************/

  void my_Task_eat(void *pdata)
  {
    //进行准备工作
    //准备穿的衣服
    //准备好酒
    //化妆
   while(1)
    {
      //等待朋友发的位置
      //或给朋友发吃饭的位置
      //也就是获取别人的请求
      //接受到消息后
      //开始一起吃饭
      //聊天
      //等待下一次彼此聚餐
    }
   }



评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

12#
ID:7209 发表于 2021-3-19 17:48 | 只看该作者
继续说说和书上没有关系的白话
  这么多任务
  有是单任务、
  有是触发类任务、
  又是周期类或重复类任务
  
  又是出外吃饭、又是买衣服、又是在家做饭吃饭
  
  这些任务应该怎么安排?
  安排不好就是-----生活的鸡飞狗跳
  这就出现一个问题----先后顺序
  那个任务应该安排在最靠前?
  那个任务可以安排的次一些?
  那个任务可以安排的靠后一些?
  哪些任务可以协调为有需求在执行的触发性任务?
  哪些任务可以安排为临时性或突发性任务?
  
  如何安排先后顺序--- 优先级 ----???
  无论几个任务这个中心非常重要
  如果不能安排好就会造成系统不能协调工作
  
  1---首先要满足这个项目对"实时性"的要求,这也是ucos吹的地方。
  2---在就是多少个任务系统协调起来最合适,想想是不是任务
      数目越少越好,要是就两个或三个四个任务
      系统跑起来都不是事
  3---在就是适当简化或屏蔽掉一些系统的多余的用不到的功能   
      这个暂时理解即可,知道在ucos中可以屏蔽一些功能
  4---任务和中断、任务和任务、重复类和单次任务之间
      联系适当或合理简化数据的同步和通信需求
      从而降低或减少对系统资源的时间上的影响
  当然,合适不合适跑跑就知道了
  实践--- 实践 ----- 实践
  

  缺一不可类任务或关键类任务
             任务优先级安排越高越好,必须保障其执行机会

  周期性频繁类任务
             任务优先级安排也要高,一般这类任务都执行时间都不会长
                 在写的时也要保证这类任务尽量简短

  快速响应类实际环境中的传感器更新数据类任务
             任务优先级安排也要高,以确保数据的实时性

  ---交换性或上下任务通信数据传递、信号数据处理类
             任务优先级安排低于快速响应类
  
  致命性危险性紧迫性破坏性非常严重性任务
             任务一般采用与中断有联系
  
  这么多都要求优先级高
  确实不好处理
  举一个例子吧
  
   中国保护动物共分为三个级别。

   一级保护动物:金丝猴、雪豹、中华白海豚、西藏野驴、梅花鹿、野牦牛藏羚羊等。

   二级保护动物:短尾猴、猕猴、藏酋猴、穿山甲、豺、黑熊、棕熊、石貂、水獭等。

   三级保护动物:黄鼬、青鼬、中华竹鼠、银星竹鼠等。

   这虽然不是一个行业的
   但是动物保护分级能很好说明就是这样的一个意思
   在国家保护动物中分1、2、3的优先性
   这也是在任务分配中各个任务的优先级   
     
前面蒸米煮面条需要等待为什么很有必要???
   答: 这是因为ucos 是完全基于优先级的操作系统
        所以一定条件下必须出让cpu占有权以便比自己优先级更低的任务
        能够运行
        这是通过调用部分系统函数来实现的
        这些函数如下表---
        一般的任务必须调用至少一个表里的函数
        只有一种情况除外
        就是单次执行的任务
        因为任务删除后肯定出让CPU
        所以可以不调用表中的函数
  
  (看看就行不用记住)
  OSFlagPend   --- 等待事件标志组的事件标志位
               (类似手机里APP的屏幕通知)
  OSQPend      --- 等待消息队列中的消息
               (类似 QQ 中别人给你打字你看到别人正在输入)
  OSSemPend    --- 等待一个信号量
               (类似摩斯密码
                 等待前线发送过来,
                 指挥所根据发送的字符
                 了解前线情况
                 还有些信号量有广播的功能)
  OSTimeDly    --- 延时
  OSMutexPend  --- 等待一个互斥信号量
                (类似对讲机 每次只能一个人说话另外一个人听)
  这些只是生活中的比喻,有些不恰当
  
  OSQPend      --- 等待消息队列中的消息
  OSTaskSuspend -- 挂起任务
  OSTimeDlyHMSM -- 延时

  上面这几个函数都能引起任务函数的时间等待

   任务由三部分组成:
   任务堆栈、任务控制块、任务函数。

任务由这三部分组成都是啥意思啊?
  答:任务堆栈 --- 理解为仓库
      任务控制块 - 理解为仓库管理员
      任务函数 --- 理解为物流司机
      现在网络购物方便实惠
      我们下单子后是给商家---这就是发送了一个请求信号量 (这里注意了)
      
      商家接收到下的单子也就是信号量开始对单子处理
      或对你购买的东西进行确认
      (商家需要和卖家知道彼此说的是啥)
     
      开始备货  (这是不是就是创建了一个任务)
      
      开始对任务进行具体操作
      货物现在需要在仓库里有个存储空间 (这就是任务堆栈了)
      货物在仓库中有进出关系
      ucos 系统在处理任务堆栈也有进出关系
      (这个和实际ucos 中一样也有先进先出或后进先出规则)
      别管是啥进出规则

      这个堆栈里或仓库里保存了这件商品的基本信息
      
      仓库管理员来管理这个商品是发给谁 (这就是任务控制块)
      仓库管理员好牛啊
      控制管理层大小是个领导
      
      库管决定后通知任务函数
      任务函数来规划是发到北京还是宇宙
      是走路地还是航空
      (这就是凭本事吃饭了自己发挥了只要能完成给客户的商品发送)
      任务其实就是个干具体工作的
      完成具体任务
      是个苦逼的打工仔
   

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

13#
ID:7209 发表于 2021-3-21 22:07 | 只看该作者
下面这几句话也是在网络上看到,感觉写的很通俗易懂供参考:

  任务状态
简单分为运行态,就绪态,阻塞态。


运行态:万事俱备,不欠东风(获得CPU控制权);

就绪态:万事俱备,只欠东风(缺少CPU控制权);

阻塞态:万事不俱备(等事件或信号),还欠东风(缺少CPU控制权);

  (运行态:老板签过字、银行正常上班有钱------可以正常发薪水)
  (就绪态:老板没签字、银行正常上班有钱 ----- 还需要等待)
  (阻塞态:老板没签字、银行正常上班没钱 ----- 还需要等待)



每个任务基本上都会游离于这三种状态。

运行到阻塞,就绪到运行称为任务切换过程。


从用户的角度看,UCOSIII的任务一共有5种状态:
    1、休眠态:
       任务已经在CPU的flash中了,
       但是还不受UCOSIII管理。

    2、就绪态:
       系统为任务分配了任务控制块,
       并且任务已经在就绪表中登记,
       这时这个任务就具有了运行的条件,
       但是没有获得CPU 的使用权
       此时任务的状态就是就绪态。

    3、运行态:
       任务获得CPU的使用权,正在运行。

    4、等待态:
       正在运行的任务需要等待一段时间,
       或者等待某个事件,
       这个任务就进入了等待态,
       此时系统就会把CPU使用权转交给别的任务。


    5、中断服务态:
       当发送中断,
       当前正在运行的任务会被挂起,
       CPU转而去执行中断服务函数,此时任务的任务状态叫做中断服务态。


白话任务堆栈、任务控制块、任务函数



  在多任务操作系统中创建任务时,都需要指定该任务的堆栈大小,
  那么这个堆栈的作用时什么呢?
  什么情况下需要用到堆栈,以及大小不够时会产生什么异常呢?



任务堆栈的作用:


1 任务处于运行状态:
当任务在运行时,
   一般都会调用各式各样的函数,
   而函数的局部变量,参数,返回值是存在于函数栈帧里的,
   每个函数都拥有独立的栈帧,
   各个栈帧使用的空间就是任务堆栈的空间。
   所以任务堆栈的作用是用于保存函数在运行/调用过程中的参数/局部变量。

   (理解堆栈就是个仓库仓库里各种各样的商品也就是各种功能不同函数)




2 任务处于切换
当运行的任务被切换时,需要保护现场(CPU中寄存器的值),
   以便于下次恢复数据。
   所以任务堆栈的作用是用于保存CPU中寄存器的值。

3

任务(低优先级的)在运行过程中,
   随时可能被切换,
   所以CPU中寄存器的值入栈的栈位置是不确定的,
   这取决于当前任务的执行情况。



4 堆栈溢出
若堆栈的空间设置太大,会浪费内存资源。
   而设置得太小,则会出现堆栈溢出,在没有MMU功能的操作系统中,
   可能会导致系统奔溃。
所以,
   需要根据任务的情况设置合适的堆栈大小。
   同时,应避免使用递归调用函数,
   函数中局部变量的分配空间不能太大。


   任务堆栈是任务的重要部分,
   堆栈是在RAM中按照“先进先出(FIFO)”的原则   
   组织的一块连续的存储空间。

   为了满足任务切换和响应中断时保存CPU寄存器中的内容及
   任务调用其它函数时的需要,
   每个任务都应该有自己的堆栈。

   一般是这样定义的
   #define START_STK_SIZE  512        //堆栈大小
   CPU_STK START_TASK_STK[START_STK_SIZE];//定义一个数组来作为任务堆栈

   一般就是这样定义的,
   明白就行不能有压力。

  任务堆栈的大小是多少呢?

  CPU_STK为 CPU_INT32U类型,
  也就是  unsigned int类型,
  为4字节的,
  那么任务堆栈
  START_TASK_STK的大小就为:512 X 4=2048字节!




  任务如何才能切换回上一个任务
  并且还能接着从上次被中断的地方开始运行?

  这一点很重要系统是如何来切换任务的?
  它是怎么知道某一个任务运行到了那里?
  它是怎么来记录任务运行过程中产生的数据?

    这就是任务堆栈初始化的工作
    这里是不是和刚学习单片机c51有些类似

    org 0x0300
    ljmp main
main: mov r0,r1
     .....
     .....
     .....
     .....
     ajmp main

     道理是一样的
     只不过在C 中我们直接定义了几个大的ROM
     每个ROM有是连续的存储空间
     好似这样的结构



     ................
     .   c51        .     
     .              .     
     .              .     
     .  void main() .     
     .              .     
     .              .         
     .              .     
     ................

     ........................................................
     .   stm32      .                                       .
     .        ucos - iii       .                            .
     ........................................................     
     .  void main() .            .            .             .
     .              .void main() .            .             .
     .              .            .void main() .             .
     .              .            .            . void main() .
     ........................................................   


     51 的ROM 空间有过2K 、16K、64K 的
     STM32 的空间就大的多了
     这样利用ucos iii 的组织能力就能在其内部分割出来
     相同或不相同的 void main(); 来
     这其实就是任务堆栈的原型了。
     至于在单片机内部是如何分配的就不知道了。




    知道堆栈的大概了那么
    恢复现场就是重新读取保存下来的CPU的内部各个寄存器数据。


    因此在创建一个新任务时,
    必须把系统启动这个任务时所需的CPU各个寄存器初始值事先存放在任务堆栈中。

    这样当任务获得CPU使用权时,

    就把任务堆栈的内容复制到CPU的各个寄存器,
    从而可以任务顺利地启动并运行。


    把任务初始数据存放到任务堆栈的工作就叫做任务堆栈的初始化,
    UCOSIII  提供了完成堆栈初始化的函数:
    OSTaskStkInit()。

    CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,
                             void          *p_arg,
                             CPU_STK       *p_stk_base,
                             CPU_STK       *p_stk_limit,
                             CPU_STK_SIZE   stk_size,
                             OS_OPT         opt)


   用户一般不会直接操作堆栈初始化函数,
   任务堆栈初始化函数由任务创建函数OSTaskCreate()调用。
   不同的CPU对于的寄存器和对堆栈的操作方式不同,
   因此在移植UCOSIII的时候需要              
   用户根据各自所选的CPU来编写任务堆栈初始化函数。



前面我们创建了一个任务堆栈,怎么使用这个任务堆栈?

作为任务创建函数OSTaskCreate()的参数,
               函数OSTaskCreate()如下:

void  OSTaskCreate (OS_TCB        *p_tcb,
                    CPU_CHAR      *p_name,
                    OS_TASK_PTR    p_task,
                    void          *p_arg,
                    OS_PRIO        prio,
                    CPU_STK       *p_stk_base,         //任务堆栈基地址
                    CPU_STK_SIZE   stk_limit,        //任务堆栈栈深
                    CPU_STK_SIZE   stk_size,        //任务堆栈大小
                    OS_MSG_QTY     q_size,
                    OS_TICK        time_quanta,
                    void          *p_ext,
                    OS_OPT         opt,
                    OS_ERR        *p_err)

    其实看着有些复杂,其实很多参数都是一定的;
    学习过后就理解
    基础东西很重要
    特别是C 语言的基础知识和算法
    因为基础的都是实际来处理具体问题的
    即便上了系统
    系统也只是起到一个调度的作用
    平衡各个任务之间的运行

函数OSTaskCreate()中的参数p_stk_base如何确定?

   根据堆栈的增长方式,
   堆栈有两种增长方式:
   向上增长:堆栈的增长方向从低地址向高地址增长。
   向下增长:堆栈的增长方向从高地址向低地址增长

   函数OSTaskCreate()中的参数p_stk_base是任务堆栈基地址,
   那么如果CPU的堆栈是向上增长的话
   那么基地址就&START_TASK_STK[0],
   如果CPU堆栈是向下增长的话
   基地址就是&START_TASK_STK[START_STK_SIZE-1]
   STM32的堆栈是向下增长的!

  写了很多废话,只是 为了了解任务堆栈
  也写的没有趣味性了
  失败




评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

14#
ID:7209 发表于 2021-3-24 13:35 | 只看该作者
任务堆栈: 就是上下文切换的时候用来保存任务的工作环境,
           若是使用STM32的话用来保存内部寄存器值,
           无论是中断还是任务切换都要产生需要保存的
           能维持任务中断或任务切换过后的正常运行的数据。
   

2 任务控制块
是任务的抽象类型,用于描述其属性和方法

     任务控制块好似让做饭这项任务的基础包
     做啥饭
     都有啥蔬菜
     油盐酱醋
     包括盛菜的盘子
     这就是任务的基础包
     里面有完成任务的必要材料
     以及去仓库拿这些食材
     厨师好似任务控制块,控制做出的饭菜味道营养
     (那么做出的菜就好比任务函数,送到不同的餐桌,起不同的功能)
   
     用来记录任务的堆栈指针、任务的当前状态、任务的优先级别
     等一些与任务管理有关的属性的表称为任务控制块
   
     用来记录仓库里商品的存放位置、仓库有多少商品、商品的价格
     等一些商品的属性就是任务控制块也就是 库管。
     而且在仓库里任务控制块制作一个excel表就是一个表格
     
     一般仓库都是一格一格这样摆放的
     也就是说只要使用这个仓库
     这样的空格子就是连续的
     对应ucos就是一条空任务链表
     既然有空的肯定有实的任务列表
     实的就是我们实际创建任务产生的任务控制块
     也就是在仓库里摆放了 100件
     实际存在的商品
     (在ucos III中规定了47个这样的参数来说明内部的消息)
   
  
      
     任务控制块是用来记录与任务相关的信息的数据结构,
     每个任务都要有自己的任务控制块。     
     任务控制块由用户自行创建,
     如下代码为创建一个任务控制块:
   
     
     
     OS_TCB StartTaskTCB;  //创建一个任务控制块

     OS_TCB为一个结构体,
     描述了任务控制块,
     任务控制块中的成员变量用户不能直接访问,
     更不可能改变他们。

     OS_TCB为一个结构体,其中有些成员采用了条件编译的方式来确定


     任务控制块初始化函数:
     OSTCBInit()
      
     下面是ucos ii中的任务控制块初始化函数
     INT8U OSTCBInit(
       INT8U prio,   //任务的优先级别
       OS_STK  *ptos,  //任务堆栈栈顶指针
       OS_STK  *pbos,  //任务堆栈低指针
       INT16U id,      //任务的标识符
       INT16U stk_size, //任务堆栈的长度
       void *pext,     //任务控制块的扩展指针
       INT16U opt);    //任务控制块的选择项
    (这里III 对任务控制块参数达到47个忒复杂了怕一下吓懵了,
     知道大概有哪些东西就行)
    INT8U OSTCBInit(
         //商品的价格
         //商品的最多的数目
         //商品最少的数目
         //商品的标志
         //仓库对该商品分配的存储空间大小
         //该商品如果在规定存储空间放不下如何在新空间来标志
         //商品的其他选择项目
         );
    该函数啥作用?
      -- 对创建该商品放入整个仓库的商品目录中
      -- 为商品进行说明
      -- 为该商品在仓库存放(堆栈内)指定位置


   
   II 和 III 任务控制块里所包含的参数不一样
   III 的参数多多了
回复

使用道具 举报

15#
ID:901261 发表于 2021-4-6 12:50 | 只看该作者
谢谢楼主的发帖,我正在学习ucosII  ,感觉你让我受到了很大的启发
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表