色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

RT-Thread自動初始化詳解

嵌入式大雜燴 ? 來源:嵌入式大雜燴 ? 作者:嵌入式大雜燴 ? 2022-06-25 21:38 ? 次閱讀

我們知道,在寫裸機程序時,當我們完成硬件初始化后,就需要在主函數中進行調用。當我們使用RT-Thread后,完全不需要這樣做了,我們可以將硬件等自動初始化。RT-Thread自動初始化機制是指初始化函數不需要被顯式調用,只需要在函數定義處通過宏定義的方式進行申明,就會在系統啟動過程中被執行,非常的方便。

1 普通初始化

前面也講了,我們在寫單片機的程序時,需要對硬件進行初始化操作,我們這里還是以LED為例。需要對LED的GPIO進行初始化后才能進一步操作。

int main(void)
{
  rt_err_t rst; 
  /* LED初始化 */
  LED_GPIO_Config();  
  
  rst = rt_thread_init(&led_thread,
  "ledshine",
  led_thread_entry,
  RT_NULL,
  &led_thread_stack[0],
  sizeof(led_thread_stack),
  RT_THREAD_PRIORITY_MAX-2,
  20);
  if(rst == RT_EOK)
  {
  rt_thread_startup(&led_thread);
  }
}

上述代碼很簡單,就是在main()函數中對LED的GPIO進行初始化,也就是調用了LED_GPIO_Config() 函數,而針對RT-Thread系統,我們在需要初始化的地方進行初始化即可,無需在main()函數或者board.c中初始化了。

2 RT-Thread初始化流程

要想搞清楚RT-Thread的自動初始化流程,那么必須的了解RT-Thread初始化流程,這一部分前文也就有講,官方也有,我們還是再來復習下。

RT-Thread支持多種平臺和多種編譯器。RT-Thread啟動代碼統一入口為 rtthread_startup(),芯片啟動文件在完成必要工作(如初始化時鐘、配置中斷向量表、初始化堆棧等)后,跳轉至 RT-Thread的啟動入口中,最后進入用戶入口 main()。RT-Thread的啟動流程如下:

1.全局關中斷,初始化與系統相關的硬件。

2.打印系統版本信息,初始化系統內核對象(如定時器、調度器)。

3.初始化用戶 main線程(同時會初始化線程棧),在 main線程中對各類模塊依次進行初始化。

4.初始化軟件定時器線程、初始化空閑線程。

5.啟動調度器,系統切換到第一個線程開始運行(如 main線程),并打開全局中斷。

pYYBAGK25hOANpYRAAERUWXJgTY108.png

在圖中標出顏色的部分需要用戶特別注意(黃色表示 libcpu移植相關的內容,綠色部分表示板級移植相關的內容)。

3 RT-Thread自動初始化原理

既然是初始化,我們這里中的重點關注初始化過程,重新整理RT-Thread初始化如下圖所示:

poYBAGK25iyATmbSAAEolP_gziE566.png

在系統啟動流程圖中,有兩個函數:rt_components_board_init() 與 rt_components_init(),其后的帶底色方框內部的函數表示被自動初始化的函數,其中:

“board init functions”為所有通過 INIT_BOARD_EXPORT(fn)申明的初始化函數。

“pre-initialization functions”為所有通過 INIT_PREV_EXPORT(fn)申明的初始化函數。

“device init functions”為所有通過 INIT_DEVICE_EXPORT(fn)申明的初始化函數。

“components init functions”為所有通過 INIT_COMPONENT_EXPORT(fn)申明的初始化函數。

“enviroment init functions”為所有通過 INIT_ENV_EXPORT(fn)申明的初始化函數。

“application init functions”為所有通過 INIT_APP_EXPORT(fn)申明的初始化函數。

rt_components_board_init() 函數執行的比較早,主要初始化相關硬件環境,執行這個函數時將會遍歷通過 INIT_BOARD_EXPORT(fn)申明的初始化函數表,并調用各個函數。

rt_components_init() 函數會在操作系統運行起來之后創建的 main線程里被調用執行,這個時候硬件環境和操作系統已經初始化完成,可以執行應用相關代碼。rt_components_init() 函數會遍歷通過剩下的其他幾個宏申明的初始化函數表。

RT-Thread的自動初始化機制使用了自定義 RTI符號段,將需要在啟動時進行初始化的函數指針放到了該段中,形成一張初始化函數表,在系統啟動過程中會遍歷該表,并調用表中的函數,達到自動初始化的目的。

自動初始化功能的宏接口定義詳細描述如下表所示:

初始化順序 宏接口 描述
1 INIT_BOARD_EXPORT(fn) 非常早期的初始化,此時調度器還未啟動
2 INIT_PREV_EXPORT(fn) 主要是用于純軟件的初始化、沒有太多依賴的函數
3 INIT_DEVICE_EXPORT(fn) 外設驅動初始化相關,比如網卡設備
4 INIT_COMPONENT_EXPORT(fn) 組件初始化,比如文件系統或者 LWIP
5 INIT_ENV_EXPORT(fn) 系統環境初始化,比如掛載文件系統
6 INIT_APP_EXPORT(fn) 應用初始化,比如 GUI應用

初始化函數主動通過這些宏接口進行申明,如 INIT_BOARD_EXPORT(rt_hw_usart_init),鏈接器會自動收集所有被申明的初始化函數,放到 RTI符號段中,該符號段位于內存分布的 RO段中,該 RTI符號段中的所有函數在系統初始化時會被自動調用。

好了,介紹性文字我就不貼了,下面直接看源代碼進一步分析。前文說過,在RT-Thread的啟動流程中,調用了兩個函數 rt_components_board_init()與 rt_components_init()就完成了上述6個部分的初始化工作。從初始化啟動流程圖中我們可以看出: rt_components_board_init()完成了第 1部分工作, rt_components_init()完成了第2-6部分的工作。那么接下來我們先看這兩個函數源代碼。


/**
 * RT-Thread Components Initialization for board
 */
void rt_components_board_init(void)
{
#if RT_DEBUG_INIT
    int result;
    const struct rt_init_desc *desc;
    for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
    {
        rt_kprintf("initialize %s", desc->fn_name);
        result = desc->fn();
        rt_kprintf(":%d done\n", result);
   }
#else
    const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
    {
        (*fn_ptr)();
    }
#endif
}

/**
 * RT-Thread Components Initialization
 */
void rt_components_init(void)
{
#if RT_DEBUG_INIT
    int result;
    const struct rt_init_desc *desc;

    rt_kprintf("do components initialization.\n");
    for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
    {
        rt_kprintf("initialize %s", desc->fn_name);
        result = desc->fn();
        rt_kprintf(":%d done\n", result);
    }
#else
    const init_fn_t *fn_ptr;

    for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
    {
        (*fn_ptr)();
    }
#endif
}

【注】rt_components_board_init() 與 rt_components_init()函數在components.c文件中實現。

可以看到兩個函數在非調試模式下都是通過for循環會遍歷位于__rt_init_rti_board_start 到 __rt_init_rti_board_end以及__rt_init_rti_board_end 到 __rt_init_rti_end之間保存的函數指針,然后依次執行這些函數。那么接下來我們看看上述函數指針。我們先編譯下,找到.map文件,我們在在文件中搜索上述的函數指針。

poYBAGK25lCAF7g2AAIZwqwwSs0219.png

又問會問,找到又能怎樣呢?怎么和上述的6個宏定義對應起來呢?不急哈,慢慢來,我們先找到上述6個宏定義又是如何實現的,找到啦,在rtdef.h中。

poYBAGK25mKAcQwYAAKv6PeRkr0395.png

宏定義又是通過INIT_EXPORT宏函數定義的,那么INIT_EXPORT又是如何實現的呢?

#ifdef _MSC_VER /* we do not support MS VC++ compiler */
    #define INIT_EXPORT(fn, level)
#else
    #if RT_DEBUG_INIT
        struct rt_init_desc
        {
            const char* fn_name;
            const init_fn_t fn;
        };
        #define INIT_EXPORT(fn, level)                                                     \
            const char __rti_##fn##_name[] = #fn;                                           \
            RT_USED const struct rt_init_desc __rt_init_desc_##fn SECTION(".rti_fn."level) = \
            { __rti_##fn##_name, fn};
    #else
        #define INIT_EXPORT(fn, level)                                                      \
            RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
    #endif
#endif

【注】上述代碼在rtdef.h中實現。

上述代碼最關鍵的代碼是:

RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn

這又是啥,看不懂啊,不急哈,我們先看看init_fn_t是啥?其定義如下:

#ifdef RT_USING_COMPONENTS_INIT
typedef int (*init_fn_t)(void);

這就是一個函數指針類型,其實就是個指針。那么SECTION又是啥呢?其定義如下:

poYBAGK25pCAErH7AAFfuVXN8u8707.png

__attribute__((used))表示這個標記這個東西是使用過的,避免出現如: warning: #177-D: variable "a" was declared but never referenced的警告。

在GCC的宏中,##后面跟變量名。__attribute__((section(x)))則表示fn被放置于指定段中。是不是還是一頭霧水,不急,我們再看看一些代碼或許你就明白了。

/*
 * Components Initialization will initialize some driver and components as following
 * order:
 * rti_start        --> 0
 * BOARD_EXPORT     --> 1
 * rti_board_end    --> 1.end
 *
 * DEVICE_EXPORT    --> 2
 * COMPONENT_EXPORT --> 3
 * FS_EXPORT        --> 4
 * ENV_EXPORT       --> 5
 * APP_EXPORT       --> 6
 *
 * rti_end          --> 6.end
 *
 * These automatically initialization, the driver or component initial function must
 * be defined with:
 * INIT_BOARD_EXPORT(fn);
 * INIT_DEVICE_EXPORT(fn);
 * ...
 * INIT_APP_EXPORT(fn);
 * etc.
 */
static int rti_start(void)
{
    return 0;
}
INIT_EXPORT(rti_start, "0");

static int rti_board_start(void)
{
    return 0;
}
INIT_EXPORT(rti_board_start, "0.end");

static int rti_board_end(void)
{
    return 0;
}
INIT_EXPORT(rti_board_end, "1.end");

static int rti_end(void)
{
    return 0;
}
INIT_EXPORT(rti_end, "6.end");

【注】以上代碼再在components.c文件中實現。

好了,我們結合上述代碼和6個宏定義,以及.map文件。

pYYBAGK25rOAR3UiAAJaDX-u5M0749.png

上圖中框選的第一行是一個Section,叫做.rti_fn.0,這個內容實際是我們通過INIT_EXPORT(rti_start, "0");完成的,我們把函數rti_start改名為__rt_init_rti_start,存入.rti_fn.0這個地方。同樣的,INIT_EXPORT(rti_board_start, "0.end");、INIT_EXPORT(rti_board_end, "1.end");、INIT_EXPORT(rti_end, "6.end");也是這里插入的。下圖就是我們插入的位置。

pYYBAGK25riADKagAAI28bnZbrg840.png

這下是不是很明白,當然啦,多看幾遍,還是很好理解的。

4 RT-Thread自動初始化實例

前文理論講了很多,源代碼也分析,估計初學者還很蒙,沒關系,慢慢理解,我們先看個例子,先用起來,后面再慢慢理解。還會是LED的例子,前文已近給出了普通初始化方式,下面我們看看如何使用自動化初始化。

/**
 * @brief 初始化LED的GPIO
 * @param None
 * @retval None
 */
int LED_GPIO_Config(void)
{ 
  /*定義一個GPIO_InitTypeDef類型的結構體*/
  GPIO_InitTypeDef GPIO_InitStructure;

  /*開啟LED的外設時鐘*/
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE); 

  /*設置IO口*/   
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //設置引腳模式為通用推挽輸出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //設置引腳速率為50MHz 

  /*調用庫函數,初始化GPIOB0*/
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //選擇要控制的GPIOB引腳 
  GPIO_Init(GPIOB, &GPIO_InitStructure); 
    
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;/*選擇要控制的引腳*/ 
  GPIO_Init(GPIOG, &GPIO_InitStructure); 
   
  /* 開啟所有led燈 */
  GPIO_SetBits(GPIOB, GPIO_Pin_0);
  GPIO_SetBits(GPIOG, GPIO_Pin_6|GPIO_Pin_7);  
  
   return 0;
}

/* LED初始化 */
INIT_BOARD_EXPORT(LED_GPIO_Config); 

我們注意最后一行代碼,這里使用INIT_BOARD_EXPORT宏定義進行初始化,其初始化時間最早,值得注意的是,上述6個宏定義修飾的函數返回值都是int,最好將返回值改為int。

如果LED配置正確,其實驗現象和普通初始化方式沒有任何區別,接下來我們再來看看.map文件有何變化,當然我說的是自動初始化部分。

pYYBAGK25uOARarPAAK0mM8C1Xw687.pngpYYBAGK25veAZhEsAAIrD_z92Ds321.png

我們可以看到多了LED初始化的信息,也說明我們前文分析的是合理的。

5 總結

好了,關于自動出初始化就講完了,我估計初學者很蒙,沒關系,我們再來總結下,還是先看看RT-Thread初始化過程,如下圖所示:

poYBAGK25iyATmbSAAEolP_gziE566.png

rt_components_board_init() 與 rt_components_init()負責初始化,其中帶底色方框內部的函數表示被自動初始化的函數。這部分應該沒是啥問題。關鍵是如何將初始化函數和6個自動初始化宏定義聯系起來這就有點燒腦子,我整理了一張關系圖,如下圖所示:

poYBAGK25xOAZEjcAAGLam2avME478.png

結合前文的講解,再結合上述兩張圖,我不想在贅述了,自行理解去吧。

參考地址:

https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/basic/basic

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 初始化
    +關注

    關注

    0

    文章

    50

    瀏覽量

    11850
  • RT-Thread
    +關注

    關注

    31

    文章

    1285

    瀏覽量

    40081
收藏 人收藏

    評論

    相關推薦

    RT-Thread qemu mps2-an385 bsp移植制作 :系統運行篇

    前面已經讓 RT-Thread 進入了 entry 入口函數,并且 調整 鏈接腳本,自動初始化與 MSH shell 的符號已經預留, 進入了 RT-Thread
    的頭像 發表于 11-14 12:27 ?827次閱讀
    <b class='flag-5'>RT-Thread</b> qemu mps2-an385 bsp移植制作 :系統運行篇

    RT-thread初始化過程是怎樣進行的

    RT-thread初始化過程是怎樣進行的?擴展補丁Sub和super的作用是什么?如何去使用它們呢?
    發表于 11-29 07:42

    如何對RT-Thread系統進行初始化

    RT-Thread是如何啟動的?如何對RT-Thread系統進行初始化呢?
    發表于 11-30 07:54

    為什么RT-Thread要采用這種復雜的方式來進行自動初始化操作呢

    在分析之前首先查閱 RT-Thread 的官方文檔RT-Thread 自動初始化機制,根據官方文檔的講述在 RTT 源碼中一共使用了 6 中順序的
    發表于 04-06 17:49

    RT-Thread自動初始化機制簡介

    RT-Thread 的時鐘管理以時鐘節拍為基礎,時鐘節拍是 RT-Thread 操作系統中最小的RT-Thread 自動初始化機制時鐘單位。
    發表于 04-06 18:08

    RT-Thread系統自動初始化機制簡介

    RT-Thread 自動初始化機制1、自動初始化機制簡介在系統啟動流程圖中,有兩個函數:rt_c
    發表于 04-12 17:43

    【原創精選】RT-Thread征文精選技術文章合集

    RT-Thread自動初始化詳解GD32 RISC-V系列 BSP框架制作與移植GD32407V-START開發板的BSP框架制作與移植基于Select/Poll實現并發服務器(一)
    發表于 07-26 14:56

    RT-Thread系統初始化與啟動流程詳細描述

    系統定時器線程voidrt_application_init ()創建用戶線程詳細描述RT-Thread 的啟動流程RT-Thread 的啟動流程,大致可以分為四個部分:(1)初始化與系統相關的硬件
    發表于 08-25 15:15

    RT-Thread自動初始化原理分析

    ;}這里我們直接就可以使用 printf 進行打印,而沒有進行一些其它的初始化,參考這個思路引出了 RT-Thread自動初始化機制。
    發表于 12-05 14:17

    一文詳解RT-Thread自動初始化

    在學RT-Thread時,經常能聽到這個詞:自動初始化。用起來也非常容易,一個宏就解決了,但是原理是什么呢?
    的頭像 發表于 07-21 10:17 ?7588次閱讀
    一文<b class='flag-5'>詳解</b><b class='flag-5'>RT-Thread</b><b class='flag-5'>自動</b><b class='flag-5'>初始化</b>

    RT-Thread學習筆記 --(3)RT-Thread自動初始化機制分析

    相信不少工程師在閱讀RT-Thread相關源代碼的時候,都會經常看到如下圖所示的宏定義,按照宏定義的命名來理解,這些宏定義似乎都是對一些...
    發表于 01-25 18:55 ?1次下載
    <b class='flag-5'>RT-Thread</b>學習筆記 --(3)<b class='flag-5'>RT-Thread</b><b class='flag-5'>自動</b><b class='flag-5'>初始化</b>機制分析

    RT-Thread全球技術大會:如何使用組件以及自動初始化流程

    RT-Thread全球技術大會:如何使用組件和自動初始化流程 ? ? ? ? ? 審核編輯:彭靜
    的頭像 發表于 05-27 15:16 ?932次閱讀
    <b class='flag-5'>RT-Thread</b>全球技術大會:如何使用組件以及<b class='flag-5'>自動</b><b class='flag-5'>初始化</b>流程

    RT-Thread自動初始化機制

    ??在分析之前首先查閱 RT-Thread 的官方文檔 [RT-Thread 自動初始化機制](https://www.rt-thread.
    的頭像 發表于 06-17 08:52 ?2634次閱讀
    <b class='flag-5'>RT-Thread</b><b class='flag-5'>自動</b><b class='flag-5'>初始化</b>機制

    rt-thread線程棧初始化參數分析

    RT-Thread 在線程初始化的代碼內有一段初始化線程堆棧的代碼
    的頭像 發表于 08-14 16:50 ?1716次閱讀
    <b class='flag-5'>rt-thread</b>線程棧<b class='flag-5'>初始化</b>參數分析

    RT-Thread使用經驗分享:鏈表未初始化造成死機

    最近在開發調試基于RT-Thread 的驅動時,遇到一個比較奇怪的死機問題,后來經過一步步排查,終于發現是驅動的鏈表節點沒有初始化造成的死機
    的頭像 發表于 10-08 14:49 ?943次閱讀
    <b class='flag-5'>RT-Thread</b>使用經驗分享:鏈表未<b class='flag-5'>初始化</b>造成死機
    主站蜘蛛池模板: 美女张开腿露尿口给男人亲| 苍井空教师BD在线观看全集| 亚洲黄色在线观看| 色男人的天堂久久综合| 欧美乱子YELLOWVIDEO| 美女伸开两腿让我爽| 久久这里只有精品2| 久久婷婷久久一区二区三区| 精品免费久久久久久影院 | 色视频色露露永久免费观看| 欧美 亚洲 另类 综合网| 摸董事长的裤裆恋老小说| 蜜桃传媒在线观看| 木凡的天空在线收听| 捏揉舔水插按摩师| 嫩草影院未满十八岁禁止入内| 男人和女人全黄一级毛片| 暖暖视频免费观看高清完整版 | 韩国伦理电影在线神马网| 国产在线观看99| 精品区2区3区4区产品乱码9| 久久精品国产午夜伦班片| 久拍国产在线观看| 麻豆精品传媒卡一卡二传媒短视频 | 国产在线一区观看| 国产精品久久久久久久久久免费| 国产高清-国产av| 国产亚洲精品久久久999密臂| 和姐姐做插得很深| 久久九九久精品国产尤物 | av天堂网2014在线| 打开双腿狠狠蹂躏蜜桃臀| 大胸美女被吊起来解开胸罩| 国产精品亚洲在钱视频| 精品免费久久久久久影院| 老司机无码精品A| 秋霞电影网午夜鲁丝片无码| 天天躁人人躁人人躁狂躁| 亚洲三级在线视频| 97精品在线观看| 高h浪荡文辣文神奇宝贝|