第4章:源码获取与结构分析
好,咱们开始动手了。这一章我带你干两件事:第一,把MAVLink官方C库从GitHub上拽下来;第二,把它的目录结构扒开看看,搞清楚每个文件夹、每个文件是干嘛的。
说实话,我第一次接触MAVLink源码时,也被它的目录结构搞得有点懵。文件夹套文件夹,头文件满天飞。但别怕,摸清规律后你会发现,这玩意儿设计得其实挺精巧。
4.1 从GitHub获取官方C库
MAVLink官方维护了两个主要的C语言仓库:
- c_library_v2:这是MAVLink 2.0协议的C语言实现,也是我们课程的主角
- c_library_v1:MAVLink 1.0的老版本,现在基本不用了
我个人习惯直接克隆仓库到本地,这样方便后续查看提交历史和版本切换。命令很简单:
git clone https://github.com/mavlink/c_library_v2.git
如果你只是临时用用,也可以直接下载ZIP压缩包。但我建议你学会用git,因为后面裁剪时,我们需要基于某个特定版本做修改,git的分支管理会帮大忙。
master 分支的最新提交。MAVLink协议还在演进,不同版本之间可能有细微差异。咱们课程基于2024年初的版本,如果你用的更新,遇到问题可以回退看看。
4.2 目录结构解析
克隆完成后,我们来看看它的目录长什么样。用 tree -L 2 命令可以看得比较清楚:
c_library_v2/
├── ardupilotmega/
├── ASLUAV/
├── autoquad/
├── common/
├── icarous/
├── minimal/
├── python_array_test/
├── standard/
├── test/
├── uAvionix/
├── README.md
├── mavlink_helpers.h
├── mavlink_types.h
├── mavlink_conversions.h
└── checksum.h
嗯,一眼看去,大部分是文件夹,每个文件夹代表一个消息集(message set)。说白了,就是不同厂商或组织定义的MAVLink消息集合。
我来给你拆解一下:
| 目录/文件 | 说明 |
|---|---|
common/ |
核心消息集,所有飞控基本都会用到。比如心跳、姿态、GPS数据都在这里 |
minimal/ |
最小消息集,只包含HEARTBEAT、PROTOCOL_VERSION等基础消息 |
standard/ |
标准消息集,比minimal多一些通用消息 |
ardupilotmega/ |
ArduPilot专用的扩展消息集 |
ASLUAV/ |
某款固定翼无人机厂商的私有消息集 |
autoquad/ |
AutoQuad飞控的私有消息集 |
icarous/ |
NASA的ICAROUS项目消息集 |
test/ |
测试用的消息集,开发时调试用 |
python_array_test/ |
测试Python数组序列化的,嵌入式开发一般用不到 |
uAvionix/ |
某家ADS-B设备厂商的私有消息集 |
common/ 和 minimal/ 是必须的。其他厂商的私有消息集,除非你的项目需要跟特定设备通信,否则统统可以砍掉。
4.3 关键文件说明
根目录下有几个头文件,它们是整个MAVLink协议栈的骨架。我一个个说:
4.3.1 mavlink_types.h
这个文件定义了MAVLink协议的基础数据类型。比如 mavlink_message_t 结构体,它代表一条完整的MAVLink消息。还有 mavlink_status_t,用来记录解析状态。
我记得第一次看这个文件时,最让我困惑的是 mavlink_message_t 里那个 payload64 数组。为什么用64位整数数组来存负载?后来才明白,这是为了对齐和序列化效率。说白了,就是让CPU处理起来更快。
4.3.2 mavlink_helpers.h
辅助函数的大本营。这里面有:
mavlink_msg_*_pack():把结构体数据打包成MAVLink消息mavlink_msg_*_decode():从MAVLink消息中解包出结构体数据mavlink_parse_char():逐字节解析MAVLink帧mavlink_finalize_message():计算并填充校验和
这些函数是咱们移植时的核心。你想想看,飞控和地面站之间来回传数据,全靠这些函数在背后干活。
4.3.3 mavlink_conversions.h
坐标转换和数学工具函数。比如:
- 欧拉角转四元数
- 四元数转欧拉角
- 经纬度转地心地固坐标系
嗯,这里要注意,这些函数不是MAVLink协议必须的。如果你飞控里已经有自己的数学库,完全可以不用这个文件。我一般只在做地面站软件时才会用到它。
4.3.4 checksum.h
CRC16校验的实现。MAVLink 2.0使用CRC16-CCITT算法来保证数据完整性。这个文件很小,就一个函数 crc_calculate()。但别小看它,校验错了,整条消息就废了。
我曾经在一个项目里遇到过诡异的问题:地面站偶尔收不到飞控的心跳包。查了两天才发现,是CRC计算时用了错误的初始值。从那以后,我每次移植都会先写个测试用例,专门验证CRC计算对不对。
4.4 消息集内部结构
我们随便进一个消息集目录看看,比如 common/:
common/
├── common.h
├── mavlink_msg_attitude.h
├── mavlink_msg_gps_raw_int.h
├── mavlink_msg_heartbeat.h
├── ... (几百个消息头文件)
└── version.h
每个 mavlink_msg_*.h 文件对应一条MAVLink消息。比如 mavlink_msg_heartbeat.h 里定义了心跳消息的打包、解包函数。
common.h 是总入口,它把所有消息头文件都include进来。你移植时,如果只需要部分消息,可以自己写一个精简版的 common.h,只包含你需要的消息。
version.h 记录了该消息集的版本号。不同版本的MAVLink协议,消息ID和字段定义可能不同。所以,保持版本一致很重要。
4.5 知识体系总览
说了这么多,我画张图帮你理清思路:
4.6 移植前的准备工作
在开始移植之前,我建议你做三件事:
- 确定你需要的消息集:如果你的飞控只跟QGroundControl通信,那
common/就够了。如果还要跟ArduPilot交互,加上ardupilotmega/。 - 检查编译器支持:MAVLink C库用了C99标准的一些特性,比如
stdint.h里的定长整数类型。如果你的MCU编译器比较老,可能需要做适配。 - 准备测试环境:我习惯先在PC上用gcc编译一遍,确认没问题了再往嵌入式平台移植。这样能快速排除语法错误。
好了,源码已经到手,目录结构也摸清了。下一章我们就要开始动手裁剪了——把那些用不上的消息集和函数统统砍掉,只留下我们需要的部分。嗯,这才是嵌入式开发的精髓:够用就好,绝不浪费一个字节。
公众号:蓝海资料掘金营,微信deep3321