野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 3918|回复: 4

[ucos] 关于裸机到UCOSIII的移植

[复制链接]
发表于 2020-6-1 16:52:23 | 显示全部楼层 |阅读模式
      刚学完UCOSIII的内核, 基本都搞懂了, 但是不清楚怎么使用
      网上都说实时操作系统尽量不要使用全局变量, 但是我用裸机编程有很多系统参数, 规范参数等用来控制全局的, 实现功能的全局变量到底该怎么去处理
回复

使用道具 举报

发表于 2020-6-12 09:13:48 | 显示全部楼层
   不得不说一点,经常做前后台编程的人,很不容易向操作系统编程转变,觉得操作系统很玄乎,自己无从下手,少了控制感;而经常做操作系统编程的人,觉得为什么要前后台编程那么麻烦,什么事情都得自己做,就不能把一些任务交给操作系统去完成,而专心于任务的实现。
   其实,我觉得这里面主要是思路以及思维方式的不同而引起的。首先,古老的前后台编程很直观简单,所有的事情都有程序员一力完成,所有的情况都被控制在手里。这个时候,程序员会有安全的感觉,如果让他立马转换成操作系统编程,立马傻眼了,我的这些工作怎么变成了任务了,这任务之间怎么会有切换,谁干的?这玩意为什么不直接操作IO口,还得交给某个我也不懂的任务去完成等等。相信大家肯定有类似的经历,至少是心理的。在前后台编程模式里面,所有的事情都是串行发生的,出了ISR程序偶尔插入执行完毕然后回到原来的主循环。对于没有操作系统概念和开发经验的程序员来说,这种自然的程序开发方式多么的无为而治。
   但是前后台编程模式有其天然的缺陷:1、CPU利用效率低下,2、编程效率低,3、移植性差。对于第一点,相应大家都知道,如主程序里面要delay几个ms一下,我们就让CPU在那里空计数玩,其实这么宝贵的CPU时间完全没必要浪费的。第二点,效率低,体现在很多方面:每个不同的项目,你都需要不同的程序组织方式,从定义到实现需要花费更多的时间;对于底层的操作,需要自己编写(尤其是移植到新的CPU上)。第三点就不用说了,一个用于STM32的程序移植到MSP430上面,它们的寄存器架构都不一样,包括固件方式都不一样,需要重写的东西太多,代价大。
   从上面这段论述,我们也可以大概解释一下只做前后台编程和操作系统编程的人员的差异和思维模式。总而言之就是,前后台编程人员对于硬件底层细节理解比较深(相对的,否则他也没法编程),而操作系统编程人员对于软件理解较深(操作系统就是要向你屏蔽硬件细节,提供API接口以及系统服务,让你摆脱datasheet和reference manual不断的烦恼)。
   因此,要想学习操作系统编程的程序员,首先要建立操作系统的基本概念,这么多年的软件工程实践已经总结出很多操作系统的基本框架和共识。然后要意识到,使用操作系统意味着什么——意味着方便快捷以及放权,方便快捷来源于操作系统编程的实现,放权意味着操作系统接管硬件并且帮你协调工作(专业点将就是调度,通俗点将就是请了一个管家)。第三,就是要敢于实践和使用,学习一项新的事物,最好的方式不是先去搞懂它是怎么工作的,而是搞清楚它能干什么,以建立直观印象。最后,如果不想止于此,去看操作系统内核实现吧,小型操作系统的内核都比较简短,一定要深入挖掘里面的东西。目前我也是在学习FreeRTOS,之前有学习uCOS-II的经验,两相交互对比,更加深了我对操作系统的理解,但是路依然很长。
   个人愚见,两种编程方式锻炼的是人的不同能力,目前嵌入式编程,操作系统的使用是很普遍的,因此有了解底层硬件的基础,对于学习和工作是有好处的。能让你了解软件如何控制硬件的工作,基础扎实,贯通上下。
   鉴于本人水平有限,文章中有错误处请读者不吝斧正。
回复 支持 1 反对 0

使用道具 举报

发表于 2020-6-12 09:18:21 | 显示全部楼层
前后台模式编程转到RTOS模式编程的痛苦历程


以下是向ba:kinsno: 讨教如何设计某个仪表上的4个数码管在上电自检时显示、参数设置时的显示以及正常模式下显示测量数据的编程思路。

ba:
我要设计的显示任务需要完成的功能如下:

       (1)、仪表上电自检后,“四个”数码管每隔500ms依次显示“1111”,“2222”,“3333”,“版本号”。
       (2)、自检完成(2秒钟)后,四个数码管开始显示测量数据
       (3)、用户按面板上的《编程》键后,四个数码管显示“待设置的参数值”,同时当前正编程的数码管位置开始闪烁
       (4)、当编程状态时,用户按下面板上的《确认》键
              (a).如果参数在合法的范围内,则保存当前参数值,然后自动将下一个待设置的参数值显示在数码管上;
              (b).否则,当设置的参数值超出范围,则数码管显示“Err”,同时数码管持续1秒钟内都显示“Err”,1秒钟后,四个数码管重新将初始的参
                  数值显示在数码管上。

               我设计的显示任务显然是一个周期性任务。其一般格式如下
               task ()
               {
                   初始化;
                   while (1)
                  {
                       显示任务代码;
                       延时节拍;   // 系统提供的API函数
                  }
               }
     问题的关键是其它任务和显示任务之间通过全局变量进行通信,同时显示任务自身又要修改通信的全局变量(例如:参数设置错误时,如果1秒钟到时,会自动修改状态机),不知道该如何办?


ba:
   操作说明:
     1、单片机上电后,状态机 status = "仪表上电自检"
     2、2秒钟后,数码管显示完“版本号”后,自检完成,状态机 status = "测量"
     3、用户按下面板上的《编程》键后,状态机 status = "编程"
         按键任务切换状态机从 “测量”---> “编程”
     4、状态机 = “编程”时,用户按下面板上的《确认》键
        如果参数设置正确,则保存当前参数到EEPROM,同时数码管自动显示下一个待编程的参数。 状态机仍然等于“编程”状态。
        如果参数设置错误(超限),则状态机=“编程错误”
     5、状态机=“编程错误”
        数码管显示“err”,同时开始计时,当计时1秒钟到时后,自动切换状态机,从“编程错误”--->“编程”

     
  以下是我的OS代码构思,请指正:   
      设计思路,利用状态机,保存当前显示任务正在处理哪个分支,问题的焦点是:
           其它任务和显示任务之间通过全局变量进行通信,同时显示任务自身又要修改通信的全局变量(例如:参数设置错误时,如果1秒钟到时,会自动修改状态机),不知道该如何办?  


  INT8U status = "仪表上电自检";    // 状态机,确定显示任务当前正在哪个分支
  void TASK_DISPLAY()   
  {   
     INT8U st1;   
     static INT8U self_n = 0;   // 用于记录“自检”已经进行了几秒钟
     static INT8U err_time = 0; // 用于记录编程错误时,显示"Err"时间已经进行了多久
     while (1)   
    {   
        OS_ENTER_CRITICAL();   
        st1 = status;          // 使用全局变量进行数据通信   
        OS_EXIT_CRITICAL();   

        switch (st1)   
       {   
          case 仪表上电自检:   
               self_n++;         //  
               switch (self_n)
               {
                   case 0:
                      数码管显示 1111;
                      break;
                   case 1:
                      数码管显示 2222;
                      break;
                   case 2:
                      数码管显示 3333;
                      break;
                   case 3:
                      数码管显示 8888;
                      break;
                  case 4:
                      数码管显示 “仪表版本号”;
                      break;
                   default:
                      status = “测量”; // 自检完毕,切换状态机="测量"
                      break;
               }
               break;   
           
          case 编程模式:   
               四个数码管显示待修改参数的参数值
               ; 还要处理在编程时,数码管闪烁(用于指示当前正在编辑哪一个数码管对应的数值)
               break;   
         case 编程错误:   
               数码管显示 "Err";
               err_time ++;
              if (err_time >= 2)
              {
                  err_time = 0 ;
                  status = “编程”; // 显示"Err"1秒钟后,切换状态机="编程"
              }   
              break;   
         case 测量模式:   
              四个数码管显示测量数据
              break;   
      }   
   
      OSTimeDelay(50); // 延时节拍(延时500ms)      
  }   

  }



kinsno:我明白了,你纠结的地方是哪?你无非就是纠结在“没有消息的时候,任务是挂起的”。
          整个系统中,肯定是有挂起的时候,但是不要紧啊,挂起了又怎么样?它还不是在显示你上一次的数据,要么是按键数,要么是AD采样数啊。
          在SWITCH外面的时候,我们接收ADI消队队列的时候,如果没有消息队列过来,它就停在你开机初始态啊,因为即便是你的AD也不是一直在显示的,你习惯了用前后台方式来操作,  事实上,AD采样显示的时候,AD采样的这段时间,你的系统不就相当于挂起了吗?

         /*
        这里的核心是状态机或者是分支,不管是按键发送消息队列,还是AD发送消息队列,都是用这同一个消息队列。利用一个消息队列或短消息组成的数组,比如NUM[10],好,我们规定
        NUM[0]=1            表示是采样任务,那么后面紧跟着的数据就是AD数据,我们解析出来显示。
        NUM[0]=2        表示是按键动作了,那么后面紧跟着的是按键数据,然后我们解析按键代码
       */

kinsno:不知道你看明白了没有?
        你在没有按键,和AD采样的时候,你的CPU还不是在空运转,这段时间不就是所谓的任务挂起了嘛。难道你的任务一直是被显示霸占着的,显示只不过是我们眼睛看着,感觉得它一直在显示,事实中间还有大量的时间是空运转。

        关键有两点:
        1、理解前后台中系统空跑的时候,比如无聊的延时,AD采样,按键中间的消抖,这不都是空跑,这不就是任务挂起吗?
        2、接任消息队列的时候,关键要设计好这个消息队列,要把所有对显示任务发送的各种情况,规划在这个里面,在你这里貌似只有按键和AD采样值。

ba:   你说的上述两点,只适合AD采集和串口等事件(事件,是瞬间发生的动作)
       例如:采样任务采集完成后,给处理任务发送一个消息。
                  处理任务在没有接收到消息时,被挂起。
                  当处理任务接收到采样任务发送的消息时,处理任务开始计算。

             串口中断接收到字符后,发送一个消息给“串口分析”任务。
                 “串口分析”任务没有接收到消息时,被挂起。
                  当 “串口分析”任务接收到消息后,开始运行,进行判断分析,是否已经接收到完整的一个数据包。

     但是我上面提到的“显示任务”不能按照“采样任务”和“串口分析”任务那样设计,必须设计成周期性任务,原因如下:
         
         (1)、在测量状态时,
                 “显示任务”必须实时显示测量数据
                   如果“显示任务”被挂起,如何“实时”呢?
         (2)、在编程状态
                 “显示任务”必须动态刷新数码管,例如当前正在编程第1个数码管上的数值,则该数码管必须一闪一闪(每隔500ms)的,用于指示当前正在编程第1个数码管的数值

          (3)、在编程状态,用户按下《确认》键时,如果参数设置错误
                 则数码管必须持续1秒钟内显示“Err”,当1秒钟到时后,又必须将原始参数值显示到数码管上。同时当前正编程
                 的数码管上开始一闪一闪的。

kinsno:
(1)、在测量状态时,
                 “显示任务”必须实时显示测量数据
                   如果“显示任务”被挂起,如何“实时”呢?
---------------------------------------------------------------
答:显示任务挂起来,是因为没人给它发消息了;如何实时?当然是采样任务给显示任务发消息,发一次,显示任务就接收一次,接收了就显示,这不就实现实时了。

…………………………………………………………………………………………………………………………………………
         (2)、在编程状态
                 “显示任务”必须动态刷新数码管,例如当前正在编程第1个数码管上的数值,则该数码管必须一闪一闪(每隔500ms)的,用于指示当前正在编程第1个数码管的数值
------------------------------------------------------------------------
答:在编程状态,它是一个死循环啊,还是可以继续接收按键消息;500ms是很快的,你可以弄成无等待接收,或者有定时接收,接不着就过去延时;接着了就显示啊。
假如你的按键发送消息之后,显示任务正在500ms之内,没关系,等500ms完了,回到接收消息处,它还是能正确收着按键消息,然后解析。事实上在这部分按键里,你没有必要弄的这么紧迫,因为它不是中断类任务,不是十万火急的任务。完全可以等500ms完了以后再来接收。

---------------------------------------------------------

大概框架如下:
Tsak_Disp()
{
  while(1)  //第一个死循环,接收按键或者AD采样
{
      收着AD就显示。
      收着按键,进入编程状态,就进入下面这个死循环
      while(1)
     {
         限时接收
         500ms 刷新形成闪烁
         if (确认键)
         {
             判断是否有错,出错err
             否则,退出。
         }      

      }
  }

}

ba:
答:显示任务挂起来,是因为没人给它发消息了;如何实时?当然是采样任务给显示任务发消息,发一次,显示任务就接收一次,接收了就显示,这不就实现实时了。
//------------------------------------------------------------------------------------------------------------


   你这样的做法是:

            键盘任务给显示任务发送“状态机status 消息”,显示接收到消息后就开始显示,否则挂起。


   这样做法有缺陷,疑问如下:
      (1)、进入“编程”状态后,就一直在“编程状态”下(除非按某一个退出键,退出编程状态,返回到测量状态)。
             难道按键任务也要定时发送“状态机status”消息吗?

             通常按键任务都设计成如下格式:
                       有按键按下,则发送按键消息。

      (2)、仪表上电时,status = “上电自检”,仪表自检结束后,状态机status=“测量”
             显然它不受“按键任务”管辖,又如何给“显示任务”发送消息呢?

ba:
我这样设计显示任务,有什么地方不对呢?请指正。

      我也对自己设计的显示任务不满意,原因如下:

             (1)、键盘任务,当检测到《编程》键按下后,会修改全局变量“status”。
                          使得 status 从 “测量模式”---> “编程模式”
             (2)、显示任务通过全局变量中获取“status ”
             (3)、显示任务发现 status = “仪表上电自检”,同时3秒到时后,会主动修改全局变量“status”。
                          使得 status 从 “仪表上电自检模式”---> “测量模式”
             (4)、键盘任务发现 status = “编程模式”,当用户按下《确认》键,保存刚刚修改的内部参数时,
                          如果发现所设置的参数值超出范围,也会主动修改全局变量“status”。
                          使得 status 从 “编程模式”---> “编程错误模式”              

             (5)、显示任务发现 status = “编程错误模式”,当1秒到时后,也会主动修改全局变量“status”。
                          使得 status 从 “编程错误模式”---> “编程模式”              

   框架示意程序如下,请指正。



INT8U status = "仪表上电自检";    // 全局变量:状态机,确定显示任务当前正在哪个分支
  void TASK_DISPLAY()   
  {   
     INT8U st1;   
     static INT8U self_n = 0;   // 用于记录“自检”已经进行了几秒钟  
     static INT8U err_time = 0; // 用于记录编程错误时,显示"Err"时间已经进行了多久  
     while (1)   
    {   
        OS_ENTER_CRITICAL();   
        st1 = status;          // 使用全局变量进行数据通信   
        OS_EXIT_CRITICAL();   

        switch (st1)   
       {   
          case 仪表上电自检:   
               self_n++;         //   
               switch (self_n)  
               {  
                   case 0:  
                      数码管显示 1111;  
                      break;  
                   case 1:  
                      数码管显示 2222;  
                      break;  
                   case 2:  
                      数码管显示 3333;  
                      break;  
                   case 3:  
                      数码管显示 8888;  
                      break;  
                  case 4:  
                      数码管显示 “仪表版本号”;  
                      break;  
                   default:  
                      status = “测量”; // 自检完毕,切换状态机="测量"  
                      break;  
               }  
               break;     
            
          case 编程模式:   
               四个数码管显示待修改参数的参数值  
               ; 还要处理在编程时,数码管闪烁(用于指示当前正在编辑哪一个数码管对应的数值)  
               break;     
         case 编程错误:     
               数码管显示 "Err";  
               err_time ++;  
              if (err_time >= 2)  
              {  
                  err_time = 0 ;  
                  status = “编程”; // 显示"Err"1秒钟后,切换状态机="编程"  
              }     
              break;     
         case 测量模式:     
              四个数码管显示测量数据  
              break;     
      }         
      OSTimeDelay(50); // 延时节拍(延时500ms)      
  }     
  }

kinsno:
晕倒。先解答你上面的问题,再给你捋一遍。
…………………………………………………………………………………………
   这样做法有缺陷,疑问如下:
      (1)、进入“编程”状态后,就一直在“编程状态”下(除非按某一个退出键,退出编程状态,返回到测量状态)。
             难道按键任务也要定时发送“状态机status”消息吗?

             通常按键任务都设计成如下格式:
                       有按键按下,则发送按键消息。

      (2)、仪表上电时,status = “上电自检”,仪表自检结束后,状态机status=“测量”
             显然它不受“按键任务”管辖,又如何给“显示任务”发送消息呢?

答:
(1)、难道你退出编程状态,是自然退出?不需要按任何键吗?从你上文描述中,明显是需要按“某一个键”退出的嘛。所以,你按下这个按键,理所当然要给显示任务发送消息,由这个消息去通知显示任务退出编程状态了。
(2)、你这个上电的四个状态“1111、2222、3333、4444”等,这些东西是在显示任务一开始的时候,就显示的嘛,显示完了,再进入死循环while(1),他们是开机状态下被执行一次就终结的一段代码。
……………………………………………………………………………………………………………………………………………………
你还停留在前后台的那种模式,从你用UCOS开始,你就得彻底抛弃全局变量这种东东,完全利用通信机制。
我来设计一下代码思路,仅代表我个人想法和思路。

------------------------------------
首先,定义一个消息队列MSQ[10](10只是我打个比方的,实际上是怎么样的,要根据实际状态去设计),这个MSQ[10]既要传递AD采样值,也要传递按键值,如果还有别的值,同样也要传递。

那么我们规定:
MSQ[0]=1  ----表示有按键按下
MSQ[0]=2  ----表示有AD采样过来
MSQ[0]=3  ----表示其它的什么功能,反正由我们自己设计。

在MSQ[0]=1时,MSQ[1]=1:表示1键,MSQ[1]=2:表示2键,MSQ[1]=3:表示3键,MSQ[1]=4:表示4键,MSQ[1]=5:表示5键,其它类推

第一个任务---扫键任务
TASKKEY
{
    while(1)
    if(有按键按下)
    {
        //要把按键值打包到MSQ里面去,如
        MSQ[0]=1  //因为我们规定MSQ[0]=1 表示有按键按下
        MSQ[1]=? //这个值具体参照你自己设定的协议来
        MSQPOST……//发送消息队列

    }

}

第二个任务---AD采样任务
TASKKEY
{
    while(1)
    if(有新采样值形成)
    {
        //要把按键值打包到MSQ里面去,如
        MSQ[0]=2  //因为我们规定MSQ[0]=2 表示有新AD采样
        MSQ[1]=? //这个值具体参照你自己设定的协议来
        MSQ[2]=?  //把具体的采样值放到MSQ[1]-MSQ[10]中来
        MSQPOST……//发送消息队列

    }

}

第三个任务----显示任务
TASKDISP
{
   显示1111,延时500
   显示2222,延时500
   显示3333,延时500
   显示4444,延时500   
   while(1)
   {
      限时等待接收消息(具体的API我忘了)
      //如果接收到消息了,那么就开始解析,根据上面我们设定好的MSQ
      if(MSQ[0]==1) //有按键按下
      {
         switch(MSQ[1])//解析各个按键,然后根据各个按键进行操作
         {
              case 1:编程模式
              {
                  显示编程模式……   
                  while(1)
                  {
                     等待按键消息来;
                     同样,解析按键值
                     if(确认)
                     {
                           退出编程模式
                     }
                  }         

               }
              case 2:什么模式
          }
      }
      else if(MSQ[0]==2)//表示有AD采样
      {
          显示AD值
      }
      
      延时500

    }
}


ba:
就是,我目前的难点还是停留在前后台编程思路中。


   
   我看到一般的教材上都说:

             (1)、为了减少任务个数,使得操作系统调度尽量简单些,要把差不多功能的任务合并到一个任务中。
             (2)、显示任务一般设计成“周期性”任务。
             (3)、如果AD采样在“AD中断服务程序中采集”,可以设计成如下形式
                       (3.1)一个AD中断程序
                       (3.2)一个AD处理任务  
                              AD处理任务设计成事件型任务,当接收到"AD中断程序"发送的消息,就立即开始得到CPU控制权,
                              因此为了快速得到CPU控制权,应该把“AD处理任务”的优先级设置的比较高。

                    当“AD中断服务程序中采集”完成,发送消息给AD处理任务,能够确保“AD处理任务”立即运行。

    普通的任务设计我能够明白(一般教材上有叙述),比较复杂的任务设计一般教材上没有讲解,因此消化过程比较缓慢。

  最主要是把前后台中比较混乱的结构如何编排成一个一个通信过程比较清晰的任务,是我目前的难点。

kinsno:
如果你要用RTOS,你一定要抛充掉你的全局变量的想法,所有任务之间,仅仅靠邮箱或消息队列。这个UCOS毒害了多少人了。
呵呵。









回复 支持 反对

使用道具 举报

发表于 2020-6-12 09:29:33 | 显示全部楼层
本帖最后由 ba_wang_mao 于 2020-6-12 09:33 编辑

       当跨入了rtos编程大门之后,回想起当初学习时脑子里想的问题,感觉很正常。当从一种编程模式向另外一种新编程模式变迁时,必然会套用原有的编程模式来思索问题。
        总结:
              RTOS编程一定要抛弃全局变量的概念(前后台编程中哪个函数想使用全局变量,使用就是了,想在哪使用,就在哪使用,太方便啦!),RTOS编程使用消息邮箱、消息队列传递变量值,一个任务要想获取另外一个任务的多个变量值,只能使用消息队列方法获取。一个任务要想获取另外一个任务的一个变量值,可以使用消息队列方法获取,也可以使用消息邮箱获取。
              前后台编程和FreeRTOS编程最大的区分就是:
                (1)、 前后台编程
                             以面向函数进行程序设计,定义的全局变量程序员想在哪个函数下使用都可以,可以随意使用。
                 (2)、FreeRTOS编程
                             以面向任务进行程序设计。任务之间通信需要使用操作系统提供的API进行传递。

                  
打个比方: 传统前后台编程相当于你到菜市场买菜,你把钱从自己钱包里掏出来交给菜贩,菜贩收到钱后,把菜交给你。交易的双方都全程可见(就像你在前后台编程时,可以随意在每个函数下使用全局变量一样)。
                 FreeRTOS编程相当于你在淘宝上买东西,你必须要按照淘宝的规程来进行交易,买家先下单,然后把钱打到淘宝帐号下,   卖家收到订单后发货,你收到货后确认签字,淘宝再把钱打给卖家。(买家和卖家相当于2个任务,交易的双方都看不到对方,只能通过系统提供的API来进行通信)

回复 支持 反对

使用道具 举报

发表于 2020-6-12 09:37:34 | 显示全部楼层
言归正题,仪表问题的正解如下,以示参考:
  1. 一、规划消息队列传输协议
  2.           定义一个消息队列MSQ[10](10只是我打个比方的,要根据实际状态去设计),这个MSQ[10]既要传递AD采样值,也要传递按键值。那么我们规定:
  3.    MSQ[0]=1  ----表示有按键按下
  4.    MSQ[0]=2  ----表示有AD采样过来
  5. --------------------------------------------------------------
  6.   在MSQ[0]=1时,
  7.              MSQ[1]=1:表示【编程键】,
  8.              MSQ[1]=2:表示【确认键】,
  9.              MSQ[1]=3:表示【上键】,
  10.              MSQ[1]=4:表示【下键】,
  11.              MSQ[1]=5:表示【移位键】
  12. 在MSQ[0] = 2时,
  13.             MSQ[1] = 测量数据高字节
  14.             MSQ[2] = 测量数据低字节

  15. --------------------------------------------------------------
  16. [b]二、规划任务
  17.        规划3个任务:
  18.             1、AD采集任务         ---> 周期性任务,每隔200毫秒执行一次,采样完成,给数码管显示任务发送消息队列
  19.             2、按键任务              ---> 周期性任务,每隔50毫秒执行一次,如果采集到按键按下,给数码管显示任务发送消息队列
  20.             3、数码管显示任务   ---->等待消息队列任务
  21. 三、具体任务代码
  22.         1、第一个任务---AD采样任务
  23.             TASKAD
  24.             {
  25.                  while(1)
  26.                  {
  27.                       采集AD值;
  28.                       if (采集到采样值)
  29.                       {
  30.                             MSQ[0]=2  //因为我们规定MSQ[0]=2 表示有新AD采样
  31.                             MSQ[1]=AD采样值的高字节 //这个值具体参照你自己设定的协议来
  32.                             MSQ[2]=AD采样值的低字节
  33.                              MSQPOST……//发送消息队列
  34.                        }
  35.                       延时节拍(200毫秒)
  36.                 }
  37.            }


  38.          第二个任务---扫键任务
  39.         TASKKEY
  40.         {
  41.               while(1)
  42.               {
  43.                    扫描按键;
  44.                    if(有按键按下)
  45.                   {
  46.                         //要把按键值打包到MSQ里面去,如
  47.                         MSQ[0]=1  //因为我们规定MSQ[0]=1 表示有按键按下
  48.                         MSQ[1]=按键代码 //这个值具体参照你自己设定的协议来
  49.                         MSQPOST……//发送消息队列
  50.                   }
  51.                  延时节拍50毫秒;
  52.               }
  53.         }

  54.   第三个任务----数码管显示任务
  55.       TASKDISP
  56.       {
  57.                 uint8_t status = 0;  //0=上电默认为测量状态   1= 编程模式
  58.                 uint8_t no;//参数号
  59.                uint8_t bit;//数码管位号
  60.   
  61.                 显示1111,延时节拍500毫秒
  62.                 显示2222,延时节拍500毫秒
  63.                 显示3333,延时节拍500毫秒
  64.                 显示4444,延时节拍500毫秒   
  65.                 显示版本号,延时节拍2秒

  66.                 while(1)
  67.                {
  68.                      限时等待接收消息队列消息
  69.                      if (收到消息队列消息数据)
  70.                      {                           
  71.                           if(MSQ[0]==1) //按键按下消息
  72.                           {
  73.                                      switch(MSQ[1])//解析各个按键,然后根据各个按键进行操作
  74.                                     {
  75.                                           case 1:编程模式
  76.                                                 if (status ==0)
  77.                                                 {                                         
  78.                                                    status = 1;//进入编程模式
  79.                                                    no = 1;//参数号=1
  80.                                                    bit=4;//最右边1个数码管编程
  81.                                                    发出最右边1个数码管开始闪烁的命令
  82.                                                    取出第1个参数号对应的数值,将该数值显示到数码管
  83.                                                  }
  84.                                                  else
  85.                                                     status=0;//退出编程模式,返回测量状态
  86.                                                 break;
  87.                                          
  88.                                          case 2:【确认键】
  89.                                                 if (status ==1)
  90.                                                 {
  91.                                                    检查参数值是否合法;
  92.                                                    if (合法)
  93.                                                    {
  94.                                                        存参数
  95.                                                        数码管显示OK;
  96.                                                        延时节拍1秒
  97.                                                        no++;//参数号+1        
  98.                                                       bit=4;//最右边1个数码管编程
  99.                                                       取出no参数号对应的数值,将该数值显示到数码管
  100.                                                  }
  101.                                                  else
  102.                                                  {
  103.                                                       数码管显示ERROR;
  104.                                                       延时节拍1秒
  105.       取出第no个参数号对应的数值,将该数值显示到数码管        
  106.                                                  }
  107.                                                  break;

  108.                                           case 3:    【上键】  
  109.                                                 if (status ==1)
  110.                                                 {
  111.                                                    bit位对应的数码管上的数字+1
  112.                                                    if (bit位对应的数码管上的数字 >9)
  113.                                                        bit位对应的数码管上的数字=0
  114.              将该数值显示到数码管
  115.                                                 }
  116.                                                  break;   

  117.                                           case 4:    【下键】  
  118.                                                 if (status ==1)
  119.                                                 {
  120.                                                    bit位对应的数码管上的数字-1
  121.                                                    if (bit位对应的数码管上的数字 < 0)
  122.                                                        bit位对应的数码管上的数字=9
  123.    将该数值显示到数码管
  124.                                                  }
  125.                                                  break;
  126.                                            case 5:【移位键】     
  127.                                                 if (status ==1)
  128.                                                 {
  129.                                                    bit-1;   //左移一位数码管
  130.                                                    if (bit <= 0)
  131.                                                        bit=4
  132.                                                    发出前一个数码管开始闪烁命令
  133.                                                 }
  134.                                                 break;

  135.                                            default:
  136.                                                 break;
  137.                                       }
  138.                               }
  139.                               else if(MSQ[0]==2) //ad采样消息
  140.                               {
  141.                                   读取AD采样数据
  142.                                   if (status ==0)//在测量状态
  143.                                      显示AD采样数据到数码管
  144.                                }                                
  145.                                                
  146.                       }//if (收到消息队列消息数据)
复制代码
回复 支持 1 反对 0

使用道具 举报

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

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-4-29 13:47 , Processed in 0.033765 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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