久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 2037|回復: 1
打印 上一主題 下一主題
收起左側

uCOS-II之任務間通信、軟件定時器補充

[復制鏈接]
跳轉到指定樓層
樓主
ID:763998 發表于 2020-6-29 09:19 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式

任務間通信

5.4.1 信號量

信號量是用來保護共享資源用的,表示共享資源的個數。共享資源被占用一個,信號量的指會減1,共享資源被釋放一個,信號量的值會加1。

(理解:USB口的占用與釋放,假設一個電腦有3個USB,插入一個USB設備,電腦的USB資源會減少1,此時電腦的USB資源還有2;拔出USB設備,電腦的USB資源會增加1,此時電腦的USB資源有3個USB)

其實信號量的本質就是一個操作系統計數器(0~65535),實際用于實現任務間同步執行。

需要掌握的函數如下:

實驗代碼如下:


  1. #include "main.h"
  2. #include "includes.h"

  3. /** 任務0 **/
  4. #define TASK0_PRI 8 //任務優先級
  5. #define TASK0_STK_SIZE               256 //任務棧大小
  6. OS_STK              stack0[TASK0_STK_SIZE]; //數組作為任務堆棧
  7. void Task0 (void *p_arg); //函數聲明

  8. /** 任務1 **/
  9. #define TASK1_PRI 9 //任務優先級
  10. #define TASK1_STK_SIZE               256 //任務棧大小
  11. OS_STK              stack1[TASK1_STK_SIZE]; //數組作為任務堆棧
  12. void Task1 (void *p_arg); //函數聲明

  13. /** 任務2 **/
  14. #define TASK2_PRI 10 //任務優先級
  15. #define TASK2_STK_SIZE               256 //任務棧大小
  16. OS_STK              stack2[TASK2_STK_SIZE]; //數組作為任務堆棧
  17. void Task2 (void *p_arg); //函數聲明

  18. OS_EVENT  *sem;//全局變量,創建信號量、等待信號量、發布信號量都需要用到該指針。

  19. int main(void)
  20. {
  21.               OSSysTickInit();//滴答定時器初始化
  22.               USART1_Init(115200);//初始化串口,用于調試
  23.             
  24.               OSInit();                             //操作系統初始化
  25.             
  26.               sem=OSSemCreate(2);//初始化--默認創建2個信號量
  27.               OSTaskCreate (Task0,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創建一個任務,任務名:Task0
  28.               OSTaskCreate (Task1,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創建一個任務,任務名:Task1
  29.               OSTaskCreate (Task2,NULL,&stack2[TASK2_STK_SIZE-1],TASK2_PRI);//創建一個任務,任務名:Task2
  30.             
  31.               OSStart();    //啟動操作系統
  32. }

  33. void Task0 (void *p_arg) //實現任務Task0
  34. {
  35.               u8 k=0;//給個默認值0,表示無鍵狀態
  36.               KEY_Init();//按鍵初始化,放在對應任務中
  37.               while(1)
  38.               {
  39.                             k=Key_Scanf(0);//按鍵掃描
  40.                             switch (k)
  41.     {
  42.                   case KEY_ONE: //按鍵1按下,發布一個信號量
  43.                                                         OSSemPost(sem);//發送信號量
  44.                                 break;
  45.                   default:
  46.                                 break;
  47.     }
  48.                             OSTimeDly(1);//高優先級釋放CPU,延遲不可太長,延時太長會影響按鍵的靈敏度。
  49.               }
  50. }

  51. void Task1 (void *p_arg) //實現任務Task1
  52. {
  53.               while(1)
  54.               {
  55.                             //等待一個信號量
  56.                             OSSemPend (sem,//信號量
  57.                  0,//超時時間
  58.                  0);//錯誤類型--沒有錯誤
  59.                             printf("任務1\r\n");
  60.                             OSTimeDly(100);//5ms一次滴答,100*5=500ms,打印一次
  61.               }
  62. }

  63. void Task2 (void *p_arg) //實現任務Task2
  64. {
  65.               while(1)
  66.               {
  67.                             //等待一個信號量
  68.                             OSSemPend (sem,//信號量
  69.                  0,//超時時間
  70.                  0);//錯誤類型--沒有錯誤
  71.                             printf("任務2\r\n");
  72.                             OSTimeDly(100);//5ms一次滴答,100*5=500ms,打印一次
  73.               }
  74. }
復制代碼


因為創建了兩個信號量,按下復位按鍵,同時打印出任務1、任務2。

按下按鍵1,打印出任務1,因為任務1的優先級比較高。

快速按下按鍵1,能打印出任務1,和任務2,因為他們都在等信號量。

5.4.2 互斥信號量

用來保護共享資源,但是這個共享資源只有一個。兩個任務同時操作一個硬件,這時候需要加互斥信號量保護。

(理解:電話亭的使用,假設電話亭里只有一個電話,有3個人想打電話,需要排隊,還需要等待,等待電話亭里面沒有人,排在前面的人就能進入電話亭打電話了)

其實互斥信號量的本質就是一個操作系統計數器(0-1)。

注意:互斥信號量中需要有一個空閑的優先級作為優先級反轉用,該優先級必須比所有能夠獲得該互斥信號量的優先級還高。

理解:假設能獲得該互斥信號量的所有任務的優先級分別為:4、10、11、13,則該空閑優先級的取值(0~3);在如,假設能獲得該互斥信號量的所有任務的優先級分別為:8、10、11、13,則該空閑優先級的取值(0~7)。

需要掌握的函數如下:

實驗代碼如下:


  1. #include "main.h"
  2. #include "includes.h"

  3. /** 任務0 **/
  4. #define TASK0_PRI 8 //任務優先級
  5. #define TASK0_STK_SIZE               256 //任務棧大小
  6. OS_STK              stack0[TASK0_STK_SIZE]; //數組作為任務堆棧
  7. void Task0 (void *p_arg); //函數聲明

  8. /** 任務1 **/
  9. #define TASK1_PRI 9 //任務優先級
  10. #define TASK1_STK_SIZE               256 //任務棧大小
  11. OS_STK              stack1[TASK1_STK_SIZE]; //數組作為任務堆棧
  12. void Task1 (void *p_arg); //函數聲明

  13. OS_EVENT  *mutex;//全局變量,創建信號量、等待信號量、發布信號量都需要用到該指針。

  14. int main(void)
  15. {
  16.               OSSysTickInit();//滴答定時器初始化
  17.               USART1_Init(115200);//初始化串口,用于調試
  18.             
  19.               OSInit();                             //操作系統初始化
  20.             
  21.               //創建互斥信號量
  22.               mutex=OSMutexCreate (5,//空閑優先級
  23.                         0);//錯誤類型
  24.               OSTaskCreate (Task0,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創建一個任務,任務名:Task0
  25.               OSTaskCreate (Task1,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創建一個任務,任務名:Task1
  26.             
  27.               OSStart();    //啟動操作系統
  28. }

  29. void Task0 (void *p_arg) //實現任務Task0
  30. {
  31.               while(1)
  32.               {
  33.                             //獲取互斥信號量
  34.                             OSMutexPend (mutex,//互斥信號量
  35.                    0,//超時時間
  36.                    0);//錯誤類型
  37.                             for(int i=0;i<5;i++)
  38.                             {
  39.                                           printf("任務0--%d\r\n",i);//
  40.                                           OSTimeDly(100);//
  41.                             }
  42.                             //釋放互斥信號量
  43.                             OSMutexPost (mutex);
  44.               }
  45. }

  46. void Task1 (void *p_arg) //實現任務Task1
  47. {
  48.               while(1)
  49.               {
  50.                             //獲取互斥信號量
  51.                             OSMutexPend (mutex,//互斥信號量
  52.                    0,//超時時間
  53.                    0);//錯誤類型
  54.                             for(int i=0;i<5;i++)
  55.                             {
  56.                                           printf("任務1--%d\r\n",i);//
  57.                                           OSTimeDly(100);//
  58.                             }
  59.                             //釋放互斥信號量
  60.                             OSMutexPost (mutex);
  61.               }
  62. }
復制代碼


燒錄代碼,結果如下圖:

實驗表明,假設多個任務在訪問同一資源,只有等正在訪問的任務使用完并釋放資源,下一個任務才能訪問使用。

假設不加互斥信號量進行互斥訪問,代碼如下,

結果如下:

5.4.3 消息郵箱

用于任務與任務之間交換數據(任務與任務之間的通信)。消息郵箱只能存放一則消息,消息的內容長短不限制。

需要掌握的函數:

實驗代碼:


  1. #include "main.h"
  2. #include "includes.h"

  3. /** 任務0 **/
  4. #define TASK0_PRI 8 //任務優先級
  5. #define TASK0_STK_SIZE               256 //任務棧大小
  6. OS_STK              stack0[TASK0_STK_SIZE]; //數組作為任務堆棧
  7. void SendTask (void *p_arg); //函數聲明

  8. /** 任務1 **/
  9. #define TASK1_PRI 9 //任務優先級
  10. #define TASK1_STK_SIZE               256 //任務棧大小
  11. OS_STK              stack1[TASK1_STK_SIZE]; //數組作為任務堆棧
  12. void ReceiveTask (void *p_arg); //函數聲明

  13. OS_EVENT  *mbox;//全局變量,創建郵箱、發送消息、接收消息,都需要用到該指針。

  14. int main(void)
  15. {
  16.               OSSysTickInit();//滴答定時器初始化
  17.               USART1_Init(115200);//初始化串口,用于調試
  18.             
  19.               OSInit();                             //操作系統初始化
  20.               //創建一個郵箱
  21.               mbox=OSMboxCreate (NULL);//初始化消息的地址:NULL
  22.             
  23.               OSTaskCreate (SendTask,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創建發送任務
  24.               OSTaskCreate (ReceiveTask,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創建接收任務
  25.             
  26.               OSStart();    //啟動操作系統
  27. }

  28. void SendTask (void *p_arg) //發送任務
  29. {
  30.               u8 k=0;//給個默認值0,表示無鍵狀態
  31.               KEY_Init();//按鍵初始化,放在對應任務中
  32.               while(1)
  33.               {
  34.                             k=Key_Scanf(0);//按鍵掃描
  35.                             switch (k)
  36.     {
  37.                   case KEY_ONE: //按鍵1按下,發布一則消息
  38.                                                         OSMboxPost (mbox,//郵箱
  39.                                                                                                                                             "hello 51黑");//郵箱內容
  40.                                 break;
  41.                                           case KEY_TWO: //按鍵2按下,發布一則消息
  42.                                                       
  43.                                                         OSMboxPost (mbox,//郵箱
  44.                                                                                                                                             "hello world");//郵箱內容
  45.                                                         OSMboxPost (mbox,//郵箱
  46.                                                                                                                                             "hello xixi");//郵箱內容
  47.                                                         OSMboxPost (mbox,//郵箱
  48.                                                                                                                                             "hello haha");//郵箱內容
  49.                                 break;
  50.                   default:
  51.                                 break;
  52.     }
  53.                             OSTimeDly(1);//釋放CPU使用權
  54.               }
  55. }

  56. void ReceiveTask (void *p_arg) //接收任務
  57. {
  58.               u8 *str;
  59.               while(1)
  60.               {
  61.                             //接收一條消息
  62.                             str=OSMboxPend (mbox,//郵箱地址
  63.                                                                                                                 0,//死等
  64.                                                                                                                 0);//發送成功
  65.                             printf("接收到的消息:%s\r\n",str);//開始默認打印三次,因為默認開始創建3個信號量。
  66.                             OSTimeDly(1);//釋放CPU使用權
  67.               }
  68. }
復制代碼


實驗結果如下:

按下按鍵1:

按下按鍵2:

原因是接收方接收不過來了,造成了數據丟失。這時需要引入消息隊列。

5.4.4 消息隊列

消息郵箱只能發送一則消息,獲取消息的地方如果處理比較慢就會丟失消息。消息隊列能存儲一隊消息,能很好的避免接收方處理能力弱而丟失消息的問題。隊列是一種數據結構,遵循先進先出原則。

需要掌握的函數:

實驗代碼:


  1. #include "main.h"
  2. #include "includes.h"

  3. /** 任務0 **/
  4. #define TASK0_PRI 8 //任務優先級
  5. #define TASK0_STK_SIZE               256 //任務棧大小
  6. OS_STK              stack0[TASK0_STK_SIZE]; //數組作為任務堆棧
  7. void SendTask (void *p_arg); //函數聲明

  8. /** 任務1 **/
  9. #define TASK1_PRI 9 //任務優先級
  10. #define TASK1_STK_SIZE               256 //任務棧大小
  11. OS_STK              stack1[TASK1_STK_SIZE]; //數組作為任務堆棧
  12. void ReceiveTask (void *p_arg); //函數聲明

  13. OS_EVENT  *q;//全局變量,創建隊列、發送消息、接收消息,都需要用到該指針。

  14.             
  15. void *queue[10];//隊列

  16. int main(void)
  17. {
  18.               OSSysTickInit();//滴答定時器初始化
  19.               USART1_Init(115200);//初始化串口,用于調試
  20.             
  21.               OSInit();                             //操作系統初始化
  22.             
  23.               //創建一個隊列
  24.               q=OSQCreate(queue,//隊列
  25.                 10);//隊列的大小
  26.             
  27.               OSTaskCreate (SendTask,NULL,&stack0[TASK0_STK_SIZE-1],TASK0_PRI);//創建發送任務
  28.               OSTaskCreate (ReceiveTask,NULL,&stack1[TASK1_STK_SIZE-1],TASK1_PRI);//創建接收任務
  29.             
  30.               OSStart();    //啟動操作系統
  31. }

  32. void SendTask (void *p_arg) //發送任務
  33. {
  34.               u8 k=0;//給個默認值0,表示無鍵狀態
  35.               KEY_Init();//按鍵初始化,放在對應任務中
  36.               while(1)
  37.               {
  38.                             k=Key_Scanf(0);//按鍵掃描
  39.                             switch (k)
  40.     {
  41.                   case KEY_ONE: //按鍵1按下,發布一則消息
  42.                                                         OSQPost (q,//隊列地址
  43.                                                                                                                 "lele");//需要發送的內容
  44.                                                         OSQPost (q,//隊列地址
  45.                 "學習");//需要發送的內容
  46.                                                         OSQPost (q,//隊列地址
  47.                 "不可能");//需要發送的內容
  48.                                 break;
  49.                                           case KEY_TWO: //按鍵2按下,發布一則消息
  50.                                                         OSQPost (q,//隊列地址
  51.                 "哈哈");//需要發送的內容
  52.                                 break;
  53.                   default:
  54.                                 break;
  55.     }
  56.                             OSTimeDly(1);//釋放CPU使用權
  57.               }
  58. }

  59. void ReceiveTask (void *p_arg) //接收任務
  60. {
  61.               char *strs;
  62.               while(1)
  63.               {
  64.                             //接收一條消息
  65.                             strs=OSQPend(q,//隊列
  66.                                                                                                                 0,//死等
  67.                                                                                                                 0);//錯誤類型
  68.                             printf("接收到的消息:%s\r\n",strs);//開始默認打印三次,因為默認開始創建3個信號量。
  69.                             OSTimeDly(1);//釋放CPU使用權
  70.               }
  71. }
復制代碼


實驗結果如下:

發送三條消息,接收到三條消息,沒有數據丟失。

5.4.5 補充

信號量:就理解成有多個電話的電話亭,這些電話是共享資源,當有很多人使用時,需要排隊(優先級),需要等待(等待信號量),當電話亭的電話空閑時(有信號量),就可以讓排在前面的人使用,依次使用。

互斥信號量:和信號量基本一致,理解成只用一個電話的電話亭,多個用戶要互斥使用這個電話。

消息郵箱:發送一條消息。如果消息發送太快,接收方接收不過來,會造成數據丟失。

消息隊列:發送多條消息。實現原理是把多條消息存放在隊列中。

最重要的最重要的是:

  • 概念的理解,領會
  • 代碼的實現、代碼的流程
  • 多實踐與思考

5.5 其他補充

5.5.1 延時函數

這里的延時與STM32的延遲有不同的含義,對于STM32F407,系統時鐘為21M,即21 000 000 次脈沖為1秒鐘=21 000 000個滴答為1秒鐘,UCOS的延時規定,200個脈沖為1秒鐘=200個滴答為1秒鐘,所以UCOS的一個滴答為5ms。

常用第一個函數,第二函數用來延時,會有點誤差,因為任務調度會消耗一點時間。

為什么使用第一個函數,能發生任務調度?看代碼如下:

因為函數的實現中有任務調度函數。

實際的任務調度代碼源頭在哪?進入看看

在點擊進入發現。進不了了。實際這個函數由匯編代碼實現

5.5.2 軟件定時器

實現UCOS軟件定時器需要注意兩點:

  • 打開定時器代碼,會發現創建定時器代碼都是灰色,需要修改一個宏


第二,定義一個優先級,編譯你就會發現這個優先級了,注意優先級不能設置跟其他任務的有效一樣。

編寫代碼驗證,編寫如下代碼:

   #include "main.h"
     #include "includes.h"
      
      
     OS_TMR  *tmr;//定時器。
      
     void MyCallback (OS_TMR *ptmr, void *p_arg);//回調函數
      
     int main(void)
{
               OSSysTickInit();//滴答定時器初始化
               USART1_Init(115200);//初始化串口,用于調試
              
               OSInit();                             //操作系統初始化
               //創建定時器
               tmr=OSTmrCreate (50,//第一次使用,規定10個滴答為1秒鐘--所以第一次定時5秒鐘
                    10,//第二次以后使用--定時1秒鐘
                    OS_TMR_OPT_PERIODIC,//循環模式
                    (void *)MyCallback,//回調函數
                    0,//回調函數參數
                    0,//定時器名字
                    0);//錯誤類型
              
               //啟動定時器
               OSTmrStart (tmr,//要啟動的定時器
                  0);//錯誤類型--成功
              
               OSStart();//啟動操作系統
}
  
void MyCallback (OS_TMR *ptmr, void *p_arg)
{
               printf("定時器創建成功\r\n");
}

實驗結果如下:

定時器,一次滴答多少時間?


5.5.3 其他函數

給調度器上鎖與解鎖,一般用于初始化任務。

臨界區:是被保護的區域,一般不允許被中斷,所以進入之前需要關閉中斷,出來時,要打開中斷功能。


全部資料51hei下載地址:

程序.7z (373.07 KB, 下載次數: 11)

UCOS之任務間通信、軟件定時器補充.docx (4.21 MB, 下載次數: 11)


評分

參與人數 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:328014 發表于 2020-6-30 18:19 | 只看該作者
好資料,51黑有你更精彩!!!
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 国产精品久久久免费 | 中文字幕第一页在线 | 亚洲精品一区在线观看 | 日韩在线观看一区 | 成人精品免费视频 | av无遮挡 | 中国一级特黄真人毛片免费观看 | 亚洲综合精品 | 亚洲精品乱码8久久久久久日本 | 自拍偷拍第一页 | av天天干 | 国产在线一区二区三区 | 久久久久一区 | 久草网站 | 亚洲一区综合 | 伦理午夜电影免费观看 | 成人精品一区二区 | 日韩一区二区福利视频 | 成人欧美一区二区三区黑人孕妇 | 天堂在线一区 | 日本中文字幕在线观看 | 精品久久久久久亚洲精品 | 国产精品久久久久久久久久三级 | 日韩av福利在线观看 | 精品视频一区二区在线观看 | 国产亚洲成av人片在线观看桃 | 精品国产91 | caoporn视频在线 | 免费精品国产 | av在线成人 | 亚洲喷水 | 看一级毛片视频 | 可以看黄的视频 | 在线不卡一区 | 欧美极品少妇xxxxⅹ免费视频 | 午夜电影福利 | 成人免费毛片在线观看 | 免费看的黄网站 | 欧美日日 | 国产一区二区精华 | 天天综合网天天综合色 |