本章開始,先新建一個基于野火STM32全系列(包含M3/4/7)開發板的的RT-Thread的工程模板,讓RT-Thread先跑起來。以后所有的RT- Thread相關的例程我們都在此模板上修改和添加代碼,不用再反反復復地新建。
1. 獲取STM32的裸機工程模板
STM32的裸機工程模板我們直接使用野火STM32開發板配套的固件庫例程即可。這里我們選取比較簡單的例 程—“GPIO輸出—使用固件庫點亮LED”作為裸機工程模板。該裸機工程模板均可以在對應板子的A盤/程序源碼 /固件庫例程的目錄下獲取到,下面以野火F103-霸道板子的光盤目錄為例,具體見圖 STM32裸機工程模板在光盤資料中的位置。
2. 下載RT-Thread Nano 源碼
Nano是Master的精簡版,去掉了一些組件和各種開發板的BSP,保留了OS的核心功能,但足夠我們使用。版本已經更新到了3.0.3版本,與Master的版本號一致。
RT-Thread Master的源碼可從RT-Thread GitHub倉庫地址:https://github.com/RT-Thread/rt-thread下載到,Nano就是從里面扣出來的。RT- Thread官方并沒有將摳出來的Nano放到他們的官方網站,而是作為一個Package放在了KEIL網站—http://www.keil.com/dd2/pack/中,供用戶下載, 具體見圖 RT-Thread Nano Package,目前的版本號是3.0.3,如果以后更新到更高的版本則以最新的版本為準。
3. 安裝RT-Thread Package
下載下來之后是一個以exe為后綴的文件,點擊安裝即可,安裝目錄與你的KEIL安裝目錄一樣,安裝成功之后, 可以在KEIL的PACK目錄下找到剛剛安裝的Package的所有的文件,具體見圖 安裝文件。
這樣安裝成功之后,就可以在KEIL里面的軟件包管理器中將RT-Thread Nano 直接添加到工程里面, 具體見圖 從KEIL的軟件包管理器中選擇RT-Thread。
4. 往裸機工程添加RT-Thread源碼
4.1. 拷貝RT-Thread Package到裸機工程根目錄
使用這種方法打包的RT-Thread 工程,拷貝到一臺沒有安裝RT-Thread Package的電腦上面是使用不了的, 會提示找不到RT-Thread的源文件。鑒于RT-Thread Package容量很小,我們直接將安裝在KEIL PACK 目 錄下的整個RT-Thread文件夾拷貝到我們的STM32裸機工程里面,讓整個RT-Thread Package 跟隨我們的 工程一起發布,具體見圖 拷貝Package到裸機工程。
圖 拷貝Package到裸機工程 中RT-Thread文件夾下就是RT-Thread Nano 的所有東西,該文件夾下的具體內容見下表。
表格 RT-dhread 文件夾內容組成
文件夾 |
文件夾 |
描述 |
---|---|---|
rtthread/3.0.3 |
bsp |
板級支持包 |
|
components/finsh |
RT-Thread組件 |
|
include |
頭文件 |
|
include/libc |
頭文件 |
|
||
|
libcpu/arm/cortex-m3 |
與處理器相關的接口文件 |
|
libcpu/arm/cortex-m4 |
與處理器相關的接口文件 |
|
libcpu/arm/cortex-m7 |
與處理器相關的接口文件 |
|
src |
RT-Thread內核源碼 |
4.2. 拷貝rtconfig.h文件到user文件夾
將RT-Thread/3.0.3/bsp文件夾下面的rtconfig.h配套文件拷貝到工程根目錄下面的user文件夾,等下我們需要對這個文件進行修改。
用戶可以通過修改這個RT-Thread內核的配置頭文件來裁剪RT-Thread的功能,所以我們把它拷貝一份放在user這個文件夾下面。user,見名之義我們就可以知道里面存放的文件都是用戶自己編寫的。
4.3. 拷貝board.c文件到user文件夾
將RT-Thread/3.0.3/bsp文件夾下面的board.c配套文件拷貝到工程根目錄下面的user文件夾,等下我們需要對這個board.c進行修改。
4.4. RT-Thread文件夾內容簡介
接下來我們對RT-Thread文件夾下面的內容做個簡單的介紹,好讓我們能夠更順心地使用RT-Thread。
4.4.1. bsp文件夾簡介
bsp文件夾里面存放的是板級支持包,即board support package的英文縮寫。RT-Thread為了推廣自己, 會給各種半導體廠商的評估板寫好驅動程序,這些驅動程序就放在bsp這個目錄下,我們這里用的是nano版本, 只有幾款開發板的驅動,具體見圖 bsp文件夾內容,如果是Master版本,則存放了非常多的開發板的驅動,具體見 圖 Master文件夾內容。bsp文件夾下面的board.c這是RT-Thread用來初始化開發板硬件的相關函數。rtconfig.h是 RT-Thread功能的配置頭文件,里面定義了很多宏,通過這些宏定義,我們可以裁剪RT-Thread的功能。 用戶在使用RT-Thread的時候,用戶只需要修改board.c和rtconfig.h這兩個文件的內容即可,其它文件 我們不需要改動。如果為了減小工程的大小,bsp文件夾下面除了board.c和rtconfig.h這兩個文件要保 留外,其它的統統可以刪除。
4.4.2. components文件夾簡介
在RT-Thread看來,除了內核,其它第三方加進來的軟件都是組件,比如gui、fatfs、lwip和finsh等。那么這些組件就放在components這個文件夾內,目前nano版本只放了finsh,其它的都被刪除了,master版本則放了非常多的組件。finsh是RT- Thread組件里面最具特色的,它通過串口打印的方式來輸出各種信息,方便我們調試程序。
4.4.3. include文件夾簡介
include目錄下面存放的是RT-Thread內核的頭文件,是內核不可分割的一部分。
4.4.4. libcpu文件夾簡介
RT-Thread是一個軟件,單片機是一個硬件,RT- Thread要想運行在一個單片機上面,它們就必須關聯在一起,那么怎么關聯?還是得通過寫代碼來關聯,這部分 關聯的文件叫接口文件,通常由匯編和C聯合編寫。這些接口文件都是跟硬件密切相關的,不同的硬件接口文件是 不一樣的,但都大同小異。編寫這些接口文件的過程我們就叫移植,移植的過程通常由RT-Thread和mcu原廠的人 來負責,移植好的這些接口文件就放在libcpu這個文件夾的目錄下。RT-Thread nano目 前在libcpu目錄下只放了cortex-m0、m3、m4和m7內核的單片機的接口文件,只要是使用了這些內核的mcu都可 以使用里面的接口文件。通常網絡上出現的叫“移植某某某RTOS到某某某MCU”的教程,其實準確來說,不能夠叫 移植,應該叫使用官方的移植,因為這些跟硬件相關的接口文件,RTOS官方都已經寫好了,我們只是使用而已。 我們本章講的移植也是使用RT-Thread官方的移植,關于這些底層的移植文件我們已經在第一部分“從0到1教你 寫RT-Thread內核”有非常詳細的講解,這里我們直接使用即可。
4.4.5. src文件夾簡介
src目錄下面存放的是RT-Thread內核的源文件,是內核的核心,我們在第一部分“從0到1教你寫RT-Thread內核”里面講解的就是這里面內容。
4.5. 添加RT-Thread源碼到工程組文件夾
在上一步我們只是將RT-Thread的源碼放到了本地工程目錄下,還沒有添加到開發環境里面的組文件夾里面。
4.5.1. 新建rtt/source和rtt/ports組
接下來我們在開發環境里面新建rtt/source和rtt/ports兩個組文件夾,其中rtt/source用于存放src文件夾的內容,rtt/ports用于存放libcpu/arm/cortex-m?文件夾的內容,“?”表示3、4或者7,具體選擇哪個得看你使用的是野火哪個型號的STM32開發板,具體 見下表。
表格 野火STM32開發板型號對應RT-Thread的接口文件
野火STM32開發板型號 |
具體芯片型號 |
RT-Thread不同內核的接口文件 |
---|---|---|
MINI |
STM32F103RCT6 |
libcpu/arm/cortex-m3 |
指南者 |
STM32F103VET6 |
libcpu/arm/cortex-m3 |
霸道 |
STM32F103ZET6 |
libcpu/arm/cortex-m3 |
霸天虎 |
STM32F407ZGT6 |
libcpu/arm/cortex-m4 |
F429-挑戰者 |
STM32F429IGT6 |
libcpu/arm/cortex-m4 |
F767-挑戰者 |
STM32F767IGT6 |
libcpu/arm/cortex-m7 |
H743-挑戰者 |
STM32H743IIT6 |
libcpu/arm/cortex-m7 |
bsp里面的rtconfig.h和board.c添加到user組文件夾下,其中rtconfig.h用于配置RT-Thread的功能, board.c用于存放硬件相關的初始化函數。源碼添加完畢之后,具體見圖 添加RT-Thread源碼到工程組文件夾。
2.4.5.2. 指定RT-Thread頭文件的路徑
RT-Thread的源碼已經添加到開發環境的組文件夾下面,編譯的時候需要為這些源文件指定頭文件的路徑,不然編譯會報錯。RT-Thread的源碼里面只有RT-Thread3.0.3componentsfinsh、RT-Thread3.0.3include和RT- Thread3.0.3includelibc這三個文件夾下面有頭文件,只需要將這三個頭文件的路徑在開發環境里面指定即可。同時我們還將RT-Thread3.0.3bsp里面的rtconfig.h這個頭文件拷貝到了工程根目錄下的user文件夾下,所以user的路徑也要加到開發環境里面。RT- Thread頭文件的路徑添加完成后的效果具體見圖 在開發環境中指定RT-Thread的頭文件的路徑。
2.5. 修改rtconfig.h
rtconfig.h是直接從RT-Thread/3.0.3/bsp文件夾下面拷貝過來的,該頭文件對裁剪整個RT-Thread所需的功能的宏均做了定義,有些宏定義被使能,有些宏定義被失能,一開始我們只需要配置最簡單的功能即可。要想隨心所欲的配置RT- Thread的功能,我們必須對這些宏定義的功能有所掌握,下面我們先簡單的介紹下這些宏定義的含義,然后再對這些宏定義進行修改。
2.5.1. rtconfig.h文件內容講解
代碼清單:移植RTT-1 (1) :頭文件RTE_Components.h是在MDK中添加RT-Thead Package時由MDK自動生成的, 目前我們沒有使用MDK中自帶的RT-Thread的Package,所以這個頭文件不存在,如果包含了該頭文件,編譯的時 候會報錯,等下修改rtconfig.h的時候需要注釋掉該頭文件。
代碼清單:移植RTT-1 (2) : Use Configuration Wizard in Context Menu: 在上下文中使用 配置向導來配置rtconfig.h中的宏定義。接下來代碼中夾雜的“”、“”“”、“”和“”這些符號是MDK自帶的配置向導控制符號,使用這些符號控制的代碼可以生成一個對應的圖形界面的配置 向導,rtconfig.h對應的配置向導具體見圖 rtconfig.h對應的配置向導。有關配置向導的語法,可在MDK的幫助文檔里面找到, 在搜索欄輸入Configuration Wizard 即可搜索到,具體見圖 Configuration-Wizard。具體每一個符號的語法我們這里不 做細講,有興趣的可以深究下。 對于我個人,還是傾向于直接修改rtconfig.h中的源碼,而不是通過這個配置 向導來修改,就好比一個老煙槍抽煙的時候你要給他加個過濾嘴,那是不可能的,這輩子都是不可能的。
代碼清單:移植RTT-1 (3) :RT-Thread的基本配置,要想RT-Thread準確無誤的跑起來,這些基本配置必須得有且正確。
代碼清單:移植RTT-1 (3)-1 :RT_THREAD_PRIORITY_MAX這個宏表示RT-Thread支持多少個優先級, 取值范圍為8~~~256,默認為32。
代碼清單:移植RTT-1 (3)-2:RT_TICK_PER_SECOND 表示操作系統每秒鐘有多少個tick,tick即是操 作系統的時鐘周期,默認為1000,即操作系統的時鐘周期tick等于1ms。
代碼清單:移植RTT-1 (3)-3:RT_ALIGN_SIZE這個宏表示CPU處理的數據需要多少個字節對齊,默認為4個字節。
代碼清單:移植RTT-1 (3)-4:RT_NAME_MAX這個宏表示內核對象名字的最大長度,取值范圍為2~~~16,默認為8。
代碼清單:移植RTT-1 (3)-5:使用RT-Thread組件初始化,默認使能。
代碼清單:移植RTT-1 (3)-6:使用用戶main函數,默認打開。
代碼清單:移植RTT-1 (3)-7:main線程棧大小,取值范圍為1~~~4086,單位為字節,默認為512。
代碼清單:移植RTT-1 (4):調試配置。包括了內核調試配置,組件調試配置和線程棧溢出檢測,目前全部關閉。
代碼清單:移植RTT-1 (5):鉤子函數配置,目前全部關閉。
代碼清單:移植RTT-1 (6):軟件定時器配置,目前關閉,不使用軟件定時器。
代碼清單:移植RTT-1 (7):內部通信配置,包括信號量、互斥量、事件、郵箱和消息隊列,根據需要配置。
代碼清單:移植RTT-1 (8):內存管理配置。
代碼清單:移植RTT-1 (8)-1:RT_USING_MEMPOOL這個宏用于表示是否使用內存池,目前關閉,不使用內存池。
代碼清單:移植RTT-1 (8)-2:RT_USING_HEAP這個宏用于表示是否堆,目前關閉,不使用堆。
代碼清單:移植RTT-1 (8)-3:RT_USING_SMALL_MEM這個宏用于表示是否使用小內存,目前使能。
代碼清單:移植RTT-1 (8)-4:RT_USING_TINY_SIZE這個宏用于表示是否使用極小內存,目前關閉,不使用。
代碼清單:移植RTT-1 (9):控制臺配置??刂婆_即是rt_kprintf()函數調試輸出的設備,通常使用串口。
代碼清單:移植RTT-1 (10):FINSH配置。
代碼清單:移植RTT-1 (11):設備配置。
代碼清單:移植RTT-1 (12):rtconfig.h配置結束。
2.5.2. rtconfig.h文件修改
rtconfig.h頭文件的內容修改的不多,具體是:注釋掉頭文件RTE_Components.h、修改了 RT_THREAD_PRIORITY_MAX、RT_TICK_PER_SECOND和RT_MAIN_THREAD_STACK_SIZE這三個宏 的大小,具體見 代碼清單:移植RTT-2 的高亮部分。
2.6. 修改board.c
2.6.1. board.c文件內容講解
board.c是直接從RT-Thread/3.0.3/bsp文件夾下面拷貝過來的,里面存放的是與硬件相關的初始化函數, 整個 board.c中的內容具體見 代碼清單:移植RTT-3。
代碼清單:移植RTT-3 (1):RT-Thread相關頭文件,rthw.h是處理器相關,rtthread與內核相關。
代碼清單:移植RTT-3 (2):SysTick相關的寄存器定義和初始化函數,這個是跟處理器相關的,等下我們直接 使用固件庫函數,可以把這部分注釋掉,也可以保留,看個人喜好。
代碼清單:移植RTT-3 (3):RT-Thread堆配置,如果同時定義了RT_USING_USER_MAIN和 RT_USING_HEAP這兩 個宏,表示RT-Thread里面創建內核對象時使用動態內存分配方案。堆可以是內部的SRAM也可以是外部的SRAM或 SDRAM,目前的方法是從內部SRAM里面分配一部分靜態內存來作為堆空間,這里配置為4KB。rt_heap_begin_get() 和rt_heap_end_get()這兩個函數表示堆的起始地址和結束地址。這兩個函數前面的宏RT_WEAK的原型是關鍵字 __weak,表示若定義,即其它地方定義了rt_heap_begin_get()和rt_heap_end_get()這兩個函數實體, 被__weak修飾的函數就會被覆蓋。
RT_USING_USER_MAIN和RT_USING_HEAP這兩個宏在rtconfig.h中定義,RT_USING_USER_MAIN默認使能,通過使能或者失能RT_USING_HEAP這個宏來選擇使用靜態或者動態內存。無論是使用靜態還是動態內存方案,使用的都是內部的SRAM,區別是使用的內存是 在程序編譯的時候分配還是在運行的時候分配。
2.6.1.1. rt_hw_board_init()函數
代碼清單:移植RTT-3 (4):RT-Thread啟動的時候會調用一個名為rt_hw_board_init()的函數,從函數名稱 我們可以知道它是用來初始化開發板硬件的,比如時鐘,比如串口等,具體初始化什么由用戶選擇。當這些硬件 初始化好之后,RT-Thread才繼續往下啟動。至于RT-Thread是哪個文件里面的哪個函數會調 用rt_hw_board_init(),我們在本章先不細講,留到接下來的“RT-Thread的啟動流程”章節再深究,這里我們 只需要知道我們用戶要自己編寫一個rt_hw_board_init()的函數供RT-Thread啟動的時候調用即可。
代碼清單:移植RTT-3 (4)-1:更新系統時鐘,如果硬件已經能夠跑起來都表示系統時鐘是沒有問題的,該函數一般由固件庫提供。
代碼清單:移植RTT-3 (4)-2:初始化系統定時器SysTick,SysTick給操作系統提供時基,1個時基我們稱之 為一個tick,tick是操作系統最小的時間單位。RT_TICK_PER_SECOND是一個在rtconfig.h中定義的宏,用于 配置SysTick每秒中斷多少次,這里配置為1000,即1秒鐘內SysTick會中斷1000次,即中斷周期為1ms。 這部 分功能等下我們會用固件庫函數SysTick_Config()來代替。
代碼清單:移植RTT-3 (4)-3:硬件BSP初始化統統放在這里,比如LED,串口,LCD等。目前我們暫時沒有初始化任何開發板的硬件。
代碼清單:移植RTT-3 (4)-4:這部分是RT-Thread為開發板組件提供的一個初始化函數,該函數在 components.c里面實現,由rtconfig.h里面的宏RT_USING_COMPONENTS_INIT決定是否調用,默認是開啟。
代碼清單:移植RTT-3 (4)-5:rt_console_set_device()是RT-Thread提供的一個控制臺設置函數,它將指定rt_kprintf()函數 的輸出內容具體從什么設備打印出來。該函數在kservice.c里面實現,由rtconfig.h里面的RT_USING_CONSOLE和RT_USING_DEVICE這兩個宏決定是否調用,目前我們暫時不用。
代碼清單:移植RTT-3 (4)-6:rt_system_heap_init()是RT-Thread提供的一個內存初始化函數, 只有在使用RT-Thread提供的動態內存分配函數時才需要使用到。該函數在mem.c里面實現,由rtconfig.h里面的RT_USING_HEAP和RT_USING_USER_MAIN這兩個決定是否調用,目前我們暫時不用。
2.6.1.2. SysTick_Handler()函數
代碼清單:移植RTT-3 (5):SysTick中斷服務函數是一個非常重要的函數,RT-Thread所有跟時間相關的事 情都在里面處理,具體實現見 代碼清單:移植RTT-4。
代碼清單:移植RTT-4 SysTick_Handler()函數
代碼清單:移植RTT-4 (1):進入中斷,對中斷計數器rt_interrupt_nest加1操作。
代碼清單:移植RTT-4(2):rt_tick_increase()用于更新時基,實現時間片,掃描系統定時器。
代碼清單:移植RTT-4(3) :退出中斷,對中斷計數器rt_interrupt_nest減1操作。
2.6.2. board.c文件修改
board.c文件內容修改的并不多,具體見代碼清單:移植RTT-5的高亮部分。
代碼清單:移植RTT-5 修改(1):在user目錄下新建一個board.h頭文件,用來包含固件庫和BSP相關的 頭文件和存放board.c里面的函數聲明,具體見 代碼清單:移植RTT-6。
代碼清單:移植RTT-5 修改(2):SysTick相關的寄存器和初始化函數統統屏蔽掉,將由固件庫文件core_cm3/4/7里面的替代。
代碼清單:移植RTT-5 修改(3):SysTick初始化函數由固件庫文件core_cm3/4/7里面的SysTick_Config()函數替代。
如果使用的是HAL庫(目前野火只在STM32 M7系列中使用HAL庫),則必須添加系統時鐘初始化函數,這個函數在 我們利用STM32CubeMX代碼生成工具配置工程時會自動給我們生成,我們只需添加到rt_hw_board_init()函數進 行初始化即可。
代碼清單:移植RTT-7 (2):初始化系統時鐘之后,需要對SysTick進行初始化,因為系統時鐘初始化函數會 在最后將SysTick的時鐘也進行初始化為HAL庫中默認的時鐘,不滿足我們系統的要求,所以我們只能使用 HAL_SYSTICK_Config將SysTick重新初始化,根據我們的RT_TICK_PER_SECOND宏定義進行配置。保證系統正常運行。
2.7. 添加core_delay.c和core_delay.h文件
只有在使用HAL庫時才需要添加core_delay.c和core_delay.h文件。野火只在其M7系列的開發板使用了HAL,M4和M3使用的是標準庫,不需要添加。
在ST的Cortex-M7內核系列的單片機中,就不再支持標準庫而是推出了HAL庫,目前,野火只在STM32 M7系列中使用HAL庫。
HAL是意思是HardwareAbstractionLayer,即硬件抽象層。用一句話概括就是現在這個庫與標準庫相比,與底 層硬件的相關性大大地降低,程序可移植性大大提高,電工寫程序更easy,可以像計算機的碼農那樣寫代碼。對于 小白來說,Coding的門檻雖然降低了,但是HAL帶來的占用內存大,編譯慢是很多老手不喜歡的,特別是我,我就 很不喜歡,編譯一次7分鐘,簡直是要了我的老命。鑒于HAL的優缺點,我個人觀點是比較適合ST Cortex-M7內核 系列這種大內存,高性能的MCU,雖然Cortex-M3/M4也有HAL庫,但是還是使用標準庫比較好。
HAL庫驅動中,由于某些外設的驅動需要使用超時判斷(比如I2C、SPI、SDIO等),需要精確延時(精度為1ms), 使用的是SysTick,但是在操作系統里面,我們需要使用SysTick來提供系統時基,那么就沖突了,怎么辦?我們 采取的做法是重寫HAL庫里面延時相關的函數,只有三個:HAL_InitTick()、HAL_GetTick()和HAL_Delay(), 這三個函數在HAL庫中都是弱定義函數(函數開頭帶__weak關鍵字),弱定義的意思是只要用戶重寫這三個函數, 原來HAL庫里面的就會無效。
在Cortex-M內核里面有一個外設叫DWT(Data Watchpoint and Trace), 該外設有一個32位的寄存器叫CYCCNT, 它是一個向上的計數器, 記錄的是內核時鐘運行的個數,最長能記錄的時間為: 10.74s = 2的32次方/400000000 (CYCNNT從0開始計數到溢出,最長的延時時間與內核的頻率有關,假設內核頻率為400M,內核時鐘跳一次的時間 大概為1/400M=2.5ns),當CYCCNT溢出之后,會清0重新開始向上計數。這種延時方案不僅精確,而且還不占用單 片機的外設資源,非常方便。所以HAL庫里面剛剛講到的需要重寫的三個函數我們都基于CYCCNT的方案來實現,具 體的實現見代碼清單:移植RTT-8和代碼清單13?9的高亮部分,其中core_delay.c和core_delay.h這兩個文件我們已經 寫好,放在user文件夾下即可,具體的使用方法看注釋。
代碼清單:移植RTT-8 (1):重寫HAL_InitTick()函數。
代碼清單:移植RTT-8 (2):重寫HAL_GetTick ()函數。
代碼清單:移植RTT-9 (3):重寫HAL_Delay ()函數。
2.8. 修改main.c
我們將原來裸機工程里面main.c的文件內容全部刪除,新增如下內容,具體見 代碼清單:移植RTT-10。
-
嵌入式
+關注
關注
5086文章
19140瀏覽量
305844 -
STM32
+關注
關注
2270文章
10904瀏覽量
356409 -
開發板
+關注
關注
25文章
5068瀏覽量
97623 -
RT-Thread
+關注
關注
31文章
1293瀏覽量
40211
發布評論請先 登錄
相關推薦
評論