本文探討了在嵌入式開發中設計嵌入式軟件架構的第四步——接口和組件設計。
在上一篇文章中,我們探討了如何將一個系統分解成域和任務,我們已經確定了該系統的數據資產。你可能還記得,我們創建的上一張圖將我們的應用程序分為安全和非安全應用程序域、硬件獨立和依賴域(由抽象層分隔),最后分為任務。我們的系統分解如圖1所示。
由于blog格式的空間和時間限制,我們建議在描述如何讓任務進行交互的架構過程中增加額外的步驟。我們想探索如何完成我們的一項任務,并設計我們的界面和組件。讓我們看看如何為電機任務定義組件及其接口。
步驟4–接口和組件設計
組件和界面設計是密切相關的活動。事實上,如果我們查看組件的標準定義,我們會發現以下描述:
軟件組件是一個組合單元,具有合同規定的接口和明確的上下文依賴關系。
我們甚至不能在不討論接口的情況下定義組件!接口是軟件開發人員用來與組件交互的工具。因此,我們開始將我們的運動任務分解為組件是有意義的,然后我們可以為每個組件定義接口。
定義軟件組件
嵌入式開發人員可以使用許多不同的方法和技術來識別將堆疊在一起以執行任務提供的期望行為的組件。我對幾乎任何任務的偏好都是將系統分解成一個分層的軟件圖,從底層驅動程序開始,一直到應用程序代碼。圖2顯示了一個示例,顯示了電機任務的這種圖。
這些組件的用途可能是不言自明的,但為了以防萬一,讓我們定義一下每個組件的作用:
pwm_drv — 微控制器上用于脈寬調制的硬件外設驅動器。
Abstraction layer — 打破電機驅動器和硬件之間的依賴關系。這允許我們使用PWM驅動器來驅動電機,或者用外部集成電路的驅動器來代替pwm_drv。
motor_drv — 設計用于控制電機的驅動器。它沒有硬件依賴性。它唯一的依賴是抽象層。
motor_sm — 跟蹤電機當前狀態和所需狀態的狀態機。
motor_app — 特定應用支持功能。這些功能可能包括收集遙測數據、檢查故障等。
motor_task — 保存實際電機任務代碼的組件。該元件依賴于其他電機元件。電機任務可能包括RTOS應用程序交互,如信號量、隊列、互斥和事件邏輯。
總之,這些組件具有接收命令的所有必要行為,該命令改變控制電機的狀態機,然后啟動電機。這種分解很酷的一點是,如果電機控制鏈的各個方面發生變化,比如新的驅動芯片、新的應用程序需求等,有了適當的接口,嵌入式開發人員只需要進行最小的更改。
定義接口
對于motor任務,我們需要定義兩類接口。首先,有一個任務接口,它接收來自其他應用程序組件的命令,告訴它電機應該做什么,比如MOTOR_ON或MOTOR_OFF。其次,每個組件都有接口。
電機任務接口設計
在定義與電機任務通信的接口時,回顧圖1中所示的數據資產是很有幫助的。我們可以看到控制器任務與電機任務交互。為了讓控制器任務告訴電機任務電機應該做什么,需要幾條信息:電機狀態、電機方向、電機轉速。
如果我們希望系統可伸縮以管理多個電機,我們甚至可以包括一個電機ID。例如,在有些應用中,狀態機通過消息控制電機,但用戶可以用消息覆蓋狀態。在這些情況下,我們甚至可能希望包含一個任務ID,以便電機任務知道哪個任務正在請求電機控制。
從數據接口的角度來看,嵌入式開發人員可以為數據定義一個結構,該結構將用于控制和電機任務的接口。用C語言編寫如下代碼:
MotorMessage_t結構定義了命令電機任務執行實際工作所需的數據接口。我們如何獲得機動任務的信息取決于項目的需求。例如,使用消息隊列、數據緩沖區或其他機制。然而,這些細節是由程序員決定的,而不是軟件架構師。
定義組件接口
有幾種不同的方法來為我們的組件設計界面。首先,我們可以做一個便簽,列出我們認為需要的功能。接下來,我們可以使用一些UML圖表工具并利用類圖,即使我們沒有編寫面向對象的代碼。最后,我們可以直接進入代碼,開始編寫測試,迫使我們為組件的行為開發接口。
類圖很棒,因為它們允許嵌入式開發人員指定屬性、操作和類之間的關系(模塊和組件也可以)。有幾件事我們應該注意。首先,電機任務使用電機應用程序、電機狀態機和電機驅動器。這里沒有繼承關系。我們可以看到電機驅動器有一個電機接口。motor應用程序使用MotorError_t枚舉,但除此之外,組件是解耦的,不進行交互。
接下來,我們定義了我們認為的操作是什么,以及組件需要的功能或方法。例如,我們可以看到電機驅動器有兩個功能:電機初始化和電機命令。電機命令采用電機消息類型和所有信息來命令電機,這是通過電機接口(抽象層)完成的。
當我們查看類圖時,我們可以看到電機任務與底層組件進行交互,并驅動電機的最終行為。首先,信息通過MotorQ進入電機任務;然后,電機任務運行電機狀態機。最后,電機任務調用電機應用程序查找錯誤,并使用狀態結果通過電機驅動器命令電機。從一個可能并不明顯的類圖中可以得出很多東西。那么,我們的架構是否遺漏了什么?
我們的設計缺少了什么?
我們可能有足夠的資源來開始實現電機應用程序代碼;然而,更多的圖表和時間可以極大地提高清晰度。例如,嵌入式開發人員可以考慮額外的UML圖,比如序列圖,來顯示控制任務與電機任務交互的時序要求。或是創建一個序列圖,以便開發人員了解電機任務如何與其他組件交互。比如電機任務是應該在任務結束的時候命令電機,還是先做,把抖動降到最低?如果出現錯誤,處理的邏輯是什么?電機應該保持運轉還是停止運轉?
你通常會發現,你至少需要三到四個UML圖來完整地指定一項任務,以便開發人員可以對其進行編碼。我們只看了幾個,這意味著還有更多的事情要做。我們可以開始開發測試,并使用測試驅動的開發來進一步發展我們的界面和對系統的理解。這可能是目前的發展方向。但是,我們知道的足夠多,隨著我們實現細節,問題將會出現,這將推動對初始架構的更改。
軟件架構設計第4步結論
正如本文所見,我們可以利用UML圖和組件堆疊圖來識別組件并定義它們的接口。此外,類圖可以成為設計師進行界面設計的優秀工具。嵌入式開發人員必須記住,雖然我們遵循一個簡單的五步過程來開發我們的架構,但這些步驟只是一個指南,而不是全部。作為架構師,我們經常需要更深入。