GD32F150實戰RT-Thread Nano

--作者:燕十三(flyingcys)

-- blog:http://blog.csdn.net/flyingcys

--QQ:294102238

何爲RT-Thread Nano?大家知道,Keil5以後採用pack形式管理芯片及各種相關組件的。RT-Thread Nano就是通過Keil pack方式發佈,在保持原有RT-Thread基本功能的情況下,實現了極小的FlashRam佔用。默認配置下,Flash可小至2.5KRam可以小至1K

目前pack包含有kernelshellmsh)、device drivers三部分功能,這3個功能可按實際使用情況按需加載。

本次使用的主芯片爲GD32F150C8T6,資源爲Flash:64KRam:8K

一、RT-Thread Nano Pack下載安裝

1. Keil5主界面上點擊“Pack Install”按鈕,即可進入Pack Install界面

 

1Keil5主界面

2. Pack Install界面下,RT-Thread Pack在右邊欄中。如未下載,可點擊“Install”下載;如已安裝,版本有更新,將提示“Update”可更新。

 

2RT-Thread Pack下載

3. 如在圖2界面“Packs”欄中未發現“RT-Thread”,可先在菜單“Packs”下點擊“Check for Updates”。Update完成後,將可看到RT-Thread Pack

 

3Pack Update

4. Pack下載完成後,Keil將自動彈出Pack安裝界面,按步驟依次完成安裝。

二、裸機最小系統工程建立

1. 本次工程使用的是芯片是GD32F150C8T664KFlash8KRamKeil5下開發須先在官網下載Keil Pack (GigaDevice.GD32F1x0_DFP.pack),並正確安裝。

 

4GD32F150 Device選擇

2. 先按照裸機Keil工程流程搭建工程,爲 測試FlashRam大小,最小工程只包含必須的Libraries文件,main函數也未作任何多餘處理。

 

5GD32F150C8T6最小工程

3. 編譯完成後,默認配置Flash1112字節、Ram2144字節

4. 修改默認啓動文件startup_gd32f1x0.s定義堆和棧大小:默認堆爲0x400,棧爲0x400。後續我們將採用RT-Thread管理內存堆,所有堆設置爲0;棧可按照main函數應用需求調整爲0x100或以上。


6:啓動文件棧和堆修改

啓動文件修改後,Ram大小爲352字節

 

7:修改堆和棧後Flash和棧佔用大小

三、kernel加載與應用

1. 加載RT-Thread Kernel:

在主界面點擊Manage Run-Time Environment”按鈕即可進入加載頁。

8Manage Run-Time Environment

RTOS”一欄中選中“RT-Thread”,並在列表中選中“kernel”,當前版本爲2.1.2

 

9RT-Thread kernel選擇

2. 確定後,keil界面上會加載RT-Threadkernel文件,更根據當前選擇芯片類型加入已移植完成的M3芯片內核代碼、配置文件等。

 

10RT-Thread kernel文件

其中:

l Kernel文件包括:

clock.c
components.c
device.c
idle.c
ipc.c
irq.c
kservice.c
mem.c
object.c
scheduler.c
thread.c
timer.c

l Cortex-m3芯片內核移植代碼:

cpuport.c
context_rvds.s

l 應用代碼及配置文件:

board.c
rtconfig.h

3. 此時再次編譯工程,編譯器會提示有函數被重複定義了。需按照如下方式做一些修改:

a) 修改gd32f10x_it.c文件,刪除如下函數:

void HardFault_Handler(void)
void PendSV_Handler(void)
void SysTick_Handler(void)

b) 按照board.c上的說明,依次完成如下操作:

 

11board.c修改流程說明

修改24行:#include “gd32f1x0.h”
修改48行:在rt_hw_board_init()函數內開啓
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
修改66行:void SysTick_Handler(void)

4. 修改main.c

加入#include <rtthread.h>
在while循環中加入rt_thread_dealy(100);

5. 再次編譯順利通過,下載至芯片運行可看到main函數中每1s可中斷一次。RT-Thread任務調度器已經正常運行。

 

12RT-Thread正常運行

通過查看.map文件可獲取當前各文件資源佔用情況。在未開啓任何優化的情況下,可以看到RT-Thread內核各文件資源佔用情況。

 

13:資源佔用表

6. 可在main函數內添加RT-Thread支持的任務、定時器、信號量等功能。Nano默認rtconfig.h配置只支持靜態任務、信號量創建。在靜態模式下,不能使用rt_thread_create/rt_thread_delete/rt_sem_create/rt_sem_delete/rt_malloc/rt_free與動態創建、刪除有關的接口。如需動態創建,需開啓RT_USING_HEAP項,詳見本篇第五部分:《RT-Thread配置》

四、RT-Thrad啓動流程分析

這次創建的keil工程雖然應用了RT-Thread嵌入式操作系統,但開發流程無不帶os開發幾乎沒有差別。都是將main作爲入口,完成硬件初始化、應用代碼添加,而且可以直接應用RT-Thread的各種功能完成產品開發。但是我們沒有添加RT-Thread相關初始化、啓動等代碼到我們的工程裏面,但實際情況是調度器已經正常運行了,這是怎麼實現的呢?

1. RT-Thread入口

我們可以在components.c文件的140行看到#ifdef RT_USING_USER_MAIN宏定義判斷,這個宏是定義在rtconfig.h文件內的,而且處於開啓狀態。同時我們可以在146行看到#if defined (__CC_ARM)的宏定義判斷,__CC_ARM就是指keil的交叉編譯器名稱。

我們可以在這裏看到定義了2個函數:$$Sub$$main()$$Super$$main()函數;這裏通過$$Sub$$main()函數在程序就如主程序之前插入一個例程,實現在不改變源代碼的情況下擴展函數功能。鏈接器通過調用$$Sub$$Main()函數取代main(),然後通過$$Super$$main再次回到main()

#if defined (__CC_ARM)
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
    rt_hw_interrupt_disable();
    rtthread_startup();
    return 0;
}

$$Sub$$main函數內調用了rt_hw_interrutp_disable()rtthread_startup()兩個函數。熟悉RT-Thread開發流程的,一看就知道這是標準的RT-Thread的啓動入口。

其中:

  • rt_hw_interrupt_disable():關中斷操作,
  • rtthread_startup():完成systick配置、timer初始化/啓動、idle任務創建、應用線程初始化、調度器啓動等工作。

int rtthread_startup(void)
{
	rt_hw_interrupt_disable();

    /* board level initalization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

    /* create init_thread */
    rt_application_init();

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

    /* start scheduler */
    rt_system_scheduler_start();

    /* never reach here */
    return 0;
}

  • rt_hw_board_init():該函數定義在board.c文件內,需要修改systick配置
  • rt_system_timer_init()/rt_system_timer_thread_init()timer初始化/啓動
  •  rt_thread_idle_init()idle任務創建
  • rt_application_init():應用線程初始化
  • rt_system_scheduler_start():調度器啓動

int rtthread_startup(void)
{
	rt_hw_interrupt_disable();

    /* board level initalization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

    /* create init_thread */
    rt_application_init();

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

    /* start scheduler */
    rt_system_scheduler_start();

    /* never reach here */
    return 0;
}

2. 應用線程入口rt_application_init()

void rt_application_init(void)
{
    rt_thread_t tid;

#ifdef RT_USING_HEAP
    tid = rt_thread_create("main", main_thread_entry, RT_NULL,
                      RT_MAIN_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 3, 20);
    RT_ASSERT(tid != RT_NULL);
#else
    rt_err_t result;

    tid = &main_thread;
    result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
                       main_stack, sizeof(main_stack), RT_THREAD_PRIORITY_MAX / 3, 20);
    RT_ASSERT(result == RT_EOK);
#endif

    rt_thread_startup(tid);
}

在這裏,我們可以看到應用線程創建了一個名爲main_thread_entry的任務,並且已經啓動了該任務。我們再次來看一下man_thread_entry任務。

/* the system main thread */
void main_thread_entry(void *parameter)
{
    extern int main(void);
    extern int $Super$$main(void);

    /* RT-Thread components initialization */
    rt_components_init();

    /* invoke system main function */
#if defined (__CC_ARM)
    $Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}

man_thread_entry任務完成了2個工作:調用rt_components_init()、進入應用代碼真正的main函數。

在這裏我們看到了$$Super$$main()的調用,在前面我們講了調用該函數可用來回到main()的。

 

14RT-Thread初始化及啓動流程

 

從以上分析可以,正是由於在rtconfig.h內開啓了RT_USING_USER_MAIN選項,編譯器在main之前插入了$$Sub$$main(),完成了RT-Thread初始化及調度器啓動工作。並且通過創建main_thread_entry任務,並通過$$Super$$main()回到main()函數。這樣看來main()函數其實只是RT-Thread的一個任務,該任務的優先級爲RT_THREAD_PRIORITY_MAX / 3,任務棧爲RT_MAIN_THREAD_STACK_SIZE

 

15RT_USING_USER_MAIN選項

五、RT-Thread配置(rtconfig.h)

RT-Thread是一個高度可配置的嵌入式實時操作系統,配置通過rtconfig.h文件實現。Nano就是在rtconfig.h配置下實現了2.5KFlash1KRam的內核應用,但是由於Nano未開啓RT_USING_HEAP選項,故只支持靜態方式創建任務及信號量。下面分步開啓rtconfig.h配置常用選項。

1. RT_USING_HEAP:開啓heap

根據芯片型號在board.c37行,修改SARM_SIZE大小,默認爲8GD32F150C8T6正好也爲8K

 

圖16SRAM_SIZE配置

開啓RT_USING_HEAP選項後,在board.crt_hw_board_init()內將調用rt_system_heap_init()

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init((void*)HEAP_BEGIN, (void*)SRAM_END);
#endif

其中:

SRAM_END:根據宏定義爲0x20000000 + SRAM_SIZE * 1024

HEAP_BEGIN

 

圖17HEAP_BEGIN定義

其中Image$$RW_IRAM1$$ZI$$Limit是鏈接器導出符號,表示ZI段的結束地址。

配置完成後,就可通過動態創建任務、信號量等方式開發軟件了。

2. RT_USING_TIMER_SOFT:開啓軟件定時器

Nano默認配置未開啓軟件定時器功能。開啓軟件定時器功能後,可創建多個軟件定時器,定時器精度爲Systick觸發精度。

 

圖18:軟件定時器開啓


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章