在 STM32 上集成 MAVLink 协议,核心思路是生成协议库、移植到工程、实现收发接口。具体可以按下面这几步来做:

🛠️ 第一步:生成你的 MAVLink C语言库

这是最关键的一步。你需要用 MAVLink 官方工具链,根据你的需求生成专属的 C 语言头文件库。

  1. 准备 Python 环境:确保电脑安装了 Python 3.6 及以上版本。然后通过命令行安装必要的 Python 包:

    bash

    pip install future lxml pymavlink

  2. 获取 MAVLink 定义文件:从 GitHub 克隆 MAVLink 仓库,里面包含了所有协议定义。

    bash

    git clone https://github.com/mavlink/mavlink.git --recursive

    克隆时务必加上 --recursive 参数,确保子模块也一并下载。

  3. 生成 C 代码

    • 图形化方式(新手友好):进入 mavlink 目录,运行 python mavgenerate.py,会打开一个图形界面。在其中选择 XML 定义文件(如 common.xml)、输出路径、语言选“C”,然后点击 Generate。

    • 命令行方式(更灵活):也可以使用 mavgen.py 脚本,例如生成 MAVLink 2.0 的代码:

      bash

      python -m pymavlink.tools.mavgen --lang=C --wire-protocol=2.0 --output=generated/mavlink ./message_definitions/v1.0/common.xml

    版本选择建议:如果只是和 PX4、QGroundControl 等主流系统通信,建议直接用 MAVLink 2.0,它支持更丰富的消息和更高的安全性,且向下兼容。

📁 第二步:移植到 STM32 工程

生成的文件都是 .h 头文件,将它们复制到你的 STM32 工程目录下(如 Drivers/MAVLINK)。

注意一个关键的编译器适配问题
在 Keil MDK 等编译器中,默认可能不支持匿名结构体和特定的字节对齐方式。你通常需要修改 mavlink_types.h 文件,将 MAVPACKED 宏的定义替换为你的编译器能识别的指令。例如,在 Keil 中可能需要这样定义:

c

#define MAVPACKED( __Declaration__ ) __Declaration__ __attribute__((packed))

💬 第三步:实现消息发送与接收

这是让 MAVLink 跑起来的核心逻辑,主要依赖于 UART 串口。

发送消息(以心跳包为例)
  1. 初始化:定义系统 ID 和组件 ID,通常系统 ID 设为 1,组件 ID 可用 MAV_COMP_ID_PERIPHERAL

  2. 打包与发送:在需要发送的位置(如定时器中断服务函数里,每秒一次),填充消息结构体,调用 mavlink_msg_xxx_pack() 打包,再通过你的 UART 发送函数发送出去。

c

// 示例:发送心跳包 (需要在工程中包含 mavlink.h)
#include "mavlink.h"

// 在系统初始化时设置
mavlink_system_t mavlink_system = {
    .sysid = 1,    // 系统ID
    .compid = 190  // 组件ID
};

void send_heartbeat(void)
{
    mavlink_message_t msg;
    uint8_t buf[MAVLINK_MAX_PACKET_LEN];

    // 1. 打包心跳消息
    mavlink_msg_heartbeat_pack(mavlink_system.sysid, mavlink_system.compid, &msg,
                               MAV_TYPE_GCS,      // 类型,如地面站
                               MAV_AUTOPILOT_INVALID, // 自驾仪类型
                               0,                 // 模式
                               0,                 // 自定义模式
                               MAV_STATE_STANDBY); // 系统状态

    // 2. 将消息编码为字节流
    uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);

    // 3. 通过你的串口函数发送出去 (例如 HAL_UART_Transmit)
    HAL_UART_Transmit(&huart1, buf, len, HAL_MAX_DELAY);
}
接收与解析消息
  1. 逐字节处理:MAVLink 官方推荐在 UART 接收中断(或 DMA 接收回调)中,将收到的每一个字节喂给状态机。

  2. 调用解析函数:使用 mavlink_parse_char() 函数处理每个字节,如果一帧完整的消息被解析出来,函数会返回成功,并将消息存入 mavlink_message_t 结构体。

c

// 在串口接收中断回调函数中
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART1) {
        uint8_t received_byte;
        HAL_UART_Receive_IT(&huart1, &received_byte, 1); // 重新开启中断接收

        mavlink_message_t msg;
        mavlink_status_t status;
        // 将收到的字节送入 MAVLink 解析器
        if (mavlink_parse_char(MAVLINK_COMM_0, received_byte, &msg, &status) == 1) {
            // 解析成功,根据 msg.msgid 处理不同的消息
            switch (msg.msgid) {
                case MAVLINK_MSG_ID_HEARTBEAT:
                    // 处理心跳包
                    break;
                // 处理其他消息...
            }
        }
    }
}

💡 一些优化建议

  • 代码裁剪:STM32F103 等资源有限的 MCU,Flash 和 RAM 可能比较紧张。可以只保留工程实际用到的消息类型,剔除其他无关定义,能有效减小体积。

  • 配合 DMA 使用:对于收发频率高、数据量大的场景,配合 UART 的 DMA 功能可以极大减轻 CPU 负担,提高通信效率。

你准备用哪款具体的 STM32 型号?如果对代码裁剪或者 DMA 配置有疑问,可以告诉我具体型号,我再帮你细化步骤。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐