PX4/Pixhawk 教程 - uavcan v1 - libcanard介绍

介绍

Libcanard 是一个uavcan/can协议的c语言实现,一般适用于高可靠性的实时嵌入式系统。它具有以下特点:

  1. 最少需要32k ROM和4.8k RAM。
  2. 满足MISRA C规则,100%覆盖测试
  3. 兼容8-64位的处理器
  4. 主要包含canard.c,canard.h,canard_dsdl.c,canard_dsdl.h四个文件。前面两个主要是协议的实现,后面两个用于DSDL对象的序列号和反序列化。
  5. uavcan需要确定性的固定时间的有界碎片的动态内存分配;推荐使用O1Hheap
  6. 针对多个平台的底层驱动:
    https://github.com/UAVCAN/platform_specific_components
  7. MISRA C 编译报告
    https://forum.uavcan.org.
  8. 这个库不是线程安全的,如果使用并行处理器,注意保证程序的同步性。
  9. 支持29bit包头的拓展can包,不支持PRT包、11bit的传统包

架构

UAVCAN协议栈主要包含两层,传输层(TRANSPORT)和表示层(PRESENTATION)。传输层用于实现不同物理层传输的统一调度形式,libcanard只支持can协议的物理层。表示层用于管理DSDL语言描述的数据包。

  • 传输层: 主要在canard.c,canard.h中。是这个库的核心。
  • 表示层:可选的DSDL支持拓展库。主要在canard_dsdl.c,canard_dsdl.h中。

这里提供了Nunavut (https://github.com/UAVCAN/nunavut)工具,用于将DSDL描述的语言转换位C语言。

传输层

传输层主要包含TX通道和RX通道,着两个通道是互相独立的,但是依赖同一个内存管理工具。TX通道存储准备向外输出的CAN帧(有优先级序列),RX通道存储收到的数据包和重组状态机(这个我还没弄懂)。传输层的移植注意保证分配的内存空间足够存放数据。

TX通道主要通过3个API来管理。当需要启动传输的时候,使用canardTxPush().这个函数会把要传输的数据分解成CAN帧,并按优先级存储到发送队列。接着,在一个专门的发射任务中,canardTxPeek()会被用来逐个地获取CAN帧,并调用物理发送函数进行数据包发送。最后,canardTxPop()被用来弹出已经发送的CAN帧,注意需要释放弹出CAN帧的空间。

类似的,RX通道也主要通过3个API来管理,canardRxAccept()用于接受一个收到的CAN帧,并把它组合到合适的传输状态机序列。canardRxSubscribe()canardRxUnsubscribe()用于管理接受哪些uavcan数据包。(默认不接受任何包)注意在订阅时,需要准备充足的内存空间,如果接受的数据超过设定的最大size,会引起OOM错误。

宏定义

// 错误类型,注意实际需要取反,是负数,
#define CANARD_ERROR_INVALID_ARGUMENT 2
#define CANARD_ERROR_OUT_OF_MEMORY 3

// 目前的can主要支持8和64字节两种模式
#define CANARD_MTU_CAN_CLASSIC 8U
#define CANARD_MTU_CAN_FD 64U

// 头部的参数范围
#define CANARD_SUBJECT_ID_MAX 8191U
#define CANARD_SERVICE_ID_MAX 511U
#define CANARD_NODE_ID_MAX 127U
#define CANARD_PRIORITY_MAX 7U
#define CANARD_TRANSFER_ID_BIT_LENGTH 5U
#define CANARD_TRANSFER_ID_MAX ((1U << CANARD_TRANSFER_ID_BIT_LENGTH) - 1U)

// 广播ID
#define CANARD_NODE_ID_UNSET 255U

数据类型

// 优先级
typedef enum
{
    CanardPriorityExceptional = 0,
    CanardPriorityImmediate   = 1,
    CanardPriorityFast        = 2,
    CanardPriorityHigh        = 3,
    CanardPriorityNominal     = 4,  ///< Nominal priority level should be the default.
    CanardPriorityLow         = 5,
    CanardPrioritySlow        = 6,
    CanardPriorityOptional    = 7,
} CanardPriority;

// 消息类型,第一个是发布-订阅者,第23是服务请求模式
typedef enum
{
    CanardTransferKindMessage  = 0,  ///< Multicast, from publisher to all subscribers.
    CanardTransferKindResponse = 1,  ///< Point-to-point, from server to client.
    CanardTransferKindRequest  = 2,  ///< Point-to-point, from client to server.
} CanardTransferKind;

// uavcan的数据包帧
typedef struct
{
    /// For RX frames: reception timestamp.
    /// For TX frames: transmission deadline.
    /// The time system may be arbitrary as long as the clock is monotonic (steady).
    CanardMicrosecond timestamp_usec;

    /// 29-bit extended ID. The bits above 29-th shall be zero.
    uint32_t extended_can_id;

    /// The useful data in the frame. The length value is not to be confused with DLC!
    /// If the payload is empty (payload_size = 0), the payload pointer may be NULL.
    /// For RX frames: the library does not expect the lifetime of the pointee to extend beyond the point of return
    /// from the API function. That is, the pointee can be invalidated immediately after the frame has been processed.
    /// For TX frames: the frame and the payload are allocated within the same dynamic memory fragment, so their
    /// lifetimes are identical; when the frame is freed, the payload is invalidated.
    /// A more detailed overview of the dataflow and related resource management issues is provided in the API docs.
    size_t      payload_size;
    const void* payload;
} CanardFrame;

// uavcan的操作句柄
struct CanardInstance
{
    /// User pointer that can link this instance with other objects.
    /// This field can be changed arbitrarily, the library does not access it after initialization.
    /// The default value is NULL.
    void* user_reference;

    /// The transport-layer maximum transmission unit (MTU). The value can be changed arbitrarily at any time.
    /// This setting defines the maximum number of bytes per CAN data frame in all outgoing transfers.
    /// Regardless of this setting, CAN frames with any MTU can always be accepted.
    ///
    /// Only the standard values should be used as recommended by the specification;
    /// otherwise, networking interoperability issues may arise. See recommended values CANARD_MTU_*.
    ///
    /// Valid values are any valid CAN frame data length value not smaller than 8.
    /// Invalid values are treated as the nearest valid value. The default is the maximum valid value.
    size_t mtu_bytes;

    /// The node-ID of the local node.
    /// Per the UAVCAN Specification, the node-ID should not be assigned more than once.
    /// Invalid values are treated as CANARD_NODE_ID_UNSET. The default value is CANARD_NODE_ID_UNSET.
    CanardNodeID node_id;

    /// Dynamic memory management callbacks. See their type documentation for details.
    /// They SHALL be valid function pointers at all times.
    /// The time complexity models given in the API documentation are made on the assumption that the memory management
    /// functions have constant complexity O(1).
    ///
    /// The following API functions may allocate memory:   canardRxAccept(), canardTxPush().
    /// The following API functions may deallocate memory: canardRxAccept(), canardRxSubscribe(), canardRxUnsubscribe().
    /// The exact memory requirement and usage model is specified for each function in its documentation.
    CanardMemoryAllocate memory_allocate;
    CanardMemoryFree     memory_free;

    /// Read-only DO NOT MODIFY THIS
    CanardRxSubscription* rx_subscriptions[CANARD_NUM_TRANSFER_KINDS];

    /// This field is for internal use only. Do not access from the application.
    struct CanardInternalTxQueueItem* _tx_queue;
};

函数介绍

  1. 初始化,会返回操作句柄
CanardInstance canardInit(const CanardMemoryAllocate memory_allocate, const CanardMemoryFree memory_free);
  1. 发送函数,把DSDL数据包帧序列化成CAN帧,并按优先级存储到缓冲区。返回值位,序列化后的CAN帧的数量,失败为负数。
int32_t canardTxPush(CanardInstance* const ins, const CanardTransfer* const transfer);
  1. 获取发送帧函数,获取发送缓冲区当前优先级最高的CAN帧,当缓冲区为空时返回NULL,否者返回一个CAN帧
const CanardFrame* canardTxPeek(const CanardInstance* const ins);
  1. 弹出发送帧函数,发送完的帧需要从发送队列弹出。注意,这个函数不会自动清理弹出的帧,需要手动清理。
void canardTxPop(CanardInstance* const ins);
  1. 接受函数,注意原来的接受函数即将被抛弃。
int8_t canardRxAccept2(CanardInstance* const        ins,
                       const CanardFrame* const     frame,
                       const uint8_t                redundant_transport_index,
                       CanardTransfer* const        out_transfer,
                       CanardRxSubscription** const out_subscription);
  1. 订阅函数,返回1代表成功,0代表订阅已存在,负数代表参数错误。
int8_t canardRxSubscribe(CanardInstance* const       ins,
                         const CanardTransferKind    transfer_kind,
                         const CanardPortID          port_id,
                         const size_t                extent,
                         const CanardMicrosecond     transfer_id_timeout_usec,
                         CanardRxSubscription* const out_subscription);
  1. 取消订阅函数,返回1代表订阅存在,0代表订阅不存在。负数代表错屋。
int8_t canardRxUnsubscribe(CanardInstance* const    ins,
                           const CanardTransferKind transfer_kind,
                           const CanardPortID       port_id);

表示层

待完成

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值