RT-Thread 元件 FinSH 使用時遇到的問題

2022-07-06 12:05:29

一、FinSH 的移植與使用問題

FinSH元件輸入無反應的問題

  • 現象:當開啟 finsh 元件後,控制檯會打相應的資訊,如下圖說是:
 \ | /
- RT -     Thread Operating System
 / | \     3.1.5 build Jul  6 2022
 2006 - 2020 Copyright by rt-thread team
do components initialization.
initialize rti_board_end:0 done
initialize finsh_system_init:0 done
RT-Thread Start......
msh >

但是輸入回車或任意內容時,無反應。當然導致這樣的現象有兩種可能,一種可能是未正常開啟相應的設定,第二種是未對接收字元的函數進行實現,具體看下面內容

  • FinSH 元件在 rtconfig.h 中的設定,如下所示:
/* --------------------------------- RT-Thread 核心部分 --------------------------------- */
 
/* 表示核心物件的名稱的最大長度,若程式碼中物件名稱的最大長度大於宏定義的長度,
 * 多餘的部分將被截掉。*/
#define RT_NAME_MAX    8

/* 位元組對齊時設定對齊的位元組個數。常使用 ALIGN(RT_ALIGN_SIZE) 進行位元組對齊。*/
#define RT_ALIGN_SIZE   4

/* 定義系統執行緒優先順序數;通常用 RT_THREAD_PRIORITY_MAX-1 定義空閒執行緒的優先順序 */
#define RT_THREAD_PRIORITY_MAX  32

/* 定義時鐘節拍,為 100 時表示 100 個 tick 每秒,一個 tick 為 10ms */
#define RT_TICK_PER_SECOND  1000

/* 檢查棧是否溢位,未定義則關閉 */
#define RT_USING_OVERFLOW_CHECK

/* 定義該宏開啟 debug 模式,未定義則關閉 */
#define RT_DEBUG

/* 開啟 debug 模式時:該宏定義為 0 時表示關閉列印元件初始化資訊,定義為 1 時表示啟用 */
#define RT_DEBUG_INIT 1

/* 開啟 debug 模式時:該宏定義為 0 時表示關閉列印執行緒切換資訊,定義為 1 時表示啟用 */
#define RT_DEBUG_THREAD 0

/* 定義該宏表示開啟勾點函數的使用,未定義則關閉 */
//#define RT_USING_HOOK

/* 定義了空閒執行緒的棧大小 */
#define IDLE_THREAD_STACK_SIZE 1024

FinSH 移植

FinSH 元件使用有三種種方式,如下:

  1. 通過 rt_hw_console_getchar() 函數獲取控制檯資料
    FinSH 執行緒的使用方式主要是通過實現rt_hw_console_getchar()函數,獲取控制檯輸入的資料,具體方式看我之前的筆記,STM32 移植 RT-Thread 標準版的 FinSH 元件

  2. 通過外設驅動中的 資料流(stm32_getc函數) 獲取控制檯資料
    具體實現方式見UART外設的移植,稍後我也會將我移植的過程發出來,有需要的可以看我之後的筆記。

  3. 通過外設驅動中的 中斷方式 獲取控制檯資料
    中斷的方式包涵了DMA的方式獲取控制檯資料。

注意: 第二和第三中方式其實都是通過RT-Thread中的外設驅動獲取的,這裡我為啥會將 資料流和中斷 分開說明,是因為他們之間會引入一個新的問題,具體見之後的流程

二、裝置為空問題

  • 現象:msh >(dev != RT_NULL) assertion failed at function:rt_device_read, line number:320

  • 原因:出現這個現象主要是在註冊裝置的時候導致的,在註冊裝置的時候才用了資料流的方式回去了資料,如下所示:

    /* 設定串列埠裝置 */
    result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
                                       RT_DEVICE_OFLAG_RDWR 
                                       | RT_DEVICE_FLAG_INT_RX  
                                       | RT_DEVICE_FLAG_INT_TX
                                       , NULL);
    
    

    相應的設定宏如下所示:

    #define RT_DEVICE_FLAG_RDONLY       0x001 /* 唯讀 */
    #define RT_DEVICE_FLAG_WRONLY       0x002 /* 只寫  */
    #define RT_DEVICE_FLAG_RDWR         0x003 /* 讀寫  */
    #define RT_DEVICE_FLAG_REMOVABLE    0x004 /* 可移除  */
    #define RT_DEVICE_FLAG_STANDALONE   0x008 /* 獨立   */
    #define RT_DEVICE_FLAG_SUSPENDED    0x020 /* 掛起  */
    #define RT_DEVICE_FLAG_STREAM       0x040 /* 流模式  */
    #define RT_DEVICE_FLAG_INT_RX       0x100 /* 中斷接收 */
    #define RT_DEVICE_FLAG_DMA_RX       0x200 /* DMA 接收 */
    #define RT_DEVICE_FLAG_INT_TX       0x400 /* 中斷傳送 */
    #define RT_DEVICE_FLAG_DMA_TX       0x800 /* DMA 傳送 */
    
    

    認真思考的小夥伴就會存在一個疑問,為啥將資料接收註冊為 流模式 會導致裝置為空了,可以猜測在某處導致了裝置丟失,我們仔細找一下程式碼就會發現在shell.c檔案中,通過了中斷的方式開啟了裝置,如下圖所示:

    現在原因找到了,那麼解決方式有兩種,如下所示:

解決辦法

  1. 將註冊裝置時,改為中斷接收的方式註冊裝置

  2. 將shell.c檔案中的發開方式改為流模式即可,只需要將 RT_DEVICE_FLAG_INT_RX 遮蔽,如下所示:

    void finsh_set_device(const char *device_name)
    {
    	rt_device_t dev = RT_NULL;
    
    	RT_ASSERT(shell != RT_NULL);
    	dev = rt_device_find(device_name);
    	if (dev == RT_NULL)
    	{
    		rt_kprintf("finsh: can not find device: %s\n", device_name);
    		return;
    	}
    
    	/* check whether it's a same device */
    	if (dev == shell->device) return;
    	/* open this device and set the new device in finsh shell */
    	if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR |  
    //					RT_DEVICE_FLAG_INT_RX |		
    					RT_DEVICE_FLAG_STREAM) == RT_EOK)
    	{
    		if (shell->device != RT_NULL)
    		{
    			/* close old finsh device */
    			rt_device_close(shell->device);
    			rt_device_set_rx_indicate(shell->device, RT_NULL);
    		}
    
    		/* clear line buffer before switch to new device */
    		memset(shell->line, 0, sizeof(shell->line));
    		shell->line_curpos = shell->line_position = 0;
    
    		shell->device = dev;
    		rt_device_set_rx_indicate(dev, finsh_rx_ind);
    	}
    }
    
    

    ** 注意:** 改為流模式後,會發現一個奇怪的現象,就是當你使用偵錯模式時,可以正常接收指令,但是正常執行時,就無任何響應,遇到這樣的現象不要慌,接著往下看。

三、FinSH 卡死問題

  • 現象:FinSH執行緒卡死,明顯的發現就是,使用偵錯模式時,可以正常接收指令,但是正常執行時,就無任何響應。

  • 原因:深入分析後,會在 shell.c 檔案中的 finsh_getchar 函數中看到號誌的使用,如下圖所示:

    現在原因找到了,那麼我們只需要在適當的時候釋放號誌即可,那我們在找找看看有麼有訊號釋放的函數,接下來我們會發現在 shell.c 檔案中 finsh_rx_ind 函數就是釋放號誌的,如圖所示:

    那麼新的問題又來了,怎麼呼叫這個函數了,因為在shell.h檔案中也沒有這個函數的定義, 不要怕我們接著找,最後在shell.c 檔案中的 finsh_set_device 函數中,會將釋放號誌的函數指標放入 rt_device 結構體中,如下圖所示:

    那麼問題就變得簡單了,解決辦法如下

  • 解決辦法:我們已經知道怎麼釋放號誌了,所以只需要在 資料接收函數(stm32_getc)中,完成資料接收後,釋放號誌即可,如下所示:

    /**
     * 接收一個字元資料 
     */
    static int stm32_getc(struct rt_serial_device *serial)
    {
    	int ch;
    	struct stm32_uart *uart;
        RT_ASSERT(serial != RT_NULL);
        uart = rt_container_of(serial, struct stm32_uart, serial);
    
    	ch = -1;
        if (USART_GetFlagStatus(uart->handle.Instance, USART_FLAG_RXNE) != RESET)
    {
    #if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \
        || defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) \
        || defined(SOC_SERIES_STM32G4)
            ch = uart->handle.Instance->RDR & 0xff;
    #else		
    //		ch = (uint16_t)uart->handle.Instance->DR & (uint16_t)0x00ff;
    		ch = (char)USART_ReceiveData(uart->handle.Instance);
    #endif
    			
    	}
    	
    	/* 呼叫裝置接收資料回撥,釋放號誌  */
    	uart->serial.parent.rx_indicate(&serial->parent, 0);
    	return ch;
    }
    
    

四、測試

解決完問題後,在控制檯輸入回車有相應的迴應就沒問題,如下圖所示: