【超詳細教程】移植RT-Thread nano,並基於 nano 添加 FinSH/shell

目錄

背景:移植RT-Thread nano,並基於  nano 添加 FinSH/shell

前提及準備工作

step1:添加rt-thread nano到裸機工程

1.1、Nano Pack 安裝

方法一:Pack Installer 安裝

方法二:手動安裝

1.2、基礎工程準備

1.3、開始移植rtt nano到裸機工程

1.4、適配 RT-Thread nano

1.5、RT-Thread Nano 配置(選配)

1.6 常見問題

step2:添加finsh到工程

2.1、添加finsh源碼

2.2、實現uart驅動

2.3、修改console名稱

2.4、下載驗證

2.5 附錄一份uart驅動供參考

2.6 常見問題


背景:移植RT-Thread nano,並基於  nano 添加 FinSH/shell

在nano上添加finsh可以有兩種方法:

1、移植finsh基於device框架【這個官方文檔中心有相關的文章了,鏈接:https://www.rt-thread.org/document/site/tutorial/nano/nano_finsh/an0033-nano-finsh/

2、移植finsh不基於device框架【本文講解這個不基於device框架的,從頭講解,如果移植rtt nano,然後基於這個nano 再移植finsh【本文基於 rtt nano 3.1.2 /3.1.1】

前提及準備工作

  1. 安裝了mdk
  2. 一個stm32 mdk裸機工程:要一份能運行的基本工程,如一個f103的可以跑led的裸機工程即可。(這裏基於hal庫做的mdk工程)
  3. 一個nano源碼:我們直接從keil中下載nano的pack包
  4. 注意,本文基於 rtt nano 3.1.2 版本,3.1.1也行

step1:添加rt-thread nano到裸機工程

注意:step1 是參考了RTT文檔中心的教程。

1.1、Nano Pack 安裝

Nano Pack 可以通過 MDK 聯網安裝,也可以手動安裝。下面開始介紹兩種安裝方式。

方法一:Pack Installer 安裝

打開 MDK 軟件,點擊工具欄的 Pack Installer 圖標:

點擊右側的 Pack,展開 Generic,可以找到 RealThread::RT-Thread,點擊 Action 欄對應的 Install ,就可以在線安裝 Nano Pack 了。另外,如果需要安裝其他版本,則需要展開 RealThread::RT-Thread,進行選擇。

方法二:手動安裝

我們也可以從官網下載安裝文件,RT-Thread Nano 離線安裝包下載,下載結束後雙擊文件進行安裝。

RT-Thread Nano 離線安裝包下載路徑:https://www.rt-thread.org/download/mdk/RealThread.RT-Thread.3.0.3.pack

1.2、基礎工程準備

在開始創建 RT-Thread 小系統之前,我們需要準備一個能正常運行的裸機工程。作爲示例,本文使用的是基於 STM32L475-Pandora 和 HAL 庫的一個 LED 閃爍程序。程序的主要截圖如下:

在我們的例程中主要做了系統初始化與LED閃爍功能,編譯下載程序後,就可以看到 LED 閃爍了。讀者可以根據自己的需要使用其他芯片,完成一個簡單的類似裸機工程。

1.3、開始移植rtt nano到裸機工程

打開已經準備好的可以運行的裸機程序,將 RT-Thread 添加到工程。如下圖,點擊 Manage Run-Time Environment。、

在 Manage Rum-Time Environment 裏”Software Component” 欄找到 RTOS,Variant 欄選擇 RT-Thread,然後勾選 kernel,點擊”OK” 就添加 RT-Thread 內核到工程了。

現在可以在 Project 看到 RT-Thread RTOS 已經添加進來了,展開 RTOS,可以看到添加到工程的文件:

 

Cortex-M 芯片內核移植代碼:

context_rvds.s
cpuport.c

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

 應用代碼及配置文件:

board.c
rtconfig.h

1.4、適配 RT-Thread nano

RT-Thread 會用到了異常處理函數 HardFault_Handler() 和懸掛處理函數 PendSV_Handler(),以及 Systick 中斷服務函數 SysTick_Handler(),所以用戶代碼需要保證這幾個函數沒有被使用,若編譯提示函數重複定義,請刪除自己定義的函數。

RT-Thread Nano 在 board.c 中默認完成了 systick 的配置,用戶可以修改宏 RT_TICK_PER_SECOND 的值配置每秒 systick 數。

RT-Thread Nano 默認是使用數組作爲 heap。

替換例程中的 delay 函數:

1). 包含 RT-Thread 的相關頭文件 <rtthread.h>

2). 刪除之前在裸機工程中做的系統配置(如hal初始化、時鐘初始化等),這是因爲RT-Thread在系統啓動時已經配置完成,否則會重複配置。

3). 將 delay() 函數替換成 rt_thread_mdelay(),如 rt_thread_mdelay(500) 將延時 500ms。

下面是完成修改的代碼:

編譯程序之後下載到芯片就可以看到基於 RT-Thread 的程序運行起來了,LED 正常閃爍。

注意事項:當添加 RT-Thread 之後,裸機中的 main() 函數會自動變成 RT-Thread 系統中 main 線程 的入口函數。由於線程不能一直獨佔 CPU,所以此時在 main() 中使用 while(1) 時,需要有讓出 CPU 的動作,比如使用 rt_thread_mdelay() 系列的函數讓出 CPU。另外3.0.3版本中還沒有 rt_thread_mdelay() 接口,可以使用 rt_thread_delay()

1.5、RT-Thread Nano 配置(選配)

用戶可以根據自己的需要通過修改 rtconfig.h 文件裏面的宏定義配置相應功能。

RT-Thread Nano 默認未開啓宏 RT_USING_HEAP,故只支持靜態方式創建任務及信號量。若要通過動態方式創建對象則需要在 rtconfig.h 文件裏開啓 RT_USING_HEAP 宏定義。

MDK 的配置嚮導 configuration Wizard 可以很方便的對工程進行配置,Value 一欄可以選中對應功能及修改相關值,等同於直接修改配置文件 rtconfig.h。

1.6 常見問題

Q1:如何升級 pack?

A:Pack 升級步驟基本如同軟件包,展開 RealThread::RT-Thread 後,選擇比較新的 Nano 版本,點擊 Install 進行安裝。如下圖所示,點擊紅色框中的 Install 進行升級,即可將 3.1.1 版本升級到 3.1.2。

step2:添加finsh到工程

2.1、添加finsh源碼

點擊Manage Run-Environment:

勾選device drivers與shell,這將自動把FinSH組件的源碼到工程,並自動添加 #define RTE_USING_DEVICE#define RTE_USING_FINSH 宏:

2.2、實現uart驅動

實現uart驅動,主要實現初始化與讀寫接口,並藉助了device註冊接口,將uart註冊到系統中,使其更方便的對接shell。需要實現的函數原型如下:

rt_err_t  (*init)   (rt_device_t dev);
rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);

//外加一個註冊的函數,註冊一個 rt_device 的實例,方便對接 FinSH 組件。

需要添加的代碼如下所示:

rt_err_t uart_init(rt_device_t dev)
{
    ...
}

rt_err_t uart_open(rt_device_t dev, rt_uint16_t oflag)
{
    return	uart_init(dev);
}

rt_size_t uart_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    ...                   
}

rt_size_t uart_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    ...
}               

struct rt_device uart_dev;
static int uart_register(void)     
{
    uart_dev.init  = uart_init;
    uart_dev.open  = uart_open;
    uart_dev.read  = uart_read;
    uart_dev.write = uart_write;
  
    rt_device_register(&uart_dev,"uart1",0);
    return 0;
}
INIT_BOARD_EXPORT(uart_register);
  • uart_init():初始化串口,包括硬件引腳的初始化與串口傳輸模式及波特率等的設置。
  • uart_open():打開串口,使用該接口僅僅是爲了對接 FinSH,沒有實際意義,直接返回串口初始化即可。
  • uart_read():讀入字符,長度size。
  • uart_write():輸出字符,長度size。注意輸出打印時,在遇到 \n 時,就在輸出 \n 之前先輸出一個 \r
  • uart_register():註冊函數,將uart設備註冊到系統中,務必使用INIT_BOARD_EXPORT()初始化該函數。

注意:RT-Thread 系統中已有的打印均以 “\n” 結尾,而並非 “\r\n”,所以在字符輸出時,需要在輸出“\n”之前輸出“\r”完成回車與換行,否則系統打印出來的信息將只有換行。

2.3、修改console名稱

打開rtconfig.h文件:將console的名稱修改爲剛註冊到系統中的uart名稱,這樣FinSH就對接到了UART端口上。如上面註冊名爲“uart1”,則在rtconfig.h中修改 RT_CONSOLE_DEVICE_NAME 的定義爲 uart1:

#define RT_CONSOLE_DEVICE_NAME      "uart1"

2.4、下載驗證

下載到開發板進行驗證,移植成功

2.5 附錄一份uart驅動供參考

static UART_HandleTypeDef UartHandle;
rt_err_t uart_init(rt_device_t dev)
{
    UartHandle.Instance = USART1;
    UartHandle.Init.BaudRate   = 115200;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = UART_PARITY_NONE;

    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        while (1);
    }

    return 0;
}

rt_err_t uart_open(rt_device_t dev, rt_uint16_t oflag)
{
    return  uart_init(dev);
}

rt_size_t uart_read(rt_device_t dev,
                    rt_off_t    pos,
                    void       *buffer,
                    rt_size_t   size)
{
    while (1)
    {
        if (HAL_UART_Receive(&UartHandle, buffer, size, 20) == 0)
        {
            return size;
        }
    }
}

rt_size_t uart_write(rt_device_t dev,
                     rt_off_t    pos,
                     const void *buffer,
                     rt_size_t   size)
{
    rt_size_t i = 0;
    char a = '\r';
    const char *val = 0;    

    val = (const char *)(buffer);
    
    __HAL_UNLOCK(&UartHandle);

    for (i = 0; i < size; i++)
    {
        if (*(val + i) == '\n')
        {
            HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 20);
        }
        HAL_UART_Transmit(&UartHandle, (uint8_t *)(val + i), 1, 20);
    }
    return 1;
}

struct rt_device uart_dev;
static int uart_register(void)
{
    uart_dev.init  = uart_init;
    uart_dev.open  = uart_open;
    uart_dev.read  = uart_read;
    uart_dev.write = uart_write;

    rt_device_register(&uart_dev, "uart1", 0);
    return 0;
}
INIT_BOARD_EXPORT(uart_register);

2.6 常見問題

Q1:輸出打印正常,卻沒有打印 msh >,也不能輸入。

A:這是由於FinSH 沒打開,所以只有打印功能,需要在rtconfig.h中打開 #define RTE_USING_FINSH 宏定義。

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