STM32移植Canard实现UVCAN

STM32移植Canard实现UVCAN协议源码技术分析

在现代嵌入式系统中,随着无人机、机器人和工业自动化设备的复杂度不断提升,传统的CAN通信方式逐渐暴露出开发效率低、扩展性差、跨平台兼容困难等问题。尽管经典CAN以其高可靠性和实时性长期占据主导地位,但面对“即插即用”、“自动识别节点”、“统一数据模型”等新需求,开发者迫切需要一种更智能、更结构化的通信方案。

正是在这样的背景下,UVCAN(原UAVCAN)应运而生——它不是对CAN物理层的替代,而是对其上层协议的一次深度重构。通过引入标准化的数据描述语言(DSDL)、服务调用机制与动态节点管理能力,UVCAN让原本“哑巴式”的CAN总线具备了自我描述和语义理解的能力。而 Canard 作为其官方轻量级C库实现,成为将这一先进协议落地到资源受限MCU的关键桥梁。

本文聚焦于如何在STM32平台上成功移植Canard并运行UVCAN协议,不局限于理论讲解,而是深入剖析从内存管理、CAN帧处理到应用层消息构造的完整链路,并结合实际代码揭示工程实践中那些容易被忽视的设计细节。


Canard:不只是一个协议栈

很多人初识Canard时会误以为它是一个完整的网络协议栈,但实际上它的定位非常精准: 一个无操作系统的、可嵌入的UVCAN协议处理引擎 。它不负责初始化硬件,也不参与任务调度,只专注于核心逻辑——把应用数据打包成符合UVCAN规范的CAN帧,或者反过来,将收到的CAN帧还原为结构化信息。

这种“中间件”式的架构设计带来了极高的灵活性。无论你使用裸机循环还是FreeRTOS,只要能提供定时滴答和CAN收发接口,就能驱动Canard工作。更重要的是,Canard采用静态内存池管理,完全避免了 malloc/free 带来的不确定性,这对于实时性要求严苛的应用至关重要。

我们来看一段典型的初始化代码:

static uint8_t canard_memory_pool[1024 * 4];
static CanardPoolAllocator allocator;
CanardInstance canard_instance;

void canard_setup(void) {
    allocator.memory_blocks = canard_memory_pool;
    allocator.block_size = CANARD_POOL_BLOCK_SIZE;
    allocator.capacity = sizeof(canard_memory_pool) / CANARD_POOL_BLOCK_SIZE;

    canardInit(&canard_instance,
               &allocator,
               127,
               onTransferReception,
               shouldAcceptTransfer,
               NULL);
}

这里有几个关键点值得深挖:

  • 内存池大小的选择 :4KB看似充裕,但在频繁传输大块数据(如固件更新)时仍可能耗尽。建议根据最大并发传输数估算所需空间。每个传输会话至少占用一个内存块,若分段过多或并发请求多,需相应扩大池子。
  • 节点ID设置为127 :这是临时做法。真正的产品应支持PnP(Plug-and-Play),即通过 uavcan.protocol.GetNodeInfo 服务动态分配ID。初始设为0表示未配置状态更为合理。
  • 回调函数的作用域 : onTransferReception 是整个协议交互的核心入口,所有接收到的有效消息都会在此汇聚;而 shouldAcceptTransfer 则用于过滤非法或无关的传输,提前丢弃可减轻CPU负担。

STM32上的CAN集成:不只是HAL封装

STM32系列MCU普遍内置bxCAN控制器,功能强大且稳定。然而,在对接Canard时,很多开发者只是简单地把HAL库的接收中断直接喂给 canardHandleRxFrame() ,却忽略了几个潜在陷阱。

首先看中断处理函数:

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    CanardFrame frame;
    CAN_RxHeaderTypeDef rx_header;

    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, frame.data) != HAL_OK) {
        return;
    }

    frame.extended_can_id = rx_header.StdId;
    frame.payload_size = rx_header.DLC;
    canardHandleRxFrame(&canard_instance, &frame, HAL_GetTick());
}

这段代码表面上没问题,但存在两个隐患:

  1. 时间戳精度不足 : HAL_GetTick() 返回毫秒级时间,而UVCAN内部某些超时判断(如传输重传)依赖微秒级精度。如果系统滴答为1ms,则可能导致误判超时,进而引发不必要的重传或会话中断。解决方案是启用定时器配合 DWT CYCCNT 寄存器提供更高精度时间基准。

  2. 中断上下文执行开销过大 : canardHandleRxFrame() 内部包含CRC校验、会话查找、内存分配等操作,耗时较长。若CAN负载较高,长时间占用中断可能导致其他外设响应延迟。理想做法是将接收到的帧放入环形缓冲区,由主循环或低优先级任务异步处理。

再来看发送环节:

int16_t canard_send_frame(const CanardFrame* frame, uint64_t timeout_usec) {
    CAN_TxHeaderTypeDef tx_header;
    uint32_t tx_mailbox;

    tx_header.StdId = frame->extended_can_id;
    tx_header.RTR = CAN_RTR_DATA;
    tx_header.IDE = CAN_ID_STD;
    tx_header.DLC = frame->payload_size;
    tx_header.TransmitGlobalTime = DISABLE;

    if (HAL_CAN_AddTxMessage(&hcan, &tx_header, (uint8_t*)frame->data, &tx_mailbox) != HAL_OK) {
        return -1;
    }

    while (HAL_CAN_IsTxMessagePending(&hcan, tx_mailbox)); // 等待发送完成

    return 0;
}

这个实现采用了 阻塞等待发送完成 的方式,虽然保证了帧顺序,但在高频率发送场景下会导致主线程卡顿。更优策略是采用非阻塞模式,利用TX中断通知机制,在 HAL_CAN_TxMailbox0CompleteCallback 中触发 canardSpin() 继续推送后续帧。

此外,关于CAN ID编码规则也需要特别注意:

CAN ID = (Message Type ID << 8) | Source Node ID

这决定了你在配置过滤器时不能只关注目标节点ID,还要覆盖所有可能用到的Port ID范围。例如,如果你要监听 NodeStatus (Port ID=0)和自定义传感器数据(Port ID=100),那么过滤器必须允许 StdId & 0xFF00 匹配这两个高位值。推荐使用 32位宽掩码模式 ,设置Filter Mask为 0xFE00 ,即可一次性捕获所有UVCAN标准帧。


数据建模的艺术:从位字段到语义表达

UVCAN最强大的地方在于其基于DSDL(Data Structure Description Language)的数据定义体系。每一个消息类型都有唯一的ID和清晰的字段语义,比如 uavcan.protocol.NodeStatus 不仅规定了5个字段的长度和顺序,还明确定义了每个枚举值的意义。

以下是一个典型的 NodeStatus 广播示例:

void broadcast_node_status(void) {
    static uint8_t transfer_id = 0;

    CanardTransfer transfer = {
        .timestamp_usec = 0,
        .priority = CanardPriorityNominal,
        .transfer_kind = CanardTransferKindBroadcast,
        .port_id = UAVCAN_PROTOCOL_NODESTATUS_PORT_ID,
        .remote_node_id = 0,
        .transfer_id = transfer_id++,
        .payload_size = 0,
        .payload = NULL
    };

    uint8_t payload[UAVCAN_PROTOCOL_NODESTATUS_MAX_SIZE] = {0};
    size_t offset = 0;

    canardEncodeScalar(payload, 32, &offset, &(uint32_t){1234});   // uptime_sec
    canardEncodeScalar(payload, 2,  &offset, &(uint8_t){0});       // health
    canardEncodeScalar(payload, 5,  &offset, &(uint8_t){5});       // mode
    canardEncodeScalar(payload, 7,  &offset, &(uint8_t){0});       // sub_mode
    canardEncodeScalar(payload, 48, &offset, &(uint64_t){123});    // vendor_specific

    transfer.payload_size = (offset + 7) / 8;
    transfer.payload = payload;

    canardBroadcast(&canard_instance, &transfer);
}

这段代码展示了UVCAN“紧凑编码”的精髓:按位写入,最小化带宽占用。但同时也带来调试难题——原始CAN报文无法直观解读。这时就需要借助开源工具链辅助开发。

比如使用 pyuavcan 可以通过Python脚本监听总线:

import pyuavcan
node = pyuavcan.make_node('socket://can0')
node.start()

status_sub = node.make_subscriber('uavcan.protocol.NodeStatus')
async def print_status():
    async for msg, info in status_sub:
        print(f"Node {info.transfer.source_node_id}: uptime={msg.uptime_sec}s, mode={msg.mode}")

import asyncio
asyncio.run(print_status())

一旦看到屏幕上清晰输出“Node 42: uptime=1234s, mode=Operational”,你就知道这套系统真的“活”起来了。


实际部署中的权衡与优化

在一个真实项目中,仅仅让协议跑通远远不够,还需考虑稳定性、可维护性和未来扩展性。

内存使用优化

Canard默认每块内存块大小为 CANARD_POOL_BLOCK_SIZE (通常32字节)。对于小消息尚可,但如果经常传输大于32字节的数据(如日志、参数块),就会产生多个分段,增加会话表压力。此时可适当增大块大小,减少碎片化。

时间同步问题

UVCAN本身不强制全局时间同步,但某些应用场景(如多电机协同控制)需要精确时间戳。可以在应用层附加PTP或GNSS时间信息,或通过 uavcan.protocol.TimeSync 服务进行粗略对齐。

多节点网络的启动顺序

当多个设备同时上电时,若都尝试广播 NodeStatus ,可能因ID冲突导致通信混乱。建议加入随机延迟(jitter)机制,错峰发送初始心跳,降低总线竞争概率。

固件升级路径

UVCAN支持通过 uavcan.protocol.file.BeginFirmwareUpdate 远程更新固件,但Canard本身不处理Flash烧录逻辑。你需要在接收到该服务请求后,跳转至Bootloader或使用双Bank机制完成更新。


结语

将Canard成功移植到STM32并非终点,而是一扇通往模块化、智能化嵌入式通信的大门。你会发现,一旦节点之间不再靠“约定俗成”的私有协议对话,而是使用标准Port ID和DSDL类型进行交流,整个系统的集成难度显著下降。

更重要的是,这种设计思维的转变——从“我能发数据”到“我表达了什么意义”——正是现代嵌入式系统迈向高可靠性与快速迭代的核心驱动力。无论是构建一台自主飞行的无人机,还是打造一条柔性生产的智能产线,UVCAN+Canard的组合都在默默支撑着设备间的无缝协作。

当你下次面对一堆杂乱的CAN报文束手无策时,不妨想想:也许我们需要的不是一个更强的解析器,而是一个能让每个节点都能“说话”的协议。

全网最详细的UAVCAN/DroneCAN链路开发(官方DroneCAN消息+自定义DroneCAN消息)附DroneCAN项目源码 全网最详细的UAVCAN/DroneCAN链路移植开发(官方DroneCAN消息+自定义DroneCAN消息)附工程移植源码 阅读详情

相关推荐

STM32移植Canard实现UVCAN协议实战指南

UVCAN(UAVCAN v1.0 over CAN FD)是一种面向高可靠性嵌入式系统的现代航空级通信协议,其核心在于类型安全、服务发现与自动拓扑管理。它基于CAN FD物理层,依赖轻量级C实现Canard库完成帧封装、ID映射、多帧重组等底层功能。技术价值体现在免配置节点接入、毫秒级健康监控与无感OTA升级能力,广泛应用于无人机飞控、电机驱动器、IMU及BMS等资源受限的实时场景。本文聚焦STM32平台——特别是F4/F7/H7系列对CAN FD控制器的支持差异、HAL驱动适配陷阱、静态内存池设计与UV

weixin_29159441的博客 220

嵌入式系统和Linux的UAVCAN协议栈的c++移植参考实现。- UAVCAN / libuavcan

UAVCAN stack in C++ WARNING libuavcan v1 is not yet complete. This is a work-in-progress. Portable reference implementation of the in C++ for embedded systems, Linux, and POSIX-compliant RTOSs. UAVCAN is a lightweight protocol designed for reliable communication in aerospace and robotic applications over robust vehicular networks.

STM32平台Canard库移植与UVCAN协议完整实现工程

UVCAN协议头中,Session ID与共同构成逻辑连接的唯一标识。二者设计需兼顾安全性、可扩展性与抗重放能力。Session ID并非固定配置值,而是由节点启动时基于硬件唯一ID(如STM32 UID寄存器)与系统启动时间戳(DWT_CYCCNT)经SHA-256哈希后截取低32位生成,确保全局唯一且不可预测:// session_id_gen.c —— Session ID动态生成函数(MISRA-C compliant)// STM32H7 UID为12字节。

weixin_26805451的博客 274

stm32或gd32移植libcanard实现UAVCAN协议

一、源码下载一、源码下载。

本博客分享软硬件一体化开发实战。硬件涵盖 PCB 设计、STM32 等 MCU 底层开发;软件包含 C++、Rust,Qt/QML、PyQt、Slint、TouchGFX、LVGL 各类 GUI 框架。方便软硬件开发者查阅学习。欢迎关注! 4717

STM32移植Canard实现UVCAN协议:资源受限MCU的高实时CAN FD通信方案

UVCAN(UAVCAN v1.0 over CAN FD)是一种面向无人机、机器人和工业嵌入式设备的轻量级确定性网络协议,其核心是基于64位主题ID的发布/订阅模型与CAN FD高速传输能力。它通过帧分片、类型安全DSDL定义和节点自发现机制,在强干扰环境下保障多节点间低延迟、高可靠的数据交换。技术价值在于突破传统CAN 2.0B的ID资源瓶颈与带宽限制,同时满足微秒级时间同步与<15μs中断响应等硬实时要求。典型应用场景包括农业无人机多传感器融合、AGV激光雷达点云回传、医疗康复机器人分布式协同控制等。

weixin_29170327的博客 321

STM32移植Canard实现UVCAN协议:从传输层到心跳节点联调实战

CAN总线在嵌入式系统中的应用日益广泛,但传统私有协议难以实现设备间的开放互操作。UVCAN(即UAVCAN)通过标准化的分层模型,定义了从物理层到应用层的完整通信框架,其中传输层负责将大消息拆分为多帧CAN报文,并通过传输句柄、序号和CRC完成接收端重组。Canard是这一协议栈中流行的轻量级C实现,不依赖操作系统和动态内存分配,特别适合STM32等裸机环境。在飞控、机器人底盘和传感器网络中,利用Canard移植UVCAN可让设备在总线上快速互认。本文以STM32为例,从内存池设计、CAN过滤器配置到心跳

weixin_29181861的博客 307

【基于STM32F407实现UAVCAN】

基于STM32F4结合vscode+python实现联合编译,支持uavcan

qq_39016531的博客 2331

UVCAN协议与Canard库在STM32上的确定性通信实践

UVCAN是一种面向资源受限嵌入式系统的确定性CAN应用层协议,其核心在于不依赖硬件升级、仅通过软件重构实现微秒级抖动控制。它基于CAN物理层,但摒弃传统协议栈范式,采用自描述帧结构、内嵌Type ID与CRC-8校验,强调节点自治与实时可验证性。技术价值体现在极低内存占用(RAM<2KB)、零拷贝设计及中断上下文可控性,特别适配STM32等Cortex-M系列MCU。典型应用场景包括风电变桨控制、机器人集群协同、高精度电机驱动等对端到端延迟敏感的工业现场。本文聚焦UVCAN协议原理、Canard轻量级实现

weixin_32349093的博客 298

uvcan实现协议canard源码:为CAN通信打开新的大门

uvcan实现协议canard源码:为CAN通信打开新的大门 去发现同类优质开源项目:https://gitcode.com/ 在嵌入式系统开发中,CAN(Controller Area Network)通信协议以其高可靠性和抗干扰能力被广泛应用。uvcan实现协议canard源码,正是为了简化CAN通信的集成过程而诞生。以下是关于此项目的详细介绍。 项目介绍 uvcan实现协议canard源码提...

gitblog_06749的博客 1104

STM32移植UVCAN协议栈:canard库集成与常见坑

CAN总线是嵌入式系统中广泛应用的现场总线,但在多节点复杂应用场景下,应用层协议不统一常导致数据解析混乱。UAVCAN(UVCAN)作为面向无人机和机器人领域的高层协议,定义了标准化的CAN应用层通信机制。canard库是专为MCU设计的UAVCAN轻量级实现,提供完整的传输层支持。以STM32为平台,系统讲解UVCAN协议栈移植的完整流程,从CAN外设选型、位时序计算到内存池设计、64位时间戳实现,再到收发路径与主循环的整合。针对移植过程中常见的过滤器配置、内存泄漏、时间戳回绕等深坑进行了详尽排查分析,并

weixin_42570484的博客 183

UAVCAN轻量级协议栈canard在STM32上的移植实战与避坑指南

CAN总线是嵌入式控制系统中常见的工业总线,但在节点数量多、协议私有化场景下,CANopen等方案暴露出配置繁琐、维护成本高等问题。UAVCAN作为面向机器人和智能设备的现代总线协议,采用自动节点ID分配、发布/订阅与请求/响应模型,简化了多节点通信。canard是UAVCAN的开源轻量级C语言实现,专为STM32这类资源受限MCU设计,运行开销小,且支持动态节点管理与固件升级。它可广泛用于飞控、无人车、分布式传感网络等场景。围绕STM32平台,重点讲解canard协议栈的移植流程、内存池设计、收发适配、D

weixin_28285943的博客 183

PX4中的DroneCAN的实现库Libuavcan及基础功能示例

--此模块运行活动节点监视器,当远程节点响应 uavcan.protocol.GetNodeInfo请求时,模块会检查其固件映像是否比当前运行的节点固件版本更新。如果当前模块是主机的话,会同时再创建一个从机运行,这是做的一个冗余机制,当检测到网络中有更高优先级的主机时,当前节点会启用从机 (suppress(false)),之后与新主机进行同步。在单线程配置中,库的线程应该在 Node<>::spin() 内阻塞。具体在 PX4 中的体现,在 uavcannode 进程中的 uavcannode 类,

龙猫的博客 1532

uvcan实现协议,canard源码

uvcan实现协议,canard源码,具体移植方式参考我的博客。

px4 uavcan linux,UAVCAN Bootloader

安装UAVCAN启动程序无人机控制器局域网络(Unmanned Aerial Vehicle Controller Area Network,UAVCAN)设备通常在出厂时就预安装了启动程序。 如果你不对UAVCAN设备进行开发,请不要试图去重复本章的任何操作。概览对于STM32设备,PX4项目包含一个标准的UAVCAN启动程序。启动程序占用了flash内存的最开始8-16KB的位置,它是设备上电...

weixin_30998047的博客 360

px4中uavcan协议介绍与实操.pdf

px4中uavcan协议介绍与实操.pdf

STM32移植canard实现UVCAN协议源码

具体移植方式参考我的博客。

UAVCAN官方指引(译)(第一段)

因为最近想对飞控进行改造,用起来更简单。因此瞧上了UAVCAN这个好东西,飞控有2个CAN接口几乎都没用过,而且本人一贯风格,从官方获取Guide作为切入点开始研究,为了备忘,特用blog记录下来。 前序 本文基于官方资料翻译,UAVCAN 指引是官方的用户文档,本文档提供为新上手准备使用UAVCAN的用户提供了一个接入点。 本指引中忽略了比如Libuavcan,PyUAVCAN等实现库的细节,和其它软件产品的特性,把重心放在了抽象的问题上。如果想取得上述软件的文档资料,请访问uavcan.org 正式的协

alphasion的博客 1474

UAVCAN_NODE_FreeRTOS-master.zip_UAVCAN_dollarlix_freertos_stm32_

STM32+FreeRTOS实现UAVCAN协议

上一篇: 树莓派+Arduino构建低成本自主车
下一篇: 1、科学与工程中数学模型的计算研究
dell8
博客等级 码龄21年 47粉丝 1274原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值