編者按:
其實很忙的,但是還是抽時間記錄了我的學習過程。這應該是我寫的格式比較正規的第3本學習筆記了,第一本《BLE4.0低功耗藍牙協議總結》、第二本《ANT協議研究》,再就是這本了《RT-Thread BLE5.0和ANT+應用開發實戰指南》,名字取得很大,哈哈哈!!目前這本已經寫到了第19章,目前規劃到了35章左右,慢慢寫吧!在這呢!感謝熊大團隊的RT-Thread作業系統,的確是一個非常好的RTOS,至少對於我來說很喜歡,因為其他RTOS就是個內核,大一點的就是Linux,太龐大了,吃不消。本文從文檔中抽出RT-Thread移植到nRF52840這篇和大家分享,這個文檔應該會在11月底發到論壇,公眾號、qq群和百度網盤的。
《RT-Thread BLE5.0和ANT+應用開發實戰指南》
當前目錄如下圖
第11章 RT-Thread移植到nRF52840
終於進入正文了,這裡再囉嗦一句,如果需要了解RT-Thread的內核等問題,可以參考RT-Thread官放的《rtthread_manual.zh》(下載地址-複製至外部瀏覽器打開:https://www.rt-thread.org/document/site/submodules/rtthread-manual-doc/README/)以及火哥的《RT-Thread 內核實現與應用開發實戰—基於STM32》(本公眾號後臺回覆:野火RT-Thread 即可下載),本書的重點還是側重藍牙理論和實戰。這個工程是由001.led_none工程修改而來,對應ZJ-SDK的002.led_RT_Thread_Doing工程。
11.1 RT-Thread的內核源碼加入基礎工程為了從0到1的移植RT-Thread,這裡先將RT-Thread的內核源碼加入到 001.led_none 基礎工程,看遇到的問題。添加的步驟是先添加.c文件,再添加.h文件路逕到頭文件,同時改了工程中文件夾的名字。
11.1.1 添加源文件添加後的工程和基礎工程對比如圖11-1所示
圖11-1 基礎工程添加RT-Thread內核源碼
11.1.2 添加頭文件路徑頭文件的添加對比如圖11-2所示,只是添加了RT-Thread的內核頭文件,以及M4核相關的頭文件。
圖11-2 基礎工程添加RT-Thread相關頭文件
11.2 編譯解除錯誤上面添加之後,進行編譯,編譯結果如圖11-3所示。編譯報出沒有rtconfig.h的頭文件,實際上也的確沒有這個頭文件,可以從官方RT-Thread源碼中的bsp目錄下的nrf52832中的工程中拷貝一個rtconfig.h來,放到本工程的config文件夾中。這樣編譯就能完全通過了。
圖11-3 編譯報錯
11.3 編程成功存儲分布再次編譯,看看勝利的果實,如圖11-4所示的存儲分布,falsh和ram的各個段都有明確的示意,堆和棧各使用了8K空間,中斷向量表使用了256位元組flash。
圖11-4 存儲分布圖
11.4 運行RT-Thread內核經過上面的處理,只需要一個銜接nRF52840和RT-Thread內核的關聯文件,以及一個初始化RT-Thread的文件,那麼就能使RT-Thread運行起來了。RT-Thread能運行起來就一定需要一個系統tick,也就是需要給RT-Thread內核創造一個產生tick的接口,實際上就是systick或者RTC的tick中斷函數,這個中斷函數中處理RT-Thread的相關東西,從而使RT-Thread和nRF52840連接在一起。
11.4.1 銜接及初始化文件來源這裡為了方便,我們依然從官方RT-Thread源碼中的bsp中nRF52832中得到一些參考。官方nRF52832工程所包含的文件如圖11-5所示。
圖11-5 官方nRF52832工程文件夾
從官方工程文件中,我們只需要4個文件,分別是application文件夾下的application.c文件和startup.c文件以及board文件夾下的board.c和board.h文件。startup文件夾是arm相關以及nrf52832相關的文件,這裡不需要。這幾個文件中application.c是用戶的函數入口,startup.c為RT-Thread系統的初始化文件,而board.c文件則是產生tick的地方。將這4個文件直接拷貝到001.len_none目錄下,如圖11-6所示。
圖11-6 拷貝銜接啟動文件後的工程目錄
11.4.2 添加文件第一次編譯排錯接下來直接將這3個.c文件添加到工程,以及包含頭文件路徑後進行第一次編譯報錯如圖11-7所示。
圖11-7 第一次編譯報錯
因為沒有使用uart,所有將這個頭文件刪掉。這樣經過多次的編譯刪掉多餘的頭文件同時添加缺少的沒有包含的頭文件路徑,一步一步地逼近編譯成功。下面統計修改的地方。
uart.hsoftdevice_handler.hnrf_drv_uart.h
nrf_drv_common.h路徑:../../../NORDIC_SDK/integration/nrfx/legacynrfx_clock.h路徑:../../../NORDIC_SDK/modules/nrfx/drivers/include
11.4.3 第二次編譯排錯經過上面的修改之後,再次編譯時,出現如圖11-8的錯誤,有多個main函數。
圖11-8 多個main函數定義
可以查看到代碼中除了原本的工程中有一個main.c文件中有main函數之外,還有一個文件application.c中也有main函數,這裡為了保持led的閃爍功能,將移除main.c文件,將main.c中的main函數功能在application.c中的main函數實現。最後修改application.c的代碼如代碼清單11-1,紅色部分為修改部分:
代碼清單11-1 修改後的application.c
#include <rtthread.h>#include <stdbool.h>#include <stdint.h>#include "nrf_delay.h"#include "nrf_gpio.h"#ifdef RT_USING_FINSH#include <finsh.h>#include <shell.h>#endifvoid rt_init_thread_entry(void* parameter){ nrf_gpio_cfg_output(ZJ_LED1); /* Toggle LEDs. */ while (true) { nrf_gpio_pin_toggle(ZJ_LED1); nrf_delay_ms(500); }}int rt_application_init(void){ rt_thread_t tid; tid = rt_thread_create("init", rt_init_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX / 3, 20); if (tid != RT_NULL) rt_thread_startup(tid); return 0;}
11.4.4第三次編譯排錯再次進行編譯出現如圖11-9的錯誤
圖11-9 第三次編譯出錯
看報錯記錄有3個錯誤:
這個應該是缺少.c文件,這裡使用了RTC,但是工程中沒有相關的.c文件,所以這個錯誤只需將對應的.c文件添加到工程,同時在sdk_config.h中將這個功能宏打開即能解決。
添加的兩個.c文件是nrfx_clock.c和nrf_drv_clock.c。
sdk_config.h文件末尾添加宏代碼清單如11-2:
代碼清單11-2 sdk_config.h文件末尾添加宏
// </h> //==========================================================// <h> nRF_Drivers //==========================================================// <e> CLOCK_ENABLED - nrf_drv_clock - CLOCK peripheral driver - legacy layer//==========================================================#ifndef CLOCK_ENABLED#define CLOCK_ENABLED 1#endif// <o> CLOCK_CONFIG_LF_SRC - LF Clock Source // <0=> RC // <1=> XTAL // <2=> Synth #ifndef CLOCK_CONFIG_LF_SRC#define CLOCK_CONFIG_LF_SRC 1#endif// <o> CLOCK_CONFIG_IRQ_PRIORITY - Interrupt priority// <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice// <0=> 0 (highest) // <1=> 1 // <2=> 2 // <3=> 3 // <4=> 4 // <5=> 5 // <6=> 6 // <7=> 7 #ifndef CLOCK_CONFIG_IRQ_PRIORITY#define CLOCK_CONFIG_IRQ_PRIORITY 7#endif// </e>// </h> //==========================================================
這個符號的是沒有初始在RAM中的段末地址,而這個地址要作為RT-Thread的heap的起始地址,既然知道了是沒有初始化的段末地址符號,那麼就可以直接搜索__bss_end這個符號試試運氣,可以找到在thumb_crt0.s中進行bss段清零時的使用到bss段末地址符號為__bss_end__(如圖11-10所示),所以將這個錯誤宏改為__bss_end__即可。
圖11-10 bss段末地址符號__bss_end__
11.4.5編譯成功存儲分布經過多次的編譯排錯終於見到了勝利的曙光,編譯的存儲分布如圖11-11所示。和只是加入源碼的圖11-4進行對比可以明顯看到某些段有變化。
圖11-11 RT-Thread啟動編譯成功存儲分布
11.5下載調試將上面的固件下載到ZJ曹孟德開發板中,可以看到LED1在閃爍,至此整個移植過程算是結束,但是我在調試時發現進入不了idle回調的休眠模式,因為在閃燈的延時函數是使用的死等延時,需要將nrf_delay_ms(500)修改為RT-Thread系統延時函數rt_thread_mdelay(500),再次編譯下載調試發現燈沒有閃爍,停止調試時,發現代碼一直在一個while等待中,如圖11-12所示。
圖11-12 RT-Thread跳不出while
NVIC_ISPR是中斷掛起設置寄存器,這個while就是等到中斷掛起寄存器中有中斷掛起,而__WFE()則是wait for event 等待事件,即下一次事件發生前都在此hold住不幹活從而使cpu進入low-power standby模式,這裡去讀NVIC_ISPR應該是有一定的邏輯問題的,經過參考其他資料和代碼,可以將上面的do-while改為如代碼清單11-3所示:
代碼清單11-1 修改do-while
{ /* No SD - we would just block interrupts globally. * BASEPRI cannot be used for that because it would prevent WFE from wake up. */ __WFE(); __WFE(); }
再次編譯下載就可以看到LED1又活躍的閃爍起來了。至此整個移植過程完畢。
11.6美化工程上面的工程即是ZJ-SDK中的002.led_RT_Thread_Doing工程,雖然能完美的工作了,但是工程文件夾的組織還需要做一個適當的調整。所以就出現了ZJ-SDK中的003.led_RT_Thread工程,這個工程將是帶有RT-Thread的基礎工程。這兩個工程的文件對比如圖11-13所示。
將board.c、startup.c以及application.c都作為RT-Thread的啟動文件,而用戶的操作將在application文件夾中的app_init.c中進行,也就是LED1閃爍的功能實現是在app_init.c中了,而在application.c中進行調用,從而進入用戶應用層。同時更改工程的名字為zj_ble_rt_thread,之後的工程名都將是這個名字,不會根據功能修改工程名,只會根據功能修改工程文件夾名。002.led_RT_Thread_Doing工程文件和003.led_RT_Thread工程文件對比圖如圖11-13所示。
board.c文件不修改也是可以運行的,但是有一些不合理的邏輯,所以0003.led_RT-Thread工程中這個文件做了大的修改,在後文的第13章分析RT-Thread基礎工程啟動流程中將會進行詳細分析。
圖11-13 工程美化
圖11-14 美化後的工程文件對比
往期精彩(點擊文章即可閱讀):
帶你進入絢麗多彩的柿餅M(一)
RT-Thread近期活動
1.在本公眾號後臺回復「野火RT-Thread」即可下載《RT-Thread內核實現與應用開發實戰指南》完整版
2.長按添加「小師妹」為好友,備註「學生」拉你進「RT-Thread官方學生」群,凡是在校學生組團報名參加大學生計劃審核通過的,即可獲得RT-Thread官方的競賽支持、培訓學習等。
你可以添加微信13924608367為好友,註明:公司+姓名,拉進 RT-Thread 官方微信交流群
RT-Thread
讓物聯網終端的開發變得簡單、快速,晶片的價值得到最大化發揮。GPLv2+協議,可免費在商業產品中使用。👇點擊進入官方論壇