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设计