<strike id="kiyse"></strike>
  • <tr id="kiyse"></tr>
  • <strike id="kiyse"></strike><samp id="kiyse"><tbody id="kiyse"></tbody></samp>
    <strike id="kiyse"><s id="kiyse"></s></strike>
    <tr id="kiyse"></tr>
    <noframes id="kiyse"><code id="kiyse"></code></noframes>
    <th id="kiyse"></th>
    <samp id="kiyse"></samp>
  • <th id="kiyse"><s id="kiyse"></s></th>
  • 移植Real Time Linux到英創(chuàng)工控主板

     2017-10-16     作者:廖光澤         
    文章標(biāo)簽:C/C++

      為了使英創(chuàng)的嵌入式主板產(chǎn)品滿足工業(yè)實(shí)時(shí)控制的應(yīng)用需求,我們對(duì)ESM335x系列主板的內(nèi)核進(jìn)行了調(diào)整,重新移植了Linux 4.1.7的系統(tǒng),在Linux 4.1.7的基礎(chǔ)上使用了kernel的RT PATCH(實(shí)時(shí)補(bǔ)丁),并且修改了相關(guān)接口的驅(qū)動(dòng)代碼,來(lái)保證操作系統(tǒng)的實(shí)時(shí)性。關(guān)于實(shí)時(shí)Linux(又稱RT Linux)系統(tǒng)的相關(guān)技術(shù)知識(shí)可以瀏覽網(wǎng)站:https://wiki.linuxfoundation.org/realtime/documentation/start進(jìn)行了解。我們將在下面介紹如何編寫面向?qū)崟r(shí)控制的應(yīng)用程序,以及在實(shí)時(shí)Linux系統(tǒng)和標(biāo)準(zhǔn)Linux系統(tǒng)環(huán)境中,應(yīng)用程序?qū)τ布录憫?yīng)延時(shí)的測(cè)試結(jié)果。


    實(shí)時(shí)應(yīng)用程序編寫


      盡管實(shí)時(shí)Linux系統(tǒng)對(duì)內(nèi)核做了大量的改動(dòng)來(lái)提供系統(tǒng)的實(shí)時(shí)性,但對(duì)于應(yīng)用程序來(lái)說(shuō),只需要對(duì)有實(shí)時(shí)要求的線程設(shè)置實(shí)時(shí)優(yōu)先級(jí)屬性,以及設(shè)置響應(yīng)的實(shí)時(shí)線程調(diào)度策略就可以了,并沒(méi)有特殊的API函數(shù)。關(guān)于設(shè)置實(shí)時(shí)優(yōu)先級(jí)以及調(diào)度策略請(qǐng)參考我們網(wǎng)站上的文章:《Linux系統(tǒng)調(diào)度簡(jiǎn)介》,本文中用到的函數(shù)以及結(jié)構(gòu)體都在其中有更為詳細(xì)的介紹。


      設(shè)置實(shí)時(shí)優(yōu)先級(jí)以及調(diào)度策略有兩種方式,一種是在創(chuàng)建線程時(shí)設(shè)置線程的屬性,這樣線程創(chuàng)建后就具有了實(shí)時(shí)優(yōu)先級(jí)并且使用設(shè)置的調(diào)度策略,函數(shù)原型如下:


      設(shè)置實(shí)時(shí)優(yōu)先級(jí)屬性:

      int pthread_attr_setschedparam (pthread_attr_t *attr, const struct sched_param *param)


      上述函數(shù)所包含的2個(gè)參數(shù)說(shuō)明如下:

      ● pthread_attr_t *attr 由pthread_attr_init函數(shù)初始化,是線程的屬性

      ● struct sched_param結(jié)構(gòu)體包含 int sched_priority,也即實(shí)施優(yōu)先級(jí),取值范圍0~99。數(shù)值越大優(yōu)先級(jí)越高,所有的實(shí)時(shí)線程優(yōu)先級(jí)都高于普通線程。為了提高系統(tǒng)的實(shí)時(shí)性,RT Linux將大部分中斷服務(wù)都改為了線程的形式,使得中斷服務(wù)可以被實(shí)時(shí)要求更高的線程搶占,中斷處理線程實(shí)時(shí)優(yōu)先級(jí)為50,操作系統(tǒng)中最重要的線程(如看門狗線程)的優(yōu)先級(jí)為99,當(dāng)知道應(yīng)用程序的處理需要等待某個(gè)特定的中斷才能繼續(xù)執(zhí)行,而且該段代碼實(shí)時(shí)性要求很高時(shí),就可以將該段代碼所在線程的實(shí)時(shí)優(yōu)先級(jí)設(shè)置為大于50小于99的值,這樣就能夠不受其他中斷處理線程的影響,最大限度的滿足實(shí)時(shí)性要求,比如我們?cè)谙旅娴臏y(cè)試程序中設(shè)置我們的線程實(shí)施優(yōu)先級(jí)為80。


      設(shè)置調(diào)度策略:

      int pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy)

      ● policy 為調(diào)度策略,可選SCHED_FIFO或者SCHED_RR,兩者都是實(shí)時(shí)調(diào)度策略,SCHED_FIFO為先進(jìn)先出,只有高實(shí)時(shí)優(yōu)先級(jí)線程能搶占當(dāng)前運(yùn)行的實(shí)時(shí)線程,SCHED_RR增加了時(shí)間片機(jī)制,使得同優(yōu)先級(jí)的實(shí)時(shí)線程能夠輪轉(zhuǎn)運(yùn)行。


      使用示例:

      // 設(shè)置調(diào)度策略為SCHED_FIFO

      res = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);

      if(res)

      {

             printf("pthread setschedpolicy faild\n");

      }

     

      struct sched_param param;

      // 設(shè)置實(shí)時(shí)優(yōu)先級(jí)為80

      param.sched_priority = 80;

      res = pthread_attr_setschedparam(&attr, &param);

      if(res)

      {

             printf("pthread setschedparam faild\n");

      }


      另一種方式是修改已經(jīng)存在的線程,設(shè)置其實(shí)時(shí)優(yōu)先級(jí)和使用的調(diào)度策略,可以在其他線程中設(shè)置,也可以在線程中自行設(shè)置,只需要知道線程的PID,線程自行設(shè)置時(shí)PID為0,函數(shù)原型如下:

      int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param)


      使用示例:

      struct sched_param param;

      // 設(shè)置實(shí)時(shí)優(yōu)先級(jí)為80并且使用調(diào)度策略SCHED_FIFO

      param.sched_priority = 80;

      if(sched_setscheduler(0, SCHED_FIFO, &param))

             printf("wrong sched_setscheduler\n");


      還有其他的函數(shù)可以設(shè)置這些相關(guān)屬性,具體請(qǐng)參考我們網(wǎng)站的文章《Linux調(diào)度策略簡(jiǎn)介》,或者其他相關(guān)資料。一般的使用是將有實(shí)時(shí)要求的功能代碼放入單獨(dú)的線程中,主程序以普通線程啟動(dòng)后建立實(shí)時(shí)線程來(lái)完成相關(guān)功能。由于實(shí)時(shí)線程的不可搶占性,不恰當(dāng)?shù)氖褂糜锌赡軙?huì)造成系統(tǒng)資源被無(wú)限占用,而影響系統(tǒng)的正常運(yùn)行,因此在開(kāi)發(fā)階段需要特別注意,規(guī)劃好實(shí)時(shí)線程的任務(wù),尤其是要檢查循環(huán)語(yǔ)句,對(duì)于循環(huán)任務(wù)的設(shè)計(jì)可以參考網(wǎng)站資料:https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/cyclic


    RT Linux的實(shí)時(shí)性測(cè)試


      在我們的測(cè)試中,由GPIO中斷作為硬件事件,具體是使用外部的方波信號(hào)發(fā)生器作為中斷源,驅(qū)動(dòng)檢測(cè)到GPIO中斷后會(huì)喚醒用戶層的中斷處理線程(該線程已設(shè)置為實(shí)時(shí)線程),實(shí)時(shí)中斷處理線程作為對(duì)中斷事件的響應(yīng),是將另一位GPIO的輸出電平反相(Toggle處理)。用示波器測(cè)量輸入的中斷信號(hào)和作為響應(yīng)的GPIO信號(hào)之間的時(shí)間延遲。程序部分代碼如下:


      struct paraset

      {

             int fd;                  // 中斷設(shè)備對(duì)應(yīng)的文件

             int gpio;                     // 線程使用的GPIO進(jìn)行toggle處理

             int rt_prio;           // 線程的實(shí)時(shí)優(yōu)先級(jí),0則設(shè)置為普通線程

      };

     

      int IRQSelectThreadFunc(void* lparam)

      {

             struct paraset * p = (struct paraset*)lparam;

             int fd = p->fd ;  // 獲取中斷設(shè)備對(duì)應(yīng)的文件識(shí)別號(hào)

             int i=0;

             printf ( "fd = %d \n", fd );

             struct sched_param param;

             param.sched_priority = p->rt_prio;      // 設(shè)置實(shí)時(shí)優(yōu)先級(jí),由主線程傳入

             if(p->rt_prio)

                    if(sched_setscheduler(0, SCHED_FIFO, &param))

                           printf("wrong sched_setscheduler\n");

             fd_set fdRead;

             struct timeval aTime;

             int ret;

             GPIO_OutEnable(gpio_fd, 1<<p->gpio);

             while(1)

             {

                    FD_ZERO(&fdRead);

                    FD_SET(fd,&fdRead);

                    aTime.tv_sec = 2;

                    aTime.tv_usec = 0;

                    // 等待中斷事件

                    ret = select ( fd+1, &fdRead, NULL, NULL, &aTime );

                    if ( ret<0 )

                           printf( "select, something wrong!\n " );

                    if ( ret>0 )

                    {

                           if ( FD_ISSET(fd, &fdRead) )

                           {

                                  //toggel one gpio

                                  nIrqCounter++;

                                  if(nIrqCounter%2)

                                         GPIO_OutClear(gpio_fd, 1<<p->gpio);

                                  else

                                         GPIO_OutSet(gpio_fd, 1<<p->gpio);

                           }

                    }

             }

             pthread_exit( NULL );

             return 0;

      }

     

      int StartPulseThread( struct paraset *p  )

      {

             pthread_attr_t           attr;

             pthread_t                  m_thread;

             int                               res;

             struct sched_param param;


             // 創(chuàng)建gpio線程

             res = pthread_attr_init(&attr);

             if( res!=0 )

             {

                    printf("Create attribute failed\n" );

             }

             // 主程序傳入的參數(shù),判斷實(shí)時(shí)優(yōu)先級(jí)是否為0,不為0則設(shè)置為實(shí)時(shí)線程

             if(p->rt_prio)

             {

                    // 設(shè)置調(diào)度策略為SCHED_FIFO

                    res = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);

                    if(res)

                    {

                           printf("pthread setschedpolicy faild\n");

                    }

                    // 設(shè)置實(shí)時(shí)優(yōu)先級(jí)

                    param.sched_priority = p->rt_prio;

                    res = pthread_attr_setschedparam(&attr, &param);

                    if(res)

                    {

                           printf("pthread setschedparam faild\n");

                    }

                    res = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

                    if(res)

                    {

                           printf("pthread setinheritshed faild\n");

                    }

                    printf("set %d rt_priority: %d\n", p->fd, p->rt_prio);

             }

             // 創(chuàng)建線程

             res = pthread_create( &m_thread, &attr, (void *(*) (void *))&IRQSelectThreadFunc, p );

             if( res!=0 )

             {

                    return -1;

             }

     

             pthread_attr_destroy( &attr );

     

        return 0;

      }


      我們用同一個(gè)測(cè)試程序分別在ESM3354和ESM3352的標(biāo)準(zhǔn)Linux版本和RT Linux上運(yùn)行,當(dāng)輸入100Hz方波信號(hào),即硬件中斷間隔為10ms時(shí),隨機(jī)測(cè)試100次中斷延時(shí)的統(tǒng)計(jì)結(jié)果如下:


    主板型號(hào)ESM3354ESM3352
    CPU信息Cortex-A8 主頻1GHzCortex-A8 主頻600MHz
    操作系統(tǒng)Linux-4.1.6RT Linux-4.1.7Linux-4.1.6RT Linux-4.1.7
    空載中斷延時(shí)最小值(us)16152620
    空載中斷延時(shí)最大值(us)24203927
    空載中斷延時(shí)平均值(us)19.6416.1630.9225.04
    空載中斷延時(shí)標(biāo)準(zhǔn)值(us)1.220.692.410.65
    滿載中斷延時(shí)最小值(us)885810080
    滿載中斷延時(shí)最大值(us)10501852200220
    滿載中斷延時(shí)平均值(us)140.7394.36183.06108.94
    滿載中斷延時(shí)標(biāo)準(zhǔn)值(us)115.1717.33246.1518.81



      從上表可以看到:

      ● 盡管在空載情況下普通Linux系統(tǒng)與RT Linux系統(tǒng)的中斷延時(shí)參數(shù)差異不大,但在滿負(fù)荷下差異就非常明顯了,特別是中斷延時(shí)最大值。均超過(guò)1ms,這意味著普通Linux只能用于對(duì)實(shí)時(shí)性要求很低的場(chǎng)合。相對(duì)的,RT Linux在這項(xiàng)指標(biāo)上有了很大的提高,已可滿足最小硬件中斷間隔為1ms的應(yīng)用。

      ● RT Linux無(wú)論在空載還是滿載的情況下,中斷延時(shí)的方差,相對(duì)其平均值都比較小,這表示RT Linux系統(tǒng)對(duì)硬件中斷的響應(yīng)延時(shí)很一致,響應(yīng)時(shí)間更穩(wěn)定,這也是良好實(shí)時(shí)性的重要指標(biāo)。

      ● ESM3354相比于ESM3352由于CPU主頻更高,響應(yīng)時(shí)間更短,但是在RT Linux上的響應(yīng)時(shí)間均方差差別不大。由于ESM3352成本更低,而對(duì)于實(shí)時(shí)應(yīng)用來(lái)說(shuō)響應(yīng)的實(shí)時(shí)性與ESM3354基本相同,所以在選購(gòu)時(shí)客戶可以優(yōu)先考慮性價(jià)比更高的ESM3352。


      進(jìn)一步,把外部中斷頻率提高一個(gè)數(shù)量級(jí),即把中斷間隔縮短至1ms,再來(lái)比較中斷響應(yīng)延時(shí)的變化。測(cè)試數(shù)據(jù)如下:


    主板型號(hào)ESM3354ESM3352
    CPU信息Cortex-A8 主頻1GHzCortex-A8 主頻600MHz
    操作系統(tǒng)RT Linux-4.1.7
    硬件中斷間隔10000us1000us10000us1000us
    空載中斷延時(shí)最小值(us)15142022
    空載中斷延時(shí)最大值(us)20182730
    空載中斷延時(shí)平均值(us)16.1614.8025.0423.25
    空載中斷延時(shí)標(biāo)準(zhǔn)值(us)0.690.570.650.92
    滿載中斷延時(shí)最小值(us)58408054
    滿載中斷延時(shí)最大值(us)185125220160
    滿載中斷延時(shí)平均值(us)94.3657.30108.9470.58
    滿載中斷延時(shí)標(biāo)準(zhǔn)值(us)17.339.6918.8111.78



      可以看到,外部中斷間隔變小之后,響應(yīng)延時(shí)并沒(méi)有變得更大,反而由于代碼執(zhí)行頻率增加而更有可能常駐高速cache從而更快的得到運(yùn)行,減小響應(yīng)延時(shí)。考慮到我們測(cè)試程序的中斷處理線程在收到中斷后處理比較簡(jiǎn)單,而實(shí)際應(yīng)用中往往需要執(zhí)行更為復(fù)雜的操作,需要更多的執(zhí)行時(shí)間,一般在50us的水平,這樣從硬件中斷開(kāi)始,到處理線程完成響應(yīng)動(dòng)作,最長(zhǎng)需要大約270us時(shí)間。


      根據(jù)以上的測(cè)試分析,可以認(rèn)為ESM335x + RT Linux系統(tǒng)是完全能滿足最小硬件中斷間隔不低于1ms的實(shí)時(shí)控制應(yīng)用,這意味著可滿足大部分的實(shí)時(shí)控制的應(yīng)用需求。


      我們也將會(huì)在近期推出ESM6800的RT Linux版本,ESM6800采用iMX6UL Cortex-A7 528MHz CPU,以低功耗、低成本為特色,特別適用于對(duì)成本敏感的批量工業(yè)智能設(shè)備。請(qǐng)關(guān)注我們官網(wǎng)以及官方微信公眾號(hào)的最新信息。


      有興趣的客戶可以直接和我們的工程師進(jìn)行溝通獲取相關(guān)文件進(jìn)行評(píng)估測(cè)試。

    文章標(biāo)簽:C/C++
    亚洲精品免费在线观看| 国产啪精品视频网站免费尤物| WWW国产亚洲精品久久麻豆| 亚洲精品永久在线观看| 国产成人精品18| 欧美精品久久久久久精品爆乳| 四虎成人精品一区二区免费网站| 亚洲国产日韩综合久久精品| 99爱在线精品免费观看| 无码国内精品久久综合88| 国产精品福利在线| 国产精品视频在线观看| 日韩精品高清自在线| 四虎一影院区永久精品| 亚洲AV无码乱码精品国产 | 国产成人精品午夜福利在线播放 | 91老司机深夜福利精品视频在线观看| 精品视频一区二区三区四区五区| 久久亚洲av无码精品浪潮| 韩日美无码精品无码| 久久九九精品国产av片国产| 99热在线精品免费播放6| 精品福利一区二区三区免费视频| 99久久99久久久精品齐齐| 国产精品久久久久久久| 精品女同一区二区三区在线| 国产三级精品久久| 精品国产免费一区二区三区香蕉| 日韩人妻无码精品一专区 | 久久国产精品偷99| 亚洲欧洲精品无码AV| 久久av老司机精品网站导航| 精品不卡一区二区| 精品国产丝袜自在线拍国| 亚洲午夜福利精品无码| 国产精品美女一区二区视频| 91大神在线精品视频一区| 久热爱精品视频在线| 午夜亚洲av永久无码精品| 亚洲精品国产美女久久久| 久久狠狠高潮亚洲精品|