嵌入式系統除錯Uboot串列埠互動除錯

HomeByte發表於2020-11-03

1 硬體連線

我們需要一個USB轉串列埠線來除錯,有兩種規格如下:

規格1

規格2

其他轉換晶片如CH340,PL2303,FTP232也都是可以的,FTP232能夠支援的最大波特率會更高

在板上可以看到如下一個介面:

我們使用串列埠2也就是UART2來做除錯口,將USB轉串列埠線的RXTXGND連線到板子上UART2的TXRXGND

2 波特率配置

如果CONFIG_BAUDRATE不是115200,修改為115200,因為不是所有晶片都可以支援高波特率的(15000000):

修改後編譯U-Boot並燒錄。

3 除錯

首先我們建立一個串列埠連線:

協議選擇SERIAL:

注意:埠號對應我們USB轉串列埠的那個埠,波特率要115200;

點選“連線”:

最後給板子上電,可以看到串列埠的UBoot日誌輸出:

4  Driver Model(DM)模型

uboot引入了驅動模型(driver model),這種驅動模型為驅動的定義和訪問介面提供了統一的方法;提高了驅動之間的相容性以及訪問的標準型。

uboot的DM主要有四個組成部分:

udevice - 簡單就是指裝置物件,可以理解為kernel中的device。

driver - udevice的驅動,可以理解為kernel中的device_driver。和底層硬體裝置通訊,並且為裝置提供面向上層的介面。

uclass - 使用相同方式的操作集的device的組。相當於是一種抽象。uclass為那些使用相同介面的裝置提供了統一的介面。

uclass_driver - 對應uclass的驅動程式。主要提供uclass操作時,如繫結udevice時的一些操作。

呼叫關係如下:

DM的模型支援原始碼在:include/dm

幾個關鍵資料結構的說明:

  1. uclass id

在uclass-id.h中定義了相關的id,部分列舉如下:

比如我們串列埠用到uclass的ID就是UCLASS_SERIAL。

2. uclass

同一類裝置屬於同一個uclass,擁有相同的uclass ID。比如說RTC晶片,

市面上RTC晶片很多,由不同的廠家生產,其記憶體暫存器定義甚至訪問介面都不一樣,所以RTC的driver肯定是不一樣的,但是從功能的角度來說,他們都是用來記錄時間的,所他們都屬於rtc-class。 uclass從層級結構來講,起到非常好的承上啟下的作用,它既能遮蔽具體裝置個體間的差異性,向使用者提供統一的介面,又能為同一類的裝置定義統一的處理函式,具體的裝置驅動只需要實現這些處理函式即可,從而簡化的裝置驅動的開發。 

我們可以在uclass.h中找到uclass的定義如下:

從裝置的角度來看,同一類的裝置(比如RTC)擁有相同的uclass ID,並全部掛在該uclass下;從驅動的角度來看,uclass driver實現通用的處理邏輯。

3. uclass_driver

同樣我們可以在uclass.h中找到struct uclass_driver的定義,這個結構體定義了一組我們訪問uclass的介面:

post_bind  // 在udevice被繫結到該uclass之後呼叫

pre_unbind // 在udevice被解綁出該uclass之前呼叫

pre_probe  // 在該uclass的一個udevice進行probe之前呼叫

post_probe // 在該uclass的一個udevice進行probe之後呼叫

pre_remove // 在該uclass的一個udevice進行remove之前呼叫

child_post_bind // 在該uclass一個udevice的一個子裝置被繫結到該udevice之後呼叫

child_pre_probe // 在該uclass的一個udevice的一個子裝置進行probe之前呼叫

init     // 安裝該uclass的時候呼叫

destroy  // 銷燬該uclass的時候呼叫

4. udevice

我們可以在device.h中找到定義:

const struct driver *driver; // 該udevice對應的driver

const char *name; // 裝置名

void *platdata; // 該udevice的平臺資料

void *parent_platdata; // 提供給父裝置使用的平臺資料

void *uclass_platdata; // 提供給所屬uclass使用的平臺資料

int of_offset; // 該udevice的dtb節點偏移,代表了dtb裡面的這個節點node

ulong driver_data; // 驅動資料

struct udevice *parent; // 父裝置

void *priv; // 私有資料的指標

struct uclass *uclass; // 所屬uclass

void *uclass_priv; // 提供給所屬uclass使用的私有資料指標

void *parent_priv; // 提供給其父裝置使用的私有資料指標

struct list_head uclass_node; // 用於連線到其所屬uclass的連結串列上

struct list_head child_head; // 連結串列頭,連線其子裝置

struct list_head sibling_node; // 用於連線到其父裝置的連結串列上

uint32_t flags; // 標識

5. driver

同樣我們可以在device.h中找到定義:

char *name;    // 驅動名

enum uclass_id id;  // 對應的uclass id

const struct udevice_id *of_match; //用於和device tree裡面的裝置節點匹配

 (*bind)   // 用於繫結目標裝置到該driver中

 (*probe)   // 用於probe目標裝置,啟用

 (*remove) // 用於remove目標裝置。禁用

 (*unbind) // 用於解綁目標裝置到該driver中

 (*ofdata_to_platdata) // probe之前,解udevice的dts節點,轉化成udevice的資料

 (*child_post_bind) // 如果目標裝置的一個子裝置被繫結之後,呼叫

 (*child_pre_probe) // 在目標裝置的一個子裝置被probe之前,呼叫

 (*child_post_remove) // 在目標裝置的一個子裝置被remove之後,呼叫

int priv_auto_alloc_size; //需要分配多少空間作為其udevice的私有資料

int platdata_auto_alloc_size; //需要分配多少空間作為其udevice的平臺資料

int per_child_auto_alloc_size;  //每個子裝置需要多少私有資料

int per_child_platdata_auto_alloc_size; //每個子裝置需要多少平臺資料

const void *ops; // 操作集,提供給uclass用,格式具體由uclass決定

uint32_t flags; // 一些標誌位

5 串列埠驅動程式及DTS

5.1 驅動程式

U-Boot中的驅動程式目錄如下(drivers):

其中serial中存放的是串列埠相關的驅動原始碼。

其中ns16550.c為串列埠驅動程式,採用的是DM的框架,我們可以在原始碼中檢視到相關的定義:

這裡的U_BOOT_DRIVER其實是一個巨集定義,這段程式碼其實就是定義一個DM driver,見上節課DM中driver的結構。udevice最終會由一組API來訪問這個driver。

5.2 裝置樹

目錄在:arch/arm/dts中:

在rk3308-u-boot.dtsi中,可以看到一個stdout指向uart2,其實就是把串列埠2作為標準輸出:

需要注意的是,serial驅動在載入的時候需要依賴clk驅動,如果在這個時候clk驅動還沒有正常載入,需要在對應uart的dts節點中加入clock-frequency屬性,同樣在該dts檔案中可以找到uart2描述:

status可以是okay也可以是disable,表示使能和關閉。

最後我們在u-boot/configs/evb-rk3308_defconfig檔案中配置好相關串列埠做控制檯以及對應的波特率就可以了。

 

==================================================================================================================================

Uboot串列埠除錯就是這樣Easy!!

如果覺得對您有幫助並想進一步深入學習交流可以掃描以下微信二維碼或加入QQ群:928840648

歡迎共同學習成長,有一群愛學習的小夥伴一起勉勵!!一起加油!!也可點選

 

筆者基於嵌入式系統框架內容如下整理編輯:

 

相關文章