ArduPilot開源代碼之AP_MSP
- 1. 源由
- 2. Library設計
- 2.1 啟動代碼
- 2.2 支持特性
- 2.3 MSP DisplayPort v.s. DJI FPV OSD
- 3. 重要例程
- 3.1 AP_MSP::init
- 3.2 AP_MSP::loop
- 3.3 AP_MSP::init_backend
- 4. 實例理解
- 5. 總結
- 6. 參考資料
1. 源由
AP_MSP
是處理MSP協議格式的報文數據應用類。
關于MSP協議格式,我們之前已經做過比較詳細討論,有興趣具體了解,請查閱相關資料:
- BetaFlight模塊設計之三十二:MSP協議模塊分析
- iNavFlight之MSP DJI協議分析
- iNavFlight之MSP DJI協議天空端請求報文
- iNavFlight之MSP DJI協議飛控端請求應答
- iNavFlight之MSP v2 Sensor報文格式
- iNavFlight之RC遙控MSP協議
2. Library設計
2.1 啟動代碼
AP_Vehicle::setup└──> AP_MSP::init└──> hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_MSP::loop, void), "MSP", 1024, AP_HAL::Scheduler::PRIORITY_IO, 1)
整個應用框架通過以下兩個函數實現:
- AP_MSP::init //初始化
- AP_MSP::loop //遍歷輪訓
2.2 支持特性
- 支持SerialProtocol_MSP:通用MSP協議
- 支持SerialProtocol_DJI_FPV:DJI FPV OSD協議
- 支持SerialProtocol_MSP_DisplayPort:MSP Display Port
2.3 MSP DisplayPort v.s. DJI FPV OSD
MSP DisplayPort 和 DJI FPV OSD 協議之間的主要區別在于它們的應用領域和功能。以下是兩者的詳細對比:
- MSP DisplayPort
MSP(MultiWii Serial Protocol) DisplayPort 是一種用于傳輸飛行控制器和顯示設備之間數據的協議。它主要用于發送實時飛行數據到顯示器或OSD(On-Screen Display)。這是一個開源協議,廣泛用于開源飛行控制器固件如Betaflight、iNav等。
特點:
- 開源協議:MSP 是開源的,允許開發者根據需要進行修改和擴展。
- 靈活性:可以傳輸各種類型的飛行數據,如GPS信息、電池狀態、飛行模式等。
- 兼容性:與多種飛行控制器和OSD設備兼容,廣泛應用于自制和定制無人機系統。
- DJI FPV OSD
DJI FPV OSD(On-Screen Display) 是由 DJI 提供的專有協議,專門用于其 FPV(First Person View)系統。這一協議用于在飛行時將實時飛行數據和視頻傳輸到FPV眼鏡或顯示器上。
特點:
- 專有協議:DJI FPV OSD 是專有協議,專門為 DJI 的 FPV 產品設計。
- 集成性強:與 DJI 生態系統高度集成,提供無縫的用戶體驗。
- 功能豐富:提供高質量的視頻傳輸和實時飛行數據,通常包括攝像頭切換、飛行模式、GPS信息、電池狀態等。
- 主要差異
-
開放性:
- MSP DisplayPort 是開源協議,允許廣泛的社區貢獻和定制。
- DJI FPV OSD 是閉源的,僅限于 DJI 設備使用。
-
應用范圍:
- MSP DisplayPort 廣泛應用于多種飛行控制器和顯示設備。
- DJI FPV OSD 專用于 DJI 的 FPV 系統。
-
兼容性:
- MSP DisplayPort 兼容多種設備和系統。
- DJI FPV OSD 僅兼容 DJI 的產品。
-
功能與擴展性:
- MSP DisplayPort 提供了靈活的飛行數據傳輸,可以根據需要擴展。
- DJI FPV OSD 集成了高質量的視頻傳輸和各種飛行數據,但其擴展性受到限制,因為它是閉源的。
總體來說,MSP DisplayPort 更適合于那些需要靈活性和可定制性的開源無人機項目,而 DJI FPV OSD 則為使用 DJI 設備的用戶提供了更集成和便捷的解決方案。
3. 重要例程
3.1 AP_MSP::init
AP_MSP::init
|
├── 獲取串行管理器的引用 (const AP_SerialManager &serial_manager = AP::serialmanager())
|
├── 初始化 UART 指針 (AP_HAL::UARTDriver *uart = nullptr)
|
├── 初始化使用 MSP 線程的后端計數 (uint8_t backends_using_msp_thread = 0)
|
├── 定義支持的 MSP 協議數組 (static const AP_SerialManager::SerialProtocol msp_protocols[])
| ├── AP_SerialManager::SerialProtocol_DJI_FPV
| ├── AP_SerialManager::SerialProtocol_MSP
| └── #if HAL_WITH_MSP_DISPLAYPORT
| └── AP_SerialManager::SerialProtocol_MSP_DisplayPort
| #endif
|
├── 遍歷 MSP 協議數組 (for (const auto msp_protocol: msp_protocols))
| ├── 遍歷協議實例 (for (uint8_t protocol_instance=0; protocol_instance<MSP_MAX_INSTANCES-_msp_status.backend_count; protocol_instance++))
| ├── 找到對應的串口 (uart = serial_manager.find_serial(msp_protocol, protocol_instance))
| |
| ├── 如果找到 UART 串口且初始化后端失敗,則跳出當前循環 (if (uart != nullptr))
| | └── if (!init_backend(_msp_status.backend_count, uart, msp_protocol)) { break; }
| |
| ├── 如果后端使用 MSP 線程,增加使用 MSP 線程的后端計數 (if (_backends[_msp_status.backend_count]->use_msp_thread()))
| | └── backends_using_msp_thread++
| |
| └── 增加后端計數 (_msp_status.backend_count++)
|
└── 如果至少找到一個使用 MSP 線程的后端,啟動協議處理線程 (if (backends_using_msp_thread > 0))└── if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_MSP::loop, void), "MSP", 1024, AP_HAL::Scheduler::PRIORITY_IO, 1)) { return; }
3.2 AP_MSP::loop
AP_MSP::loop()
│
├── 初始化 UART (for loop)
│ ├── _backends[i] != nullptr && _backends[i]->use_msp_thread()
│ └── _backends[i]->init_uart()
│
├── 無限循環 while (true)├── 延遲 hal.scheduler->delay(10)├── 獲取當前時間 const uint32_t now = AP_HAL::millis()├── 閃爍邏輯│ ├── (uint32_t(now * 0.00143) & 0x01) != _msp_status.flashing_on│ └── (uint32_t(now * 0.0005) & 0x01) != _msp_status.slow_flashing_on├── 檢測飛行模式變化│ ├── AP::notify().flags.flight_mode != _msp_status.last_flight_mode│ └── now - _msp_status.last_flight_mode_change_ms > OSD_FLIGHT_MODE_FOCUS_TIME├── 屏幕變化檢測 (if OSD_ENABLED)│ └── osd != nullptr│ └── _msp_status.current_screen != screen || !_msp_status.osd_initialized│ └── update_osd_item_settings()└── 處理后臺數據 (for loop)├── _backends[i] != nullptr && _backends[i]->use_msp_thread()├── _backends[i]->hide_osd_items()├── _backends[i]->process_incoming_data()└── _backends[i]->process_outgoing_data()
3.3 AP_MSP::init_backend
AP_MSP::init_backend
|
|-- if (protocol == AP_SerialManager::SerialProtocol_MSP)
| |-- 創建 AP_MSP_Telem_Generic 實例
|
|-- else if (protocol == AP_SerialManager::SerialProtocol_DJI_FPV)
| |-- 創建 AP_MSP_Telem_DJI 實例
|
|-- #if HAL_WITH_MSP_DISPLAYPORT
| |-- else if (protocol == AP_SerialManager::SerialProtocol_MSP_DisplayPort)
| |-- 創建 AP_MSP_Telem_DisplayPort 實例
|
|-- else
| |-- 返回 false
|
|-- if (_backends[backend_idx] != nullptr)
| |-- 初始化后端實例
| |-- 返回 true
|
|-- 返回 false
4. 實例理解
- ArduPilot開源飛控之AP_Baro_MSP
- TBD … … //后續補充
5. 總結
其實Ardupilot代碼從框架設計的角度來看,非常透明、簡潔、當然實際應用過程也存在一些資源剪裁問題。
總的來說,目前betaflight/inav開發航模生態下,MSP協議還是非常普及的。了解這塊協議,對于兼容和集成會有很大的幫助。
6. 參考資料
【1】ArduPilot開源飛控系統之簡單介紹
【2】ArduPilot之開源代碼Task介紹
【3】ArduPilot飛控啟動&運行過程簡介
【4】ArduPilot之開源代碼Library&Sketches設計
【5】ArduPilot之開源代碼Sensor Drivers設計