這是一個(gè)航順 HK32F030 的 RT-Thread Nano 移植示例,記錄了在 Keil 裸機(jī)工程的基礎(chǔ)上進(jìn)行 RT-Thread Nano 移植的全過(guò)程。在按文檔中心的指導(dǎo)進(jìn)行移植的過(guò)程中基本沒(méi)有遇到問(wèn)題,只是由于 HK32F030 的RAM 較小,無(wú)法啟用 FinSH。移植工程已經(jīng)分享在Gitee RT-Thread-Nano-HK32F030。
開(kāi)源地址:https://gitee.com/CraztTnspt/rt-thread-nano-hk32-f030
(請(qǐng)復(fù)制至外部瀏覽器打開(kāi))
硬件信息:
MCU: 航順 HK32F030MF4P6 , RAM: 2KB, ROM:16KB
開(kāi)發(fā)板:hk32f030 - 立創(chuàng)EDA (https://lceda.cn/whj4674672/hk32f030)由 @whj467467222 設(shè)計(jì)
參考文檔
RT-Thread Nano 簡(jiǎn)介與下載(https://docs.rt-thread.org/#/rt-thread-version/rt-thread-nano/an0038-nano-introduction)
0.準(zhǔn)備移植
在移植 RT-Thread Nano 之前,需要準(zhǔn)備一個(gè)能正常運(yùn)行的裸機(jī)工程。航順的庫(kù)文件包 HK32F030Mxx_Library_V1.1.4.7z 中提供了 HK32F030 的標(biāo)準(zhǔn)庫(kù)、啟動(dòng)文件等,還有一個(gè)裸機(jī)工程模板,整理后得到這里移植使用的裸機(jī)工程。
編譯后燒錄,看到LED閃爍,裸機(jī)程序正常運(yùn)行。實(shí)測(cè)可以使用Jlink 或 CMSIS-DAP 燒錄調(diào)試,而使用 ST-Link 無(wú)法識(shí)別到 HK32F030。
之后就可以開(kāi)始 RT-Thread Nano 的移植了。
1.Nano Pack 安裝
Nano Pack 可以在 Keil MDK IDE 內(nèi)進(jìn)行安裝,也可以手動(dòng)安裝。這里選擇手動(dòng)安裝Pack,從官網(wǎng)下載安裝文件:RT-Thread Nano 離線安裝包,下載結(jié)束后雙擊文件進(jìn)行安裝。
然后將 RT-Thread Nano 添加到工程中。點(diǎn)擊 Manage Run-Time Environment
在 Manage Rum-Time Environment 內(nèi)打開(kāi) RTOS 欄,勾選 kernal,點(diǎn)擊 OK 后就將 RT-Thread 內(nèi)核加入到工程中了。
現(xiàn)在能在 Keil 的 Project 欄看到 RTOS,展開(kāi)后可以看到 RT-Thread Nano 的文件已經(jīng)加入了工程。
2.適配 RT-Thread Nano
中斷與異常處理
RT-Thread 會(huì)接管異常處理函數(shù) HardFault_Handler() 和懸掛處理函數(shù) PendSV_Handler(),這兩個(gè)函數(shù)已由 RT-Thread 實(shí)現(xiàn),所以需要?jiǎng)h除工程里中斷服務(wù)例程文件 Drivers/hk32f030m_it.c 中的這兩個(gè)函數(shù),避免在編譯時(shí)產(chǎn)生重復(fù)定義。如果此時(shí)對(duì)工程進(jìn)行編譯,沒(méi)有出現(xiàn)函數(shù)重復(fù)定義的錯(cuò)誤,則不用做修改。
系統(tǒng)時(shí)鐘配置
現(xiàn)在需要在 RTOS/board.c 中實(shí)現(xiàn) 系統(tǒng)時(shí)鐘配置(為 MCU、外設(shè)提供工作時(shí)鐘)與 os tick 的配置 (為操作系統(tǒng)提供心跳 / 節(jié)拍)。
1void rt_hw_board_init()
2{
3 /* System Clock Update */ 4 SystemCoreClockUpdate();
5 6 /* System Tick Configuration */ 7 _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
8 9 /* Call components board initial (use INIT_BOARD_EXPORT()) */10#ifdef RT_USING_COMPONENTS_INIT11 rt_components_board_init();
12#endif1314#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)15 rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
16#endif17}
上面的代碼中,SystemCoreClockUpdate() 對(duì)系統(tǒng)時(shí)鐘進(jìn)行更新,_SysTick_Config() 配置了 OS Tick。此處 OS Tick 使用滴答定時(shí)器 systick 實(shí)現(xiàn),需要在 board.c 中實(shí)現(xiàn) SysTick_Handler() 中斷服務(wù)例程,調(diào)用 RT-Thread 提供的 rt_tick_increase() ,如下:
1void SysTick_Handler(void)
2{
3 /* enter interrupt */ 4 rt_interrupt_enter();
5 6 rt_tick_increase();
7 8 /* leave interrupt */ 9 rt_interrupt_leave();
10}
由于 SysTick_Handler() 中斷服務(wù)例程由用戶在 board.c 中重新實(shí)現(xiàn),作為系統(tǒng) OS Tick,所以還需要?jiǎng)h除工程里中原本已經(jīng)實(shí)現(xiàn)的 SysTick_Handler() ,避免在編譯時(shí)產(chǎn)生重復(fù)定義。如果此時(shí)對(duì)工程進(jìn)行編譯,沒(méi)有出現(xiàn)函數(shù)重復(fù)定義的錯(cuò)誤,則不用做修改。
內(nèi)存堆初始化
系統(tǒng)內(nèi)存堆的初始化在 board.c 中的 rt_hw_board_init() 函數(shù)中完成,內(nèi)存堆功能是否使用取決于宏 RT_USING_HEAP 是否開(kāi)啟,RT-Thread Nano 默認(rèn)不開(kāi)啟內(nèi)存堆功能,這樣可以保持一個(gè)較小的體積,不用為內(nèi)存堆開(kāi)辟空間。開(kāi)啟系統(tǒng) heap 將可以使用動(dòng)態(tài)內(nèi)存功能,如使用 rt_malloc、rt_free 以及各種系統(tǒng)動(dòng)態(tài)創(chuàng)建對(duì)象的 API。若需要使用系統(tǒng)內(nèi)存堆功能,則打開(kāi) RT_USING_HEAP 宏定義即可,此時(shí)內(nèi)存堆初始化函數(shù) rt_system_heap_init() 將被調(diào)用。
3.編寫(xiě)第一個(gè)應(yīng)用
移植好 RT-Thread Nano 之后,則可以開(kāi)始編寫(xiě)第一個(gè)應(yīng)用代碼驗(yàn)證移植結(jié)果。此時(shí) main() 函數(shù)就轉(zhuǎn)變成 RT-Thread 操作系統(tǒng)的一個(gè)線程,現(xiàn)在可以在 main() 函數(shù)中實(shí)現(xiàn)第一個(gè)應(yīng)用:板載 LED 指示燈閃爍。
首先在文件首部增加 RT-Thread 的相關(guān)頭文件 《rtthread.h》 。
在 main() 函數(shù)中(也就是在 main 線程中)實(shí)現(xiàn) LED 閃爍代碼:初始化 LED 引腳、在循環(huán)中點(diǎn)亮 / 熄滅 LED。
將延時(shí)函數(shù)替換為 RT-Thread 提供的延時(shí)函數(shù) rt_thread_mdelay()。該函數(shù)會(huì)發(fā)起系統(tǒng)調(diào)度,切換到其他線程運(yùn)行,體現(xiàn)了線程的實(shí)時(shí)性。
此時(shí)可以看到 LED 閃爍,雖然現(xiàn)象與裸機(jī)程序一致,但 RT-Thread 已經(jīng)在 HK32F030 上成功運(yùn)行。
使用 RTOS 造成固件變大后,通過(guò)CMSIS-DAP 燒錄程序可能出現(xiàn)失敗現(xiàn)象
將 CMSIS-DAP 的 SW 調(diào)試速度調(diào)低為 500kHz 后燒錄成功
4.移植控制臺(tái) FinSH
由于 RAM 的大小有限,這里 FinSH 的移植未能完成
在 Nano 上添加 UART 控制臺(tái)
在 RT-Thread Nano 上添加 UART 控制臺(tái)打印功能后,就可以在代碼中使用 RT-Thread 提供的打印函數(shù) rt_kprintf() 進(jìn)行信息打印,從而獲取自定義的打印信息,方便定位代碼 bug 或者獲取系統(tǒng)當(dāng)前運(yùn)行狀態(tài)等。實(shí)現(xiàn)控制臺(tái)打?。ㄐ枰_認(rèn) rtconfig.h 中已使能 RT_USING_CONSOLE 宏定義),需要完成基本的硬件初始化,以及對(duì)接一個(gè)系統(tǒng)輸出字符的函數(shù),本小節(jié)將詳細(xì)說(shuō)明。
實(shí)現(xiàn)串口初始化
使用串口對(duì)接控制臺(tái)的打印,首先需要初始化串口,如引腳、波特率等。 uart_init() 需要在 board.c 中的 rt_hw_board_init() 函數(shù)中調(diào)用。
1static int uart_init(void);
示例代碼:如下是基于 HK32 庫(kù)的 HK32F030 串口初始化程序,參考航順例程編寫(xiě)。實(shí)現(xiàn)了串口的發(fā)送并配置了串口中斷接收。
1#define USART1_TX_PORT GPIOA 2#define USART1_TX_PIN GPIO_Pin_3 3#define USART1_TX_IO_CLK_EN() RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE) 4 5#define USART1_RX_PORT GPIOD 6#define USART1_RX_PIN GPIO_Pin_6 7#define USART1_RX_IO_CLK_EN() RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE) 8 9static void USART_GPIO_Configurature(void);
10static void USART_NVIC_Configurature(void);
11static int uart_init(void);
1213static int uart_init(void)
14{
15 USART_InitTypeDef m_usart;
1617 USART_GPIO_Configurature();
1819 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
20 m_usart.USART_BaudRate = 115200;
21 m_usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
22 m_usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
23 m_usart.USART_Parity = USART_Parity_No;
24 m_usart.USART_StopBits = USART_StopBits_1;
25 m_usart.USART_WordLength = USART_WordLength_8b;
26 USART_Init(USART1, &m_usart);
27 USART_Cmd(USART1, ENABLE);
2829 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
3031 USART_NVIC_Configurature();
3233 return 0;
34}
35//INIT_BOARD_EXPORT(uart_init);3637static void USART_GPIO_Configurature(void)
38{
39 GPIO_InitTypeDef m_gpio;
4041 USART1_TX_IO_CLK_EN();
42 USART1_RX_IO_CLK_EN();
4344 m_gpio.GPIO_Mode = GPIO_Mode_AF;
45 m_gpio.GPIO_OType = GPIO_OType_PP;
46 m_gpio.GPIO_Pin = USART1_TX_PIN;
47 m_gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
48 m_gpio.GPIO_Speed = GPIO_Speed_10MHz;
49 GPIO_Init(USART1_TX_PORT, &m_gpio);
50 GPIO_PinAFConfig(USART1_TX_PORT,GPIO_PinSource3,GPIO_AF_1);
5152 m_gpio.GPIO_Pin = USART1_RX_PIN;
53 GPIO_Init(USART1_RX_PORT, &m_gpio);
54 GPIO_PinAFConfig(USART1_RX_PORT,GPIO_PinSource6,GPIO_AF_1);
55}
56static void USART_NVIC_Configurature(void)
57{
58 NVIC_SetPriority(USART1_IRQn,0);
59 NVIC_EnableIRQ(USART1_IRQn);
60}
實(shí)現(xiàn) rt_hw_console_output
實(shí)現(xiàn) finsh 組件輸出一個(gè)字符,即實(shí)現(xiàn) uart 輸出一個(gè)字符:
注:注意:RT-Thread 系統(tǒng)中已有的打印均以 結(jié)尾,而并非 ,所以在字符輸出時(shí),需要在輸出 之前輸出 ,完成回車(chē)與換行,否則系統(tǒng)打印出來(lái)的信息將只有換行。
示例代碼:如下是基于HK32 庫(kù)實(shí)現(xiàn)的串口驅(qū)動(dòng)對(duì)接 rt_hw_console_output()
1void USART1_SendByte(uint8_t ch)
2{
3 while((USART1-》ISR & USART_ISR_TXE) == 0);
4 USART1-》TDR = ch;
5}
6void rt_hw_console_output(const char *str)
7{
8 rt_size_t i = 0, size = 0;
9 char a = ‘
’;
1011 size = rt_strlen(str);
12 for (i = 0; i 《 size; i++)
13 {
14 if (*(str + i) == ‘
’)
15 {
16 USART1_SendByte((uint8_t)a);
17 }
18 USART1_SendByte(*(str + i));
19 }
20}
將對(duì)應(yīng)代碼加入 board.c ,編譯并燒錄后,可以看到終端(或串口助手中輸出的rtt logo):

至此就可以使用 rt_kprintf() 打印調(diào)試信息了。
在 Nano 上添加 FinSH 組件
RT-Thread FinSH 是 RT-Thread 的命令行組件(shell),提供一套供用戶在命令行調(diào)用的操作接口,主要用于調(diào)試或查看系統(tǒng)信息。它可以使用串口 / 以太網(wǎng) / USB 等與 PC 機(jī)進(jìn)行通信。這里使用串口方式,在 Nano 上實(shí)現(xiàn) FinSH 功能。
Keil 添加 FinSH 源碼
打開(kāi) Manage Run-Environment。
勾選 shell 然后點(diǎn)擊OK,將 FinSH 組件的源碼到工程。
這時(shí)看到 RTOS Group 中加入了以下 FinSH 文件。
實(shí)現(xiàn) rt_hw_console_getchar()
要實(shí)現(xiàn) FinSH 組件功能(既可以打印也能輸入命令進(jìn)行調(diào)試),需要在 board.c 中對(duì)接控制臺(tái)輸入函數(shù),實(shí)現(xiàn)字符輸入:
rt_hw_console_getchar():控制臺(tái)獲取一個(gè)字符,即在該函數(shù)中實(shí)現(xiàn) uart 獲取字符,可以使用查詢方式獲取(注意不要死等,在未獲取到字符時(shí),需要讓出 CPU),也可以使用中斷方式獲取。
示例代碼:(未實(shí)現(xiàn))
1char rt_hw_console_getchar(void)
2{
3 int ch = -1;
4 // 接收一個(gè)字符5 。。.
6 return ch;
7}
加入 FinSH 后 RAM 空間不足
這時(shí)編譯會(huì)出現(xiàn)報(bào)錯(cuò):

看編譯輸出應(yīng)該是存儲(chǔ)空間不足,超出RAM大小154 Bytes,嘗試將編譯器優(yōu)化等級(jí)調(diào)高至 Level2 ,但仍會(huì)報(bào)錯(cuò)。
然后嘗試在 rtconfig.h 中調(diào)小 RT_CONSOLEBUF_SIZE 與 FINSH_THREAD_STACK_SIZE ,編譯成功??梢钥创藭r(shí)的內(nèi)存占用:Program Size: Code=9458 RO-data=922 RW-data=144 ZI-data=1832 ,ROM占用為 Code+RO+RW=10524 Byte,RAM 占用為 RW+ZI=1976 Byte,RAM即將耗盡。同時(shí)因?yàn)檎{(diào)小了線程運(yùn)行棧,程序運(yùn)行時(shí)會(huì)產(chǎn)生 hard fault,因此不再考慮將 finsh 移植至nano上。
為了正常使用,應(yīng)當(dāng)關(guān)閉 FinSH 組件,在RTE_Components.h中注釋 RTE_USING_FINSH,此時(shí)程序大小為:Program Size: Code=5756 RO-data=572 RW-data=120 ZI-data=1264 ,ROM占用為 Code+RO+RW=6448 Byte,RAM 占用為 RW+ZI=1384 Byte,剩余空間較為充裕。也可以通過(guò)在 Manage Run-Environment 中關(guān)閉 shell,移除 FinSH 組件。
至此,在 HK32F030MF4P6 上的 RT-Thread Nano 移植工作就完成了。
編輯:jq
-
RT-Thread
+關(guān)注
關(guān)注
32文章
1583瀏覽量
44515
原文標(biāo)題:【國(guó)產(chǎn)MCU系列】在 HK32F030 上移植 RT-Thread Nano
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
RISC-V單片機(jī)快速入門(mén)02,移植RT_Thread Nano系統(tǒng)
FlashDB移植到rt-thread nano,運(yùn)行時(shí)控制臺(tái)無(wú)輸出怎么解決?
移植最新版的rt-thread nano時(shí)程序進(jìn)入到entry就報(bào)錯(cuò),為什么?
如何移植rt-thread到野火imx6ull開(kāi)發(fā)板上?
使用RT-Thread studio 開(kāi)發(fā)RT-Thread Nano項(xiàng)目的時(shí)候 串口的驅(qū)動(dòng)是要自己寫(xiě)嗎?
CubeMX移植RT-Thread 3.1.5文件重復(fù)怎么解決?
如何在RT-Thread上創(chuàng)建一個(gè)GD32F470工程?
RT-Thread Nano移植后動(dòng)態(tài)創(chuàng)建線程創(chuàng)建不了怎么解決?
恩智浦MCU教程 基于MCUXpresso和FRDM-MCXA346的RT-Thread Nano移植
如何移植 RT-Thread Nano 并創(chuàng)建 2 個(gè)線程?
RT-Thread Nano硬核移植指南:手把手實(shí)現(xiàn)VGLite圖形驅(qū)動(dòng)適配 | 技術(shù)集結(jié)
RT-Thread Nano移植后動(dòng)態(tài)創(chuàng)建線程創(chuàng)建不了怎么處理?
RT-Thread Nano源碼bsp工程無(wú)法正常使用msh,輸入msh命令無(wú)響應(yīng)怎么解決?
移植最新版的rt-thread nano時(shí)發(fā)現(xiàn)程序只要一進(jìn)去entry函數(shù)就直接跳到一個(gè)死循環(huán)里,為什么?
如何將RT-Thread移植到NXP MCUXPressoIDE上
如何在在HK32F030MF4P6上移植RT-Thread Nano
評(píng)論